From 8b7cf4b9a0a5a80b3ff076d0eb3b3b8b11344c7e Mon Sep 17 00:00:00 2001 From: Morteza Shojaei <31728528+mortezashojaei@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:23:40 +0330 Subject: [PATCH 001/223] refactor: improve contract verification classes to support deferent types of contract verifications --- .../src/deploy/verify/BaseContractVerifier.ts | 207 ++++++++++ .../sdk/src/deploy/verify/ContractVerifier.ts | 358 +++++------------- 2 files changed, 306 insertions(+), 259 deletions(-) create mode 100644 typescript/sdk/src/deploy/verify/BaseContractVerifier.ts diff --git a/typescript/sdk/src/deploy/verify/BaseContractVerifier.ts b/typescript/sdk/src/deploy/verify/BaseContractVerifier.ts new file mode 100644 index 00000000000..da0b19b3d5b --- /dev/null +++ b/typescript/sdk/src/deploy/verify/BaseContractVerifier.ts @@ -0,0 +1,207 @@ +import { Logger } from 'pino'; + +import { isZeroishAddress, rootLogger, sleep } from '@hyperlane-xyz/utils'; + +import { ExplorerFamily } from '../../metadata/chainMetadataTypes.js'; +import { MultiProvider } from '../../providers/MultiProvider.js'; +import { ChainName } from '../../types.js'; + +import { + BuildArtifact, + ContractVerificationInput, + SolidityStandardJsonInput, +} from './types.js'; +import { FamilyVerificationDelay } from './utils.js'; + +export abstract class BaseContractVerifier { + protected logger = rootLogger.child({ module: this.constructor.name }); + protected contractSourceMap: { [contractName: string]: string } = {}; + protected readonly standardInputJson: SolidityStandardJsonInput; + + constructor( + protected readonly multiProvider: MultiProvider, + buildArtifact: BuildArtifact, + ) { + this.standardInputJson = buildArtifact.input; + this.createContractSourceMapFromBuildArtifacts(); + } + + protected createContractSourceMapFromBuildArtifacts(): void { + const contractRegex = /contract\s+([A-Z][a-zA-Z0-9]*)/g; + Object.entries(this.standardInputJson.sources).forEach( + ([sourceName, { content }]) => { + const matches = content.matchAll(contractRegex); + for (const match of matches) { + const contractName = match[1]; + if (contractName) { + this.contractSourceMap[contractName] = sourceName; + } + } + }, + ); + } + + public async verifyContract( + chain: ChainName, + input: ContractVerificationInput, + logger = this.logger, + ): Promise { + const verificationLogger = logger.child({ + chain, + name: input.name, + address: input.address, + }); + + if (!this.shouldVerifyContract(chain, input, verificationLogger)) { + return; + } + + const explorerApi = this.multiProvider.tryGetExplorerApi(chain); + + await sleep( + FamilyVerificationDelay[ + explorerApi?.family as keyof typeof FamilyVerificationDelay + ] ?? 0, + ); + await this.verify(chain, input, verificationLogger); + } + + protected shouldVerifyContract( + chain: ChainName, + input: ContractVerificationInput, + verificationLogger: Logger, + ): boolean { + const metadata = this.multiProvider.tryGetChainMetadata(chain); + const rpcUrl = metadata?.rpcUrls[0].http ?? ''; + if (rpcUrl.includes('localhost') || rpcUrl.includes('127.0.0.1')) { + verificationLogger.debug('Skipping verification for local endpoints'); + return false; + } + + const explorerApi = this.multiProvider.tryGetExplorerApi(chain); + if (!explorerApi) { + verificationLogger.debug('No explorer API set, skipping'); + return false; + } + + if (!explorerApi.family) { + verificationLogger.debug(`No explorer family set, skipping`); + return false; + } + + if (explorerApi.family === ExplorerFamily.Other) { + verificationLogger.debug(`Unsupported explorer family, skipping`); + return false; + } + + if (isZeroishAddress(input.address)) return false; + if (Array.isArray(input.constructorArguments)) { + verificationLogger.debug( + 'Constructor arguments in legacy format, skipping', + ); + return false; + } + + return true; + } + + protected abstract verify( + chain: ChainName, + input: ContractVerificationInput, + verificationLogger: Logger, + ): Promise; + + protected getImplementationData( + chain: ChainName, + input: ContractVerificationInput, + verificationLogger: Logger, + ) { + const sourceName = this.contractSourceMap[input.name]; + if (!sourceName) { + const errorMessage = `Contract '${input.name}' not found in provided build artifact`; + verificationLogger.error(errorMessage); + throw new Error(`[${chain}] ${errorMessage}`); + } + + const filteredStandardInputJson = + this.filterStandardInputJsonByContractName( + input.name, + this.standardInputJson, + verificationLogger, + ); + + return this.prepareImplementationData( + sourceName, + input, + filteredStandardInputJson, + ); + } + + protected abstract prepareImplementationData( + sourceName: string, + input: ContractVerificationInput, + filteredStandardInputJson: SolidityStandardJsonInput, + ): any; + + protected filterStandardInputJsonByContractName( + contractName: string, + input: SolidityStandardJsonInput, + verificationLogger: Logger, + ): SolidityStandardJsonInput { + verificationLogger.trace( + { contractName }, + 'Filtering unused contracts from solidity standard input JSON....', + ); + const filteredSources: SolidityStandardJsonInput['sources'] = {}; + const sourceFiles: string[] = Object.keys(input.sources); + const contractFile: string = this.contractSourceMap[contractName]; + const queue: string[] = [contractFile]; + const processed = new Set(); + + while (queue.length > 0) { + const file = queue.shift()!; + if (processed.has(file)) continue; + processed.add(file); + + filteredSources[file] = input.sources[file]; + + const content = input.sources[file].content; + const importStatements = this.getAllImportStatements(content); + + importStatements.forEach((importStatement) => { + const importPath = importStatement.match(/["']([^"']+)["']/)?.[1]; + if (importPath) { + const resolvedPath = this.resolveImportPath(file, importPath); + if (sourceFiles.includes(resolvedPath)) queue.push(resolvedPath); + } + }); + } + + return { + ...input, + sources: filteredSources, + }; + } + + protected getAllImportStatements(content: string): string[] { + const importRegex = + /import\s+(?:(?:(?:"[^"]+"|'[^']+')\s*;)|(?:{[^}]+}\s+from\s+(?:"[^"]+"|'[^']+')\s*;)|(?:\s*(?:"[^"]+"|'[^']+')\s*;))/g; + return content.match(importRegex) || []; + } + + protected resolveImportPath(currentFile: string, importPath: string): string { + if (importPath.startsWith('@') || importPath.startsWith('http')) { + return importPath; + } + const currentDir = currentFile.split('/').slice(0, -1).join('/'); + const resolvedPath = importPath.split('/').reduce((acc, part) => { + if (part === '..') { + acc.pop(); + } else if (part !== '.') { + acc.push(part); + } + return acc; + }, currentDir.split('/')); + return resolvedPath.join('/'); + } +} diff --git a/typescript/sdk/src/deploy/verify/ContractVerifier.ts b/typescript/sdk/src/deploy/verify/ContractVerifier.ts index 49ecd30980f..1ccd839d0b8 100644 --- a/typescript/sdk/src/deploy/verify/ContractVerifier.ts +++ b/typescript/sdk/src/deploy/verify/ContractVerifier.ts @@ -1,13 +1,14 @@ import fetch from 'cross-fetch'; -import { ethers } from 'ethers'; import { Logger } from 'pino'; +import { buildArtifact as zksyncBuildArtifact } from '@hyperlane-xyz/core/buildArtifact-zksync.js'; import { rootLogger, sleep, strip0x } from '@hyperlane-xyz/utils'; import { ExplorerFamily } from '../../metadata/chainMetadataTypes.js'; import { MultiProvider } from '../../providers/MultiProvider.js'; import { ChainMap, ChainName } from '../../types.js'; +import { BaseContractVerifier } from './BaseContractVerifier.js'; import { BuildArtifact, CompilerOptions, @@ -19,12 +20,8 @@ import { SolidityStandardJsonInput, } from './types.js'; -export class ContractVerifier { +export class ContractVerifier extends BaseContractVerifier { protected logger = rootLogger.child({ module: 'ContractVerifier' }); - - protected contractSourceMap: { [contractName: string]: string } = {}; - - protected readonly standardInputJson: SolidityStandardJsonInput; protected readonly compilerOptions: CompilerOptions; constructor( @@ -33,86 +30,128 @@ export class ContractVerifier { buildArtifact: BuildArtifact, licenseType: CompilerOptions['licenseType'], ) { - this.standardInputJson = buildArtifact.input; - + super(multiProvider, buildArtifact); const compilerversion = `v${buildArtifact.solcLongVersion}`; - - // double check compiler version matches expected format const versionRegex = /v(\d.\d.\d+)\+commit.\w+/; const matches = versionRegex.exec(compilerversion); if (!matches) { throw new Error(`Invalid compiler version ${compilerversion}`); } - - // set compiler options - // only license type is configurable, empty if not provided this.compilerOptions = { codeformat: 'solidity-standard-json-input', compilerversion, licenseType, }; - - // process input to create mapping of contract names to source names - // this is required to construct the fully qualified contract name - const contractRegex = /contract\s+([A-Z][a-zA-Z0-9]*)/g; - Object.entries(buildArtifact.input.sources).forEach( - ([sourceName, { content }]) => { - const matches = content.matchAll(contractRegex); - for (const match of matches) { - const contractName = match[1]; - if (contractName) { - this.contractSourceMap[contractName] = sourceName; - } - } - }, - ); + if (zksyncBuildArtifact?.zk_version) + this.compilerOptions.zksolcversion = `v${zksyncBuildArtifact.zk_version}`; } - public async verifyContract( + protected async verify( chain: ChainName, input: ContractVerificationInput, - logger = this.logger, + verificationLogger: Logger, ): Promise { - const verificationLogger = logger.child({ - chain, - name: input.name, - address: input.address, - }); + const contractType: string = input.isProxy ? 'proxy' : 'implementation'; - const metadata = this.multiProvider.tryGetChainMetadata(chain); - const rpcUrl = metadata?.rpcUrls[0].http ?? ''; - if (rpcUrl.includes('localhost') || rpcUrl.includes('127.0.0.1')) { - verificationLogger.debug('Skipping verification for local endpoints'); - return; - } + verificationLogger.debug(`📝 Verifying ${contractType}...`); - const explorerApi = this.multiProvider.tryGetExplorerApi(chain); - if (!explorerApi) { - verificationLogger.debug('No explorer API set, skipping'); - return; - } + const data = input.isProxy + ? this.getProxyData(input) + : this.getImplementationData(chain, input, verificationLogger); - if (!explorerApi.family) { - verificationLogger.debug(`No explorer family set, skipping`); - return; - } + try { + const guid: string = await this.submitForm( + chain, + input.isProxy + ? ExplorerApiActions.VERIFY_PROXY + : ExplorerApiActions.VERIFY_IMPLEMENTATION, + verificationLogger, + data, + ); - if (explorerApi.family === ExplorerFamily.Other) { - verificationLogger.debug(`Unsupported explorer family, skipping`); - return; - } + verificationLogger.trace( + { guid }, + `Retrieved guid from verified ${contractType}.`, + ); + + await this.checkStatus( + chain, + input, + verificationLogger, + guid, + contractType, + ); + + const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl( + chain, + input.address, + ); - if (input.address === ethers.constants.AddressZero) return; - if (Array.isArray(input.constructorArguments)) { verificationLogger.debug( - 'Constructor arguments in legacy format, skipping', + { + addressUrl: addressUrl + ? `${addressUrl}#code` + : `Could not retrieve ${contractType} explorer URL.`, + }, + `✅ Successfully verified ${contractType}.`, + ); + } catch (error) { + verificationLogger.debug( + { error }, + `Verification of ${contractType} failed`, ); - return; + throw error; } + } - await this.verify(chain, input, verificationLogger); + private async checkStatus( + chain: ChainName, + input: ContractVerificationInput, + verificationLogger: Logger, + guid: string, + contractType: string, + ): Promise { + verificationLogger.trace({ guid }, `Checking ${contractType} status...`); + await this.submitForm( + chain, + input.isProxy + ? ExplorerApiActions.CHECK_PROXY_STATUS + : ExplorerApiActions.CHECK_IMPLEMENTATION_STATUS, + verificationLogger, + { + guid: guid, + }, + ); + } + + private getProxyData(input: ContractVerificationInput) { + return { + address: input.address, + expectedimplementation: input.expectedimplementation, + }; } + protected prepareImplementationData( + sourceName: string, + input: ContractVerificationInput, + filteredStandardInputJson: SolidityStandardJsonInput, + ) { + return { + sourceCode: JSON.stringify(filteredStandardInputJson), + contractname: `${sourceName}:${input.name}`, + contractaddress: input.address, + constructorArguements: strip0x(input.constructorArguments ?? ''), + ...this.compilerOptions, + }; + } + + /** + * @notice Submits the verification form to the explorer API + * @param chain The name of the chain where the contract is deployed + * @param verificationLogger A logger instance for verification-specific logging + * @param options Additional options for the API request + * @returns The response from the explorer API + */ private async submitForm( chain: ChainName, action: ExplorerApiActions, @@ -125,7 +164,6 @@ export class ContractVerifier { apiKey = this.apiKeys[chain], } = this.multiProvider.getExplorerApi(chain); const params = new URLSearchParams(); - params.set('module', 'contract'); params.set('action', action); if (apiKey) params.set('apikey', apiKey); @@ -140,6 +178,7 @@ export class ContractVerifier { if (isGetRequest) url.search = params.toString(); switch (family) { + case ExplorerFamily.ZkSync: case ExplorerFamily.Etherscan: timeout = 5000; break; @@ -256,203 +295,4 @@ export class ContractVerifier { await sleep(timeout); return responseJson.result; } - - private async verify( - chain: ChainName, - input: ContractVerificationInput, - verificationLogger: Logger, - ): Promise { - const contractType: string = input.isProxy ? 'proxy' : 'implementation'; - - verificationLogger.debug(`📝 Verifying ${contractType}...`); - - const data = input.isProxy - ? this.getProxyData(input) - : this.getImplementationData(chain, input, verificationLogger); - - try { - const guid: string = await this.submitForm( - chain, - input.isProxy - ? ExplorerApiActions.VERIFY_PROXY - : ExplorerApiActions.VERIFY_IMPLEMENTATION, - verificationLogger, - data, - ); - - verificationLogger.trace( - { guid }, - `Retrieved guid from verified ${contractType}.`, - ); - - await this.checkStatus( - chain, - input, - verificationLogger, - guid, - contractType, - ); - - const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl( - chain, - input.address, - ); - - verificationLogger.debug( - { - addressUrl: addressUrl - ? `${addressUrl}#code` - : `Could not retrieve ${contractType} explorer URL.`, - }, - `✅ Successfully verified ${contractType}.`, - ); - } catch (error) { - verificationLogger.debug( - { error }, - `Verification of ${contractType} failed`, - ); - throw error; - } - } - - private async checkStatus( - chain: ChainName, - input: ContractVerificationInput, - verificationLogger: Logger, - guid: string, - contractType: string, - ): Promise { - verificationLogger.trace({ guid }, `Checking ${contractType} status...`); - await this.submitForm( - chain, - input.isProxy - ? ExplorerApiActions.CHECK_PROXY_STATUS - : ExplorerApiActions.CHECK_IMPLEMENTATION_STATUS, - verificationLogger, - { - guid: guid, - }, - ); - } - - private getProxyData(input: ContractVerificationInput) { - return { - address: input.address, - expectedimplementation: input.expectedimplementation, - }; - } - - private getImplementationData( - chain: ChainName, - input: ContractVerificationInput, - verificationLogger: Logger, - ) { - const sourceName = this.contractSourceMap[input.name]; - if (!sourceName) { - const errorMessage = `Contract '${input.name}' not found in provided build artifact`; - verificationLogger.error(errorMessage); - throw new Error(`[${chain}] ${errorMessage}`); - } - - const filteredStandardInputJson = - this.filterStandardInputJsonByContractName( - input.name, - this.standardInputJson, - verificationLogger, - ); - - return { - sourceCode: JSON.stringify(filteredStandardInputJson), - contractname: `${sourceName}:${input.name}`, - contractaddress: input.address, - /* TYPO IS ENFORCED BY API */ - constructorArguements: strip0x(input.constructorArguments ?? ''), - ...this.compilerOptions, - }; - } - - /** - * Filters the solidity standard input for a specific contract name. - * - * This is a BFS impl to traverse the source input dependency graph. - * 1. Named contract file is set as root node. - * 2. The next level is formed by the direct imports of the contract file. - * 3. Each subsequent level's dependencies form the next level, etc. - * 4. The queue tracks the next files to process, and ensures the dependency graph explorered level by level. - */ - private filterStandardInputJsonByContractName( - contractName: string, - input: SolidityStandardJsonInput, - verificationLogger: Logger, - ): SolidityStandardJsonInput { - verificationLogger.trace( - { contractName }, - 'Filtering unused contracts from solidity standard input JSON....', - ); - const filteredSources: SolidityStandardJsonInput['sources'] = {}; - const sourceFiles: string[] = Object.keys(input.sources); - const contractFile: string = this.getContractFile( - contractName, - sourceFiles, - ); - const queue: string[] = [contractFile]; - const processed = new Set(); - - while (queue.length > 0) { - const file = queue.shift()!; - if (processed.has(file)) continue; - processed.add(file); - - filteredSources[file] = input.sources[file]; - - const content = input.sources[file].content; - const importStatements = this.getAllImportStatements(content); - - importStatements.forEach((importStatement) => { - const importPath = importStatement.match(/["']([^"']+)["']/)?.[1]; - if (importPath) { - const resolvedPath = this.resolveImportPath(file, importPath); - if (sourceFiles.includes(resolvedPath)) queue.push(resolvedPath); - } - }); - } - - return { - ...input, - sources: filteredSources, - }; - } - - private getContractFile(contractName: string, sourceFiles: string[]): string { - const contractFile = sourceFiles.find((file) => - file.endsWith(`/${contractName}.sol`), - ); - if (!contractFile) { - throw new Error(`Contract ${contractName} not found in sources.`); - } - return contractFile; - } - - private getAllImportStatements(content: string) { - const importRegex = - /import\s+(?:(?:(?:"[^"]+"|'[^']+')\s*;)|(?:{[^}]+}\s+from\s+(?:"[^"]+"|'[^']+')\s*;)|(?:\s*(?:"[^"]+"|'[^']+')\s*;))/g; - return content.match(importRegex) || []; - } - - private resolveImportPath(currentFile: string, importPath: string): string { - /* Use as-is for external dependencies and absolute imports */ - if (importPath.startsWith('@') || importPath.startsWith('http')) { - return importPath; - } - const currentDir = currentFile.split('/').slice(0, -1).join('/'); - const resolvedPath = importPath.split('/').reduce((acc, part) => { - if (part === '..') { - acc.pop(); - } else if (part !== '.') { - acc.push(part); - } - return acc; - }, currentDir.split('/')); - return resolvedPath.join('/'); - } } From a3111ec621905ea92e666be50879e093b6ea0763 Mon Sep 17 00:00:00 2001 From: Morteza Shojaei <31728528+mortezashojaei@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:27:40 +0330 Subject: [PATCH 002/223] feat: add ZKSync contract verification support with custom compiler options --- .../verify/PostDeploymentContractVerifier.ts | 12 +- .../deploy/verify/ZKSyncContractVerifier.ts | 158 ++++++++++++++++++ typescript/sdk/src/deploy/verify/types.ts | 9 + typescript/sdk/src/deploy/verify/utils.ts | 121 +++++++++++++- typescript/sdk/src/index.ts | 1 + 5 files changed, 291 insertions(+), 10 deletions(-) create mode 100644 typescript/sdk/src/deploy/verify/ZKSyncContractVerifier.ts diff --git a/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts b/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts index b657b8e7e68..bd2cb71112b 100644 --- a/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts +++ b/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts @@ -5,14 +5,16 @@ import { MultiProvider } from '../../providers/MultiProvider.js'; import { ChainMap } from '../../types.js'; import { MultiGeneric } from '../../utils/MultiGeneric.js'; +import { BaseContractVerifier } from './BaseContractVerifier.js'; import { ContractVerifier } from './ContractVerifier.js'; +import { ZKSyncContractVerifier } from './ZKSyncContractVerifier.js'; import { BuildArtifact, CompilerOptions, VerificationInput } from './types.js'; export class PostDeploymentContractVerifier extends MultiGeneric { protected logger = rootLogger.child({ module: 'PostDeploymentContractVerifier', }); - protected readonly contractVerifier: ContractVerifier; + protected contractVerifier: BaseContractVerifier; constructor( verificationInputs: ChainMap, @@ -35,6 +37,14 @@ export class PostDeploymentContractVerifier extends MultiGeneric { // can check explorer family here to avoid doing these checks for each input in verifier const { family } = this.multiProvider.getExplorerApi(chain); + + if (family === ExplorerFamily.ZkSync) { + this.logger.debug('Using ZkSync verifier'); + this.contractVerifier = new ZKSyncContractVerifier( + this.multiProvider, + ); + } + if (family === ExplorerFamily.Other) { this.logger.warn( `Skipping verification for ${chain} due to unsupported explorer family.`, diff --git a/typescript/sdk/src/deploy/verify/ZKSyncContractVerifier.ts b/typescript/sdk/src/deploy/verify/ZKSyncContractVerifier.ts new file mode 100644 index 00000000000..4110d1f5e23 --- /dev/null +++ b/typescript/sdk/src/deploy/verify/ZKSyncContractVerifier.ts @@ -0,0 +1,158 @@ +import fetch from 'cross-fetch'; +import { Logger } from 'pino'; + +import { buildArtifact } from '@hyperlane-xyz/core/buildArtifact-zksync.js'; +import { rootLogger } from '@hyperlane-xyz/utils'; + +import { MultiProvider } from '../../providers/MultiProvider.js'; +import { ChainName } from '../../types.js'; + +import { BaseContractVerifier } from './BaseContractVerifier.js'; +import { + BuildArtifact, + ContractVerificationInput, + SolidityStandardJsonInput, + ZKSyncCompilerOptions, +} from './types.js'; + +/** + * @title ZKSyncContractVerifier + * @notice Handles the verification of ZkSync contracts on block explorers + * @dev This class manages the process of verifying ZkSync contracts, including + * preparing verification data and submitting it to the appropriate explorer API + * Note: Etherscan verification is managed by the ContractVerifier class + * Blockscout verification is not currently supported on ZkSync + */ +export class ZKSyncContractVerifier extends BaseContractVerifier { + protected logger = rootLogger.child({ module: 'ZKSyncContractVerifier' }); + + protected readonly standardInputJson: SolidityStandardJsonInput; + protected readonly compilerOptions: ZKSyncCompilerOptions; + + /** + * @notice Creates a new ZKSyncContractVerifier instance + * @param multiProvider An instance of MultiProvider for interacting with multiple chains + */ + constructor(protected readonly multiProvider: MultiProvider) { + super(multiProvider, buildArtifact); + this.standardInputJson = (buildArtifact as BuildArtifact).input; + + const compilerZksolcVersion = `v${ + (buildArtifact as { zk_version: string }).zk_version + }`; + const compilerSolcVersion = (buildArtifact as BuildArtifact) + .solcLongVersion; + + this.compilerOptions = { + codeFormat: 'solidity-standard-json-input', + compilerSolcVersion, + compilerZksolcVersion, + optimizationUsed: true, + }; + } + + /** + * @notice Verifies a contract on the specified chain + * @param chain The name of the chain where the contract is deployed + * @param input The contract verification input data + * @param verificationLogger A logger instance for verification-specific logging + */ + protected async verify( + chain: ChainName, + input: ContractVerificationInput, + verificationLogger: Logger, + ): Promise { + const contractType: string = input.isProxy ? 'proxy' : 'implementation'; + + verificationLogger.debug(`📝 Verifying ${contractType}...`); + + const data = this.getImplementationData(chain, input, verificationLogger); + + try { + const verificationId: string = await this.submitForm( + chain, + verificationLogger, + data, + ); + + verificationLogger.trace( + { verificationId }, + `Retrieved verificationId from verified ${contractType}.`, + ); + } catch (error) { + verificationLogger.debug( + { error }, + `Verification of ${contractType} failed`, + ); + throw error; + } + } + + protected prepareImplementationData( + sourceName: string, + input: ContractVerificationInput, + filteredStandardInputJson: SolidityStandardJsonInput, + ) { + return { + sourceCode: filteredStandardInputJson, + contractName: `${sourceName}:${input.name}`, + contractAddress: input.address, + constructorArguments: `0x${input.constructorArguments || ''}`, + ...this.compilerOptions, + }; + } + + /** + * @notice Submits the verification form to the explorer API + * @param chain The name of the chain where the contract is deployed + * @param verificationLogger A logger instance for verification-specific logging + * @param options Additional options for the API request + * @returns The response from the explorer API + */ + private async submitForm( + chain: ChainName, + verificationLogger: Logger, + options?: Record, + ): Promise { + const { apiUrl, family } = this.multiProvider.getExplorerApi(chain); + + const url = new URL(apiUrl); + verificationLogger.trace( + { apiUrl, chain }, + 'Sending request to explorer...', + ); + + const response = await fetch(url.toString(), { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(options), + }); + let responseJson; + try { + responseJson = await response.json(); + verificationLogger.trace( + { apiUrl, chain }, + 'Parsing response from explorer...', + ); + } catch (error) { + verificationLogger.trace( + { + error, + failure: response.statusText, + status: response.status, + chain, + apiUrl, + family, + }, + 'Failed to parse response from explorer.', + ); + throw new Error( + `Failed to parse response from explorer (${apiUrl}, ${chain}): ${ + response.statusText || 'UNKNOWN STATUS TEXT' + } (${response.status || 'UNKNOWN STATUS'})`, + ); + } + + return responseJson; + } +} diff --git a/typescript/sdk/src/deploy/verify/types.ts b/typescript/sdk/src/deploy/verify/types.ts index 3ef1dd9cd36..585359edae5 100644 --- a/typescript/sdk/src/deploy/verify/types.ts +++ b/typescript/sdk/src/deploy/verify/types.ts @@ -27,6 +27,7 @@ export type SolidityStandardJsonInput = { export type BuildArtifact = { input: SolidityStandardJsonInput; solcLongVersion: string; + zk_version?: string; //only for zksync }; // see https://etherscan.io/contract-license-types @@ -51,6 +52,14 @@ export type CompilerOptions = { codeformat: 'solidity-standard-json-input'; compilerversion: string; // see https://etherscan.io/solcversions for list of support versions licenseType?: ExplorerLicenseType; + zksolcversion?: string; //only for zksync chains +}; + +export type ZKSyncCompilerOptions = { + codeFormat: 'solidity-standard-json-input'; + compilerSolcVersion: string; + compilerZksolcVersion: string; + optimizationUsed: boolean; }; export enum ExplorerApiActions { diff --git a/typescript/sdk/src/deploy/verify/utils.ts b/typescript/sdk/src/deploy/verify/utils.ts index 85f5877bfc8..490b963bfdd 100644 --- a/typescript/sdk/src/deploy/verify/utils.ts +++ b/typescript/sdk/src/deploy/verify/utils.ts @@ -1,8 +1,10 @@ import { ethers, utils } from 'ethers'; +import { Hex, decodeFunctionData, parseAbi } from 'viem'; import { ProxyAdmin__factory, TransparentUpgradeableProxy__factory, + ZKSyncArtifact, } from '@hyperlane-xyz/core'; import { Address, assert, eqAddress } from '@hyperlane-xyz/utils'; @@ -71,6 +73,36 @@ export function getContractVerificationInput({ ); } +export async function getContractVerificationInputForZKSync({ + name, + contract, + constructorArgs, + artifact, + isProxy, + expectedimplementation, +}: { + name: string; + contract: ethers.Contract; + constructorArgs: any[]; + artifact: ZKSyncArtifact; + isProxy?: boolean; + expectedimplementation?: Address; +}): Promise { + const args = encodeArguments(artifact.abi, constructorArgs); + return buildVerificationInput( + name, + contract.address, + args, + isProxy, + expectedimplementation, + ); +} + +export function encodeArguments(abi: any, constructorArgs: any[]): string { + const contractInterface = new utils.Interface(abi); + return contractInterface.encodeDeploy(constructorArgs).replace('0x', ''); +} + /** * Check if the artifact should be added to the verification inputs. * @param verificationInputs - The verification inputs for the chain. @@ -93,7 +125,14 @@ export function shouldAddVerificationInput( } /** - * Retrieves the constructor args using their respective Explorer and/or RPC (eth_getTransactionByHash) + * @notice Defines verification delay times for different blockchain explorer families. + * @dev This constant object associates explorer families with specific delay times (in milliseconds) + */ +export const FamilyVerificationDelay = { + [ExplorerFamily.Etherscan]: 40000, +} as const; + +/** Retrieves the constructor args using their respective Explorer and/or RPC (eth_getTransactionByHash) */ export async function getConstructorArgumentsApi({ chainName, @@ -119,6 +158,13 @@ export async function getConstructorArgumentsApi({ multiProvider, }); break; + case ExplorerFamily.ZkSync: + constructorArgs = await getZKSyncConstructorArgs({ + chainName, + contractAddress, + multiProvider, + }); + break; case ExplorerFamily.Blockscout: constructorArgs = await getBlockScoutConstructorArgs({ chainName, @@ -133,20 +179,19 @@ export async function getConstructorArgumentsApi({ return constructorArgs; } -export async function getEtherscanConstructorArgs({ - bytecode, +async function getConstructorArgsFromExplorer({ chainName, + blockExplorerApiKey, + blockExplorerApiUrl, contractAddress, multiProvider, }: { - bytecode: string; + blockExplorerApiKey?: string; + blockExplorerApiUrl: string; chainName: string; contractAddress: Address; multiProvider: MultiProvider; -}): Promise { - const { apiUrl: blockExplorerApiUrl, apiKey: blockExplorerApiKey } = - multiProvider.getExplorerApi(chainName); - +}) { const url = new URL(blockExplorerApiUrl); url.searchParams.append('module', 'contract'); url.searchParams.append('action', 'getcontractcreation'); @@ -176,11 +221,69 @@ export async function getEtherscanConstructorArgs({ }), }); + return creationTxResp.json(); +} + +export async function getEtherscanConstructorArgs({ + bytecode, + chainName, + contractAddress, + multiProvider, +}: { + bytecode: string; + chainName: string; + contractAddress: Address; + multiProvider: MultiProvider; +}): Promise { + const { apiUrl: blockExplorerApiUrl, apiKey: blockExplorerApiKey } = + multiProvider.getExplorerApi(chainName); + // Truncate the deployment bytecode - const creationInput: string = (await creationTxResp.json()).result.input; + const creationTxResp = await getConstructorArgsFromExplorer({ + chainName, + blockExplorerApiKey, + blockExplorerApiUrl, + contractAddress, + multiProvider, + }); + const creationInput: string = creationTxResp.result.input; return creationInput.substring(bytecode.length); } +export async function getZKSyncConstructorArgs({ + chainName, + contractAddress, + multiProvider, +}: { + chainName: string; + contractAddress: Address; + multiProvider: MultiProvider; +}): Promise { + const { apiUrl, apiKey: blockExplorerApiKey } = + multiProvider.getExplorerApi(chainName); + + // Create the API URL using Registry blockExplorers.apiUrl + // Assumes that ZkSync uses something like `https://zero-network.calderaexplorer.xyz/verification/contract_verification`. + const blockExplorerApiUrl = new URL('/api', new URL(apiUrl).origin).href; + + // Truncate the deployment bytecode + const creationTxResp = await getConstructorArgsFromExplorer({ + chainName, + blockExplorerApiKey, + blockExplorerApiUrl, + contractAddress, + multiProvider, + }); + const creationInput: string = creationTxResp.result.input; + + const res = decodeFunctionData({ + abi: parseAbi(['function create(bytes32,bytes32,bytes)']), + data: creationInput as Hex, + }); + + return res.args[2].replace('0x', ''); +} + export async function getBlockScoutConstructorArgs({ chainName, contractAddress, diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 6d07fc12ac0..62a65ed95f8 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -105,6 +105,7 @@ export { ViolationType, } from './deploy/types.js'; export { ContractVerifier } from './deploy/verify/ContractVerifier.js'; +export { ZKSyncContractVerifier } from './deploy/verify/ZKSyncContractVerifier.js'; export { PostDeploymentContractVerifier } from './deploy/verify/PostDeploymentContractVerifier.js'; export { BuildArtifact, From 1d20acdd4e79623dc55141130130c360f3598ced Mon Sep 17 00:00:00 2001 From: Morteza Shojaei <31728528+mortezashojaei@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:46:53 +0330 Subject: [PATCH 003/223] docs(changeset): Add ZKSync contract verification with custom compiler options and refactor verification classes --- .changeset/orange-cats-stare.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/orange-cats-stare.md diff --git a/.changeset/orange-cats-stare.md b/.changeset/orange-cats-stare.md new file mode 100644 index 00000000000..a1c431b5e45 --- /dev/null +++ b/.changeset/orange-cats-stare.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Add ZKSync contract verification with custom compiler options and refactor verification classes From b982da0d379464f51eae39754a4396b2102bee5b Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion Date: Wed, 12 Mar 2025 15:19:57 +0100 Subject: [PATCH 004/223] chore: update Solidity version in zk-hardhat configuration from 0.8.19 to 0.8.22 --- solidity/zk-hardhat.config.cts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/zk-hardhat.config.cts b/solidity/zk-hardhat.config.cts index 618fe65a5be..34b5559fb00 100644 --- a/solidity/zk-hardhat.config.cts +++ b/solidity/zk-hardhat.config.cts @@ -20,7 +20,7 @@ module.exports = { }, }, solidity: { - version: '0.8.19', + version: '0.8.22', settings: { optimizer: { enabled: true, From 3e27f07696634a2352691835efaf525a927dc2cf Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 15 Apr 2025 08:59:44 +0100 Subject: [PATCH 005/223] feat(cosmos-sdk): use prebuilt simapp image for docker-compose (#5874) ### Description feat(cosmos-sdk): use prebuilt simapp image for docker-compose - create pipeline that can be manually triggered to create new `hyperlane-cosmos-simapp` images - use prebuilt simapp image in docker-compose to save time pulling before e2e ### Drive-by changes ### Related issues ### Backward compatibility ### Testing ci - https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/runs/14361666216 --------- Co-authored-by: Troy Kessler --- .github/workflows/simapp-docker.yml | 62 +++++++++++++++++++++++++++++ typescript/cosmos-sdk/Dockerfile | 15 ++++--- typescript/cosmos-sdk/README.md | 6 +++ typescript/cosmos-sdk/compose.yaml | 2 +- 4 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/simapp-docker.yml diff --git a/.github/workflows/simapp-docker.yml b/.github/workflows/simapp-docker.yml new file mode 100644 index 00000000000..a44c0e2718c --- /dev/null +++ b/.github/workflows/simapp-docker.yml @@ -0,0 +1,62 @@ +name: Build and Push Cosmos Simapp Image to GCR +on: + workflow_dispatch: + inputs: + hyperlane_cosmos_branch: + description: 'Branch, ref, or tag to build' + default: 'v1.0.0-beta0' + +concurrency: + group: build-push-cosmos-simapp-${{ github.ref }} + cancel-in-progress: true + +jobs: + check-env: + runs-on: ubuntu-latest + # assign output from step to job output + outputs: + gcloud-service-key: ${{ steps.gcloud-service-key.outputs.defined }} + steps: + - id: gcloud-service-key + # assign GCLOUD_SERVICE_KEY to env for access in conditional + env: + GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} + if: "${{ env.GCLOUD_SERVICE_KEY != '' }}" + # runs if GCLOUD_SERVICE_KEY is defined, so we set the output to true + run: echo "defined=true" >> $GITHUB_OUTPUT + + build-and-push-to-gcr: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + # uses check-env to determine if secrets.GCLOUD_SERVICE_KEY is defined + needs: [check-env] + if: needs.check-env.outputs.gcloud-service-key == 'true' + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + submodules: recursive + - name: Set up Depot CLI + uses: depot/setup-action@v1 + - name: Login to GCR + uses: docker/login-action@v3 + with: + registry: gcr.io + username: _json_key + password: ${{ secrets.GCLOUD_SERVICE_KEY }} + - name: Build and push + uses: depot/build-push-action@v1 + with: + project: 3cpjhx94qv + context: ./typescript/cosmos-sdk + file: ./typescript/cosmos-sdk/Dockerfile + push: true + tags: | + gcr.io/abacus-labs-dev/hyperlane-cosmos-simapp:${{ github.event.inputs.hyperlane_cosmos_branch || 'v1.0.0-beta0' }} + build-args: | + BRANCH_NAME=${{ github.event.inputs.hyperlane_cosmos_branch || 'v1.0.0-beta0' }} + platforms: linux/amd64,linux/arm64 diff --git a/typescript/cosmos-sdk/Dockerfile b/typescript/cosmos-sdk/Dockerfile index 28d6e6a382c..9c880958796 100644 --- a/typescript/cosmos-sdk/Dockerfile +++ b/typescript/cosmos-sdk/Dockerfile @@ -2,17 +2,22 @@ FROM golang:1.22 WORKDIR /app -# install latest updates -RUN apt update && apt upgrade -y +# install latest updates and clean up +RUN apt update && apt upgrade -y \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* -# install hypd -RUN git clone --depth 1 --branch v1.0.0-beta0 https://github.com/bcp-innovations/hyperlane-cosmos.git \ +# Define a build argument for the branch name +ARG BRANCH_NAME=v1.0.0-beta0 + +# install hypd from the specified branch +RUN git clone --depth 1 --branch $BRANCH_NAME https://github.com/bcp-innovations/hyperlane-cosmos.git \ && cd hyperlane-cosmos \ && make build-simapp \ && mv build/hypd /app \ && /app/hypd init-sample-chain \ && cd .. \ - && rm -r hyperlane-cosmos + && rm -rf hyperlane-cosmos # rpc EXPOSE 26657 diff --git a/typescript/cosmos-sdk/README.md b/typescript/cosmos-sdk/README.md index 55ca1ab0d5a..35f6fd8197e 100644 --- a/typescript/cosmos-sdk/README.md +++ b/typescript/cosmos-sdk/README.md @@ -63,6 +63,12 @@ await signer.signAndBroadcast(signer.getAccounts()[0], [txs...]); Node 18 or newer is required. +## Testing + +We have a `cosmos-sdk-e2e` job in CI that first runs a local node and then runs a suite of end-to-end tests. The `hyperlane-cosmos-simapp` image is created ad-hoc by the `hypd-docker` workflow, intended to be triggered manually by a developer when a new hyperlane-cosmos release is made. + +> Note: When updating the `cosmos-sdk` and `cosmos-types` package to a new `hyperlane-cosmos` version, it's important to release a new `hyperlane-cosmos-simapp` image and update the tag used in the `cosmos-sdk-e2e` job. This ensures that the end-to-end tests run against the correct version of the `hyperlane-cosmos` module. + ## Contribute First you need to install the dependencies by running `yarn install`. diff --git a/typescript/cosmos-sdk/compose.yaml b/typescript/cosmos-sdk/compose.yaml index c66732413e3..98efb10438c 100644 --- a/typescript/cosmos-sdk/compose.yaml +++ b/typescript/cosmos-sdk/compose.yaml @@ -1,6 +1,6 @@ services: hyperlane-cosmos-simapp: - build: . + image: gcr.io/abacus-labs-dev/hyperlane-cosmos-simapp:v1.0.0-beta0 ports: - 26657:26657 - 1317:1317 From 3b615c892db24a6a386c4019fe5d3dee02257f42 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Tue, 15 Apr 2025 09:53:47 -0400 Subject: [PATCH 006/223] feat: Add proxyAdmin.owner to ownerOverrides in Checker (#5931) ### Description This PR adds the proxyAdmin.owner to the Checker ownerOverrides such that it checks proxyAdmin.owner instead of the top-level owner. This is needed for edge cases where we have proxyAdmin owner that is different than the top-level owner. For example `LUMIA/arbitrum-avalanche-base-bsc-ethereum-lumiaprism-optimism-polygon` has not transferred their proxyAdmin.owner ### Backward compatibility Yes - only checks if proxyAdmin is explicitly supplied. ### Testing Manual, tested by rebasing ontop of another [PR](https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/5886) which reads an offending config (LUMIA/arbitrum-avalanche-...) from the registry --- .changeset/tame-sheep-retire.md | 5 +++++ typescript/sdk/src/router/ProxiedRouterChecker.ts | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 .changeset/tame-sheep-retire.md diff --git a/.changeset/tame-sheep-retire.md b/.changeset/tame-sheep-retire.md new file mode 100644 index 00000000000..0b0b65b0f86 --- /dev/null +++ b/.changeset/tame-sheep-retire.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Adds the proxyAdmin.owner to the Checker ownerOverrides such that it checks proxyAdmin.owner instead of always using the top-level owner diff --git a/typescript/sdk/src/router/ProxiedRouterChecker.ts b/typescript/sdk/src/router/ProxiedRouterChecker.ts index a297329ea43..5ae5c16c4b4 100644 --- a/typescript/sdk/src/router/ProxiedRouterChecker.ts +++ b/typescript/sdk/src/router/ProxiedRouterChecker.ts @@ -13,11 +13,17 @@ export abstract class ProxiedRouterChecker< getOwnableOverrides(chain: ChainName): AddressesMap | undefined { const config = this.configMap[chain]; let ownableOverrides = config?.ownerOverrides; + // timelock and proxyAdmin are mutally exclusive if (config?.timelock) { ownableOverrides = { ...ownableOverrides, proxyAdmin: this.app.getAddresses(chain).timelockController, }; + } else if (config?.proxyAdmin) { + ownableOverrides = { + ...ownableOverrides, + proxyAdmin: config.proxyAdmin.owner, + }; } return ownableOverrides; } From c0bc208ccc770b243274616539b6a44bb4a6f73f Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 15 Apr 2025 15:16:48 +0100 Subject: [PATCH 007/223] feat: use `aws-sdk-s3` for S3 operations (#5928) ### Description - Moving to `aws-sdk-s3` instead of rusoto for all S3 operations. We still require rusoto for KMS - Moved to a static set of S3 clients that are used globally for all anonymous S3 operations. Clients are region specific so there are some ugly types. I preferred the static set to minimize how invasive this change is - Some context here https://hyperlaneworkspace.slack.com/archives/C08GR6PBPGT/p1744561237142619?thread_ts=1744392583.189179&cid=C08GR6PBPGT - Web Identity support is still there despite it being deleted, see https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credproviders.html and https://docs.rs/aws-config/latest/aws_config/web_identity_token/index.html#environment-variable-configuration ### Drive-by changes - sets `resolver = "2"`. This resulted in some slightly different compile warnings / errors due to various features now being activated or not activated ### Related issues ### Backward compatibility ### Testing - I tested locally with my local prepares but tbh there isn't a nice way of testing functionality in e2e or unit tests with a lot of this. So I'll roll out to RC cautiously --- rust/main/Cargo.lock | 608 ++++++++++++++++-- rust/main/Cargo.toml | 8 + rust/main/agents/scraper/Cargo.toml | 3 +- rust/main/agents/validator/Cargo.toml | 4 + rust/main/agents/validator/src/settings.rs | 6 +- rust/main/hyperlane-base/Cargo.toml | 4 +- .../src/settings/checkpoint_syncer.rs | 14 +- .../hyperlane-base/src/types/s3_storage.rs | 189 ++++-- .../src/traits/pending_operation.rs | 1 - rust/main/utils/abigen/src/lib.rs | 2 + 10 files changed, 720 insertions(+), 119 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 4123f312e8f..7b9fb9127aa 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -521,6 +521,438 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-config" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b96342ea8948ab9bef3e6234ea97fc32e2d8a88d8fb6a084e52267317f94b6b" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http 0.60.12", + "aws-smithy-json 0.60.7", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex 0.4.3", + "http 0.2.12", + "hyper 0.14.30", + "ring 0.17.8", + "time", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4471bef4c22a06d2c7a1b6492493d3fdf24a805323109d6874f9c94d5906ac14" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aff45ffe35196e593ea3b9dd65b320e51e2dda95aff4390bc459e461d09c6ad" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http 0.62.0", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid 1.11.0", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.65.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ba2c5c0f2618937ce3d4a5ad574b86775576fa24006bcb3128c6e2cbf3c34e" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http 0.60.12", + "aws-smithy-json 0.61.3", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex 0.4.3", + "hmac 0.12.1", + "http 0.2.12", + "http-body 0.4.6", + "lru", + "once_cell", + "percent-encoding", + "regex-lite", + "sha2 0.10.8", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ca43a4ef210894f93096039ef1d6fa4ad3edfabb3be92b80908b9f2e4b4eab" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.60.12", + "aws-smithy-json 0.61.3", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fea2f3a8bb3bd10932ae7ad59cc59f65f270fc9183a7e91f501dc5efbef7ee" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.60.12", + "aws-smithy-json 0.60.7", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ada54e5f26ac246dc79727def52f7f8ed38915cb47781e2a72213957dc3a7d5" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.60.12", + "aws-smithy-json 0.60.7", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d03c3c05ff80d54ff860fe38c726f6f494c639ae975203a101335f223386db" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http 0.62.0", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex 0.4.3", + "hmac 0.12.1", + "http 0.2.12", + "http 1.2.0", + "once_cell", + "p256 0.11.1", + "percent-encoding", + "ring 0.17.8", + "sha2 0.10.8", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1a71073fca26775c8b5189175ea8863afb1c9ea2cceb02a5de5ad9dfbaa795" +dependencies = [ + "aws-smithy-http 0.60.12", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex 0.4.3", + "http 0.2.12", + "http-body 0.4.6", + "md-5 0.10.6", + "pin-project-lite", + "sha1", + "sha2 0.10.8", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c45d3dddac16c5c59d553ece225a88870cf81b7b813c9cc17b78cf4685eac7a" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5949124d11e538ca21142d1fba61ab0a2a2c1bc3ed323cdb3e4b878bfb83166" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.2.0", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aff1159006441d02e57204bf57a1b890ba68bedb6904ffd2873c1c4c11c546b" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.4.7", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-rustls", + "pin-project-lite", + "rustls 0.21.12", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445d065e76bc1ef54963db400319f1dd3ebb3e0a74af20f7f7630625b0cc7cc0" +dependencies = [ + "aws-smithy-runtime-api", + "once_cell", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0152749e17ce4d1b47c7747bdfec09dac1ccafdcbc741ebf9daa2a373356730f" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http 0.62.0", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.2.0", + "http-body 0.4.6", + "http-body 1.0.1", + "once_cell", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da37cf5d57011cb1753456518ec76e31691f1f474b73934a284eb2a1c76510f" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.2.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836155caafba616c0ff9b07944324785de2ab016141c3550bd1c07882f8cee8f" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.2.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3873f8deed8927ce8d04487630dc9ff73193bab64742a61d050e57a68dec4125" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "axum" version = "0.6.20" @@ -719,6 +1151,16 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -1108,13 +1550,23 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bzip2-sys" version = "0.1.11+1.0.8" @@ -1705,7 +2157,7 @@ dependencies = [ "ed25519-zebra 4.0.3", "k256 0.13.4", "num-traits", - "p256", + "p256 0.13.2", "rand_core 0.6.4", "rayon", "sha2 0.10.8", @@ -1853,6 +2305,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -2270,6 +2731,19 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -3309,9 +3783,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "feature-probe" @@ -3416,6 +3890,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -3630,7 +4110,7 @@ dependencies = [ "fuel-types", "k256 0.13.4", "lazy_static", - "p256", + "p256 0.13.2", "rand 0.8.5", "secp256k1", "serde", @@ -3913,9 +4393,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" @@ -4197,6 +4677,17 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hashers" version = "1.0.1" @@ -4501,9 +4992,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -4555,7 +5046,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -4586,7 +5077,7 @@ dependencies = [ "futures-util", "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.2", + "hyper 1.6.0", "pin-project-lite", "socket2 0.5.7", "tokio", @@ -4609,6 +5100,8 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "aws-config", + "aws-sdk-s3", "axum 0.6.20", "backtrace", "backtrace-oneline", @@ -4618,6 +5111,7 @@ dependencies = [ "config", "console-subscriber", "convert_case 0.6.0", + "dashmap", "derive-new", "derive_builder", "ed25519-dalek 1.0.1", @@ -4646,7 +5140,6 @@ dependencies = [ "rocksdb", "rusoto_core", "rusoto_kms", - "rusoto_s3", "rusoto_sts", "serde", "serde_json", @@ -5647,6 +6140,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "lz4-sys" version = "1.10.0" @@ -6240,9 +6742,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" @@ -6364,6 +6866,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "overload" version = "0.1.1" @@ -6376,6 +6884,17 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + [[package]] name = "p256" version = "0.13.2" @@ -7368,6 +7887,12 @@ dependencies = [ "regex-syntax 0.8.4", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -7771,19 +8296,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "rusoto_s3" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aae4677183411f6b0b412d66194ef5403293917d66e70ab118f07cc24c5b14d" -dependencies = [ - "async-trait", - "bytes", - "futures", - "rusoto_core", - "xml-rs", -] - [[package]] name = "rusoto_signature" version = "0.48.0" @@ -7926,9 +8438,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", "once_cell", @@ -7992,9 +8504,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -8476,9 +8988,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -8531,9 +9043,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2 1.0.93", "quote 1.0.37", @@ -10299,7 +10811,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.20", + "rustls 0.23.19", "tokio", ] @@ -10468,7 +10980,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-timeout 0.5.2", "hyper-util", "percent-encoding", @@ -10897,6 +11409,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -10928,6 +11446,7 @@ name = "validator" version = "0.1.0" dependencies = [ "async-trait", + "aws-config", "axum 0.6.20", "chrono", "config", @@ -10948,6 +11467,7 @@ dependencies = [ "mockall", "prometheus", "reqwest", + "rusoto_core", "serde", "serde_json", "thiserror", @@ -10999,6 +11519,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "walkdir" version = "2.5.0" @@ -11491,6 +12017,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "ya-gcp" version = "0.11.3" diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 64258fc4652..d99cd3c5884 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -23,6 +23,7 @@ members = [ "utils/hex", "utils/run-locally", ] +resolver = "2" [workspace.package] documentation = "https://docs.hyperlane.xyz" @@ -38,6 +39,12 @@ anyhow = "1.0" async-trait = "0.1" async-rwlock = "1.3" auto_impl = "1.0" +aws-config = { version = "1.1.7", features = ["behavior-version-latest"] } +# AWS deps are pinned to be compatible with rustc 1.80.1 +aws-sdk-s3 = "=1.65.0" +aws-sdk-sso = "=1.50.0" +aws-sdk-ssooidc = "=1.50.0" +aws-sdk-sts = "=1.50.0" axum = "0.6.1" backtrace = "0.3" base64 = "0.21.2" @@ -62,6 +69,7 @@ cosmwasm-std = "*" crunchy = "0.2" ctrlc = "3.2" curve25519-dalek = { version = "~3.2", features = ["serde"] } +dashmap = "5" derive-new = "0.5" derive_builder = "0.12" derive_more = "0.99" diff --git a/rust/main/agents/scraper/Cargo.toml b/rust/main/agents/scraper/Cargo.toml index 937d4b09a02..e5b9a40f99d 100644 --- a/rust/main/agents/scraper/Cargo.toml +++ b/rust/main/agents/scraper/Cargo.toml @@ -20,7 +20,7 @@ itertools.workspace = true num-bigint.workspace = true num-traits.workspace = true prometheus.workspace = true -sea-orm = { workspace = true } +sea-orm = { workspace = true, features = ["mock"] } serde.workspace = true serde_json.workspace = true thiserror.workspace = true @@ -35,7 +35,6 @@ migration = { path = "migration" } [dev-dependencies] reqwest.workspace = true -sea-orm = { workspace = true, features = ["mock"]} tokio-test = "0.4" tracing-test.workspace = true ethers-prometheus = { path = "../../ethers-prometheus", features = ["serde"] } diff --git a/rust/main/agents/validator/Cargo.toml b/rust/main/agents/validator/Cargo.toml index 44d507993d8..7c9ac96dea6 100644 --- a/rust/main/agents/validator/Cargo.toml +++ b/rust/main/agents/validator/Cargo.toml @@ -10,6 +10,7 @@ version.workspace = true [dependencies] async-trait.workspace = true +aws-config.workspace = true axum.workspace = true chrono.workspace = true config.workspace = true @@ -37,6 +38,9 @@ hyperlane-base = { path = "../../hyperlane-base" } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } +# Dependency version is determined by ethers +rusoto_core = '*' + [dev-dependencies] mockall.workspace = true tokio-test.workspace = true diff --git a/rust/main/agents/validator/src/settings.rs b/rust/main/agents/validator/src/settings.rs index c95a7ad2bde..07d0e57692f 100644 --- a/rust/main/agents/validator/src/settings.rs +++ b/rust/main/agents/validator/src/settings.rs @@ -6,6 +6,7 @@ use std::{collections::HashSet, path::PathBuf, time::Duration}; +use aws_config::Region; use derive_more::{AsMut, AsRef, Deref, DerefMut}; use eyre::{eyre, Context}; use hyperlane_base::{ @@ -259,7 +260,8 @@ fn parse_checkpoint_syncer(syncer: ValueParser) -> ConfigResult = syncer .chain(&mut err) .get_key("region") .parse_from_str("Expected aws region") @@ -274,7 +276,7 @@ fn parse_checkpoint_syncer(syncer: ValueParser) -> ConfigResult() + .context("Invalid region when parsing storage location")? + .name() + .to_owned(), + ), }) } "file" => Ok(CheckpointSyncerConf::LocalStorage { diff --git a/rust/main/hyperlane-base/src/types/s3_storage.rs b/rust/main/hyperlane-base/src/types/s3_storage.rs index 8f77b1c12ef..307c0586123 100644 --- a/rust/main/hyperlane-base/src/types/s3_storage.rs +++ b/rust/main/hyperlane-base/src/types/s3_storage.rs @@ -1,25 +1,21 @@ use std::{fmt, sync::OnceLock, time::Duration}; use async_trait::async_trait; +use aws_config::{timeout::TimeoutConfig, BehaviorVersion, ConfigLoader, Region}; +use aws_sdk_s3::{ + error::SdkError, operation::get_object::GetObjectError as SdkGetObjectError, Client, +}; +use dashmap::DashMap; use derive_new::new; use eyre::{bail, Result}; -use futures_util::TryStreamExt; use hyperlane_core::{ReorgEvent, SignedAnnouncement, SignedCheckpointWithMessageId}; use prometheus::IntGauge; -use rusoto_core::{ - credential::{Anonymous, AwsCredentials, StaticProvider}, - Region, RusotoError, -}; -use rusoto_s3::{GetObjectError, GetObjectRequest, PutObjectRequest, S3Client, S3}; -use tokio::time::timeout; +use tokio::sync::OnceCell; -use crate::types::utils; -use crate::{settings::aws_credentials::AwsChainCredentialsProvider, CheckpointSyncer}; +use crate::CheckpointSyncer; -/// The timeout for S3 requests. Rusoto doesn't offer timeout configuration -/// out of the box, so S3 requests must be wrapped with a timeout. -/// See https://github.com/rusoto/rusoto/issues/1795. -const S3_REQUEST_TIMEOUT_SECONDS: u64 = 30; +/// The timeout for all S3 operations. +const S3_REQUEST_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Clone, new)] /// Type for reading/writing to S3 @@ -30,16 +26,25 @@ pub struct S3Storage { folder: Option, /// The region of the bucket. region: Region, - /// A client with AWS credentials. - #[new(default)] - authenticated_client: OnceLock, - /// A client without credentials for anonymous requests. + /// A client with AWS credentials. This client is not initialized globally and has a lifetime + /// tied to the S3Storage instance, so if heavy use of this client is expected, S3Storage + /// itself should be long-lived. #[new(default)] - anonymous_client: OnceLock, + authenticated_client: OnceCell, /// The latest seen signed checkpoint index. latest_index: Option, } +/// A global cache of anonymous S3 clients, per region. +/// We've seen freshly created S3 clients make expensive DNS / TCP +/// requests when creating them. This cache allows us to reuse +/// anonymous clients across the entire agent. +static ANONYMOUS_CLIENT_CACHE: OnceLock>> = OnceLock::new(); + +fn get_anonymous_client_cache() -> &'static DashMap> { + ANONYMOUS_CLIENT_CACHE.get_or_init(DashMap::new) +} + impl fmt::Debug for S3Storage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("S3Storage") @@ -52,73 +57,87 @@ impl fmt::Debug for S3Storage { impl S3Storage { async fn write_to_bucket(&self, key: String, body: &str) -> Result<()> { - let req = PutObjectRequest { - key: self.get_composite_key(key), - bucket: self.bucket.clone(), - body: Some(Vec::from(body).into()), - content_type: Some("application/json".to_owned()), - ..Default::default() - }; - timeout( - Duration::from_secs(S3_REQUEST_TIMEOUT_SECONDS), - self.authenticated_client().put_object(req), - ) - .await??; + self.authenticated_client() + .await + .put_object() + .bucket(self.bucket.clone()) + .key(self.get_composite_key(key)) + .body(Vec::from(body).into()) + .content_type("application/json") + .send() + .await?; + Ok(()) } - /// Uses an anonymous client. This should only be used for publicly accessible buckets. async fn anonymously_read_from_bucket(&self, key: String) -> Result>> { - let req = GetObjectRequest { - key: self.get_composite_key(key), - bucket: self.bucket.clone(), - ..Default::default() - }; - let get_object_result = timeout( - Duration::from_secs(S3_REQUEST_TIMEOUT_SECONDS), - self.anonymous_client().get_object(req), - ) - .await?; - + let get_object_result = self + .anonymous_client() + .await + .get_object() + .bucket(self.bucket.clone()) + .key(self.get_composite_key(key)) + .send() + .await; match get_object_result { - Ok(res) => match res.body { - Some(body) => Ok(Some(body.map_ok(|b| b.to_vec()).try_concat().await?)), - None => Ok(None), + Ok(res) => Ok(Some(res.body.collect().await?.into_bytes().to_vec())), + Err(SdkError::ServiceError(err)) => match err.err() { + SdkGetObjectError::NoSuchKey(_) => Ok(None), + _ => bail!(err.into_err()), }, - Err(RusotoError::Service(GetObjectError::NoSuchKey(_))) => Ok(None), Err(e) => bail!(e), } } - /// Gets an authenticated S3Client, creating it if it doesn't already exist. - fn authenticated_client(&self) -> &S3Client { - self.authenticated_client.get_or_init(|| { - S3Client::new_with( - utils::http_client_with_timeout().unwrap(), - AwsChainCredentialsProvider::new(), - self.region.clone(), - ) - }) + /// Gets an authenticated S3 client, creating it if it doesn't already exist + /// within &self. + async fn authenticated_client(&self) -> &Client { + self.authenticated_client + .get_or_init(|| async { + let config = self.default_aws_sdk_config_loader().load().await; + Client::new(&config) + }) + .await } - /// Gets an anonymous S3Client, creating it if it doesn't already exist. + /// Gets an anonymous S3 client, creating it if it doesn't already exist globally. /// An anonymous client doesn't have AWS credentials and will not sign S3 - /// requests with any credentials. + /// requests with any credentials. We globally cache the clients per region to avoid + /// expensive DNS / TCP initialization. /// We've experienced an inability to make GetObjectRequests to public /// S3 buckets when signing with credentials from an AWS account not from the - /// S3 bucket's AWS account. - fn anonymous_client(&self) -> &S3Client { - self.anonymous_client.get_or_init(|| { - // By default, these credentials are anonymous, see https://docs.rs/rusoto_credential/latest/rusoto_credential/struct.AwsCredentials.html#anonymous-example - let credentials = AwsCredentials::default(); - assert!(credentials.is_anonymous(), "AWS credentials not anonymous"); - - S3Client::new_with( - utils::http_client_with_timeout().unwrap(), - StaticProvider::from(credentials), - self.region.clone(), - ) + /// S3 bucket's AWS account. Additionally, this allows relayer operators to not + /// require AWS credentials. + async fn anonymous_client(&self) -> Client { + let cell = get_anonymous_client_cache() + .entry(self.region.clone()) + .or_default(); + + cell.get_or_init(|| async { + let config = self + .default_aws_sdk_config_loader() + // Make anonymous, important to not require AWS credentials + // to operate the relayer + .no_credentials() + .load() + .await; + Client::new(&config) }) + .await + .clone() + } + + /// A default ConfigLoader with timeout, region, and behavior version. + /// Unless overriden, credentials will be loaded from the env. + fn default_aws_sdk_config_loader(&self) -> aws_config::ConfigLoader { + ConfigLoader::default() + .timeout_config( + TimeoutConfig::builder() + .operation_timeout(S3_REQUEST_TIMEOUT) + .build(), + ) + .behavior_version(BehaviorVersion::latest()) + .region(self.region.clone()) } fn get_composite_key(&self, key: String) -> String { @@ -211,9 +230,9 @@ impl CheckpointSyncer for S3Storage { fn announcement_location(&self) -> String { match self.folder.as_deref() { - None | Some("") => format!("s3://{}/{}", self.bucket, self.region.name()), + None | Some("") => format!("s3://{}/{}", self.bucket, self.region), Some(folder_str) => { - format!("s3://{}/{}/{}", self.bucket, self.region.name(), folder_str) + format!("s3://{}/{}/{}", self.bucket, self.region, folder_str) } } } @@ -233,3 +252,31 @@ impl CheckpointSyncer for S3Storage { .map_err(Into::into) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_announcement_location() { + // Test with a folder + let s3_storage = S3Storage::new( + "test-bucket".to_string(), + Some("test-folder".to_string()), + Region::new("us-east-1"), + None, + ); + let location = s3_storage.announcement_location(); + assert_eq!(location, "s3://test-bucket/us-east-1/test-folder"); + + // Test without a folder + let s3_storage = S3Storage::new( + "test-bucket".to_string(), + None, + Region::new("us-east-1"), + None, + ); + let location = s3_storage.announcement_location(); + assert_eq!(location, "s3://test-bucket/us-east-1"); + } +} diff --git a/rust/main/hyperlane-core/src/traits/pending_operation.rs b/rust/main/hyperlane-core/src/traits/pending_operation.rs index e2e9b702595..1e00e19807b 100644 --- a/rust/main/hyperlane-core/src/traits/pending_operation.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation.rs @@ -156,7 +156,6 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { fn reset_attempts(&mut self); /// Set the number of times this operation has been retried. - #[cfg(any(test, feature = "test-utils"))] fn set_retries(&mut self, retries: u32); /// Get the number of times this operation has been retried. diff --git a/rust/main/utils/abigen/src/lib.rs b/rust/main/utils/abigen/src/lib.rs index 3fe21a55bb4..18b1811165c 100644 --- a/rust/main/utils/abigen/src/lib.rs +++ b/rust/main/utils/abigen/src/lib.rs @@ -68,6 +68,8 @@ pub fn generate_bindings_for_dir( /// Generate the bindings for a given ABI and return the new module name. Will /// create a file within the designated path with the correct `{module_name}.rs` /// format. +// We allow unused variables due to some feature flagging. +#[allow(unused_variables)] pub fn generate_bindings( contract_path: impl AsRef, output_dir: impl AsRef, From 79f40eb05524d3cf0673beed5b80eaeef6bd4e0a Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:55:49 -0400 Subject: [PATCH 008/223] feat: Update check-warp-deploy to fetch all warp route configurations from the registry and checks them (#5864) ### Description This PR updates check-warp-deploy to fetch all warp route configurations from the registry and checks them. ### Drive-by changes ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5242 ### Backward compatibility Yes ### Testing Manual --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- typescript/infra/config/warp.ts | 26 +++++++++++++++++ .../infra/scripts/check/check-warp-deploy.ts | 29 ++++++++++++------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index c2a4a22b70c..7dc05c485cb 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -162,6 +162,32 @@ async function getConfigFromMergedRegistry( return populateWarpRouteMailboxAddresses(warpRoute, registry); } +/** + * Retrieves all Warp configurations for the specified Warp route ID by fetching it from the MergedRegistry + * Also, populates their mailbox + * Will return in the form { [warRouteId]: { ...config } } + */ +export async function getWarpConfigMapFromMergedRegistry( + registryUris: string[], +): Promise>> { + const registry = getRegistry({ + registryUris, + enableProxy: true, + }); + const warpRouteMap = await registry.getWarpDeployConfigs(); + assert( + warpRouteMap, + `Warp route Configs not found for registry URIs: ${registryUris.join( + ', ', + )}`, + ); + return promiseObjAll( + objMap(warpRouteMap, async (_, warpRouteConfig) => + populateWarpRouteMailboxAddresses(warpRouteConfig, registry), + ), + ); +} + /** * Populates warp route configuration by filling in mailbox addresses for each chain entry * @param warpRoute The warp route configuration diff --git a/typescript/infra/scripts/check/check-warp-deploy.ts b/typescript/infra/scripts/check/check-warp-deploy.ts index 6fc578bb819..27d1f38d281 100644 --- a/typescript/infra/scripts/check/check-warp-deploy.ts +++ b/typescript/infra/scripts/check/check-warp-deploy.ts @@ -1,11 +1,13 @@ import chalk from 'chalk'; import { Gauge, Registry } from 'prom-client'; +import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry'; import { ChainName } from '@hyperlane-xyz/sdk'; +import { assert } from '@hyperlane-xyz/utils'; import { WarpRouteIds } from '../../config/environments/mainnet3/warp/warpIds.js'; -import { getWarpAddresses } from '../../config/registry.js'; -import { warpConfigGetterMap } from '../../config/warp.js'; +import { DEFAULT_REGISTRY_URI } from '../../config/registry.js'; +import { getWarpConfigMapFromMergedRegistry } from '../../config/warp.js'; import { submitMetrics } from '../../src/utils/metrics.js'; import { Modules, getWarpRouteIdsInteractive } from '../agent-utils.js'; import { getEnvironmentConfig } from '../core-utils.js'; @@ -40,6 +42,11 @@ async function main() { WarpRouteIds.ArbitrumBaseBlastBscEthereumGnosisLiskMantleModeOptimismPolygonScrollZeroNetworkZoraMainnet, ]; + const registries = [DEFAULT_GITHUB_REGISTRY, DEFAULT_REGISTRY_URI]; + const warpCoreConfigMap = await getWarpConfigMapFromMergedRegistry( + registries, + ); + let warpIdsToCheck: string[]; if (interactive) { warpIdsToCheck = await getWarpRouteIdsInteractive(); @@ -47,22 +54,21 @@ async function main() { console.log(chalk.yellow('Skipping the following warp routes:')); routesToSkip.forEach((route) => console.log(chalk.yellow(`- ${route}`))); - warpIdsToCheck = Object.keys(warpConfigGetterMap).filter( + warpIdsToCheck = Object.keys(warpCoreConfigMap).filter( (warpRouteId) => !routesToSkip.includes(warpRouteId), ); } - // Determine which chains have warp configs - const chainsWithWarpConfigs = warpIdsToCheck.reduce((chains, warpRouteId) => { - const warpAddresses = getWarpAddresses(warpRouteId); - Object.keys(warpAddresses).forEach((chain) => chains.add(chain)); + // Get all the chains from warpCoreConfigMap. Used to initialize the MultiProvider. + const warpConfigChains = warpIdsToCheck.reduce((chains, warpRouteId) => { + const warpConfigs = warpCoreConfigMap[warpRouteId]; + assert(warpConfigs, `Config not found in registry for ${warpRouteId}`); + Object.keys(warpConfigs).forEach((chain) => chains.add(chain)); return chains; }, new Set()); console.log( - `Found warp configs for chains: ${Array.from(chainsWithWarpConfigs) - .sort() - .join(', ')}`, + `Found warp configs for chains: ${Array.from(warpConfigChains).join(', ')}`, ); // Get the multiprovider once to avoid recreating it for each warp route @@ -74,7 +80,7 @@ async function main() { undefined, undefined, undefined, - Array.from(chainsWithWarpConfigs), + Array.from(warpConfigChains), ); // TODO: consider retrying this if check throws an error @@ -93,6 +99,7 @@ async function main() { fork, false, multiProvider, + registries, ); await governor.check(); From 254cee2588ffb949e1a0f43414b7a26ac290019e Mon Sep 17 00:00:00 2001 From: Jamin <57451149+yjamin@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:28:59 +0200 Subject: [PATCH 009/223] feat: higher s3 DoS resilience (#5877) ### Description Contains following changes: - New `MetadataBuildError` when the MultiSig contains more than `50` validators - Fetch metadata in S3 buckets and check for size constraints first (50KiB) - Stricter S3 Request Timeout when fetching objects (30s -> 10s) - Parallelized checkpoint fetching logic: - Use `1-10` workers to fetch signatures for one checkpoint index ### Drive-by changes None ### Related issues [Github](https://github.com/hyperlane-xyz/issues/issues/401) - [Linear](https://linear.app/hyperlane-xyz/issue/BACK-130/pre-tge-fixes-for-known-dos-vectors-are-documented-known) Also includes https://linear.app/hyperlane-xyz/issue/BACK-146/make-multisigcheckpointsyncerget-validator-latest-checkpoints-and ### Backward compatibility ### Testing Wrote a unit test for ensuring the logic stays the same. Can be executed with ``` cargo test --package hyperlane-base --lib -- types::multisig::test::test_s3_checkpoint_syncer --exact --show-output ``` --- .../agents/relayer/src/msg/metadata/base.rs | 2 + .../relayer/src/msg/metadata/base_builder.rs | 3 +- .../relayer/src/msg/metadata/multisig/base.rs | 15 ++ .../agents/relayer/src/msg/pending_message.rs | 4 + .../main/hyperlane-base/src/types/multisig.rs | 253 +++++++++++++++--- .../hyperlane-base/src/types/s3_storage.rs | 39 ++- 6 files changed, 272 insertions(+), 44 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/base.rs b/rust/main/agents/relayer/src/msg/metadata/base.rs index 5d9174fc8fe..fe9561676cb 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base.rs @@ -38,6 +38,8 @@ pub enum MetadataBuildError { MaxIsmDepthExceeded(u32), #[error("Exceeded max count when building metadata ({0})")] MaxIsmCountReached(u32), + #[error("Exceeded max validator count when building metadata ({0})")] + MaxValidatorCountReached(u32), #[error("Aggregation threshold not met ({0})")] AggregationThresholdNotMet(u32), } diff --git a/rust/main/agents/relayer/src/msg/metadata/base_builder.rs b/rust/main/agents/relayer/src/msg/metadata/base_builder.rs index ca029a05e1d..188d55c3448 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base_builder.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base_builder.rs @@ -239,8 +239,7 @@ impl BuildsBaseMetadata for BaseMetadataBuilder { } Ok(MultisigCheckpointSyncer::new( checkpoint_syncers, - self.metrics.clone(), - app_context, + app_context.map(|ctx| (self.metrics.clone(), ctx)), )) } } diff --git a/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs b/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs index dbb91eba451..0f610b82efc 100644 --- a/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs @@ -38,6 +38,8 @@ pub enum MetadataToken { Signatures, } +const MAX_VALIDATOR_SET_SIZE: usize = 50; + #[async_trait] pub trait MultisigIsmMetadataBuilder: AsRef + Send + Sync { fn module_type(&self) -> ModuleType; @@ -185,6 +187,19 @@ impl MetadataBuilder for T { return Err(MetadataBuildError::CouldNotFetch); } + // Dismiss large validator sets + if validators.len() > MAX_VALIDATOR_SET_SIZE { + info!( + ?ism_address, + validator_count = validators.len(), + max_validator_count = MAX_VALIDATOR_SET_SIZE, + "Skipping metadata: Too many validators in ISM" + ); + return Err(MetadataBuildError::MaxValidatorCountReached( + validators.len() as u32, + )); + } + info!(hyp_message=?message, ?validators, threshold, "List of validators and threshold for message"); let checkpoint_syncer = match self diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index 6c623ca3dcc..572e89aec70 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -1015,6 +1015,10 @@ impl PendingMessage { warn!(threshold, "Aggregation threshold not met"); self.on_reprepare(Some(err), ReprepareReason::CouldNotFetchMetadata) } + MetadataBuildError::MaxValidatorCountReached(count) => { + warn!(count, "Max validator count reached"); + self.on_reprepare(Some(err), ReprepareReason::ErrorBuildingMetadata) + } }); let build_metadata_end = Instant::now(); diff --git a/rust/main/hyperlane-base/src/types/multisig.rs b/rust/main/hyperlane-base/src/types/multisig.rs index 35fe3715098..9cbbfb40117 100644 --- a/rust/main/hyperlane-base/src/types/multisig.rs +++ b/rust/main/hyperlane-base/src/types/multisig.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use derive_new::new; use eyre::Result; +use futures::StreamExt; use tracing::{debug, instrument, warn}; use hyperlane_core::{ @@ -17,8 +18,7 @@ use crate::{CheckpointSyncer, CoreMetrics}; pub struct MultisigCheckpointSyncer { /// The checkpoint syncer for each valid validator signer address checkpoint_syncers: HashMap>, - metrics: Arc, - app_context: Option, + metrics: Option<(Arc, String)>, // first arg is the metrics, second is the app context } impl MultisigCheckpointSyncer { @@ -31,42 +31,56 @@ impl MultisigCheckpointSyncer { validators: &[H256], origin: &HyperlaneDomain, destination: &HyperlaneDomain, - ) -> Vec { + ) -> Vec<(H160, u32)> { // Get the latest_index from each validator's checkpoint syncer. // If a validator does not return a latest index, None is recorded so // this can be surfaced in the metrics. let mut latest_indices: HashMap> = HashMap::with_capacity(validators.len()); - for validator in validators { - let address = H160::from(*validator); - debug!( - ?address, - "Getting latest checkpoint from validator via checkpoint syncer", - ); - if let Some(checkpoint_syncer) = self.checkpoint_syncers.get(&address) { - // Gracefully handle errors getting the latest_index - match checkpoint_syncer.latest_index().await { - Ok(Some(index)) => { - debug!(?address, ?index, "Validator returned latest index"); - latest_indices.insert(H160::from(*validator), Some(index)); - } - result => { - debug!( - ?address, - ?result, - "Failed to get latest index from validator" - ); - latest_indices.insert(H160::from(*validator), None); - } + let syncer = validators + .iter() + .map(|v| H160::from(*v)) + .filter_map(|v| { + if let Some(checkpoint_syncer) = self.checkpoint_syncers.get(&v) { + Some((v, checkpoint_syncer)) + } else { + warn!(validator=%v, "Checkpoint syncer is not provided for validator"); + None + } + }) + .collect::>(); + let futures = syncer + .iter() + .map( + |(v, checkpoint_syncer)| async move { (v, checkpoint_syncer.latest_index().await) }, + ) + .collect::>(); + + let validator_index_results = futures::stream::iter(futures) + .buffer_unordered(10) + .collect::>() + .await; + + for (validator, latest_index) in validator_index_results { + match latest_index { + Ok(Some(index)) => { + debug!(?validator, ?index, "Validator returned latest index"); + latest_indices.insert(*validator, Some(index)); + } + result => { + debug!( + ?validator, + ?result, + "Failed to get latest index from validator" + ); + latest_indices.insert(*validator, None); } - } else { - warn!(?address, "Checkpoint syncer is not provided for validator"); } } - if let Some(app_context) = &self.app_context { - self.metrics + if let Some((metrics, app_context)) = &self.metrics { + metrics .validator_metrics .set_validator_latest_checkpoints( origin, @@ -78,7 +92,10 @@ impl MultisigCheckpointSyncer { } // Filter out any validators that did not return a latest index - latest_indices.values().copied().flatten().collect() + latest_indices + .into_iter() + .filter_map(|(address, index)| index.map(|i| (address, i))) + .collect() } /// Attempts to get the latest checkpoint with a quorum of signatures among @@ -119,8 +136,15 @@ impl MultisigCheckpointSyncer { // Sort in descending order. The n'th index will represent // the highest index for which we (supposedly) have (n+1) signed checkpoints - latest_indices.sort_by(|a, b| b.cmp(a)); - if let Some(&highest_quorum_index) = latest_indices.get(threshold - 1) { + latest_indices.sort_by(|a, b| b.1.cmp(&a.1)); + + // create a slice with the sorted validators + let validators = latest_indices + .iter() + .map(|(address, _)| H256::from(*address)) + .collect::>(); + + if let Some(&(_, highest_quorum_index)) = latest_indices.get(threshold - 1) { // The highest viable checkpoint index is the minimum of the highest index // we (supposedly) have a quorum for, and the maximum index for which we can // generate a proof. @@ -129,9 +153,10 @@ impl MultisigCheckpointSyncer { debug!(%start_index, %highest_quorum_index, "Highest quorum index is below the minimum index"); return Ok(None); } + for index in (minimum_index..=start_index).rev() { if let Ok(Some(checkpoint)) = - self.fetch_checkpoint(validators, threshold, index).await + self.fetch_checkpoint(&validators, threshold, index).await { return Ok(Some(checkpoint)); } @@ -157,14 +182,39 @@ impl MultisigCheckpointSyncer { let mut signed_checkpoints_per_root: HashMap> = HashMap::new(); - for validator in validators.iter() { - let addr = H160::from(*validator); - if let Some(checkpoint_syncer) = self.checkpoint_syncers.get(&addr) { + // we iterate in batches of N=threshold*1.5 to avoid waiting for all validators. + // This reaches a quorum faster without having to fetch all the signatures. + + // Also limit this number in case we have a large threshold + let batch_size = (threshold as f64 * 1.5) as usize; + let batch_size = batch_size.clamp(1, 10); + + for validators in validators.chunks(batch_size) { + // Go through each validator and get the checkpoint syncer. + // Create a future for each validator that fetches its signed checkpoint + let futures = validators + .iter() + .filter_map(|address| { + if let Some(syncer) = self.checkpoint_syncers.get(&H160::from(*address)) { + Some((address, syncer)) + } else { + debug!(validator=%address, "Checkpoint syncer not found"); + None + } + }) + .map(|(address, syncer)| { + let checkpoint_syncer = syncer.clone(); + async move { (address, checkpoint_syncer.fetch_checkpoint(index).await) } + }) + .collect::>(); + + let checkpoints = futures::future::join_all(futures).await; + + for (validator, checkpoint) in checkpoints { // Gracefully ignore an error fetching the checkpoint from a validator's // checkpoint syncer, which can happen if the validator has not // signed the checkpoint at `index`. - if let Ok(Some(signed_checkpoint)) = checkpoint_syncer.fetch_checkpoint(index).await - { + if let Ok(Some(signed_checkpoint)) = checkpoint { // If the signed checkpoint is for a different index, ignore it if signed_checkpoint.value.index != index { debug!( @@ -216,12 +266,135 @@ impl MultisigCheckpointSyncer { "Unable to find signed checkpoint" ); } - } else { - debug!(%validator, "Unable to find checkpoint syncer"); - continue; } } debug!("No quorum checkpoint found for message"); Ok(None) } } + +#[cfg(test)] +mod test { + + use std::str::FromStr; + + use aws_config::Region; + use hyperlane_core::KnownHyperlaneDomain; + + use crate::S3Storage; + + use super::*; + + #[tokio::test] + #[ignore] + #[tracing_test::traced_test] + async fn test_s3_checkpoint_syncer() { + let validators = vec![ + ( + "0x4d966438fe9E2B1e7124c87bBB90cB4F0F6C59a1", + ( + "hyperlane-mainnet3-arbitrum-validator-0".to_string(), + Region::new("us-east-1"), + ), + ), + ( + "0x5450447aeE7B544c462C9352bEF7cAD049B0C2Dc", + ( + "zpl-hyperlane-v3-arbitrum".to_string(), + Region::new("eu-central-1"), + ), + ), + ( + "0xec68258A7c882AC2Fc46b81Ce80380054fFB4eF2", + ( + "dsrv-hyperlane-v3-validator-signatures-validator7-arbitrum".to_string(), + Region::new("eu-central-1"), + ), + ), + ( + "0x38C7A4ca1273ead2E867d096aDBCDD0e2AcB21D8", + ( + "hyperlane-v3-validator-signatures-everstake-one-arbitrum".to_string(), + Region::new("us-east-2"), + ), + ), + ( + "0xb3AC35d3988bCA8C2fFD195b1c6bee18536B317b", + ( + "can-outrun-imperial-starships-v3-arbitrum".to_string(), + Region::new("eu-west-1"), + ), + ), + ( + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + ( + "hyperlane-validator-signatures-bwarelabs-ethereum/arbitrum".to_string(), + Region::new("eu-north-1"), + ), + ), + ( + "0xc4b877Dd49ABe9B38EA9184683f9664c0F9FADe3", + ( + "arbitrum-validator-signatures/arbitrum".to_string(), + Region::new("us-east-1"), + ), + ), + ]; + + let syncers = validators + .iter() + .map(|(address, (bucket, region))| { + let syncer = S3Storage::new(bucket.clone(), None, region.clone(), None); + ( + H160::from_str(address).unwrap(), + Arc::new(syncer) as Arc, + ) + }) + .collect::>(); + + // Create a multisig checkpoint syncer + let multisig_syncer = MultisigCheckpointSyncer::new(syncers, None); + + let validators = validators + .iter() + .map(|(address, _)| { + let address: H256 = H160::from_str(address).unwrap().into(); + address + }) + .collect::>(); + + // get the latest checkpoint from each validator + let mut latest_indices = multisig_syncer + .get_validator_latest_checkpoints_and_update_metrics( + validators.as_slice(), + &HyperlaneDomain::Known(KnownHyperlaneDomain::Arbitrum), + &HyperlaneDomain::Known(KnownHyperlaneDomain::Arbitrum), + ) + .await; + latest_indices.sort_by(|a, b| b.cmp(a)); + + let lowest_index = *latest_indices.last().unwrap(); + + let start_time = std::time::Instant::now(); + + for threshold in 2..=6 { + println!("Starting to fetch checkpoints with threshold {}", threshold); + if let Some(&(_, highest_quorum_index)) = latest_indices.get(threshold - 1) { + let result = multisig_syncer + .fetch_checkpoint_in_range( + validators.as_slice(), + threshold, + lowest_index.1, + highest_quorum_index, + &HyperlaneDomain::Known(KnownHyperlaneDomain::Arbitrum), + &HyperlaneDomain::Known(KnownHyperlaneDomain::Arbitrum), + ) + .await; + assert!(result.is_ok(), "Failed to fetch checkpoint"); + } + } + + let elapsed = start_time.elapsed(); + println!("Fetched checkpoints in {}ms", elapsed.as_millis()); + } +} diff --git a/rust/main/hyperlane-base/src/types/s3_storage.rs b/rust/main/hyperlane-base/src/types/s3_storage.rs index 307c0586123..a014a9a9f80 100644 --- a/rust/main/hyperlane-base/src/types/s3_storage.rs +++ b/rust/main/hyperlane-base/src/types/s3_storage.rs @@ -3,7 +3,9 @@ use std::{fmt, sync::OnceLock, time::Duration}; use async_trait::async_trait; use aws_config::{timeout::TimeoutConfig, BehaviorVersion, ConfigLoader, Region}; use aws_sdk_s3::{ - error::SdkError, operation::get_object::GetObjectError as SdkGetObjectError, Client, + error::SdkError, + operation::{get_object::GetObjectError as SdkGetObjectError, head_object::HeadObjectError}, + Client, }; use dashmap::DashMap; use derive_new::new; @@ -15,7 +17,8 @@ use tokio::sync::OnceCell; use crate::CheckpointSyncer; /// The timeout for all S3 operations. -const S3_REQUEST_TIMEOUT: Duration = Duration::from_secs(30); +const S3_REQUEST_TIMEOUT: Duration = Duration::from_secs(10); +const S3_MAX_OBJECT_SIZE: i64 = 50 * 1024; // 50KiB #[derive(Clone, new)] /// Type for reading/writing to S3 @@ -70,7 +73,39 @@ impl S3Storage { Ok(()) } + /// Check if the metadata for the object satisfies our size constraints. + /// If the object is too big, we return an error. + async fn check_metadata(&self, key: String) -> Result { + let metadata_req = self + .anonymous_client() + .await + .head_object() + .bucket(self.bucket.clone()) + .key(self.get_composite_key(key.clone())) + .send() + .await; + match metadata_req { + Ok(value) => match value.content_length { + Some(length) if length >= S3_MAX_OBJECT_SIZE => { + bail!("Object size for key {key} is too big: {}KiB", length / 1024); + } + Some(_) => Ok(true), + None => Ok(false), + }, + Err(SdkError::ServiceError(err)) => match err.err() { + HeadObjectError::NotFound(_) => Ok(false), + _ => bail!(err.into_err()), + }, + Err(e) => bail!(e), + } + } + async fn anonymously_read_from_bucket(&self, key: String) -> Result>> { + // check for metadata first + if !self.check_metadata(key.clone()).await? { + return Ok(None); + } + let get_object_result = self .anonymous_client() .await From e0fb68acacc6d48d89156cd1c9fb75435955da72 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 16 Apr 2025 17:29:14 +0800 Subject: [PATCH 010/223] feat: Routing ISM caching (#5932) ### Description adds caching via `message.origin` for routing ISM ### Related issues - fixes https://linear.app/hyperlane-xyz/issue/ENG-1283/routing-ism-caching-for-the-default-ism-hyper-warp-route ### Backward compatibility Yes ### Testing None --- .../relayer/src/msg/metadata/routing.rs | 84 +++++++++++++++++-- .../hyperlane-base/src/types/s3_storage.rs | 2 +- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/routing.rs b/rust/main/agents/relayer/src/msg/metadata/routing.rs index 3fedd4eb3a2..7d990284725 100644 --- a/rust/main/agents/relayer/src/msg/metadata/routing.rs +++ b/rust/main/agents/relayer/src/msg/metadata/routing.rs @@ -1,13 +1,14 @@ use async_trait::async_trait; use derive_more::Deref; use derive_new::new; +use hyperlane_base::cache::FunctionCallCache; use tracing::instrument; -use hyperlane_core::{HyperlaneMessage, H256}; +use hyperlane_core::{HyperlaneMessage, ModuleType, H256}; use super::{ - base::MessageMetadataBuildParams, MessageMetadataBuilder, Metadata, MetadataBuildError, - MetadataBuilder, + base::MessageMetadataBuildParams, IsmCachePolicy, MessageMetadataBuilder, Metadata, + MetadataBuildError, MetadataBuilder, }; #[derive(Clone, Debug, new, Deref)] @@ -30,10 +31,79 @@ impl MetadataBuilder for RoutingIsmMetadataBuilder { .build_routing_ism(ism_address) .await .map_err(|err| MetadataBuildError::FailedToBuild(err.to_string()))?; - let module = ism - .route(message) - .await - .map_err(|err| MetadataBuildError::FailedToBuild(err.to_string()))?; + + let ism_domain = ism.domain().name(); + let message_domain = self.base.base_builder().origin_domain(); + let fn_key = "route"; + + let cache_policy = self + .base_builder() + .ism_cache_policy_classifier() + .get_cache_policy(self.root_ism, ism.domain(), ModuleType::Routing) + .await; + + let cache_result: Option = match cache_policy { + // if cache is ISM specific, we use the message origin for caching + IsmCachePolicy::IsmSpecific => { + let params_cache_key = (ism.address(), message.origin); + self.base_builder() + .cache() + .get_cached_call_result(ism_domain, fn_key, ¶ms_cache_key) + .await + } + // if cache is Message specific, we use the message id for caching + IsmCachePolicy::MessageSpecific => { + let params_cache_key = (ism.address(), message.id()); + self.base_builder() + .cache() + .get_cached_call_result(ism_domain, fn_key, ¶ms_cache_key) + .await + } + } + .map_err(|err| { + tracing::warn!(error = %err, "Error when caching call result for {:?}", fn_key); + }) + .ok() + .flatten(); + + let module = + match cache_result { + Some(result) => result, + None => { + let module = ism + .route(message) + .await + .map_err(|err| MetadataBuildError::FailedToBuild(err.to_string()))?; + + // store result in cache + match cache_policy { + IsmCachePolicy::IsmSpecific => { + let params_cache_key = (ism.address(), message.origin); + self.base_builder().cache().cache_call_result( + message_domain.name(), + fn_key, + ¶ms_cache_key, + &module, + ).await + } + IsmCachePolicy::MessageSpecific => { + let params_cache_key = (ism.address(), message.id()); + self.base_builder().cache().cache_call_result( + message_domain.name(), + fn_key, + ¶ms_cache_key, + &module, + ).await + } + } + .map_err(|err| { + tracing::warn!(error = %err, "Error when caching call result for {:?}", fn_key); + }) + .ok(); + module + } + }; + self.base.build(module, message, params).await } } diff --git a/rust/main/hyperlane-base/src/types/s3_storage.rs b/rust/main/hyperlane-base/src/types/s3_storage.rs index a014a9a9f80..8aa6ff225dc 100644 --- a/rust/main/hyperlane-base/src/types/s3_storage.rs +++ b/rust/main/hyperlane-base/src/types/s3_storage.rs @@ -163,7 +163,7 @@ impl S3Storage { } /// A default ConfigLoader with timeout, region, and behavior version. - /// Unless overriden, credentials will be loaded from the env. + /// Unless overridden, credentials will be loaded from the env. fn default_aws_sdk_config_loader(&self) -> aws_config::ConfigLoader { ConfigLoader::default() .timeout_config( From 1ccc10b62be4b54c0c5d688eaa298ee313478fe1 Mon Sep 17 00:00:00 2001 From: Jamin <57451149+yjamin@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:30:31 +0200 Subject: [PATCH 011/223] feat: async checkpoint syncer builder (#5941) ### Description Build the `checkpoint_syncer` in parallel for each validator in the `BaseMetadataBuilder` - limits the number of concurrent requests to 10 ### Drive-by changes ### Related issues [Linear](https://linear.app/hyperlane-xyz/issue/BACK-148/make-basemetadatabuilderbuild-checkpoint-syncer-more-concurrent) ### Backward compatibility ### Testing Ran E2E tests --- .../relayer/src/msg/metadata/base_builder.rs | 121 +++++++++++------- 1 file changed, 74 insertions(+), 47 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/base_builder.rs b/rust/main/agents/relayer/src/msg/metadata/base_builder.rs index 188d55c3448..4973e53728f 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base_builder.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base_builder.rs @@ -5,6 +5,7 @@ use std::{collections::HashMap, fmt::Debug, str::FromStr, sync::Arc}; use derive_new::new; use eyre::Context; +use futures::{stream, StreamExt}; use tokio::sync::RwLock; use tracing::{debug, info, warn}; @@ -180,63 +181,89 @@ impl BuildsBaseMetadata for BaseMetadataBuilder { // Only use the most recently announced location for now. let mut checkpoint_syncers: HashMap> = HashMap::new(); - for (&validator, validator_storage_locations) in validators.iter().zip(storage_locations) { - debug!(hyp_message=?message, ?validator, ?validator_storage_locations, "Validator and its storage locations for message"); - for storage_location in validator_storage_locations.iter().rev() { - let Ok(config) = CheckpointSyncerConf::from_str(storage_location) else { - debug!( - ?validator, - ?storage_location, - "Could not parse checkpoint syncer config for validator" - ); - continue; - }; - // If this is a LocalStorage based checkpoint syncer and it's not - // allowed, ignore it - if !self.allow_local_checkpoint_syncers - && matches!(config, CheckpointSyncerConf::LocalStorage { .. }) - { - debug!( - ?config, - "Ignoring disallowed LocalStorage based checkpoint syncer" - ); - continue; + let result = validators + .iter() + .zip(storage_locations) + .filter_map(|(validator, validator_storage_locations)| { + debug!(hyp_message=?message, ?validator, ?validator_storage_locations, "Validator and its storage locations for message"); + if validator_storage_locations.is_empty() { + // If the validator has not announced any storage locations, we skip it + // and log a warning. + warn!(?validator, "Validator has not announced any storage locations; see https://docs.hyperlane.xyz/docs/operators/validators/announcing-your-validator"); + return None; } - match config.build_and_validate(None).await { - Ok(checkpoint_syncer) => { - // found the syncer for this validator - checkpoint_syncers.insert(validator.into(), checkpoint_syncer.into()); - break; - } - Err(CheckpointSyncerBuildError::ReorgEvent(reorg_event)) => { - // If a reorg event has been posted to a checkpoint syncer, - // we refuse to build - return Err(CheckpointSyncerBuildError::ReorgEvent(reorg_event)); - } - Err(err) => { - debug!( - error=%err, - ?config, - ?validator, - "Error when loading checkpoint syncer; will attempt to use the next config" - ); + let future = async move { + // Reverse the order of storage locations to prefer the most recently announced + for storage_location in validator_storage_locations.iter().rev() { + let Ok(config) = CheckpointSyncerConf::from_str(storage_location) else { + debug!( + ?validator, + ?storage_location, + "Could not parse checkpoint syncer config for validator" + ); + continue; + }; + + // If this is a LocalStorage based checkpoint syncer and it's not + // allowed, ignore it + if !self.allow_local_checkpoint_syncers + && matches!(config, CheckpointSyncerConf::LocalStorage { .. }) + { + debug!( + ?config, + "Ignoring disallowed LocalStorage based checkpoint syncer" + ); + continue; + } + + match config.build_and_validate(None).await { + Ok(checkpoint_syncer) => { + // found the syncer for this validator + return Ok(Some((*validator, checkpoint_syncer))); + } + Err(CheckpointSyncerBuildError::ReorgEvent(reorg_event)) => { + // If a reorg event has been posted to a checkpoint syncer, + // we refuse to build + // This will result in a short circuit and return an error for the entire build process of all syncers + return Err(CheckpointSyncerBuildError::ReorgEvent(reorg_event)); + } + Err(err) => { + debug!( + error=%err, + ?config, + ?validator, + "Error when loading checkpoint syncer; will attempt to use the next config" + ); + } + } } - } - } - if checkpoint_syncers.get(&validator.into()).is_none() { - if validator_storage_locations.is_empty() { - warn!(?validator, "Validator has not announced any storage locations; see https://docs.hyperlane.xyz/docs/operators/validators/announcing-your-validator"); - } else { warn!( ?validator, ?validator_storage_locations, "No valid checkpoint syncer configs for validator" ); - } - } + Ok(None) + }; + Some(future) + }) + .collect::>(); + + let checkpoint_syncers_results = stream::iter(result) + .buffer_unordered(10) // Limit the number of concurrent tasks + .collect::>() + .await + .into_iter() + .collect::, _>>()? // Collect results into a single vector and return if any of them returns an error + .into_iter() + .flatten() // Flatten Option<_> + .collect::>(); + + for (validator, checkpoint_syncer) in checkpoint_syncers_results { + checkpoint_syncers.insert(validator.into(), checkpoint_syncer.into()); } + Ok(MultisigCheckpointSyncer::new( checkpoint_syncers, app_context.map(|ctx| (self.metrics.clone(), ctx)), From 98f51729a2a949a1e210b3583bd6fe3692d7049c Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Wed, 16 Apr 2025 10:43:38 +0100 Subject: [PATCH 012/223] feat: quiet down some AWS SDK logging (#5940) ### Description - Quiets down some noisy logs from #5928 ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/hyperlane-base/src/settings/trace/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/main/hyperlane-base/src/settings/trace/mod.rs b/rust/main/hyperlane-base/src/settings/trace/mod.rs index 9f06b3ac0b8..ff10f5743d0 100644 --- a/rust/main/hyperlane-base/src/settings/trace/mod.rs +++ b/rust/main/hyperlane-base/src/settings/trace/mod.rs @@ -76,6 +76,8 @@ impl TracingConfig { .with_target("tendermint", Level::Info) .with_target("tokio", Level::Debug) .with_target("tokio_util", Level::Debug) + .with_target("aws_smithy", Level::Info) + .with_target("aws_sdk", Level::Info) // Enable Trace level for Tokio if you want to use tokio-console // .with_target("tokio", Level::Trace) // .with_target("tokio_util", Level::Trace) From e6f6d61a0dbf911d847b59d1e43b7a2b31b3c4a5 Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:54:58 +0200 Subject: [PATCH 013/223] feat: Refactor ZKsync artifact generation and validation logic (#5920) ### Description This PR refactors the ZKsync artifact generation system and updates the Hardhat configuration: - Refactored artifact generation code for improved maintainability - Added validation to make sure only zksolc-compiled artifacts are processed - Updated zksolc compiler version from 1.5.3 to 1.5.12 to speed up compilation ### Drive-by changes - Improved code organization with configuration object - Refactored string templates to class-based static methods - Added better error handling for artifact validation ### Related issues None ### Backward compatibility Yes ### Testing Manual --- .changeset/famous-taxis-dream.md | 5 + solidity/generate-artifact-exports.mjs | 148 +++++++++++++------------ solidity/zk-hardhat.config.cts | 10 +- 3 files changed, 94 insertions(+), 69 deletions(-) create mode 100644 .changeset/famous-taxis-dream.md diff --git a/.changeset/famous-taxis-dream.md b/.changeset/famous-taxis-dream.md new file mode 100644 index 00000000000..5471470bf31 --- /dev/null +++ b/.changeset/famous-taxis-dream.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/core': minor +--- + +Refactor ZKsync artifact generation and validation logic diff --git a/solidity/generate-artifact-exports.mjs b/solidity/generate-artifact-exports.mjs index 69327ffda46..f55f463c807 100755 --- a/solidity/generate-artifact-exports.mjs +++ b/solidity/generate-artifact-exports.mjs @@ -3,41 +3,58 @@ import { basename, dirname, join } from 'path'; import { glob } from 'typechain'; import { fileURLToPath } from 'url'; -const cwd = process.cwd(); +const CONFIG = { + cwd: process.cwd(), + outputDir: 'dist/zksync/', + artifactsDir: 'artifacts', + artifactGlobs: [ + `!./artifacts-zk/!(build-info)/**/*.dbg.json`, + `./artifacts-zk/!(build-info)/**/+([a-zA-Z0-9_]).json`, + ], + formatIdentifier: 'hh-zksolc-artifact-1', +}; + const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const ROOT_OUTPUT_DIR = join(__dirname, 'dist/zksync/'); -const ARTIFACTS_OUTPUT_DIR = join(ROOT_OUTPUT_DIR, 'artifacts'); +const ROOT_OUTPUT_DIR = join(__dirname, CONFIG.outputDir); +const ARTIFACTS_OUTPUT_DIR = join(ROOT_OUTPUT_DIR, CONFIG.artifactsDir); /** * @notice Templates for TypeScript artifact generation */ -const TEMPLATES = { - JS_ARTIFACT: `\ -export const {name} = {artifact}; -`, - - DTS_ARTIFACT: `\ -import type { ZKSyncArtifact } from '../types.js'; - -export declare const {name}: ZKSyncArtifact; -`, +class Templates { + static jsArtifact(name, artifact) { + return `export const ${name} = ${JSON.stringify(artifact)};`; + } - JS_INDEX: `\ -{imports} + static dtsArtifact(name) { + return `import type { ZKSyncArtifact } from '../types.js'; +export declare const ${name}: ZKSyncArtifact;`; + } + static jsIndex(imports, exports) { + return `${imports} export const zkSyncContractArtifacts = [ -{exports} -]; -`, +${exports} +];`; + } - DTS_INDEX: `\ -import type { ZKSyncArtifact } from './types.js'; + static dtsIndex() { + return `import type { ZKSyncArtifact } from './types.js'; +export declare const zkSyncContractArtifacts: readonly ZKSyncArtifact[];`; + } -export declare const zkSyncContractArtifacts: readonly ZKSyncArtifact[]; -`, -}; + // Generates a single import line for a contract in index file + static importLine(name) { + return `import { ${name} } from './artifacts/${name}.js';`; + } + + // Generates a single export line for a contract in index file + static exportLine(name) { + return ` ${name},`; + } +} class ArtifactGenerator { constructor() { @@ -50,10 +67,7 @@ class ArtifactGenerator { * @return {string[]} Array of file paths matching the glob pattern */ getArtifactPaths() { - return glob(cwd, [ - `!./artifacts-zk/!(build-info)/**/*.dbg.json`, - `./artifacts-zk/!(build-info)/**/+([a-zA-Z0-9_]).json`, - ]); + return glob(CONFIG.cwd, CONFIG.artifactGlobs); } /** @@ -73,45 +87,6 @@ class ArtifactGenerator { return JSON.parse(content); } - /** - * @notice Generates JavaScript content for a contract artifact - */ - generateJavaScriptContent(name, artifact) { - return TEMPLATES.JS_ARTIFACT.replace('{name}', name).replace( - '{artifact}', - JSON.stringify(artifact, null, 2), - ); - } - - /** - * @notice Generates TypeScript declaration content for a contract artifact - */ - generateDeclarationContent(name) { - return TEMPLATES.DTS_ARTIFACT.replace('{name}', name); - } - - /** - * @notice Generates index file contents - */ - generateIndexContents(artifactNames) { - const imports = artifactNames - .map((name) => `import { ${name} } from './artifacts/${name}.js';`) - .join('\n'); - const exports = artifactNames.map((name) => ` ${name},`).join('\n'); - - const jsContent = TEMPLATES.JS_INDEX.replace('{imports}', imports).replace( - '{exports}', - exports, - ); - - const dtsContent = TEMPLATES.DTS_INDEX.replace( - '{imports}', - imports, - ).replace('{exports}', exports); - - return { jsContent, dtsContent }; - } - /** * @notice Processes a single artifact file */ @@ -124,15 +99,35 @@ class ArtifactGenerator { const artifact = await this.readArtifactFile(filePath); + /** + * @notice Validates that the artifact was compiled with zksolc + * + * Format examples: + * - Valid: "_format": "hh-zksolc-artifact-1" (compiled with zksolc) + * - Invalid: "_format": "hh-sol-artifact-1" (standard Solidity compilation) + */ + if ( + !artifact._format || + !artifact._format.includes(CONFIG.formatIdentifier) + ) { + throw new Error( + `Artifact ${name} validation failed: invalid _format property. Expected ${ + CONFIG.formatIdentifier + } but got '${ + artifact._format || 'undefined' + }'. It may not be properly compiled with zksolc.`, + ); + } + // Generate and write .js file - const jsContent = this.generateJavaScriptContent(name, artifact); + const jsContent = Templates.jsArtifact(name, artifact); await fs.writeFile( join(ROOT_OUTPUT_DIR, 'artifacts', `${name}.js`), jsContent, ); // Generate and write .d.ts file - const dtsContent = this.generateDeclarationContent(name); + const dtsContent = Templates.dtsArtifact(name); await fs.writeFile( join(ROOT_OUTPUT_DIR, 'artifacts', `${name}.d.ts`), dtsContent, @@ -141,6 +136,23 @@ class ArtifactGenerator { this.processedFiles.add(name); } + /** + * @notice Generates index file contents + */ + generateIndexContents(artifactNames) { + const imports = artifactNames + .map((name) => Templates.importLine(name)) + .join('\n'); + const exports = artifactNames + .map((name) => Templates.exportLine(name)) + .join('\n'); + + const jsContent = Templates.jsIndex(imports, exports); + const dtsContent = Templates.dtsIndex(); + + return { jsContent, dtsContent }; + } + async generate() { try { await this.createOutputDirectory(); diff --git a/solidity/zk-hardhat.config.cts b/solidity/zk-hardhat.config.cts index 645359382ae..2e6caed3043 100644 --- a/solidity/zk-hardhat.config.cts +++ b/solidity/zk-hardhat.config.cts @@ -10,10 +10,18 @@ import { rootHardhatConfig } from './rootHardhatConfig.cjs'; module.exports = { ...rootHardhatConfig, zksolc: { - version: '1.5.3', + version: '1.5.12', compilerSource: 'binary', enableEraVMExtensions: true, }, + defaultNetwork: 'ZKsyncInMemoryNode', + networks: { + ZKsyncInMemoryNode: { + url: 'http://127.0.0.1:8011', + ethNetwork: '', + zksync: true, + }, + }, paths: { sources: './contracts', cache: './cache-zk', From ccfbb6c6afe74c04df8fbb09973e02ebada878ef Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 16 Apr 2025 12:56:09 +0100 Subject: [PATCH 014/223] feat: add governance safes (#5943) ### Description - add governance safes --- .../mainnet3/governance/safe/regular.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 typescript/infra/config/environments/mainnet3/governance/safe/regular.ts diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts b/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts new file mode 100644 index 00000000000..acb47bbc64f --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts @@ -0,0 +1,27 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +export const regularSafes: ChainMap
= { + abstract: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + arbitrum: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + base: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + berachain: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + blast: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + bsc: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + ethereum: '0x562Dfaac27A84be6C96273F5c9594DA1681C0DA7', + fraxtal: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + hyperevm: '0x290Eb7bbf939A36B2c350a668c04815E49757eDC', + linea: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + mantapacific: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + mode: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + optimism: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + sei: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + sophon: '0x113d3a19031Fe5DB58884D6aa54545dD4De499c0', + swell: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + taiko: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + treasure: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zeronetwork: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zksync: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zklink: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zircuit: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', +}; From a646f9ca1fd49044c696d971538b7a04f94f375d Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:25:04 +0200 Subject: [PATCH 015/223] feat: zksync deployment logic (#5208) ### Description This PR adds ZKSync-specific deployment logic to the SDK, introducing specialized utilities for contract deployment, artifact management, and technical stack compatibility checks for ZKSync. [PR to feat/zksync-sdk-provider ](https://github.com/txfusion/hyperlane-monorepo/pull/21) ### Drive-by changes - Introduced ZKSyncDeployer class and utils for fetching zksync contract artifacts - Added ZkSync to ExplorerFamily enum - Added checks for ISM Compatibility ### Related issues https://linear.app/hyperlane-xyz/issue/ENG-1151 ### Backward compatibility Yes ### Testing Build passing --------- Co-authored-by: Morteza Shojaei <31728528+mortezashojaei@users.noreply.github.com> Co-authored-by: mshojaei-txfusion <138107084+mshojaei-txfusion@users.noreply.github.com> Co-authored-by: Le Yu <6251863+ltyu@users.noreply.github.com> Co-authored-by: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> --- .changeset/eleven-cows-dance.md | 5 + typescript/sdk/src/deploy/proxy.ts | 11 +- .../sdk/src/deploy/proxyFactoryUtils.ts | 18 ++ typescript/sdk/src/hook/types.ts | 18 ++ typescript/sdk/src/index.ts | 7 +- typescript/sdk/src/ism/types.ts | 13 ++ typescript/sdk/src/ism/utils.ts | 35 ++++ .../sdk/src/metadata/chainMetadataTypes.ts | 1 + typescript/sdk/src/utils/zksync.ts | 32 +++ typescript/sdk/src/zksync/ZKSyncDeployer.ts | 185 ++++++++++++++++++ 10 files changed, 320 insertions(+), 5 deletions(-) create mode 100644 .changeset/eleven-cows-dance.md create mode 100644 typescript/sdk/src/deploy/proxyFactoryUtils.ts create mode 100644 typescript/sdk/src/utils/zksync.ts create mode 100644 typescript/sdk/src/zksync/ZKSyncDeployer.ts diff --git a/.changeset/eleven-cows-dance.md b/.changeset/eleven-cows-dance.md new file mode 100644 index 00000000000..adc16422bd2 --- /dev/null +++ b/.changeset/eleven-cows-dance.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Added ZKSync specific deployment logic and artifact related utils diff --git a/typescript/sdk/src/deploy/proxy.ts b/typescript/sdk/src/deploy/proxy.ts index 674bd32d849..ecd00215977 100644 --- a/typescript/sdk/src/deploy/proxy.ts +++ b/typescript/sdk/src/deploy/proxy.ts @@ -1,4 +1,5 @@ import { ethers } from 'ethers'; +import { Provider as ZKSyncProvider } from 'zksync-ethers'; import { ProxyAdmin__factory } from '@hyperlane-xyz/core'; import { Address, ChainId, eqAddress } from '@hyperlane-xyz/utils'; @@ -7,6 +8,8 @@ import { transferOwnershipTransactions } from '../contracts/contracts.js'; import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; import { DeployedOwnableConfig } from '../types.js'; +type EthersLikeProvider = ethers.providers.Provider | ZKSyncProvider; + export type UpgradeConfig = { timelock: { delay: number; @@ -19,7 +22,7 @@ export type UpgradeConfig = { }; export async function proxyImplementation( - provider: ethers.providers.Provider, + provider: EthersLikeProvider, proxy: Address, ): Promise
{ // Hardcoded storage slot for implementation per EIP-1967 @@ -31,7 +34,7 @@ export async function proxyImplementation( } export async function isInitialized( - provider: ethers.providers.Provider, + provider: EthersLikeProvider, contract: Address, ): Promise { // Using OZ's Initializable 4.9 which keeps it at the 0x0 slot @@ -43,7 +46,7 @@ export async function isInitialized( } export async function proxyAdmin( - provider: ethers.providers.Provider, + provider: EthersLikeProvider, proxy: Address, ): Promise
{ // Hardcoded storage slot for admin per EIP-1967 @@ -72,7 +75,7 @@ export function proxyConstructorArgs( } export async function isProxy( - provider: ethers.providers.Provider, + provider: EthersLikeProvider, proxy: Address, ): Promise { const admin = await proxyAdmin(provider, proxy); diff --git a/typescript/sdk/src/deploy/proxyFactoryUtils.ts b/typescript/sdk/src/deploy/proxyFactoryUtils.ts new file mode 100644 index 00000000000..b4a27c709eb --- /dev/null +++ b/typescript/sdk/src/deploy/proxyFactoryUtils.ts @@ -0,0 +1,18 @@ +import { ethers } from 'ethers'; + +import { objMap } from '@hyperlane-xyz/utils'; + +import { proxyFactoryFactories } from './contracts.js'; +import { ProxyFactoryFactoriesAddresses } from './types.js'; + +/** + * Creates a default ProxyFactoryFactoriesAddresses object with all values set to ethers.constants.AddressZero. + * @returns {ProxyFactoryFactoriesAddresses} An object with all factory addresses set to AddressZero. + */ +export function createDefaultProxyFactoryFactories(): ProxyFactoryFactoriesAddresses { + const defaultAddress = ethers.constants.AddressZero; + return objMap( + proxyFactoryFactories, + () => defaultAddress, + ) as ProxyFactoryFactoriesAddresses; +} diff --git a/typescript/sdk/src/hook/types.ts b/typescript/sdk/src/hook/types.ts index ef02e2bfa58..170071a4d3a 100644 --- a/typescript/sdk/src/hook/types.ts +++ b/typescript/sdk/src/hook/types.ts @@ -46,6 +46,24 @@ export enum HookType { CCIP = 'ccipHook', } +export const HookTypeToContractNameMap: Record< + Exclude, + string +> = { + [HookType.MERKLE_TREE]: 'merkleTreeHook', + [HookType.INTERCHAIN_GAS_PAYMASTER]: 'interchainGasPaymaster', + [HookType.AGGREGATION]: 'staticAggregationHook', + [HookType.PROTOCOL_FEE]: 'protocolFee', + [HookType.OP_STACK]: 'opStackHook', + [HookType.ROUTING]: 'domainRoutingHook', + [HookType.FALLBACK_ROUTING]: 'fallbackDomainRoutingHook', + [HookType.AMOUNT_ROUTING]: 'amountRoutingHook', + [HookType.PAUSABLE]: 'pausableHook', + [HookType.ARB_L2_TO_L1]: 'arbL2ToL1Hook', + [HookType.MAILBOX_DEFAULT]: 'defaultHook', + [HookType.CCIP]: 'ccipHook', +}; + export type MerkleTreeHookConfig = z.infer; export type IgpHookConfig = z.infer; export type ProtocolFeeHookConfig = z.infer; diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 077d8fee8bb..fd333c3c999 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -217,7 +217,12 @@ export { WeightedMultisigIsmConfig, WeightedMultisigIsmConfigSchema, } from './ism/types.js'; -export { collectValidators, moduleCanCertainlyVerify } from './ism/utils.js'; +export { + collectValidators, + moduleCanCertainlyVerify, + isStaticDeploymentSupported, + isIsmCompatible, +} from './ism/utils.js'; export { AgentChainMetadata, AgentChainMetadataSchema, diff --git a/typescript/sdk/src/ism/types.ts b/typescript/sdk/src/ism/types.ts index bb7baf455d7..cf40129c9f9 100644 --- a/typescript/sdk/src/ism/types.ts +++ b/typescript/sdk/src/ism/types.ts @@ -75,6 +75,19 @@ export const MUTABLE_ISM_TYPE = [ IsmType.PAUSABLE, ]; +/** + * @notice Statically deployed ISM types + * @dev ISM types with immutable config embedded in contract bytecode via MetaProxy + */ +export const STATIC_ISM_TYPES = [ + IsmType.AGGREGATION, + IsmType.MERKLE_ROOT_MULTISIG, + IsmType.MESSAGE_ID_MULTISIG, + IsmType.WEIGHTED_MERKLE_ROOT_MULTISIG, + IsmType.WEIGHTED_MESSAGE_ID_MULTISIG, + IsmType.ICA_ROUTING, +]; + // mapping between the two enums export function ismTypeToModuleType(ismType: IsmType): ModuleType { switch (ismType) { diff --git a/typescript/sdk/src/ism/utils.ts b/typescript/sdk/src/ism/utils.ts index 30486035498..b93db8858a8 100644 --- a/typescript/sdk/src/ism/utils.ts +++ b/typescript/sdk/src/ism/utils.ts @@ -27,6 +27,7 @@ import { import { getChainNameFromCCIPSelector } from '../ccip/utils.js'; import { HyperlaneContracts } from '../contracts/types.js'; import { ProxyFactoryFactories } from '../deploy/contracts.js'; +import { ChainTechnicalStack } from '../metadata/chainMetadataTypes.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { ChainName } from '../types.js'; @@ -37,6 +38,7 @@ import { ModuleType, RoutingIsmConfig, RoutingIsmDelta, + STATIC_ISM_TYPES, ismTypeToModuleType, } from './types.js'; @@ -586,3 +588,36 @@ export function collectValidators( return new Set(validators); } + +/** + * Determines if static ISM deployment is supported on a given chain's technical stack + * @dev Currently, only ZkSync does not support static deployments + * @param chainTechnicalStack - The technical stack of the target chain + * @returns boolean - true if static deployment is supported, false for ZkSync + */ +export function isStaticDeploymentSupported( + chainTechnicalStack: ChainTechnicalStack | undefined, +): boolean { + return chainTechnicalStack !== ChainTechnicalStack.ZkSync; +} + +/** + * Checks if the given ISM type is compatible with the chain's technical stack. + * + * @param {Object} params - The parameters object + * @param {ChainTechnicalStack | undefined} params.chainTechnicalStack - The technical stack of the chain + * @param {IsmType} params.ismType - The type of Interchain Security Module (ISM) + * @returns {boolean} True if the ISM type is compatible with the chain, false otherwise + */ +export function isIsmCompatible({ + chainTechnicalStack, + ismType, +}: { + chainTechnicalStack: ChainTechnicalStack | undefined; + ismType: IsmType; +}): boolean { + // Skip compatibility check for non-static ISMs as they're always supported + if (!STATIC_ISM_TYPES.includes(ismType)) return true; + + return isStaticDeploymentSupported(chainTechnicalStack); +} diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index 332c483d260..0eb9e6434b4 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -23,6 +23,7 @@ export enum ExplorerFamily { Blockscout = 'blockscout', Routescan = 'routescan', Voyager = 'voyager', + ZkSync = 'zksync', Other = 'other', } diff --git a/typescript/sdk/src/utils/zksync.ts b/typescript/sdk/src/utils/zksync.ts new file mode 100644 index 00000000000..0b97e4330c2 --- /dev/null +++ b/typescript/sdk/src/utils/zksync.ts @@ -0,0 +1,32 @@ +import { ZKSyncArtifact, loadAllZKSyncArtifacts } from '@hyperlane-xyz/core'; + +/** + * @dev Retrieves a ZkSync artifact by its contract name or qualified name. + * @param name The name of the contract or qualified name in the format "sourceName:contractName". + * @return The corresponding ZKSyncArtifact if found, or undefined if not found. + */ +export const getZKSyncArtifactByContractName = async ( + name: string, +): Promise => { + // Load all ZkSync artifacts + const allArtifacts = loadAllZKSyncArtifacts(); + + // Find the artifact that matches the contract name or qualified name + const artifact = Object.values(allArtifacts).find( + ({ contractName, sourceName }: ZKSyncArtifact) => { + const lowerCaseContractName = contractName.toLowerCase(); + const lowerCaseName = name.toLowerCase(); + + // Check if the contract name matches + if (lowerCaseContractName === lowerCaseName) { + return true; + } + + // Check if the qualified name matches + const qualifiedName = `${sourceName}:${contractName}`; + return qualifiedName === name; // Return true if qualified name matches + }, + ); + + return artifact; +}; diff --git a/typescript/sdk/src/zksync/ZKSyncDeployer.ts b/typescript/sdk/src/zksync/ZKSyncDeployer.ts new file mode 100644 index 00000000000..92069263115 --- /dev/null +++ b/typescript/sdk/src/zksync/ZKSyncDeployer.ts @@ -0,0 +1,185 @@ +import { BigNumber, BytesLike, Overrides, utils } from 'ethers'; +import { + Contract, + ContractFactory, + Wallet, + types as zksyncTypes, +} from 'zksync-ethers'; + +import { ZKSyncArtifact } from '@hyperlane-xyz/core'; +import { assert } from '@hyperlane-xyz/utils'; + +import { defaultZKProviderBuilder } from '../providers/providerBuilders.js'; +import { getZKSyncArtifactByContractName } from '../utils/zksync.js'; + +/** + * Class for deploying contracts to the ZKSync network. + */ +export class ZKSyncDeployer { + public zkWallet: Wallet; + public deploymentType?: zksyncTypes.DeploymentType; + + constructor(zkWallet: Wallet, deploymentType?: zksyncTypes.DeploymentType) { + this.deploymentType = deploymentType; + + this.zkWallet = zkWallet.connect( + zkWallet.provider ?? + defaultZKProviderBuilder([{ http: 'http://127.0.0.1:8011' }], 260), + ); + } + + /** + * Loads and validates a ZKSync contract artifact by name + * @param contractTitle - Contract name or qualified name (sourceName:contractName) + * + * @returns The ZKSync artifact + */ + private async loadArtifact(contractTitle: string): Promise { + const artifact = await getZKSyncArtifactByContractName(contractTitle); + assert(artifact, `No ZKSync artifact for contract ${contractTitle} found!`); + return artifact; + } + + /** + * Estimates the price of calling a deploy transaction in ETH. + * + * @param artifact The previously loaded artifact object. + * @param constructorArguments List of arguments to be passed to the contract constructor. + * + * @returns Calculated fee in ETH wei + */ + public async estimateDeployFee( + artifact: ZKSyncArtifact, + constructorArguments: any[], + ): Promise { + const gas = await this.estimateDeployGas(artifact, constructorArguments); + const gasPrice = await this.zkWallet.provider.getGasPrice(); + return gas.mul(gasPrice); + } + + /** + * Estimates the amount of gas needed to execute a deploy transaction. + * + * @param artifact The previously loaded artifact object. + * @param constructorArguments List of arguments to be passed to the contract constructor. + * + * @returns Calculated amount of gas. + */ + public async estimateDeployGas( + artifact: ZKSyncArtifact, + constructorArguments: any[], + ): Promise { + const factoryDeps = await this.extractFactoryDeps(artifact); + + const factory = new ContractFactory( + artifact.abi, + artifact.bytecode, + this.zkWallet, + this.deploymentType, + ); + + // Encode deploy transaction so it can be estimated. + const deployTx = factory.getDeployTransaction(...constructorArguments, { + customData: { + factoryDeps, + }, + }); + deployTx.from = this.zkWallet.address; + + return this.zkWallet.provider.estimateGas(deployTx); + } + + /** + * Sends a deploy transaction to the zkSync network. + * For now, it will use defaults for the transaction parameters: + * - fee amount is requested automatically from the zkSync server. + * + * @param artifact The previously loaded artifact object. + * @param constructorArguments List of arguments to be passed to the contract constructor. + * @param overrides Optional object with additional deploy transaction parameters. + * @param additionalFactoryDeps Additional contract bytecodes to be added to the factory dependencies list. + * + * @returns A contract object. + */ + public async deploy( + artifact: ZKSyncArtifact, + constructorArguments: any[] = [], + overrides?: Overrides, + additionalFactoryDeps?: BytesLike[], + ): Promise { + const baseDeps = await this.extractFactoryDeps(artifact); + const additionalDeps = additionalFactoryDeps + ? additionalFactoryDeps.map((val) => utils.hexlify(val)) + : []; + const factoryDeps = [...baseDeps, ...additionalDeps]; + + const factory = new ContractFactory( + artifact.abi, + artifact.bytecode, + this.zkWallet, + this.deploymentType, + ); + + const { customData, ..._overrides } = overrides ?? {}; + + // Encode and send the deploy transaction providing factory dependencies. + const contract = await factory.deploy(...constructorArguments, { + ..._overrides, + customData: { + ...customData, + factoryDeps, + }, + }); + + await contract.deployed(); + + return contract; + } + + /** + * Extracts factory dependencies from the artifact. + * + * @param artifact Artifact to extract dependencies from + * + * @returns Factory dependencies in the format expected by SDK. + */ + async extractFactoryDeps(artifact: ZKSyncArtifact): Promise { + const visited = new Set(); + + visited.add(`${artifact.sourceName}:${artifact.contractName}`); + return this.extractFactoryDepsRecursive(artifact, visited); + } + + private async extractFactoryDepsRecursive( + artifact: ZKSyncArtifact, + visited: Set, + ): Promise { + // Load all the dependency bytecodes. + // We transform it into an array of bytecodes. + const factoryDeps: string[] = []; + for (const dependencyHash in artifact.factoryDeps) { + if ( + Object.prototype.hasOwnProperty.call( + artifact.factoryDeps, + dependencyHash, + ) + ) { + const dependencyContract = artifact.factoryDeps[dependencyHash]; + if (!visited.has(dependencyContract)) { + const dependencyArtifact = await this.loadArtifact( + dependencyContract, + ); + factoryDeps.push(dependencyArtifact.bytecode); + visited.add(dependencyContract); + const transitiveDeps = await this.extractFactoryDepsRecursive( + dependencyArtifact, + visited, + ); + factoryDeps.push(...transitiveDeps); + } + } + } + + return factoryDeps; + } +} From 0e663ad85ddb681b684d80212039e8ae5c82b1c7 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 16 Apr 2025 13:37:10 +0100 Subject: [PATCH 016/223] feat: Add mixing of message processing depending on configuration (#5937) ### Description Currently we sort new messages in prepare queue depending on nonce and their ids. It gives us the same order of messages in every instance of Relayer we are running. It means that all instances of relayer will attempt to deliver the same message and one of them will succeed while others will fail. It is quite wasteful on resources. We would like to make the order of new messages in prepare queue different for each instance of relayer so that each instance mostly working on a distinct message while merely checking if a message was delivered if it was processed by another instance of relayer. We are fine with the order being stochastic, i.e. the order of messages will be different for each instance of relayer but it will be deterministic for each instance of relayer. We introduce a salt which will be configured with distinct value for each relayer and we will hash message id with the salt producing the stochastic ordering of messages. This behaviour will be configured with environment variables: `HYPERLANE_RELAYER_MIXING_ENABLED` to enable mixing of messages in each relayer instance and `HYPERLANE_RELAYER_MIXING_SALT` to specify the salt. We add a server endpoint so that environment variables can be set on the fly in Relayer. This endpoint will be exposed if environment variable `HYPERLANE_RELAYER_ENVIRONMENT_VARIABLE_ENDPOINT_ENABLED` is set to `true` when relayer starts. We don't cache the values of the environment variables so that the behaviour of relayer can be changes without restarting it. ### Backward compatibility Yes - when environment variable `HYPERLANE_RELAYER_MIXING_ENABLED` is not specified, relayer behaves as usual. ### Testing Manual --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../src/server/environment_variable.rs | 188 ++++++++++++++++++ rust/main/agents/relayer/src/server/mod.rs | 11 + .../src/traits/pending_operation.rs | 40 ++-- .../src/traits/pending_operation/tests.rs | 154 ++++++++++++++ 4 files changed, 377 insertions(+), 16 deletions(-) create mode 100644 rust/main/agents/relayer/src/server/environment_variable.rs create mode 100644 rust/main/hyperlane-core/src/traits/pending_operation/tests.rs diff --git a/rust/main/agents/relayer/src/server/environment_variable.rs b/rust/main/agents/relayer/src/server/environment_variable.rs new file mode 100644 index 00000000000..ded358ef5a9 --- /dev/null +++ b/rust/main/agents/relayer/src/server/environment_variable.rs @@ -0,0 +1,188 @@ +use std::env; + +use axum::{extract::State, routing, Json, Router}; +use derive_new::new; +use serde::{Deserialize, Serialize}; + +const ENVIRONMENT_VARIABLE: &str = "/environment_variable"; + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] +pub struct SetEnvironmentVariableRequest { + name: String, + value: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct EnvironmentVariableResponse { + name: String, + value: Option, + message: String, +} + +#[derive(new, Clone)] +pub struct EnvironmentVariableApi {} + +async fn get_environment_variable( + State(_): State, + Json(body): Json, +) -> Result, String> { + let value = env::var(&body.name).ok(); + + let response = EnvironmentVariableResponse { + name: body.name, + value, + message: "got".to_string(), + }; + + Ok(Json(response)) +} + +async fn set_environment_variable( + State(_): State, + Json(body): Json, +) -> Result, String> { + let message = match &body.value { + None => { + env::remove_var(&body.name); + "unset" + } + Some(value) => { + env::set_var(&body.name, value); + "set" + } + }; + + let response = EnvironmentVariableResponse { + name: body.name, + value: body.value, + message: message.to_string(), + }; + + Ok(Json(response)) +} + +impl EnvironmentVariableApi { + pub fn router(&self) -> Router { + Router::new() + .route("/", routing::get(get_environment_variable)) + .route("/", routing::post(set_environment_variable)) + .with_state(self.clone()) + } + + pub fn get_route(&self) -> (&'static str, Router) { + (ENVIRONMENT_VARIABLE, self.router()) + } +} + +#[cfg(test)] +mod tests { + use std::env::VarError::NotPresent; + use std::net::SocketAddr; + + use axum::http::StatusCode; + use serde_json::{json, Value}; + + use super::*; + + const NAME: &str = "TEST_ENVIRONMENT_VAR"; + const VALUE: &str = "TEST_VALUE"; + + #[derive(Debug)] + struct TestServerSetup { + pub socket_address: SocketAddr, + } + + fn setup_test_server() -> TestServerSetup { + let api = EnvironmentVariableApi::new(); + let (path, router) = api.get_route(); + + let app = Router::new().nest(path, router); + + let server = + axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); + let addr = server.local_addr(); + tokio::spawn(server); + + TestServerSetup { + socket_address: addr, + } + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_environment_variable() { + let TestServerSetup { + socket_address: addr, + .. + } = setup_test_server(); + + let set = set(); + let response = request(addr, &set, true).await; + assert_eq!(NAME, response.name); + assert_eq!(Some(VALUE.to_string()), response.value); + assert_eq!("set", response.message); + assert_eq!(VALUE, env::var(NAME).unwrap()); + + let get = get_or_remove(); + let response = request(addr, &get, false).await; + assert_eq!(NAME, response.name); + assert_eq!(Some(VALUE.to_string()), response.value); + assert_eq!("got", response.message); + assert_eq!(VALUE, env::var(NAME).unwrap()); + + let remove = get_or_remove(); + let response = request(addr, &remove, true).await; + assert_eq!(NAME, response.name); + assert_eq!(None, response.value); + assert_eq!("unset", response.message); + assert_eq!(Err(NotPresent), env::var(NAME)); + + let get = get_or_remove(); + let response = request(addr, &get, false).await; + assert_eq!(NAME, response.name); + assert_eq!(None, response.value); + assert_eq!("got", response.message); + assert_eq!(Err(NotPresent), env::var(NAME)); + } + + async fn request(addr: SocketAddr, body: &Value, post: bool) -> EnvironmentVariableResponse { + let client = reqwest::Client::new(); + + let builder = if post { + client.post(format!("http://{}{}", addr, ENVIRONMENT_VARIABLE)) + } else { + client.get(format!("http://{}{}", addr, ENVIRONMENT_VARIABLE)) + }; + + let request = builder.json(&body).build().unwrap(); + let response = tokio::spawn(client.execute(request)) + .await + .unwrap() + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = response + .json::() + .await + .unwrap(); + response + } + + fn set() -> Value { + json!( + { + "name": NAME, + "value": VALUE, + } + ) + } + + fn get_or_remove() -> Value { + json!( + { + "name": NAME, + } + ) + } +} diff --git a/rust/main/agents/relayer/src/server/mod.rs b/rust/main/agents/relayer/src/server/mod.rs index 03019d98353..c01af7310c5 100644 --- a/rust/main/agents/relayer/src/server/mod.rs +++ b/rust/main/agents/relayer/src/server/mod.rs @@ -1,6 +1,7 @@ use axum::Router; use derive_new::new; use std::collections::HashMap; +use std::env; use tokio::sync::broadcast::Sender; use crate::msg::op_queue::OperationPriorityQueue; @@ -10,6 +11,9 @@ pub const ENDPOINT_MESSAGES_QUEUE_SIZE: usize = 100; pub use list_messages::*; pub use message_retry::*; +use crate::server::environment_variable::EnvironmentVariableApi; + +mod environment_variable; mod list_messages; mod message_retry; @@ -44,6 +48,13 @@ impl Server { routes.push(ListOperationsApi::new(op_queues).get_route()); } + let expose_environment_variable_endpoint = + env::var("HYPERLANE_RELAYER_ENVIRONMENT_VARIABLE_ENDPOINT_ENABLED") + .map_or(false, |v| v == "true"); + if expose_environment_variable_endpoint { + routes.push(EnvironmentVariableApi::new().get_route()); + } + routes } } diff --git a/rust/main/hyperlane-core/src/traits/pending_operation.rs b/rust/main/hyperlane-core/src/traits/pending_operation.rs index 1e00e19807b..b75d599ffe5 100644 --- a/rust/main/hyperlane-core/src/traits/pending_operation.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + env, fmt::{Debug, Display}, io::Write, sync::Arc, @@ -10,6 +11,7 @@ use async_trait::async_trait; use num::CheckedDiv; use prometheus::IntGauge; use serde::{Deserialize, Serialize}; +use sha3::{digest::Update, Digest, Keccak256}; use strum::Display; use tracing::warn; @@ -362,18 +364,34 @@ impl Eq for QueueOperation {} impl Ord for QueueOperation { fn cmp(&self, other: &Self) -> Ordering { use Ordering::*; + + fn salted_hash(id: &H256, salt: &[u8]) -> H256 { + H256::from_slice(Keccak256::new().chain(id).chain(salt).finalize().as_slice()) + } + match (self.next_attempt_after(), other.next_attempt_after()) { (Some(a), Some(b)) => a.cmp(&b), // No time means it should come before (None, Some(_)) => Less, (Some(_), None) => Greater, (None, None) => { - if self.origin_domain_id() == other.origin_domain_id() { - // Should execute in order of nonce for the same origin - self.priority().cmp(&other.priority()) + let mixing = + env::var("HYPERLANE_RELAYER_MIXING_ENABLED").map_or(false, |v| v == "true"); + if !mixing { + if self.origin_domain_id() == other.origin_domain_id() { + // Should execute in order of nonce for the same origin + self.priority().cmp(&other.priority()) + } else { + // There is no priority between these messages, so arbitrarily use the id + self.id().cmp(&other.id()) + } } else { - // There is no priority between these messages, so arbitrarily use the id - self.id().cmp(&other.id()) + let salt = env::var("HYPERLANE_RELAYER_MIXING_SALT") + .map_or(0, |v| v.parse::().unwrap_or(0)) + .to_vec(); + let self_hash = salted_hash(&self.id(), &salt); + let other_hash = salted_hash(&other.id(), &salt); + self_hash.cmp(&other_hash) } } } @@ -396,14 +414,4 @@ pub enum PendingOperationResult { } #[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_encoding_pending_operation_status() { - let status = PendingOperationStatus::Retry(ReprepareReason::CouldNotFetchMetadata); - let encoded = status.to_vec(); - let decoded = PendingOperationStatus::read_from(&mut &encoded[..]).unwrap(); - assert_eq!(status, decoded); - } -} +mod tests; diff --git a/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs b/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs new file mode 100644 index 00000000000..1428ef7c47e --- /dev/null +++ b/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs @@ -0,0 +1,154 @@ +use std::cmp::Ord; +use std::env; + +use super::*; + +#[derive(Debug, Serialize)] +struct MockQueueOperation { + id: H256, + origin_domain_id: u32, + priority: u32, +} + +#[async_trait] +#[typetag::serialize] +impl PendingOperation for MockQueueOperation { + fn id(&self) -> H256 { + self.id + } + fn priority(&self) -> u32 { + self.priority + } + fn origin_domain_id(&self) -> u32 { + self.origin_domain_id + } + fn next_attempt_after(&self) -> Option { + None + } + fn retrieve_status_from_db(&self) -> Option { + None + } + fn destination_domain(&self) -> &HyperlaneDomain { + unimplemented!() + } + fn sender_address(&self) -> &H256 { + unimplemented!() + } + fn recipient_address(&self) -> &H256 { + unimplemented!() + } + fn app_context(&self) -> Option { + None + } + fn get_metric(&self) -> Option> { + None + } + fn set_metric(&mut self, _metric: Arc) {} + fn status(&self) -> PendingOperationStatus { + unimplemented!() + } + fn set_status(&mut self, _status: PendingOperationStatus) {} + async fn prepare(&mut self) -> PendingOperationResult { + unimplemented!() + } + async fn submit(&mut self) -> PendingOperationResult { + unimplemented!() + } + fn set_submission_outcome(&mut self, _outcome: TxOutcome) {} + fn get_tx_cost_estimate(&self) -> Option { + None + } + async fn confirm(&mut self) -> PendingOperationResult { + unimplemented!() + } + fn set_operation_outcome( + &mut self, + _submission_outcome: TxOutcome, + _submission_estimated_cost: U256, + ) { + } + fn set_next_attempt_after(&mut self, _delay: Duration) {} + fn reset_attempts(&mut self) {} + #[cfg(any(test, feature = "test-utils"))] + fn set_retries(&mut self, _retries: u32) {} + fn get_retries(&self) -> u32 { + 0 + } + async fn payload(&self) -> ChainResult> { + unimplemented!() + } + fn on_reprepare( + &mut self, + _err_msg: Option, + _reason: ReprepareReason, + ) -> PendingOperationResult { + unimplemented!() + } +} + +impl TryBatchAs for MockQueueOperation {} + +#[test] +fn test_encoding_pending_operation_status() { + let status = PendingOperationStatus::Retry(ReprepareReason::CouldNotFetchMetadata); + let encoded = status.to_vec(); + let decoded = PendingOperationStatus::read_from(&mut &encoded[..]).unwrap(); + assert_eq!(status, decoded); +} + +#[test] +fn test_queue_operation_ord_without_mixing() { + env::set_var("HYPERLANE_RELAYER_MIXING_ENABLED", "false"); + + let op1 = Box::new(MockQueueOperation { + id: H256::from_low_u64_be(1), + origin_domain_id: 1, + priority: 10, + }) as QueueOperation; + let op2 = Box::new(MockQueueOperation { + id: H256::from_low_u64_be(2), + origin_domain_id: 1, + priority: 5, + }) as QueueOperation; + + assert!(op1 > op2); // Higher priority value means lower priority +} + +#[test] +fn test_queue_operation_ord_with_mixing() { + env::set_var("HYPERLANE_RELAYER_MIXING_ENABLED", "true"); + env::set_var("HYPERLANE_RELAYER_MIXING_SALT", "123"); + + let op1 = Box::new(MockQueueOperation { + id: H256::from_low_u64_be(1), + origin_domain_id: 1, + priority: 10, + }) as QueueOperation; + let op2 = Box::new(MockQueueOperation { + id: H256::from_low_u64_be(2), + origin_domain_id: 1, + priority: 5, + }) as QueueOperation; + + // Calculate salted hashes for both operations + let salt = env::var("HYPERLANE_RELAYER_MIXING_SALT") + .map_or(0, |v| v.parse::().unwrap_or(0)) + .to_vec(); + let salted_hash_op1 = H256::from_slice( + Keccak256::new() + .chain(op1.id()) + .chain(&salt) + .finalize() + .as_slice(), + ); + let salted_hash_op2 = H256::from_slice( + Keccak256::new() + .chain(op2.id()) + .chain(&salt) + .finalize() + .as_slice(), + ); + + // Assert that the ordering matches the salted hash comparison + assert_eq!(op1.cmp(&op2), salted_hash_op1.cmp(&salted_hash_op2)); +} From eb3054c59184573f67f79a801965c8e4cc2ed3ce Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 16 Apr 2025 14:57:59 +0100 Subject: [PATCH 017/223] feat: add scripts to update safe owners (#5950) ### Description - add script to propose update owner transactions of governance safes ### Testing Manual --- .../mainnet3/governance/safe/safeConfig.ts | 7 ++ typescript/infra/scripts/agent-utils.ts | 7 ++ .../safes/governance/update-signers.ts | 91 +++++++++++++++++++ typescript/infra/src/utils/safe.ts | 19 ++-- 4 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts create mode 100644 typescript/infra/scripts/safes/governance/update-signers.ts diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts b/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts new file mode 100644 index 00000000000..14b7c0d1063 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts @@ -0,0 +1,7 @@ +import { Address } from '@hyperlane-xyz/utils'; + +export const SIGNERS: Address[] = [ + // TODO: add signers +]; + +export const THRESHOLD = 1; diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index 459222620cd..2939c72a339 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -345,6 +345,13 @@ export function withSkipReview(args: Argv) { .default('skipReview', false); } +export function withPropose(args: Argv) { + return args + .describe('propose', 'Propose') + .boolean('propose') + .default('propose', false); +} + // Interactively gets a single warp route ID export async function getWarpRouteIdInteractive() { const choices = Object.values(WarpRouteIds) diff --git a/typescript/infra/scripts/safes/governance/update-signers.ts b/typescript/infra/scripts/safes/governance/update-signers.ts new file mode 100644 index 00000000000..c2cd9da8402 --- /dev/null +++ b/typescript/infra/scripts/safes/governance/update-signers.ts @@ -0,0 +1,91 @@ +import Safe from '@safe-global/protocol-kit'; +import yargs from 'yargs'; + +import { ChainName } from '@hyperlane-xyz/sdk'; +import { rootLogger } from '@hyperlane-xyz/utils'; + +import { Contexts } from '../../../config/contexts.js'; +import { regularSafes } from '../../../config/environments/mainnet3/governance/safe/regular.js'; +import { + SIGNERS, + THRESHOLD, +} from '../../../config/environments/mainnet3/governance/safe/safeConfig.js'; +import { AnnotatedCallData } from '../../../src/govern/HyperlaneAppGovernor.js'; +import { SafeMultiSend } from '../../../src/govern/multisend.js'; +import { Role } from '../../../src/roles.js'; +import { getSafeAndService, updateSafeOwner } from '../../../src/utils/safe.js'; +import { withPropose } from '../../agent-utils.js'; +import { getEnvironmentConfig } from '../../core-utils.js'; + +async function main() { + const { propose } = await withPropose(yargs(process.argv.slice(2))).argv; + + const envConfig = getEnvironmentConfig('mainnet3'); + const multiProvider = await envConfig.getMultiProvider( + Contexts.Hyperlane, + Role.Deployer, + true, + Object.keys(regularSafes), + ); + + for (const [chain, safeAddress] of Object.entries(regularSafes)) { + let safeSdk: Safe.default; + try { + ({ safeSdk } = await getSafeAndService( + chain, + multiProvider, + safeAddress, + )); + } catch (error) { + rootLogger.error(`[${chain}] could not get safe: ${error}`); + continue; + } + + let safeMultiSend: SafeMultiSend; + try { + safeMultiSend = new SafeMultiSend( + multiProvider, + chain as ChainName, + safeAddress, + ); + } catch (error) { + rootLogger.error(`[${chain}] could not get safe multi send: ${error}`); + continue; + } + + let transactions: AnnotatedCallData[]; + try { + transactions = await updateSafeOwner(safeSdk, SIGNERS, THRESHOLD); + } catch (error) { + rootLogger.error(`[${chain}] could not update safe owner: ${error}`); + continue; + } + + rootLogger.info(`[${chain}] Generated transactions for updating signers`); + rootLogger.info(`[${chain}] ${JSON.stringify(transactions, null, 2)}`); + + if (propose) { + try { + await safeMultiSend.sendTransactions( + transactions.map((call) => ({ + to: call.to, + data: call.data, + value: call.value, + })), + ); + rootLogger.info(`[${chain}] Successfully sent transactions`); + } catch (error) { + rootLogger.error(`[${chain}] could not send transactions: ${error}`); + } + } + } + + if (!propose) { + rootLogger.info('Skipping sending transactions, pass --propose to send'); + } +} + +main().catch((error) => { + rootLogger.error(error); + process.exit(1); +}); diff --git a/typescript/infra/src/utils/safe.ts b/typescript/infra/src/utils/safe.ts index 258fc48ca0c..04fea612177 100644 --- a/typescript/infra/src/utils/safe.ts +++ b/typescript/infra/src/utils/safe.ts @@ -310,15 +310,20 @@ export async function deleteSafeTx( export async function updateSafeOwner( safeSdk: Safe.default, + owners?: Address[], + threshold?: number, ): Promise { - const threshold = await safeSdk.getThreshold(); - const owners = await safeSdk.getOwners(); - const newOwners = safeSigners.signers; - const ownersToRemove = owners.filter( + const currentThreshold = await safeSdk.getThreshold(); + const newThreshold = threshold ?? currentThreshold; + + const currentOwners = await safeSdk.getOwners(); + const newOwners = owners ?? safeSigners.signers; + + const ownersToRemove = currentOwners.filter( (owner) => !newOwners.some((newOwner) => eqAddress(owner, newOwner)), ); const ownersToAdd = newOwners.filter( - (newOwner) => !owners.some((owner) => eqAddress(newOwner, owner)), + (newOwner) => !currentOwners.some((owner) => eqAddress(newOwner, owner)), ); rootLogger.info(chalk.magentaBright('Owners to remove:', ownersToRemove)); @@ -329,7 +334,7 @@ export async function updateSafeOwner( for (const ownerToRemove of ownersToRemove) { const { data: removeTxData } = await safeSdk.createRemoveOwnerTx({ ownerAddress: ownerToRemove, - threshold, + threshold: newThreshold, }); transactions.push({ to: removeTxData.to, @@ -342,7 +347,7 @@ export async function updateSafeOwner( for (const ownerToAdd of ownersToAdd) { const { data: addTxData } = await safeSdk.createAddOwnerTx({ ownerAddress: ownerToAdd, - threshold, + threshold: newThreshold, }); transactions.push({ to: addTxData.to, From f878cd7d160a8a4f85124ffabef6bb16bc647c96 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Wed, 16 Apr 2025 22:01:51 +0100 Subject: [PATCH 018/223] feat: app-context specific ISM cache configs (#5945) ### Description - Moves away from the default ISM cache config to a vec of ISM cache configs with a selector that can match either default ISM use or a specific app context - Motivating use case here is the testnet4 load testing doesn't actually use the default ISM. This will also allow us to cache other routes like ezETH etc that don't use the default ISM directly ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/Cargo.lock | 1 + .../relayer/src/msg/metadata/aggregation.rs | 7 +- .../agents/relayer/src/msg/metadata/base.rs | 224 +++++++++++++++--- .../src/msg/metadata/message_builder.rs | 5 +- .../relayer/src/msg/metadata/multisig/base.rs | 1 + .../relayer/src/msg/metadata/routing.rs | 7 +- rust/main/agents/relayer/src/msg/processor.rs | 4 +- rust/main/agents/relayer/src/relayer.rs | 4 +- rust/main/agents/relayer/src/settings/mod.rs | 75 +++--- .../main/chains/hyperlane-ethereum/Cargo.toml | 1 + .../src/rpc_clients/trait_builder.rs | 36 ++- .../src/cache/moka/dynamic_expiry.rs | 19 +- .../main/hyperlane-base/src/cache/moka/mod.rs | 4 +- .../config/environments/mainnet3/agent.ts | 38 +-- .../config/environments/testnet4/agent.ts | 110 ++++++--- typescript/infra/src/agents/index.ts | 2 +- typescript/infra/src/config/agent/relayer.ts | 8 +- typescript/sdk/src/index.ts | 1 + typescript/sdk/src/metadata/agentConfig.ts | 24 +- 19 files changed, 434 insertions(+), 137 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 7b9fb9127aa..759ba180f87 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -5332,6 +5332,7 @@ version = "0.1.0" dependencies = [ "abigen", "async-trait", + "dashmap", "derive-new", "ethers", "ethers-contract", diff --git a/rust/main/agents/relayer/src/msg/metadata/aggregation.rs b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs index 383baaf8653..ddd5c54a19b 100644 --- a/rust/main/agents/relayer/src/msg/metadata/aggregation.rs +++ b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs @@ -140,7 +140,12 @@ impl AggregationIsmMetadataBuilder { let params_cache_key = match self .base_builder() .ism_cache_policy_classifier() - .get_cache_policy(self.root_ism, ism.domain(), ModuleType::Aggregation) + .get_cache_policy( + self.root_ism, + ism.domain(), + ModuleType::Aggregation, + self.base.app_context.as_ref(), + ) .await { // To have the cache key be more succinct, we use the message id diff --git a/rust/main/agents/relayer/src/msg/metadata/base.rs b/rust/main/agents/relayer/src/msg/metadata/base.rs index fe9561676cb..8c180568d1b 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base.rs @@ -210,11 +210,22 @@ pub enum IsmCachePolicy { IsmSpecific, } +#[derive(Debug, Clone, Default, Deserialize, PartialEq)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum IsmCacheSelector { + #[default] + DefaultIsm, + AppContext { + context: String, + }, +} + /// Configuration for ISM caching behavior. /// Fields are renamed to be all lowercase / without underscores to match /// the format expected by the settings parsing. #[derive(Debug, Clone, Default, Deserialize)] pub struct IsmCacheConfig { + selector: IsmCacheSelector, #[serde(deserialize_with = "deserialize_module_types", rename = "moduletypes")] module_types: HashSet, chains: Option>, @@ -259,7 +270,7 @@ impl IsmCacheConfig { #[derive(Debug, new)] pub struct IsmCachePolicyClassifier { default_ism_getter: DefaultIsmCache, - default_ism_cache_config: IsmCacheConfig, + ism_cache_configs: Vec, } impl IsmCachePolicyClassifier { @@ -269,28 +280,36 @@ impl IsmCachePolicyClassifier { root_ism: H256, domain: &HyperlaneDomain, ism_module_type: ModuleType, + app_context: Option<&String>, ) -> IsmCachePolicy { - let default_ism = match self.default_ism_getter.get().await { - Ok(default_ism) => default_ism, - Err(err) => { - tracing::warn!(?err, "Error fetching default ISM for ISM cache policy, falling back to default cache policy"); - return IsmCachePolicy::default(); + for config in &self.ism_cache_configs { + let matches_module = match &config.selector { + IsmCacheSelector::DefaultIsm => { + let default_ism = match self.default_ism_getter.get().await { + Ok(default_ism) => default_ism, + Err(err) => { + tracing::warn!(?err, "Error fetching default ISM for ISM cache policy, attempting next config"); + continue; + } + }; + root_ism == default_ism + } + IsmCacheSelector::AppContext { + context: selector_app_context, + } => app_context.map_or(false, |app_context| app_context == selector_app_context), + }; + + if matches_module + && config.matches_chain(domain.name()) + && config.matches_module_type(ism_module_type) + { + tracing::trace!( + ?domain, + ism_cache_config =? config, + "Using configured default ISM cache policy" + ); + return config.cache_policy; } - }; - - if root_ism == default_ism - && self.default_ism_cache_config.matches_chain(domain.name()) - && self - .default_ism_cache_config - .matches_module_type(ism_module_type) - { - tracing::trace!( - ?default_ism, - ?domain, - cache_policy =? self.default_ism_cache_config.cache_policy, - "Using configured default ISM cache policy" - ); - return self.default_ism_cache_config.cache_policy; } IsmCachePolicy::default() @@ -305,6 +324,7 @@ mod tests { #[test] fn test_ism_cache_config() { let config = IsmCacheConfig { + selector: IsmCacheSelector::DefaultIsm, module_types: HashSet::from([ModuleType::Aggregation]), chains: Some(HashSet::from(["foochain".to_owned()])), cache_policy: IsmCachePolicy::IsmSpecific, @@ -322,24 +342,46 @@ mod tests { // Module type 2 is the numeric version of ModuleType::Aggregation let json = r#" { + "selector": { + "type": "defaultIsm" + }, "moduletypes": [2], "chains": ["foochain"], "cachepolicy": "ismSpecific" } "#; - let config: IsmCacheConfig = serde_json::from_str(json).unwrap(); + assert_eq!(config.selector, IsmCacheSelector::DefaultIsm); assert_eq!( config.module_types, HashSet::from([ModuleType::Aggregation]) ); assert_eq!(config.chains, Some(HashSet::from(["foochain".to_owned()]))); assert_eq!(config.cache_policy, IsmCachePolicy::IsmSpecific); + + let json = r#" + { + "selector": { + "type": "appContext", + "context": "foo" + }, + "moduletypes": [2], + "chains": ["foochain"], + "cachepolicy": "ismSpecific" + } + "#; + let config: IsmCacheConfig = serde_json::from_str(json).unwrap(); + assert_eq!( + config.selector, + IsmCacheSelector::AppContext { + context: "foo".to_string(), + }, + ); } #[tokio::test] - async fn test_ism_cache_policy_classifier() { + async fn test_ism_cache_policy_classifier_default_ism() { let default_ism = H256::zero(); let mock_mailbox = MockMailboxContract::new_with_default_ism(default_ism); @@ -347,37 +389,163 @@ mod tests { let default_ism_getter = DefaultIsmCache::new(mailbox); let default_ism_cache_config = IsmCacheConfig { + selector: IsmCacheSelector::DefaultIsm, module_types: HashSet::from([ModuleType::Aggregation]), chains: Some(HashSet::from(["foochain".to_owned()])), cache_policy: IsmCachePolicy::IsmSpecific, }; let classifier = - IsmCachePolicyClassifier::new(default_ism_getter, default_ism_cache_config); + IsmCachePolicyClassifier::new(default_ism_getter, vec![default_ism_cache_config]); // We meet the criteria for the cache policy let domain = HyperlaneDomain::new_test_domain("foochain"); let cache_policy = classifier - .get_cache_policy(default_ism, &domain, ModuleType::Aggregation) + .get_cache_policy(default_ism, &domain, ModuleType::Aggregation, None) .await; assert_eq!(cache_policy, IsmCachePolicy::IsmSpecific); // Different ISM module type, should not match let cache_policy = classifier - .get_cache_policy(default_ism, &domain, ModuleType::Routing) + .get_cache_policy(default_ism, &domain, ModuleType::Routing, None) .await; assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); // ISM not default ISM, should not match let cache_policy = classifier - .get_cache_policy(H256::repeat_byte(0xfe), &domain, ModuleType::Routing) + .get_cache_policy(H256::repeat_byte(0xfe), &domain, ModuleType::Routing, None) + .await; + assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); + + // Different domain, should not match + let domain = HyperlaneDomain::new_test_domain("barchain"); + let cache_policy = classifier + .get_cache_policy(default_ism, &domain, ModuleType::Routing, None) + .await; + assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); + } + + #[tokio::test] + async fn test_ism_cache_policy_classifier_app_context() { + let default_ism = H256::zero(); + let mock_mailbox = MockMailboxContract::new_with_default_ism(default_ism); + let mailbox: Arc = Arc::new(mock_mailbox); + // Unused for this test + let default_ism_getter = DefaultIsmCache::new(mailbox); + + let app_context_cache_config = IsmCacheConfig { + selector: IsmCacheSelector::AppContext { + context: "foo".to_string(), + }, + module_types: HashSet::from([ModuleType::Aggregation]), + chains: Some(HashSet::from(["foochain".to_owned()])), + cache_policy: IsmCachePolicy::IsmSpecific, + }; + + let classifier = + IsmCachePolicyClassifier::new(default_ism_getter, vec![app_context_cache_config]); + + // We meet the criteria for the cache policy + let domain = HyperlaneDomain::new_test_domain("foochain"); + let cache_policy = classifier + .get_cache_policy( + // To make extra sure we're testing the app context match, + // let's use a different ISM address + H256::repeat_byte(0xfe), + &domain, + ModuleType::Aggregation, + Some(&"foo".to_string()), + ) + .await; + assert_eq!(cache_policy, IsmCachePolicy::IsmSpecific); + + // Different app context, should not match + let cache_policy = classifier + .get_cache_policy( + default_ism, + &domain, + ModuleType::Routing, + Some(&"bar".to_string()), + ) + .await; + assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); + + // No app context, should not match + let cache_policy = classifier + .get_cache_policy(H256::repeat_byte(0xfe), &domain, ModuleType::Routing, None) .await; assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); // Different domain, should not match let domain = HyperlaneDomain::new_test_domain("barchain"); let cache_policy = classifier - .get_cache_policy(default_ism, &domain, ModuleType::Routing) + .get_cache_policy( + default_ism, + &domain, + ModuleType::Routing, + Some(&"foo".to_string()), + ) + .await; + assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); + } + + #[tokio::test] + async fn test_ism_cache_policy_classifier_multiple_policies() { + let default_ism = H256::zero(); + let mock_mailbox = MockMailboxContract::new_with_default_ism(default_ism); + let mailbox: Arc = Arc::new(mock_mailbox); + // Unused for this test + let default_ism_getter = DefaultIsmCache::new(mailbox); + + let app_context_cache_config = IsmCacheConfig { + selector: IsmCacheSelector::AppContext { + context: "foo".to_string(), + }, + module_types: HashSet::from([ModuleType::Aggregation]), + chains: Some(HashSet::from(["foochain".to_owned()])), + cache_policy: IsmCachePolicy::IsmSpecific, + }; + + let default_ism_cache_config = IsmCacheConfig { + selector: IsmCacheSelector::DefaultIsm, + module_types: HashSet::from([ModuleType::Routing]), + chains: Some(HashSet::from(["foochain".to_owned()])), + cache_policy: IsmCachePolicy::IsmSpecific, + }; + + let classifier = IsmCachePolicyClassifier::new( + default_ism_getter, + vec![app_context_cache_config, default_ism_cache_config], + ); + + // We meet the criteria for the app context cache policy + let domain = HyperlaneDomain::new_test_domain("foochain"); + let cache_policy = classifier + .get_cache_policy( + // To make extra sure we're testing the app context match, + // let's use a different ISM address + H256::repeat_byte(0xfe), + &domain, + ModuleType::Aggregation, + Some(&"foo".to_string()), + ) + .await; + assert_eq!(cache_policy, IsmCachePolicy::IsmSpecific); + + // We meet the criteria for the default ISM cache policy + let cache_policy = classifier + .get_cache_policy(default_ism, &domain, ModuleType::Routing, None) + .await; + assert_eq!(cache_policy, IsmCachePolicy::IsmSpecific); + + // Different app context and not default ISM, should not match + let cache_policy = classifier + .get_cache_policy( + H256::repeat_byte(0xfe), + &domain, + ModuleType::Routing, + Some(&"bar".to_string()), + ) .await; assert_eq!(cache_policy, IsmCachePolicy::MessageSpecific); } diff --git a/rust/main/agents/relayer/src/msg/metadata/message_builder.rs b/rust/main/agents/relayer/src/msg/metadata/message_builder.rs index c441171afc2..69ffc4d6ef7 100644 --- a/rust/main/agents/relayer/src/msg/metadata/message_builder.rs +++ b/rust/main/agents/relayer/src/msg/metadata/message_builder.rs @@ -215,8 +215,7 @@ mod test { use crate::{ msg::metadata::{ base::MetadataBuildError, message_builder::build_message_metadata, DefaultIsmCache, - IsmAwareAppContextClassifier, IsmCacheConfig, IsmCachePolicyClassifier, - MessageMetadataBuildParams, + IsmAwareAppContextClassifier, IsmCachePolicyClassifier, MessageMetadataBuildParams, }, settings::matching_list::{Filter, ListElement, MatchingList}, test_utils::{ @@ -279,7 +278,7 @@ mod test { base_builder.responses.app_context_classifier = Some(app_context_classifier); base_builder.responses.ism_cache_policy_classifier = Some(IsmCachePolicyClassifier::new( default_ism_getter, - IsmCacheConfig::default(), + Default::default(), )); base_builder } diff --git a/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs b/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs index 0f610b82efc..fe83f29d414 100644 --- a/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs @@ -119,6 +119,7 @@ pub trait MultisigIsmMetadataBuilder: AsRef + Send + Syn self.as_ref().root_ism, multisig_ism.domain(), self.module_type(), + self.as_ref().app_context.as_ref(), ) .await { diff --git a/rust/main/agents/relayer/src/msg/metadata/routing.rs b/rust/main/agents/relayer/src/msg/metadata/routing.rs index 7d990284725..e0eb23df795 100644 --- a/rust/main/agents/relayer/src/msg/metadata/routing.rs +++ b/rust/main/agents/relayer/src/msg/metadata/routing.rs @@ -39,7 +39,12 @@ impl MetadataBuilder for RoutingIsmMetadataBuilder { let cache_policy = self .base_builder() .ism_cache_policy_classifier() - .get_cache_policy(self.root_ism, ism.domain(), ModuleType::Routing) + .get_cache_policy( + self.root_ism, + ism.domain(), + ModuleType::Routing, + self.base.app_context.as_ref(), + ) .await; let cache_result: Option = match cache_policy { diff --git a/rust/main/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs index 59410fed31e..4de1469fa6d 100644 --- a/rust/main/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -442,7 +442,7 @@ mod test { msg::{ gas_payment::GasPaymentEnforcer, metadata::{ - BaseMetadataBuilder, DefaultIsmCache, IsmAwareAppContextClassifier, IsmCacheConfig, + BaseMetadataBuilder, DefaultIsmCache, IsmAwareAppContextClassifier, IsmCachePolicyClassifier, }, }, @@ -562,7 +562,7 @@ mod test { cache, db.clone(), IsmAwareAppContextClassifier::new(default_ism_getter.clone(), vec![]), - IsmCachePolicyClassifier::new(default_ism_getter, IsmCacheConfig::default()), + IsmCachePolicyClassifier::new(default_ism_getter, Default::default()), ) } diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index eca980b8074..d0cb8192f86 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -346,7 +346,7 @@ impl BaseAgent for Relayer { ), IsmCachePolicyClassifier::new( default_ism_getter.clone(), - settings.default_ism_cache_config.clone(), + settings.ism_cache_configs.clone(), ), ); @@ -1132,7 +1132,7 @@ mod test { allow_local_checkpoint_syncers: true, metric_app_contexts: Vec::new(), allow_contract_call_caching: true, - default_ism_cache_config: Default::default(), + ism_cache_configs: Default::default(), max_retries: 1, } } diff --git a/rust/main/agents/relayer/src/settings/mod.rs b/rust/main/agents/relayer/src/settings/mod.rs index bc5cc2791b9..e3f4e259f14 100644 --- a/rust/main/agents/relayer/src/settings/mod.rs +++ b/rust/main/agents/relayer/src/settings/mod.rs @@ -66,8 +66,8 @@ pub struct RelayerSettings { pub metric_app_contexts: Vec<(MatchingList, String)>, /// Whether to allow contract call caching at all. pub allow_contract_call_caching: bool, - /// The default ISM cache policy to use for all messages that use the default ISM. - pub default_ism_cache_config: IsmCacheConfig, + /// The ISM cache policies to use + pub ism_cache_configs: Vec, /// Maximum number of retries per operation pub max_retries: u32, } @@ -329,10 +329,10 @@ impl FromRawConf for RelayerSettings { .parse_bool() .unwrap_or(true); - let default_ism_cache_config = p + let ism_cache_configs = p .chain(&mut err) - .get_opt_key("defaultIsmCacheConfig") - .and_then(parse_ism_cache_config) + .get_opt_key("ismCacheConfigs") + .and_then(parse_ism_cache_configs) .unwrap_or_default(); let max_message_retries = p @@ -355,7 +355,7 @@ impl FromRawConf for RelayerSettings { allow_local_checkpoint_syncers, metric_app_contexts, allow_contract_call_caching, - default_ism_cache_config, + ism_cache_configs, max_retries: max_message_retries, }) } @@ -397,37 +397,16 @@ fn parse_matching_list(p: ValueParser) -> ConfigResult { err.into_result(ml) } -fn parse_json_object(p: ValueParser) -> Option<(ConfigPath, Value)> { +fn parse_ism_cache_configs(p: ValueParser) -> ConfigResult> { let mut err = ConfigParsingError::default(); - match p { - ValueParser { - val: Value::String(array_str), - cwp, - } => serde_json::from_str::(array_str) - .context("Expected JSON string") - .take_err(&mut err, || cwp.clone()) - .map(|v| (cwp, recase_json_value(v, Case::Flat))), - ValueParser { - val: value @ Value::Object(_), - cwp, - } => Some((cwp, value.clone())), - _ => Err(eyre!("Expected JSON object or stringified JSON")) - .take_err(&mut err, || p.cwp.clone()), - } -} - -fn parse_ism_cache_config(p: ValueParser) -> ConfigResult { - let mut err = ConfigParsingError::default(); - - let raw_object = parse_json_object(p.clone()).map(|(_, v)| v); - let Some(raw_object) = raw_object else { - return err.into_result(IsmCacheConfig::default()); + let raw_list = parse_json_array(p.clone()).map(|(_, v)| v); + let Some(raw_list) = raw_list else { + return err.into_result(Default::default()); }; - - let p = ValueParser::new(p.cwp.clone(), &raw_object); + let p = ValueParser::new(p.cwp.clone(), &raw_list); let ml = p - .parse_value::("Expected ISM cache config") + .parse_value::>("Expected ISM cache configs") .take_config_err(&mut err) .unwrap_or_default(); @@ -482,4 +461,34 @@ mod test { assert_eq!(res, vec![valid_address1, valid_address2]); assert!(!err.is_ok()); } + + #[test] + fn test_parse_ism_cache_configs() { + let raw = r#" + [ + { + "selector": { + "type": "defaultIsm" + }, + "moduletypes": [2], + "chains": ["foochain"], + "cachepolicy": "ismSpecific" + }, + { + "selector": { + "type": "appContext", + "context": "foo" + }, + "moduletypes": [2], + "chains": ["foochain"], + "cachepolicy": "ismSpecific" + } + ] + "#; + + let value = serde_json::from_str::(raw).unwrap(); + let p = ValueParser::new(ConfigPath::default(), &value); + let configs = parse_ism_cache_configs(p).unwrap(); + assert_eq!(configs.len(), 2); + } } diff --git a/rust/main/chains/hyperlane-ethereum/Cargo.toml b/rust/main/chains/hyperlane-ethereum/Cargo.toml index ecc004d0295..b1b4fbd01fd 100644 --- a/rust/main/chains/hyperlane-ethereum/Cargo.toml +++ b/rust/main/chains/hyperlane-ethereum/Cargo.toml @@ -10,6 +10,7 @@ version.workspace = true [dependencies] # Main block async-trait.workspace = true +dashmap.workspace = true derive-new.workspace = true ethers-contract.workspace = true ethers-core.workspace = true diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs index 7cd78eb5907..98138a9e102 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs @@ -1,9 +1,10 @@ use std::fmt::Debug; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::time::Duration; use async_trait::async_trait; +use dashmap::DashMap; use ethers::middleware::gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}; use ethers::middleware::gas_oracle::{ GasCategory, GasOracle, GasOracleMiddleware, Polygon, ProviderOracle, @@ -295,7 +296,27 @@ where GasEscalatorMiddleware::new(provider, escalator, FREQUENCY) } +/// Builds a new HTTP provider with the given URL. fn build_http_provider(url: Url) -> ChainResult { + let client = get_reqwest_client(&url)?; + Ok(Http::new_with_client(url, client)) +} + +/// Gets a cached reqwest client for the given URL, or builds a new one if it doesn't exist. +fn get_reqwest_client(url: &Url) -> ChainResult { + let client_cache = get_reqwest_client_cache(); + if let Some(client) = client_cache.get(url) { + return Ok(client.clone()); + } + let client = build_new_reqwest_client(url.clone())?; + client_cache.insert(url.clone(), client.clone()); + Ok(client) +} + +/// Builds a new reqwest client with the given URL. +/// Generally `get_reqwest_client` should be used instead of this function, +/// as it caches the client for reuse. +fn build_new_reqwest_client(url: Url) -> ChainResult { let mut queries_to_keep = vec![]; let mut headers = reqwest::header::HeaderMap::new(); @@ -325,11 +346,20 @@ fn build_http_provider(url: Url) -> ChainResult { .clear() .extend_pairs(queries_to_keep); - let http_client = Client::builder() + let client = Client::builder() .timeout(HTTP_CLIENT_TIMEOUT) .default_headers(headers) .build() .map_err(EthereumProviderConnectionError::from)?; - Ok(Http::new_with_client(url, http_client)) + Ok(client) +} + +/// A cache for reqwest clients, indexed by URL. +/// Generally creating a new Reqwest client is expensive due to some DNS +/// resolutions, so we cache them for reuse. +static REQWEST_CLIENT_CACHE: OnceLock> = OnceLock::new(); + +fn get_reqwest_client_cache() -> &'static DashMap { + REQWEST_CLIENT_CACHE.get_or_init(DashMap::new) } diff --git a/rust/main/hyperlane-base/src/cache/moka/dynamic_expiry.rs b/rust/main/hyperlane-base/src/cache/moka/dynamic_expiry.rs index 26767e29e77..0cd73178155 100644 --- a/rust/main/hyperlane-base/src/cache/moka/dynamic_expiry.rs +++ b/rust/main/hyperlane-base/src/cache/moka/dynamic_expiry.rs @@ -1,11 +1,24 @@ -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::{ + sync::OnceLock, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; use chrono::{offset::LocalResult, TimeZone, Utc}; use moka::Expiry; use serde::{Deserialize, Serialize}; /// Default expiration time for cache entries. -pub const DEFAULT_EXPIRATION: Duration = Duration::from_secs(60 * 2); +static DEFAULT_EXPIRATION: OnceLock = OnceLock::new(); + +pub fn default_expiration() -> Duration { + *DEFAULT_EXPIRATION.get_or_init(|| { + let secs = std::env::var("HYP_CACHEDEFAULTEXPIRATIONSECONDS") + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(120); // default: 2 minutes + Duration::from_secs(secs) + }) +} /// The type of expiration for a cache entry. /// @@ -55,7 +68,7 @@ impl Expiration { .or(Some(Duration::ZERO)) } ExpirationType::Never => None, - ExpirationType::Default => Some(DEFAULT_EXPIRATION), + ExpirationType::Default => Some(default_expiration()), } } diff --git a/rust/main/hyperlane-base/src/cache/moka/mod.rs b/rust/main/hyperlane-base/src/cache/moka/mod.rs index 8148b925ba5..e86d88697db 100644 --- a/rust/main/hyperlane-base/src/cache/moka/mod.rs +++ b/rust/main/hyperlane-base/src/cache/moka/mod.rs @@ -116,7 +116,7 @@ mod test { use hyperlane_core::{H256, U256}; - use crate::cache::moka::dynamic_expiry::DEFAULT_EXPIRATION; + use crate::cache::moka::dynamic_expiry::default_expiration; use super::*; @@ -300,7 +300,7 @@ mod test { // The second entry should have a TTL between 5 and 10 seconds .is_some_and(|duration| duration.as_secs() > 5 && duration.as_secs() <= 10), 2 => ttl.is_some_and(|duration| { - let default_secs = DEFAULT_EXPIRATION.as_secs(); + let default_secs = default_expiration().as_secs(); // The third entry should have a TTL of > 90% of the default duration.as_secs() > ((default_secs * 9) / 10) && duration.as_secs() <= default_secs diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 0414198bbc4..3a3eafca1f5 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -9,6 +9,7 @@ import { GasPaymentEnforcementPolicyType, IsmCacheConfig, IsmCachePolicy, + IsmCacheSelectorType, MatchingList, ModuleType, RpcConsensusType, @@ -740,19 +741,24 @@ const blacklist: MatchingList = [ })), ]; -const defaultIsmCacheConfig: IsmCacheConfig = { - // Default ISM Routing ISMs change configs based off message content, - // so they are not specified here. - moduleTypes: [ - ModuleType.AGGREGATION, - ModuleType.MERKLE_ROOT_MULTISIG, - ModuleType.MESSAGE_ID_MULTISIG, - ], - // SVM is explicitly not cached as the default ISM is a multisig ISM - // that routes internally. - chains: ethereumChainNames, - cachePolicy: IsmCachePolicy.IsmSpecific, -}; +const ismCacheConfigs: Array = [ + { + selector: { + type: IsmCacheSelectorType.DefaultIsm, + }, + // Default ISM Routing ISMs change configs based off message content, + // so they are not specified here. + moduleTypes: [ + ModuleType.AGGREGATION, + ModuleType.MERKLE_ROOT_MULTISIG, + ModuleType.MESSAGE_ID_MULTISIG, + ], + // SVM is explicitly not cached as the default ISM is a multisig ISM + // that routes internally. + chains: ethereumChainNames, + cachePolicy: IsmCachePolicy.IsmSpecific, + }, +]; const hyperlane: RootAgentConfig = { ...contextBase, @@ -768,7 +774,7 @@ const hyperlane: RootAgentConfig = { blacklist, gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, - defaultIsmCacheConfig, + ismCacheConfigs, allowContractCallCaching: true, resources: relayerResources, }, @@ -809,7 +815,7 @@ const releaseCandidate: RootAgentConfig = { // whitelist: releaseCandidateHelloworldMatchingList, gasPaymentEnforcement, metricAppContextsGetter, - defaultIsmCacheConfig, + ismCacheConfigs, allowContractCallCaching: true, resources: relayerResources, }, @@ -842,7 +848,7 @@ const neutron: RootAgentConfig = { blacklist, gasPaymentEnforcement, metricAppContextsGetter, - defaultIsmCacheConfig, + ismCacheConfigs, allowContractCallCaching: true, resources: relayerResources, }, diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index bc3b321bf80..d76efb607a4 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -3,6 +3,7 @@ import { GasPaymentEnforcementPolicyType, IsmCacheConfig, IsmCachePolicy, + IsmCacheSelectorType, ModuleType, RpcConsensusType, } from '@hyperlane-xyz/sdk'; @@ -14,6 +15,7 @@ import { } from '../../../src/config/agent/agent.js'; import { BaseRelayerConfig, + MetricAppContext, routerMatchingList, } from '../../../src/config/agent/relayer.js'; import { ALL_KEY_ROLES, Role } from '../../../src/roles.js'; @@ -228,26 +230,68 @@ const scraperResources = { }, }; -const defaultIsmCacheConfig: IsmCacheConfig = { - // Default ISM Routing ISMs change configs based off message content, - // so they are not specified here. - moduleTypes: [ - ModuleType.AGGREGATION, - ModuleType.MERKLE_ROOT_MULTISIG, - ModuleType.MESSAGE_ID_MULTISIG, - ], - // SVM is explicitly not cached as the default ISM is a multisig ISM - // that routes internally. - chains: ethereumChainNames, - cachePolicy: IsmCachePolicy.IsmSpecific, -}; - -const relayBlacklist: BaseRelayerConfig['blacklist'] = [ +// Kessel is a load test, these are contracts involved in the load +// test that we want to have certain relayers focus on or ignore. +const kesselMatchingList = [ { - // Ignore kessel runner test recipients. - // All 5 test recipients have the same address. recipientAddress: '0x492b3653A38e229482Bab2f7De4A094B18017246', }, +]; + +const kesselAppContext = 'kessel'; + +const metricAppContextsGetter = (): MetricAppContext[] => [ + { + name: 'helloworld', + matchingList: routerMatchingList(helloWorld[Contexts.Hyperlane].addresses), + }, + { + name: kesselAppContext, + matchingList: kesselMatchingList, + }, +]; + +const ismCacheConfigs: Array = [ + { + selector: { + type: IsmCacheSelectorType.DefaultIsm, + }, + // Default ISM Routing ISMs change configs based off message content, + // so they are not specified here. + moduleTypes: [ + ModuleType.AGGREGATION, + ModuleType.MERKLE_ROOT_MULTISIG, + ModuleType.MESSAGE_ID_MULTISIG, + ], + // SVM is explicitly not cached as the default ISM is a multisig ISM + // that routes internally. + chains: ethereumChainNames, + cachePolicy: IsmCachePolicy.IsmSpecific, + }, + { + selector: { + type: IsmCacheSelectorType.AppContext, + context: kesselAppContext, + }, + // Default ISM Routing ISMs change configs based off message content, + // so they are not specified here. + moduleTypes: [ + ModuleType.AGGREGATION, + ModuleType.MERKLE_ROOT_MULTISIG, + ModuleType.MESSAGE_ID_MULTISIG, + ModuleType.ROUTING, + ], + // SVM is explicitly not cached as the default ISM is a multisig ISM + // that routes internally. + chains: ethereumChainNames, + cachePolicy: IsmCachePolicy.IsmSpecific, + }, +]; + +const relayBlacklist: BaseRelayerConfig['blacklist'] = [ + // Ignore kessel runner test recipients. + // All 5 test recipients have the same address. + ...kesselMatchingList, { // In an effort to reduce some giant retry queues that resulted // from spam txs to the old TestRecipient before we were charging for @@ -283,15 +327,8 @@ const hyperlane: RootAgentConfig = { }, blacklist: [...releaseCandidateHelloworldMatchingList, ...relayBlacklist], gasPaymentEnforcement, - metricAppContextsGetter: () => [ - { - name: 'helloworld', - matchingList: routerMatchingList( - helloWorld[Contexts.Hyperlane].addresses, - ), - }, - ], - defaultIsmCacheConfig, + metricAppContextsGetter, + ismCacheConfigs, allowContractCallCaching: true, resources: relayerResources, }, @@ -327,7 +364,8 @@ const releaseCandidate: RootAgentConfig = { }, blacklist: relayBlacklist, gasPaymentEnforcement, - defaultIsmCacheConfig, + metricAppContextsGetter, + ismCacheConfigs, allowContractCallCaching: true, resources: relayerResources, }, @@ -362,17 +400,19 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'ef039ae-20250411-104801', + tag: '8e87bb6-20250416-174849', }, - whitelist: [ - { - recipientAddress: '0x492b3653A38e229482Bab2f7De4A094B18017246', - }, - ], + whitelist: kesselMatchingList, gasPaymentEnforcement, - defaultIsmCacheConfig, + metricAppContextsGetter, + ismCacheConfigs, allowContractCallCaching: true, - resources: relayerResources, + resources: { + requests: { + cpu: '20000m', + memory: '32Gi', + }, + }, }, }; diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 871e6a348e9..a4e056c2442 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -185,7 +185,7 @@ export class RelayerHelmManager extends OmniscientAgentHelmManager { addressBlacklist: config.addressBlacklist, metricAppContexts: config.metricAppContexts, gasPaymentEnforcement: config.gasPaymentEnforcement, - defaultIsmCacheConfig: config.defaultIsmCacheConfig, + ismCacheConfigs: config.ismCacheConfigs, }; const envConfig = objOmitKeys( config, diff --git a/typescript/infra/src/config/agent/relayer.ts b/typescript/infra/src/config/agent/relayer.ts index 2d5f71d8d2f..56fc06f513b 100644 --- a/typescript/infra/src/config/agent/relayer.ts +++ b/typescript/infra/src/config/agent/relayer.ts @@ -54,7 +54,7 @@ export interface BaseRelayerConfig { transactionGasLimit?: BigNumberish; skipTransactionGasLimitFor?: string[]; metricAppContextsGetter?: () => MetricAppContext[]; - defaultIsmCacheConfig?: IsmCacheConfig; + ismCacheConfigs?: Array; allowContractCallCaching?: boolean; } @@ -67,7 +67,7 @@ export type RelayerConfigMapConfig = Pick< | 'addressBlacklist' | 'gasPaymentEnforcement' | 'metricAppContexts' - | 'defaultIsmCacheConfig' + | 'ismCacheConfigs' >; // The rest of the config is intended to be set as env vars. export type RelayerEnvConfig = Omit< @@ -137,8 +137,8 @@ export class RelayerConfigHelper extends AgentConfigHelper { baseConfig.metricAppContextsGetter(), ); } - if (baseConfig.defaultIsmCacheConfig) { - relayerConfig.defaultIsmCacheConfig = baseConfig.defaultIsmCacheConfig; + if (baseConfig.ismCacheConfigs) { + relayerConfig.ismCacheConfigs = baseConfig.ismCacheConfigs; } relayerConfig.allowContractCallCaching = baseConfig.allowContractCallCaching; diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index fd333c3c999..37e567bfd45 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -247,6 +247,7 @@ export { GasPaymentEnforcementPolicyType, IsmCacheConfig, IsmCachePolicy, + IsmCacheSelectorType, RelayerConfig, RpcConsensusType, ScraperConfig, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index b2f4b406435..9eb673408e2 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -355,7 +355,25 @@ export enum IsmCachePolicy { IsmSpecific = 'ismSpecific', } +export enum IsmCacheSelectorType { + DefaultIsm = 'defaultIsm', + AppContext = 'appContext', +} + +const IsmCacheSelector = z.discriminatedUnion('type', [ + z.object({ + type: z.literal(IsmCacheSelectorType.DefaultIsm), + }), + z.object({ + type: z.literal(IsmCacheSelectorType.AppContext), + context: z.string(), + }), +]); + const IsmCacheConfigSchema = z.object({ + selector: IsmCacheSelector.describe( + 'The selector to use for the ISM cache policy', + ), moduleTypes: z .array(z.nativeEnum(ModuleType)) .describe('The ISM module types to use the cache policy for.'), @@ -420,11 +438,11 @@ export const RelayerAgentConfigSchema = AgentConfigSchema.extend({ .describe( 'A list of app contexts and their matching lists to use for metrics. A message will be classified as the first matching app context.', ), - defaultIsmCacheConfig: z - .union([IsmCacheConfigSchema, z.string().min(1)]) + ismCacheConfigs: z + .union([z.array(IsmCacheConfigSchema), z.string().min(1)]) .optional() .describe( - 'The default ISM cache config to use for all chains. If not specified, default caching will be used.', + 'The ISM cache configs to be used. If not specified, default caching will be used.', ), allowContractCallCaching: z .boolean() From 893f53ca1e391db265330250d4721a403b300d65 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 16 Apr 2025 23:09:34 +0100 Subject: [PATCH 019/223] feat: batching retries (#5930) ### Description - reuses a lot of logic from `call_and_retry_n_times`, but since passing references around isn't 'static (as required by `call_and_retry_n_times`), creates a standalone function to retry batching submissions - adds a new method to the `Mailbox` trait: `supports_batching`, which is `false` by default and only enabled on EVM. If this is false, batching isn't retried, since it will always fail. This is not the cleanest approach but good enough, given we'll reimplement batching in the EVM adapter anyway ### Backward compatibility Yes ### Testing Unit tests --------- Co-authored-by: Trevor Porter --- rust/main/Cargo.lock | 2 + rust/main/agents/relayer/Cargo.toml | 2 + rust/main/agents/relayer/src/msg/mod.rs | 1 + rust/main/agents/relayer/src/msg/op_batch.rs | 451 ++++++++++++++++++ rust/main/agents/relayer/src/msg/op_queue.rs | 8 +- .../agents/relayer/src/msg/op_submitter.rs | 137 +----- .../agents/relayer/src/msg/pending_message.rs | 2 +- rust/main/agents/relayer/src/msg/processor.rs | 10 +- rust/main/agents/relayer/src/relayer.rs | 1 + rust/main/agents/scraper/src/agent.rs | 1 + .../src/providers/grpc/tests.rs | 1 + .../src/contracts/mailbox.rs | 148 +++++- .../src/contracts/multicall.rs | 21 +- .../src/contracts/validator_announce.rs | 2 + .../src/rpc_clients/fallback.rs | 2 +- rust/main/chains/hyperlane-ethereum/src/tx.rs | 55 ++- .../hyperlane-base/src/settings/parser/mod.rs | 7 + rust/main/hyperlane-core/src/config/mod.rs | 4 + .../main/hyperlane-core/src/traits/mailbox.rs | 21 +- rust/main/hyperlane-test/src/mocks/mailbox.rs | 23 +- .../chains/sealevel/adapter/tests/config.rs | 1 + typescript/infra/src/agents/index.ts | 1 + .../sdk/src/metadata/chainMetadataTypes.ts | 5 + 23 files changed, 711 insertions(+), 195 deletions(-) create mode 100644 rust/main/agents/relayer/src/msg/op_batch.rs diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 759ba180f87..1b4422a28a2 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -7952,12 +7952,14 @@ dependencies = [ "serde_json", "strum 0.26.3", "submitter", + "tempfile", "thiserror", "tokio", "tokio-metrics", "tokio-test", "tracing", "tracing-futures", + "tracing-subscriber", "tracing-test", "typetag", "uuid 1.11.0", diff --git a/rust/main/agents/relayer/Cargo.toml b/rust/main/agents/relayer/Cargo.toml index 29102a8a737..b37ff4c9bec 100644 --- a/rust/main/agents/relayer/Cargo.toml +++ b/rust/main/agents/relayer/Cargo.toml @@ -64,10 +64,12 @@ once_cell.workspace = true mockall.workspace = true tokio-test.workspace = true tracing-test.workspace = true +tracing-subscriber.workspace = true hyperlane-test = { path = "../../hyperlane-test" } hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async", "test-utils"] } ethers-prometheus = { path = "../../ethers-prometheus", features = ["serde"] } +tempfile.workspace = true [features] default = ["color-eyre", "oneline-errors"] diff --git a/rust/main/agents/relayer/src/msg/mod.rs b/rust/main/agents/relayer/src/msg/mod.rs index 5f7a5791d02..c95337526b6 100644 --- a/rust/main/agents/relayer/src/msg/mod.rs +++ b/rust/main/agents/relayer/src/msg/mod.rs @@ -30,6 +30,7 @@ pub(crate) mod blacklist; pub(crate) mod gas_payment; pub(crate) mod metadata; +pub(crate) mod op_batch; pub(crate) mod op_queue; pub(crate) mod op_submitter; pub(crate) mod processor; diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs new file mode 100644 index 00000000000..30abad29647 --- /dev/null +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -0,0 +1,451 @@ +use std::{sync::Arc, time::Duration}; + +use derive_new::new; +use hyperlane_core::{ + rpc_clients::DEFAULT_MAX_RPC_RETRIES, total_estimated_cost, BatchResult, + ChainCommunicationError, ChainResult, ConfirmReason, HyperlaneDomain, Mailbox, + PendingOperation, PendingOperationStatus, QueueOperation, TxOutcome, +}; +use itertools::{Either, Itertools}; +use tokio::time::sleep; +use tracing::{info, instrument, warn}; + +use super::{ + op_queue::OpQueue, + op_submitter::{submit_single_operation, SerialSubmitterMetrics}, + pending_message::CONFIRM_DELAY, +}; + +const BATCH_RETRY_SLEEP_DURATION: Duration = Duration::from_millis(100); + +#[derive(new, Debug)] +pub(crate) struct OperationBatch { + operations: Vec, + #[allow(dead_code)] + domain: HyperlaneDomain, +} + +impl OperationBatch { + #[instrument(skip_all, fields(domain=%self.domain, batch_size=self.operations.len()))] + pub async fn submit( + self, + prepare_queue: &mut OpQueue, + confirm_queue: &mut OpQueue, + metrics: &SerialSubmitterMetrics, + ) { + let excluded_ops = match self.try_submit_as_batch(metrics).await { + Ok(batch_result) => { + Self::handle_batch_result(self.operations, batch_result, confirm_queue).await + } + Err(e) => { + warn!(error=?e, batch=?self.operations, "Error when submitting batch"); + self.operations + } + }; + + if !excluded_ops.is_empty() { + warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Falling back to serial submission."); + OperationBatch::new(excluded_ops, self.domain) + .submit_serially(prepare_queue, confirm_queue, metrics) + .await; + } + } + + #[instrument(skip(self, metrics), ret, level = "debug")] + async fn try_submit_as_batch( + &self, + metrics: &SerialSubmitterMetrics, + ) -> ChainResult { + // We already assume that the relayer submits to a single mailbox per destination. + // So it's fine to use the first item in the batch to get the mailbox. + let Some(first_item) = self.operations.first() else { + return Err(ChainCommunicationError::BatchIsEmpty); + }; + let Some(mailbox) = first_item.try_get_mailbox() else { + // no need to update the metrics since all operations are excluded + return Ok(BatchResult::failed(self.operations.len())); + }; + let outcome = self + .submit_batch_with_retry(mailbox, DEFAULT_MAX_RPC_RETRIES, BATCH_RETRY_SLEEP_DURATION) + .await?; + let ops_submitted = self.operations.len() - outcome.failed_indexes.len(); + metrics.ops_submitted.inc_by(ops_submitted as u64); + Ok(outcome) + } + + async fn submit_batch_with_retry( + &self, + mailbox: Arc, + max_retries: usize, + sleep_period: Duration, + ) -> ChainResult { + if !mailbox.supports_batching() { + return Ok(BatchResult::failed(self.operations.len())); + } + let mut last_error = None; + let ops = self.operations.iter().collect_vec(); + let op_ids = ops.iter().map(|op| op.id()).collect_vec(); + for retry_number in 1..=max_retries { + match mailbox.process_batch(ops.clone()).await { + Ok(res) => return Ok(res), + Err(err) => { + warn!(retries=retry_number, ?max_retries, error=?err, ids=?op_ids, "Retrying batch submission"); + last_error = Some(err); + sleep(sleep_period).await; + } + } + } + let error = last_error.unwrap_or(ChainCommunicationError::BatchingFailed); + Err(error) + } + + /// Process the operations sent by a batch. + /// Returns the operations that were not sent + async fn handle_batch_result( + operations: Vec, + batch_result: BatchResult, + confirm_queue: &mut OpQueue, + ) -> Vec> { + let (sent_ops, excluded_ops): (Vec<_>, Vec<_>) = + operations.into_iter().enumerate().partition_map(|(i, op)| { + if !batch_result.failed_indexes.contains(&i) { + Either::Left(op) + } else { + Either::Right(op) + } + }); + + if let Some(outcome) = batch_result.outcome { + info!(batch_size=sent_ops.len(), outcome=?outcome, batch=?sent_ops, ?excluded_ops, "Submitted transaction batch"); + Self::update_sent_ops_state(sent_ops, outcome, confirm_queue).await; + } + excluded_ops + } + + async fn update_sent_ops_state( + sent_ops: Vec>, + outcome: TxOutcome, + confirm_queue: &mut OpQueue, + ) { + let total_estimated_cost = total_estimated_cost(sent_ops.as_slice()); + for mut op in sent_ops { + op.set_operation_outcome(outcome.clone(), total_estimated_cost); + op.set_next_attempt_after(CONFIRM_DELAY); + confirm_queue + .push( + op, + Some(PendingOperationStatus::Confirm( + ConfirmReason::SubmittedBySelf, + )), + ) + .await; + } + } + + async fn submit_serially( + self, + prepare_queue: &mut OpQueue, + confirm_queue: &mut OpQueue, + metrics: &SerialSubmitterMetrics, + ) { + for op in self.operations.into_iter() { + submit_single_operation(op, prepare_queue, confirm_queue, metrics).await; + } + } +} + +#[cfg(test)] +mod tests { + + use std::{str::FromStr, sync::Arc}; + + use crate::{ + merkle_tree::builder::MerkleTreeBuilder, + msg::{ + gas_payment::GasPaymentEnforcer, + metadata::{ + BaseMetadataBuilder, DefaultIsmCache, IsmAwareAppContextClassifier, + IsmCachePolicyClassifier, + }, + op_queue::test::MockPendingOperation, + pending_message::{MessageContext, PendingMessage}, + processor::test::{ + dummy_cache_metrics, dummy_submission_metrics, DummyApplicationOperationVerifier, + }, + }, + settings::{ + matching_list::MatchingList, GasPaymentEnforcementConf, GasPaymentEnforcementPolicy, + }, + }; + use ethers::utils::hex; + use hyperlane_base::{ + cache::{LocalCache, MeteredCache, MeteredCacheConfig, OptionalCache}, + db::{HyperlaneRocksDB, DB}, + settings::{ChainConf, ChainConnectionConf, CoreContractAddresses}, + CoreMetrics, + }; + use hyperlane_core::{ + config::OperationBatchConfig, Decode, HyperlaneMessage, KnownHyperlaneDomain, + MessageSubmissionData, ReorgPeriod, SubmitterType, H160, U256, + }; + use hyperlane_ethereum::{ConnectionConf, RpcConnectionConf}; + use hyperlane_test::mocks::{MockMailboxContract, MockValidatorAnnounceContract}; + use tokio::sync::RwLock; + + use super::*; + + fn dummy_pending_operation( + mailbox: Arc, + domain: HyperlaneDomain, + ) -> Box { + let seconds_to_next_attempt = 10; + let mut mock_pending_operation = + MockPendingOperation::new(seconds_to_next_attempt, domain.clone()); + mock_pending_operation.mailbox = Some(mailbox); + Box::new(mock_pending_operation) as Box + } + + #[tokio::test] + async fn test_handle_batch_result_succeeds() { + let mut mock_mailbox = MockMailboxContract::new(); + let dummy_domain: HyperlaneDomain = KnownHyperlaneDomain::Alfajores.into(); + + mock_mailbox.expect_supports_batching().return_const(true); + mock_mailbox.expect_process_batch().returning(move |_ops| { + let batch_result = BatchResult::new(None, vec![]); + Ok(batch_result) + }); + let mock_mailbox = Arc::new(mock_mailbox) as Arc; + let operation = dummy_pending_operation(mock_mailbox.clone(), dummy_domain.clone()); + + let operations = vec![operation]; + let op_batch = OperationBatch::new(operations, dummy_domain); + let batch_result = op_batch + .submit_batch_with_retry(mock_mailbox, 1, Duration::from_secs(0)) + .await + .unwrap(); + assert!( + batch_result.failed_indexes.is_empty(), + "Batch result should not have failed indexes" + ) + } + + #[tokio::test] + async fn test_handle_batch_result_fails() { + let mut mock_mailbox = MockMailboxContract::new(); + let dummy_domain: HyperlaneDomain = KnownHyperlaneDomain::Alfajores.into(); + + mock_mailbox.expect_supports_batching().return_const(true); + mock_mailbox + .expect_process_batch() + .returning(move |_ops| Err(ChainCommunicationError::BatchingFailed)); + let mock_mailbox = Arc::new(mock_mailbox) as Arc; + let operation = dummy_pending_operation(mock_mailbox.clone(), dummy_domain.clone()); + + let operations = vec![operation]; + let op_batch = OperationBatch::new(operations, dummy_domain); + let result = op_batch + .submit_batch_with_retry(mock_mailbox, 1, Duration::from_secs(0)) + .await; + assert!(matches!( + result, + Err(ChainCommunicationError::BatchingFailed) + )); + } + + #[tokio::test] + async fn test_handle_batch_succeeds_eventually() { + let _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .try_init(); + let mut mock_mailbox = MockMailboxContract::new(); + let dummy_domain: HyperlaneDomain = KnownHyperlaneDomain::Alfajores.into(); + + let mut counter = 0; + mock_mailbox.expect_supports_batching().return_const(true); + mock_mailbox.expect_process_batch().returning(move |_ops| { + counter += 1; + if counter < 5 { + return Err(ChainCommunicationError::BatchingFailed); + } + let batch_result = BatchResult::new(None, vec![]); + Ok(batch_result) + }); + let mock_mailbox = Arc::new(mock_mailbox) as Arc; + let operation = dummy_pending_operation(mock_mailbox.clone(), dummy_domain.clone()); + + let operations = vec![operation]; + let op_batch = OperationBatch::new(operations, dummy_domain); + let batch_result = op_batch + .submit_batch_with_retry(mock_mailbox, 10, Duration::from_secs(0)) + .await + .unwrap(); + assert!( + batch_result.failed_indexes.is_empty(), + "Batch result should not have failed indexes" + ); + } + + #[tokio::test] + async fn test_handle_batch_result_fails_if_not_supported() { + let mut mock_mailbox = MockMailboxContract::new(); + let dummy_domain: HyperlaneDomain = KnownHyperlaneDomain::Alfajores.into(); + + mock_mailbox.expect_supports_batching().return_const(false); + mock_mailbox.expect_process_batch().returning(move |_ops| { + let batch_result = BatchResult::new(None, vec![]); + Ok(batch_result) + }); + let mock_mailbox = Arc::new(mock_mailbox) as Arc; + let operation = dummy_pending_operation(mock_mailbox.clone(), dummy_domain.clone()); + + let operations = vec![operation]; + let op_batch = OperationBatch::new(operations, dummy_domain); + let batch_result = op_batch + .submit_batch_with_retry(mock_mailbox, 1, Duration::from_secs(0)) + .await + .unwrap(); + assert!( + batch_result.failed_indexes.len() == 1, + "Batching should fail if not supported" + ) + } + + #[tokio::test] + #[ignore] + async fn benchmarking_with_real_rpcs() { + let _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .try_init(); + + let arb_chain_conf = ChainConf { + domain: HyperlaneDomain::Known(hyperlane_core::KnownHyperlaneDomain::Arbitrum), + // TODO + signer: None, + submitter: SubmitterType::Classic, + estimated_block_time: Duration::from_secs(1), + reorg_period: ReorgPeriod::from_blocks(10), + addresses: CoreContractAddresses { + mailbox: H160::from_str("0x979Ca5202784112f4738403dBec5D0F3B9daabB9") + .unwrap() + .into(), + validator_announce: H160::from_str("0x1df063280C4166AF9a725e3828b4dAC6c7113B08") + .unwrap() + .into(), + ..Default::default() + }, + connection: ChainConnectionConf::Ethereum(ConnectionConf { + rpc_connection: RpcConnectionConf::HttpFallback { + urls: vec![ + "https://arbitrum.drpc.org".parse().unwrap(), + "https://endpoints.omniatech.io/v1/arbitrum/one/public" + .parse() + .unwrap(), + ], + }, + transaction_overrides: Default::default(), + operation_batch: OperationBatchConfig { + batch_contract_address: None, + max_batch_size: 32, + bypass_batch_simulation: false, + }, + }), + metrics_conf: Default::default(), + index: Default::default(), + }; + + // https://explorer.hyperlane.xyz/message/0x29160a18c6e27c2f14ebe021207ac3f90664507b9c5aacffd802b2afcc15788a + // Base -> Arbitrum, uses the default ISM + let message_bytes = hex::decode("0300139ebf000021050000000000000000000000005454cf5584939f7f884e95dba33fecd6d40b8fe20000a4b1000000000000000000000000fd34afdfbac1e47afc539235420e4be4a206f26d0000000000000000000000008650ee37ba2b0a8ac5954a04b46ee07093eab7f90000000000000000000000000000000000000000000000004563918244f40000").unwrap(); + let message = HyperlaneMessage::read_from(&mut &message_bytes[..]).unwrap(); + let base_domain = HyperlaneDomain::new_test_domain("base"); + let temp_dir = tempfile::tempdir().unwrap(); + let db = DB::from_path(temp_dir.path()).unwrap(); + let base_db = HyperlaneRocksDB::new(&base_domain, db); + + let core_metrics = CoreMetrics::new("test", 9090, Default::default()).unwrap(); + let arb_mailbox: Arc = arb_chain_conf + .build_mailbox(&core_metrics) + .await + .unwrap() + .into(); + + let cache = OptionalCache::new(Some(MeteredCache::new( + LocalCache::new("test-cache"), + dummy_cache_metrics(), + MeteredCacheConfig { + cache_name: "test-cache".to_owned(), + }, + ))); + let base_va = Arc::new(MockValidatorAnnounceContract::default()); + let default_ism_getter = DefaultIsmCache::new(arb_mailbox.clone()); + let core_metrics = Arc::new(core_metrics); + let metadata_builder = BaseMetadataBuilder::new( + base_domain.clone(), + arb_chain_conf.clone(), + Arc::new(RwLock::new(MerkleTreeBuilder::new())), + base_va, + false, + core_metrics.clone(), + cache.clone(), + base_db.clone(), + IsmAwareAppContextClassifier::new(default_ism_getter.clone(), vec![]), + IsmCachePolicyClassifier::new(default_ism_getter, Default::default()), + ); + let message_context = Arc::new(MessageContext { + destination_mailbox: arb_mailbox, + origin_db: Arc::new(base_db.clone()), + cache: cache.clone(), + metadata_builder: Arc::new(metadata_builder), + origin_gas_payment_enforcer: Arc::new(GasPaymentEnforcer::new( + vec![GasPaymentEnforcementConf { + policy: GasPaymentEnforcementPolicy::None, + matching_list: MatchingList::default(), + }], + base_db.clone(), + )), + transaction_gas_limit: Default::default(), + metrics: dummy_submission_metrics(), + application_operation_verifier: Some(Arc::new(DummyApplicationOperationVerifier {})), + }); + + let attempts = 2; + let batch_size = 32; + + let mut pending_messages = vec![]; + // Message found here https://basescan.org/tx/0x65345812a1f7df6236292d52d50418a090c84e2c901912bede6cadb9810a9882#eventlog + let metadata = + "0x000000100000001000000010000001680000000000000000000000100000015800000000000000000000000019dc38aeae620380430c200a6e990d5af5480117dbd3d5e656de9dcf604fcc90b52a3b97d9f3573b4a0733e824f1358e515698cf00139eaa5452e030aa937f6b14162a44ec3327f6832bbf16e4b0d6df452524af1c1a04e875b4ce7ac0da92aa08838a89f2a126eef23f6b6a08b6cdbe9e9e804b321088b91b034f9466eed2da1dcc36cb220b887b15f3e111a179142c27e4a0b6d6b7a291e22577d6296d82b7c3f29e8989ec1161d853aba0982b2db28b9a9917226c2c27111c41c99e6a84e7717740f901528062385e659b4330e7227593a334be532d27bcf24f3f13bf4fc1a860e96f8d6937984ea83ef61c8ea30d48cc903f6ff725406a4d1ce73f46064b3403ea4c720b770f4389d7259b275f085c6a98cef9a04880a249b42c382ba34a63031debbfb5b9b232ffd9ee45ff63a7249e83c7e9720f9e978a431b".as_bytes().to_vec(); + + for b in 0..batch_size { + let mut pending_message = PendingMessage::new( + message.clone(), + message_context.clone(), + PendingOperationStatus::FirstPrepareAttempt, + Some(format!("test-{}", b)), + attempts, + ); + pending_message.submission_data = Some(Box::new(MessageSubmissionData { + metadata: metadata.clone(), + gas_limit: U256::from(615293), + })); + pending_messages.push(pending_message); + } + + let arb_domain = HyperlaneDomain::new_test_domain("arbitrum"); + let serial_submitter_metrics = + SerialSubmitterMetrics::new(core_metrics.clone(), &arb_domain); + + let operation_batch = OperationBatch::new( + pending_messages + .into_iter() + .map(|msg| Box::new(msg) as Box) + .collect(), + arb_domain, + ); + operation_batch + .try_submit_as_batch(&serial_submitter_metrics) + .await + .unwrap(); + } +} diff --git a/rust/main/agents/relayer/src/msg/op_queue.rs b/rust/main/agents/relayer/src/msg/op_queue.rs index 7209631b73d..7ca5452d841 100644 --- a/rust/main/agents/relayer/src/msg/op_queue.rs +++ b/rust/main/agents/relayer/src/msg/op_queue.rs @@ -168,8 +168,8 @@ pub mod test { use hyperlane_core::{ ChainResult, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneDomainTechnicalStack, - HyperlaneDomainType, HyperlaneMessage, KnownHyperlaneDomain, PendingOperationResult, - ReprepareReason, TryBatchAs, TxOutcome, H256, U256, + HyperlaneDomainType, HyperlaneMessage, KnownHyperlaneDomain, Mailbox, + PendingOperationResult, ReprepareReason, TryBatchAs, TxOutcome, H256, U256, }; use crate::{ @@ -189,6 +189,8 @@ pub mod test { seconds_to_next_attempt: u64, destination_domain: HyperlaneDomain, retry_count: u32, + #[serde(skip)] + pub mailbox: Option>, } impl MockPendingOperation { @@ -202,6 +204,7 @@ pub mod test { recipient_address: H256::random(), origin_domain_id: 0, retry_count: 0, + mailbox: None, } } @@ -221,6 +224,7 @@ pub mod test { domain_protocol: HyperlaneDomainProtocol::Ethereum, domain_technical_stack: HyperlaneDomainTechnicalStack::Other, }, + mailbox: None, } } diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index ccb381d651a..47d83e4da20 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -5,31 +5,29 @@ use std::fmt::Debug; 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 itertools::{Either, Itertools}; use num_traits::Zero; use prometheus::{IntCounter, IntGaugeVec}; use tokio::sync::{broadcast::Sender, mpsc, Mutex}; use tokio::task::JoinHandle; use tokio::time::sleep; use tokio_metrics::TaskMonitor; -use tracing::{debug, error, info, info_span, instrument, trace, warn, Instrument}; +use tracing::{debug, error, info_span, instrument, trace, warn, Instrument}; use hyperlane_base::db::{HyperlaneDb, HyperlaneRocksDB}; use hyperlane_base::CoreMetrics; use hyperlane_core::{ - total_estimated_cost, BatchResult, ChainCommunicationError, ChainResult, ConfirmReason::{self, *}, - HyperlaneDomain, HyperlaneDomainProtocol, PendingOperation, PendingOperationResult, - PendingOperationStatus, QueueOperation, ReprepareReason, TxOutcome, + HyperlaneDomain, HyperlaneDomainProtocol, PendingOperationResult, PendingOperationStatus, + QueueOperation, ReprepareReason, }; use submitter::{Entrypoint, FullPayload, PayloadDispatcherEntrypoint, PayloadId}; use crate::msg::pending_message::CONFIRM_DELAY; use crate::server::MessageRetryRequest; +use super::op_batch::OperationBatch; use super::op_queue::OpQueue; use super::op_queue::OperationPriorityQueue; @@ -525,7 +523,7 @@ async fn prepare_op( } #[instrument(skip(prepare_queue, confirm_queue, metrics), ret, level = "debug")] -async fn submit_single_operation( +pub(crate) async fn submit_single_operation( mut op: QueueOperation, prepare_queue: &mut OpQueue, confirm_queue: &mut OpQueue, @@ -796,138 +794,39 @@ async fn send_back_on_failed_submisison( #[derive(Debug, Clone)] pub struct SerialSubmitterMetrics { - submitter_queue_length: IntGaugeVec, - ops_prepared: IntCounter, - ops_submitted: IntCounter, - ops_confirmed: IntCounter, - ops_failed: IntCounter, - ops_dropped: IntCounter, + pub(crate) submitter_queue_length: IntGaugeVec, + pub(crate) ops_prepared: IntCounter, + pub(crate) ops_submitted: IntCounter, + pub(crate) ops_confirmed: IntCounter, + pub(crate) ops_failed: IntCounter, + pub(crate) ops_dropped: IntCounter, } impl SerialSubmitterMetrics { - pub fn new(metrics: &CoreMetrics, destination: &HyperlaneDomain) -> Self { + pub fn new(metrics: impl AsRef, destination: &HyperlaneDomain) -> Self { let destination = destination.name(); Self { - submitter_queue_length: metrics.submitter_queue_length(), + submitter_queue_length: metrics.as_ref().submitter_queue_length(), ops_prepared: metrics + .as_ref() .operations_processed_count() .with_label_values(&["prepared", destination]), ops_submitted: metrics + .as_ref() .operations_processed_count() .with_label_values(&["submitted", destination]), ops_confirmed: metrics + .as_ref() .operations_processed_count() .with_label_values(&["confirmed", destination]), ops_failed: metrics + .as_ref() .operations_processed_count() .with_label_values(&["failed", destination]), ops_dropped: metrics + .as_ref() .operations_processed_count() .with_label_values(&["dropped", destination]), } } } - -#[derive(new, Debug)] -struct OperationBatch { - operations: Vec, - #[allow(dead_code)] - domain: HyperlaneDomain, -} - -impl OperationBatch { - async fn submit( - self, - prepare_queue: &mut OpQueue, - confirm_queue: &mut OpQueue, - metrics: &SerialSubmitterMetrics, - ) { - let excluded_ops = match self.try_submit_as_batch(metrics).await { - Ok(batch_result) => { - Self::handle_batch_result(self.operations, batch_result, confirm_queue).await - } - Err(e) => { - warn!(error=?e, batch=?self.operations, "Error when submitting batch"); - self.operations - } - }; - - if !excluded_ops.is_empty() { - warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Falling back to serial submission."); - OperationBatch::new(excluded_ops, self.domain) - .submit_serially(prepare_queue, confirm_queue, metrics) - .await; - } - } - - #[instrument(skip(metrics), ret, level = "debug")] - async fn try_submit_as_batch( - &self, - metrics: &SerialSubmitterMetrics, - ) -> ChainResult { - // We already assume that the relayer submits to a single mailbox per destination. - // So it's fine to use the first item in the batch to get the mailbox. - let Some(first_item) = self.operations.first() else { - return Err(ChainCommunicationError::BatchIsEmpty); - }; - let outcome = if let Some(mailbox) = first_item.try_get_mailbox() { - mailbox - .try_process_batch(self.operations.iter().collect_vec()) - .await? - } else { - BatchResult::failed(self.operations.len()) - }; - let ops_submitted = self.operations.len() - outcome.failed_indexes.len(); - metrics.ops_submitted.inc_by(ops_submitted as u64); - Ok(outcome) - } - - /// Process the operations sent by a batch. - /// Returns the operations that were not sent - async fn handle_batch_result( - operations: Vec, - batch_result: BatchResult, - confirm_queue: &mut OpQueue, - ) -> Vec> { - let (sent_ops, excluded_ops): (Vec<_>, Vec<_>) = - operations.into_iter().enumerate().partition_map(|(i, op)| { - if !batch_result.failed_indexes.contains(&i) { - Either::Left(op) - } else { - Either::Right(op) - } - }); - - if let Some(outcome) = batch_result.outcome { - info!(batch_size=sent_ops.len(), outcome=?outcome, batch=?sent_ops, ?excluded_ops, "Submitted transaction batch"); - Self::update_sent_ops_state(sent_ops, outcome, confirm_queue).await; - } - excluded_ops - } - - async fn update_sent_ops_state( - sent_ops: Vec>, - outcome: TxOutcome, - confirm_queue: &mut OpQueue, - ) { - let total_estimated_cost = total_estimated_cost(sent_ops.as_slice()); - for mut op in sent_ops { - op.set_operation_outcome(outcome.clone(), total_estimated_cost); - op.set_next_attempt_after(CONFIRM_DELAY); - confirm_queue - .push(op, Some(PendingOperationStatus::Confirm(SubmittedBySelf))) - .await; - } - } - - async fn submit_serially( - self, - prepare_queue: &mut OpQueue, - confirm_queue: &mut OpQueue, - metrics: &SerialSubmitterMetrics, - ) { - for op in self.operations.into_iter() { - submit_single_operation(op, prepare_queue, confirm_queue, metrics).await; - } - } -} diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index 572e89aec70..13713535060 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -96,7 +96,7 @@ pub struct PendingMessage { submitted: bool, #[new(default)] #[serde(skip_serializing)] - submission_data: Option>, + pub(crate) submission_data: Option>, #[new(default)] num_retries: u32, #[new(value = "Instant::now()")] diff --git a/rust/main/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs index 4de1469fa6d..27e27f192fd 100644 --- a/rust/main/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -404,7 +404,7 @@ impl MessageProcessorMetrics { } #[cfg(test)] -mod test { +pub mod test { use std::time::Instant; use prometheus::{CounterVec, IntCounter, IntCounterVec, Opts, Registry}; @@ -451,7 +451,7 @@ mod test { use super::*; - struct DummyApplicationOperationVerifier {} + pub struct DummyApplicationOperationVerifier {} #[async_trait] impl ApplicationOperationVerifier for DummyApplicationOperationVerifier { @@ -464,7 +464,7 @@ mod test { } } - fn dummy_processor_metrics(domain_id: u32) -> MessageProcessorMetrics { + pub fn dummy_processor_metrics(domain_id: u32) -> MessageProcessorMetrics { MessageProcessorMetrics { max_last_known_message_nonce_gauge: IntGauge::new( "dummy_max_last_known_message_nonce_gauge", @@ -478,7 +478,7 @@ mod test { } } - fn dummy_cache_metrics() -> MeteredCacheMetrics { + pub fn dummy_cache_metrics() -> MeteredCacheMetrics { MeteredCacheMetrics { hit_count: IntCounterVec::new( prometheus::Opts::new("dummy_hit_count", "help string"), @@ -493,7 +493,7 @@ mod test { } } - fn dummy_submission_metrics() -> MessageSubmissionMetrics { + pub fn dummy_submission_metrics() -> MessageSubmissionMetrics { MessageSubmissionMetrics { origin: "".to_string(), destination: "".to_string(), diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index d0cb8192f86..66b96f94e29 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -1088,6 +1088,7 @@ mod test { operation_batch: OperationBatchConfig { batch_contract_address: None, max_batch_size: 1, + ..Default::default() }, }), metrics_conf: PrometheusMiddlewareConf { diff --git a/rust/main/agents/scraper/src/agent.rs b/rust/main/agents/scraper/src/agent.rs index 12277ce8602..be28db8f6f1 100644 --- a/rust/main/agents/scraper/src/agent.rs +++ b/rust/main/agents/scraper/src/agent.rs @@ -445,6 +445,7 @@ mod test { operation_batch: OperationBatchConfig { batch_contract_address: None, max_batch_size: 1, + ..Default::default() }, }), metrics_conf: PrometheusMiddlewareConf { diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs b/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs index a3fa5cf8014..72568df4fe0 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs @@ -64,6 +64,7 @@ fn provider(address: &str) -> WasmGrpcProvider { OperationBatchConfig { batch_contract_address: None, max_batch_size: 1, + ..Default::default() }, NativeToken { decimals: 6, diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index 07f1b2269d7..a0fec99e02c 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -8,6 +8,9 @@ use std::sync::Arc; use async_trait::async_trait; use derive_new::new; use ethers::prelude::Middleware; +use ethers::providers::ProviderError; +use ethers::types::transaction::eip2718::TypedTransaction; +use ethers::types::{Block, BlockNumber, H256 as TxHash}; use ethers_contract::builders::ContractCall; use ethers_contract::{Multicall, MulticallResult}; use ethers_core::utils::WEI_IN_ETHER; @@ -15,6 +18,7 @@ use futures_util::future::join_all; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512}; use itertools::Itertools; +use tokio::sync::Mutex; use tracing::instrument; use hyperlane_core::{ @@ -28,7 +32,9 @@ use crate::error::HyperlaneEthereumError; use crate::interfaces::arbitrum_node_interface::ArbitrumNodeInterface; use crate::interfaces::i_mailbox::{IMailbox as EthereumMailboxInternal, IMAILBOX_ABI}; use crate::interfaces::mailbox::DispatchFilter; -use crate::tx::{call_with_reorg_period, fill_tx_gas_params, report_tx}; +use crate::tx::{ + call_with_reorg_period, estimate_eip1559_fees, fill_tx_gas_params, report_tx, Eip1559Fee, +}; use crate::{ BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod, TransactionOverrides, @@ -275,6 +281,14 @@ where provider: Arc, arbitrum_node_interface: Option>>, conn: ConnectionConf, + cache: Arc>, +} + +#[derive(Debug, Default)] +pub struct EthereumMailboxCache { + pub is_contract: HashMap, + pub latest_block: Option>, + pub eip1559_fee: Option, } impl EthereumMailbox @@ -304,16 +318,15 @@ where provider, arbitrum_node_interface, conn: conn.clone(), + cache: Default::default(), } } - /// Returns a ContractCall that processes the provided message. - async fn process_contract_call( + fn contract_call( &self, message: &HyperlaneMessage, metadata: &[u8], tx_gas_estimate: Option, - with_gas_estimate_buffer: bool, ) -> ChainResult> { let mut tx = self.contract.process( metadata.to_vec().into(), @@ -322,6 +335,18 @@ where if let Some(gas_estimate) = tx_gas_estimate { tx = tx.gas(gas_estimate); } + Ok(tx) + } + + /// Returns a ContractCall that processes the provided message. + async fn process_contract_call( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + tx_gas_estimate: Option, + with_gas_estimate_buffer: bool, + ) -> ChainResult> { + let tx = self.contract_call(message, metadata, tx_gas_estimate)?; fill_tx_gas_params( tx, @@ -329,6 +354,7 @@ where &self.conn.transaction_overrides.clone(), &self.domain, with_gas_estimate_buffer, + self.cache.clone(), ) .await } @@ -380,6 +406,53 @@ where domain: self.domain.clone(), } } + + async fn submit_multicall( + &self, + multicall: &mut Multicall, + contract_calls: Vec>, + cache: Arc>, + ) -> ChainResult { + let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()).await?; + let call_with_gas_overrides = fill_tx_gas_params( + batch, + self.provider.clone(), + &self.conn.transaction_overrides.clone(), + &self.domain, + true, + cache, + ) + .await?; + let outcome = report_tx(call_with_gas_overrides).await?; + Ok(BatchResult::new(Some(outcome.into()), vec![])) + } + + async fn simulate_and_submit_batch( + &self, + multicall: &mut Multicall, + contract_calls: Vec>, + cache: Arc>, + ) -> ChainResult { + let batch_simulation = self.simulate_batch(multicall, contract_calls).await?; + batch_simulation.try_submit(cache).await + } + + async fn refresh_block_and_fee_cache(&self, tx: &TypedTransaction) -> ChainResult<()> { + let latest_block = self + .provider + .get_block(BlockNumber::Latest) + .await + .map_err(ChainCommunicationError::from_other)? + .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?; + let eip1559_fee = + estimate_eip1559_fees(self.provider.clone(), None, &latest_block, &self.domain, tx) + .await + .ok(); + let mut cache = self.cache.lock().await; + cache.latest_block = Some(latest_block); + cache.eip1559_fee = eip1559_fee; + Ok(()) + } } #[derive(new)] @@ -397,9 +470,12 @@ impl BatchSimulation { } impl BatchSimulation { - pub async fn try_submit(self) -> ChainResult { + pub async fn try_submit( + self, + cache: Arc>, + ) -> ChainResult { if let Some(submittable_batch) = self.call { - let batch_outcome = submittable_batch.submit().await?; + let batch_outcome = submittable_batch.submit(cache).await?; Ok(BatchResult::new( Some(batch_outcome), self.excluded_call_indexes, @@ -418,13 +494,14 @@ pub struct SubmittableBatch { } impl SubmittableBatch { - pub async fn submit(self) -> ChainResult { + pub async fn submit(self, cache: Arc>) -> ChainResult { let call_with_gas_overrides = fill_tx_gas_params( self.call, self.provider, &self.transaction_overrides, &self.domain, true, + cache, ) .await?; let outcome = report_tx(call_with_gas_overrides).await?; @@ -504,37 +581,62 @@ where Ok(receipt.into()) } + /// Returns true if the mailbox supports batching + fn supports_batching(&self) -> bool { + true + } + #[instrument(skip(self, ops), fields(size=%ops.len()))] - async fn try_process_batch<'a>( - &self, - ops: Vec<&'a QueueOperation>, - ) -> ChainResult { + async fn process_batch<'a>(&self, ops: Vec<&'a QueueOperation>) -> ChainResult { let messages = ops .iter() .map(|op| op.try_batch()) .collect::>>>()?; - let mut multicall = build_multicall(self.provider.clone(), &self.conn, self.domain.clone()) - .await - .map_err(|e| HyperlaneEthereumError::MulticallError(e.to_string()))?; - let contract_call_futures = messages + let mut multicall = build_multicall( + self.provider.clone(), + &self.conn, + self.domain.clone(), + self.cache.clone(), + ) + .await + .map_err(|e| HyperlaneEthereumError::MulticallError(e.to_string()))?; + + let contract_calls = messages .iter() - .map(|batch_item| async { - self.process_contract_call( + .map(|batch_item| { + self.contract_call( &batch_item.data, &batch_item.submission_data.metadata, Some(batch_item.submission_data.gas_limit), - true, ) - .await }) - .collect::>(); - let contract_calls = join_all(contract_call_futures) + .collect::>>()?; + if let Some(contract_call) = contract_calls.first() { + self.refresh_block_and_fee_cache(&contract_call.tx).await?; + } + let filled_tx_params_futures = contract_calls.iter().map(|tx| { + fill_tx_gas_params( + tx.clone(), + self.provider.clone(), + &self.conn.transaction_overrides, + &self.domain, + true, + self.cache.clone(), + ) + }); + let contract_calls = join_all(filled_tx_params_futures) .await .into_iter() .collect::>>()?; - let batch_simulation = self.simulate_batch(&mut multicall, contract_calls).await?; - batch_simulation.try_submit().await + if self.conn.operation_batch.bypass_batch_simulation { + // submit the tx without checking if subcalls would revert + self.submit_multicall(&mut multicall, contract_calls, self.cache.clone()) + .await + } else { + self.simulate_and_submit_batch(&mut multicall, contract_calls, self.cache.clone()) + .await + } } #[instrument(skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs index 68d5703e7d3..1011f9456c0 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs @@ -5,10 +5,13 @@ use ethers_contract::{builders::ContractCall, Multicall, MulticallResult, Multic use hyperlane_core::{ utils::hex_or_base58_to_h256, ChainResult, HyperlaneDomain, HyperlaneProvider, U256, }; +use tokio::sync::Mutex; use tracing::warn; use crate::{ConnectionConf, EthereumProvider}; +use super::EthereumMailboxCache; + const ALLOW_BATCH_FAILURES: bool = true; /// Conservative estimate picked by subtracting the gas used by individual calls from the total cost of `aggregate3` @@ -21,13 +24,27 @@ pub async fn build_multicall( provider: Arc, conn: &ConnectionConf, domain: HyperlaneDomain, + cache: Arc>, ) -> eyre::Result> { let address = conn .operation_batch .batch_contract_address .unwrap_or(hex_or_base58_to_h256("0xcA11bde05977b3631167028862bE2a173976CA11").unwrap()); - let ethereum_provider = EthereumProvider::new(provider.clone(), domain); - if !ethereum_provider.is_contract(&address).await? { + let is_contract_cache = { + let cache = cache.lock().await; + cache.is_contract.get(&address).cloned() + }; + let is_contract = match is_contract_cache { + Some(is_contract) => is_contract, + None => { + let ethereum_provider = EthereumProvider::new(provider.clone(), domain); + let is_contract = ethereum_provider.is_contract(&address).await?; + cache.lock().await.is_contract.insert(address, is_contract); + is_contract + } + }; + + if !is_contract { return Err(eyre::eyre!("Multicall contract not found at address")); } let multicall = match Multicall::new(provider.clone(), Some(address.into())).await { diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs index 099a5c5caef..a51a255e439 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs @@ -98,6 +98,8 @@ where &self.conn.transaction_overrides, &self.domain, true, + // pass an empty value as the cache + Default::default(), ) .await } diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs index a252635aa91..5750bc7b150 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs @@ -98,7 +98,7 @@ where type Error = ProviderError; // TODO: Refactor to use `FallbackProvider::call` - #[instrument] + #[instrument(skip(self, params))] async fn request(&self, method: &str, params: T) -> Result where T: Debug + Serialize + Send + Sync, diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index 830d75df45a..788dee4ab4a 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -19,12 +19,12 @@ use ethers_core::{ }, }; use hyperlane_core::{ - utils::bytes_to_hex, ChainCommunicationError, ChainResult, HyperlaneDomain, ReorgPeriod, H256, - U256, + ChainCommunicationError, ChainResult, HyperlaneDomain, ReorgPeriod, H256, U256, }; -use tracing::{debug, error, info, warn}; +use tokio::sync::Mutex; +use tracing::{debug, error, info, instrument, warn}; -use crate::{EthereumReorgPeriod, Middleware, TransactionOverrides}; +use crate::{EthereumMailboxCache, EthereumReorgPeriod, Middleware, TransactionOverrides}; /// An amount of gas to add to the estimated gas pub const GAS_ESTIMATE_BUFFER: u32 = 75_000; @@ -60,19 +60,13 @@ where M: Middleware + 'static, D: Detokenize, { - let data = tx - .tx - .data() - .map(|b| bytes_to_hex(b)) - .unwrap_or_else(|| "None".into()); - let to = tx .tx .to() .cloned() .unwrap_or_else(|| NameOrAddress::Address(Default::default())); - info!(?to, %data, tx=?tx.tx, "Dispatching transaction"); + info!(?to, from=?tx.tx.from(), gas_limit=?tx.tx.gas(), gas_price=?tx.tx.gas_price(), nonce=?tx.tx.nonce(), "Dispatching transaction"); let dispatch_fut = tx.send(); let dispatched = dispatch_fut .await? @@ -80,6 +74,7 @@ where track_pending_tx(dispatched).await } +#[instrument(skip(pending_tx))] pub(crate) async fn track_pending_tx( pending_tx: PendingTransaction<'_, P>, ) -> ChainResult { @@ -116,6 +111,7 @@ pub(crate) async fn fill_tx_gas_params( transaction_overrides: &TransactionOverrides, domain: &HyperlaneDomain, with_gas_limit_overrides: bool, + cache: Arc>, ) -> ChainResult> where M: Middleware + 'static, @@ -134,13 +130,20 @@ where } } let gas_limit = estimated_gas_limit; + let (cached_latest_block, cached_eip1559_fee) = { + let cache = cache.lock().await; + (cache.latest_block.clone(), cache.eip1559_fee) + }; // Cap the gas limit to the block gas limit - let latest_block = provider - .get_block(BlockNumber::Latest) - .await - .map_err(ChainCommunicationError::from_other)? - .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?; + let latest_block = match cached_latest_block { + Some(block) => block, + None => provider + .get_block(BlockNumber::Latest) + .await + .map_err(ChainCommunicationError::from_other)? + .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?, + }; let block_gas_limit: U256 = latest_block.gas_limit.into(); let gas_limit = if gas_limit > block_gas_limit { warn!( @@ -159,9 +162,11 @@ where return Ok(tx.gas_price(gas_price).gas(gas_limit)); } - let Ok((base_fee, max_fee, max_priority_fee)) = - estimate_eip1559_fees(provider, None, &latest_block, domain, &tx.tx).await - else { + let eip1559_fee_result = match cached_eip1559_fee { + Some(fee) => Ok(fee), + None => estimate_eip1559_fees(provider.clone(), None, &latest_block, domain, &tx.tx).await, + }; + let Ok((base_fee, max_fee, max_priority_fee)) = eip1559_fee_result else { // Is not EIP 1559 chain return Ok(tx.gas(gas_limit)); }; @@ -208,14 +213,20 @@ where type FeeEstimator = fn(EthersU256, Vec>) -> (EthersU256, EthersU256); +pub type Eip1559Fee = ( + EthersU256, // base fee + EthersU256, // max fee + EthersU256, // max priority fee +); + /// Use this to estimate EIP 1559 fees with some chain-specific logic. -async fn estimate_eip1559_fees( +pub(crate) async fn estimate_eip1559_fees( provider: Arc, estimator: Option, latest_block: &Block, domain: &HyperlaneDomain, tx: &TypedTransaction, -) -> ChainResult<(EthersU256, EthersU256, EthersU256)> +) -> ChainResult where M: Middleware + 'static, { @@ -230,7 +241,7 @@ async fn estimate_eip1559_fees_zksync( provider: Arc, latest_block: &Block, tx: &TypedTransaction, -) -> ChainResult<(EthersU256, EthersU256, EthersU256)> +) -> ChainResult where M: Middleware + 'static, { diff --git a/rust/main/hyperlane-base/src/settings/parser/mod.rs b/rust/main/hyperlane-base/src/settings/parser/mod.rs index 418ffa67fe3..0069aa8f088 100644 --- a/rust/main/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/main/hyperlane-base/src/settings/parser/mod.rs @@ -220,6 +220,12 @@ fn parse_chain( .parse_u32() .unwrap_or(1); + let bypass_batch_simulation = chain + .chain(&mut err) + .get_opt_key("bypassBatchSimulation") + .parse_bool() + .unwrap_or(false); + cfg_unwrap_all!(&chain.cwp, err: [domain]); let connection = build_connection_conf( domain.domain_protocol(), @@ -230,6 +236,7 @@ fn parse_chain( OperationBatchConfig { batch_contract_address, max_batch_size, + bypass_batch_simulation, }, ); diff --git a/rust/main/hyperlane-core/src/config/mod.rs b/rust/main/hyperlane-core/src/config/mod.rs index a0a29d36d52..6b8b09462e2 100644 --- a/rust/main/hyperlane-core/src/config/mod.rs +++ b/rust/main/hyperlane-core/src/config/mod.rs @@ -27,8 +27,12 @@ pub type NoFilter = (); pub struct OperationBatchConfig { /// Optional batch contract address (e.g. Multicall3 on EVM chains) pub batch_contract_address: Option, + /// Batch size pub max_batch_size: u32, + + /// bypass batch simulation + pub bypass_batch_simulation: bool, } /// A trait that allows for constructing `Self` from a raw config type. diff --git a/rust/main/hyperlane-core/src/traits/mailbox.rs b/rust/main/hyperlane-core/src/traits/mailbox.rs index 52f3168e25d..e7f97238f02 100644 --- a/rust/main/hyperlane-core/src/traits/mailbox.rs +++ b/rust/main/hyperlane-core/src/traits/mailbox.rs @@ -4,8 +4,8 @@ use async_trait::async_trait; use derive_new::new; use crate::{ - traits::TxOutcome, utils::domain_hash, BatchItem, ChainCommunicationError, ChainResult, - HyperlaneContract, HyperlaneMessage, QueueOperation, ReorgPeriod, TxCostEstimate, H256, U256, + traits::TxOutcome, utils::domain_hash, ChainCommunicationError, ChainResult, HyperlaneContract, + HyperlaneMessage, QueueOperation, ReorgPeriod, TxCostEstimate, H256, U256, }; /// Interface for the Mailbox chain contract. Allows abstraction over different @@ -40,21 +40,16 @@ pub trait Mailbox: HyperlaneContract + Send + Sync + Debug { tx_gas_limit: Option, ) -> ChainResult; - /// Process a message with a proof against the provided signed checkpoint - async fn process_batch( - &self, - _messages: &[BatchItem], - ) -> ChainResult { - // Batching is not supported by default - Err(ChainCommunicationError::BatchingFailed) + /// True if the destination chain supports batching + /// (i.e. if the mailbox contract will succeed on a `process_batch` call) + fn supports_batching(&self) -> bool { + // Default to false + false } /// Try process the given operations as a batch. Returns the outcome of the /// batch (if one was submitted) and the operations that were not submitted. - async fn try_process_batch<'a>( - &self, - _ops: Vec<&'a QueueOperation>, - ) -> ChainResult { + async fn process_batch<'a>(&self, _ops: Vec<&'a QueueOperation>) -> ChainResult { // Batching is not supported by default Err(ChainCommunicationError::BatchingFailed) } diff --git a/rust/main/hyperlane-test/src/mocks/mailbox.rs b/rust/main/hyperlane-test/src/mocks/mailbox.rs index 5074d2a0be2..dc6b9bb00e2 100644 --- a/rust/main/hyperlane-test/src/mocks/mailbox.rs +++ b/rust/main/hyperlane-test/src/mocks/mailbox.rs @@ -55,6 +55,14 @@ mock! { message: &HyperlaneMessage, metadata: &[u8], ) -> Vec {} + + pub fn process_batch<'a>( + &self, + ops: Vec<&'a QueueOperation>, + ) -> ChainResult {} + + pub fn supports_batching(&self) -> bool { + } } } @@ -91,13 +99,6 @@ impl Mailbox for MockMailboxContract { self.process(message, metadata, tx_gas_limit) } - async fn process_batch( - &self, - messages: &[BatchItem], - ) -> ChainResult { - self.process_batch(messages).await - } - async fn process_estimate_costs( &self, message: &HyperlaneMessage, @@ -113,6 +114,14 @@ impl Mailbox for MockMailboxContract { ) -> ChainResult> { Ok(self.process_calldata(message, metadata)) } + + async fn process_batch<'a>(&self, ops: Vec<&'a QueueOperation>) -> ChainResult { + self.process_batch(ops) + } + + fn supports_batching(&self) -> bool { + self.supports_batching() + } } impl HyperlaneChain for MockMailboxContract { diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs index b4ec65905f9..035510cbc71 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs @@ -29,6 +29,7 @@ fn test_configuration_fields() { operation_batch: OperationBatchConfig { batch_contract_address: None, max_batch_size: expected_max_batch_size, + ..Default::default() }, native_token: Default::default(), priority_fee_oracle: Default::default(), diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index a4e056c2442..4a0d921fa9c 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -112,6 +112,7 @@ export abstract class AgentHelmManager extends HelmManager protocol: metadata.protocol, blocks: { reorgPeriod }, maxBatchSize: 32, + bypassBatchSimulation: false, priorityFeeOracle, transactionSubmitter, }; diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index 0eb9e6434b4..76d9415eff3 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -199,6 +199,11 @@ export const ChainMetadataSchemaObject = z.object({ .optional() .describe('Block settings for the chain/deployment.'), + bypassBatchSimulation: z + .boolean() + .optional() + .describe('Whether to bypass batch simulation for this chain.'), + chainId: z .union([ZNzUint, z.string()]) .describe(`The chainId of the chain. Uses EIP-155 for EVM chains`), From c757b6a1893f0f3176868af0e31234fa282ddb33 Mon Sep 17 00:00:00 2001 From: Jason Guo <33064781+Xaroz@users.noreply.github.com> Date: Wed, 16 Apr 2025 18:46:14 -0400 Subject: [PATCH 020/223] chore: add whole RPC array to chainMetadataToViemChain (#5953) ### Description Include array of RPC urls to `chainMetadataToViemChain` function ### Drive-by changes No ### Related issues ### Backward compatibility ### Testing Storybook Manual --- .changeset/dirty-gifts-grin.md | 5 +++++ typescript/sdk/src/metadata/chainMetadataConversion.ts | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/dirty-gifts-grin.md diff --git a/.changeset/dirty-gifts-grin.md b/.changeset/dirty-gifts-grin.md new file mode 100644 index 00000000000..88cb0b7ace4 --- /dev/null +++ b/.changeset/dirty-gifts-grin.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Include entire RPC array for chainMetadataToViemChain diff --git a/typescript/sdk/src/metadata/chainMetadataConversion.ts b/typescript/sdk/src/metadata/chainMetadataConversion.ts index cbe50446a10..15cd34ee466 100644 --- a/typescript/sdk/src/metadata/chainMetadataConversion.ts +++ b/typescript/sdk/src/metadata/chainMetadataConversion.ts @@ -12,14 +12,15 @@ import { import { PROTOCOL_TO_DEFAULT_NATIVE_TOKEN } from '../token/nativeTokenMetadata.js'; export function chainMetadataToViemChain(metadata: ChainMetadata): Chain { + const rpcUrls = metadata.rpcUrls.map((rpcUrl) => rpcUrl.http); return defineChain({ id: getChainIdNumber(metadata), name: metadata.displayName || metadata.name, network: metadata.name, nativeCurrency: metadata.nativeToken || test1.nativeToken!, rpcUrls: { - public: { http: [metadata.rpcUrls[0].http] }, - default: { http: [metadata.rpcUrls[0].http] }, + public: { http: rpcUrls }, + default: { http: rpcUrls }, }, blockExplorers: metadata.blockExplorers?.length ? { From 4d941815fdaa25ca7463984861d199f17e8d03d2 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Thu, 17 Apr 2025 11:14:09 +0100 Subject: [PATCH 021/223] feat: add governance signers (#5952) ### Description - add signers --- .../mainnet3/governance/safe/safeConfig.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts b/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts index 14b7c0d1063..277bd56b055 100644 --- a/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts +++ b/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts @@ -1,7 +1,16 @@ import { Address } from '@hyperlane-xyz/utils'; export const SIGNERS: Address[] = [ - // TODO: add signers + '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', // 1 + '0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA', // 2 + '0x2f43Ac3cD6A22E4Ba20d3d18d116b1f9420eD84B', // 3 + '0xfae231524539698f1d136d7b21e3b4144cdbf2a3', // 4 + '0x2C073004A6e4f37377F848193d6433260Ebe9b99', // 5 + '0x9f500df92175b2ac36f8d443382b219d211d354a', // 6 + '0x82950a6356316272dF1928C72F5F0A44D9673c88', // 7 + '0x861FC61a961F8AFDf115B8DE274101B9ECea2F26', // 8 + '0x3b548E88BA3259A6f45DEeA91449cdda5cF164b3', // 9 + '0xD5c0D17cCb9071D27a4F7eD8255F59989b9aee0d', // 10 ]; export const THRESHOLD = 1; From d7cb7ab1f413c510c66bf8152e4cdbdcbacbc359 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 09:38:31 -0400 Subject: [PATCH 022/223] Version Packages (#5918) 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@7.1.0 ### Minor Changes - e6f6d61a0: Refactor ZKsync artifact generation and validation logic ### Patch Changes - @hyperlane-xyz/utils@12.1.0 ## @hyperlane-xyz/sdk@12.1.0 ### Minor Changes - acbf5936a: New check: HyperlaneRouterChecker now compares the list of domains the Router is enrolled with against the warp route expectations. It will raise a violation for missing remote domains. `check-deploy` and `check-warp-deploy` scripts use this new check. - c757b6a18: Include entire RPC array for chainMetadataToViemChain - a646f9ca1: Added ZKSync specific deployment logic and artifact related utils - 3b615c892: Adds the proxyAdmin.owner to the Checker ownerOverrides such that it checks proxyAdmin.owner instead of always using the top-level owner ### Patch Changes - Updated dependencies [e6f6d61a0] - @hyperlane-xyz/core@7.1.0 - @hyperlane-xyz/utils@12.1.0 ## @hyperlane-xyz/cli@12.1.0 ### Patch Changes - Updated dependencies [acbf5936a] - Updated dependencies [c757b6a18] - Updated dependencies [a646f9ca1] - Updated dependencies [3b615c892] - @hyperlane-xyz/sdk@12.1.0 - @hyperlane-xyz/utils@12.1.0 ## @hyperlane-xyz/helloworld@12.1.0 ### Patch Changes - Updated dependencies [acbf5936a] - Updated dependencies [c757b6a18] - Updated dependencies [a646f9ca1] - Updated dependencies [e6f6d61a0] - Updated dependencies [3b615c892] - @hyperlane-xyz/sdk@12.1.0 - @hyperlane-xyz/core@7.1.0 ## @hyperlane-xyz/widgets@12.1.0 ### Patch Changes - Updated dependencies [acbf5936a] - Updated dependencies [c757b6a18] - Updated dependencies [a646f9ca1] - Updated dependencies [3b615c892] - @hyperlane-xyz/sdk@12.1.0 - @hyperlane-xyz/utils@12.1.0 ## @hyperlane-xyz/utils@12.1.0 ## @hyperlane-xyz/infra@12.1.0 ### Minor Changes - acbf5936a: New check: HyperlaneRouterChecker now compares the list of domains the Router is enrolled with against the warp route expectations. It will raise a violation for missing remote domains. `check-deploy` and `check-warp-deploy` scripts use this new check. ### Patch Changes - Updated dependencies [acbf5936a] - Updated dependencies [c757b6a18] - Updated dependencies [a646f9ca1] - Updated dependencies [3b615c892] - @hyperlane-xyz/sdk@12.1.0 - @hyperlane-xyz/helloworld@12.1.0 - @hyperlane-xyz/utils@12.1.0 ## @hyperlane-xyz/ccip-server@12.1.0 ## @hyperlane-xyz/github-proxy@12.1.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/blue-chefs-obey.md | 9 ------- .changeset/dirty-gifts-grin.md | 5 ---- .changeset/eleven-cows-dance.md | 5 ---- .changeset/famous-taxis-dream.md | 5 ---- .changeset/tame-sheep-retire.md | 5 ---- solidity/CHANGELOG.md | 10 ++++++++ solidity/contracts/PackageVersioned.sol | 2 +- 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/github-proxy/CHANGELOG.md | 2 ++ typescript/github-proxy/package.json | 2 +- typescript/helloworld/CHANGELOG.md | 12 ++++++++++ typescript/helloworld/package.json | 6 ++--- typescript/infra/CHANGELOG.md | 19 +++++++++++++++ typescript/infra/package.json | 8 +++---- typescript/sdk/CHANGELOG.md | 18 ++++++++++++++ typescript/sdk/package.json | 6 ++--- typescript/utils/CHANGELOG.md | 2 ++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 11 +++++++++ typescript/widgets/package.json | 6 ++--- yarn.lock | 32 ++++++++++++------------- 26 files changed, 126 insertions(+), 68 deletions(-) delete mode 100644 .changeset/blue-chefs-obey.md delete mode 100644 .changeset/dirty-gifts-grin.md delete mode 100644 .changeset/eleven-cows-dance.md delete mode 100644 .changeset/famous-taxis-dream.md delete mode 100644 .changeset/tame-sheep-retire.md diff --git a/.changeset/blue-chefs-obey.md b/.changeset/blue-chefs-obey.md deleted file mode 100644 index 7a00a1c81f2..00000000000 --- a/.changeset/blue-chefs-obey.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@hyperlane-xyz/infra': minor -'@hyperlane-xyz/sdk': minor ---- - -New check: HyperlaneRouterChecker now compares the list of domains -the Router is enrolled with against the warp route expectations. -It will raise a violation for missing remote domains. -`check-deploy` and `check-warp-deploy` scripts use this new check. diff --git a/.changeset/dirty-gifts-grin.md b/.changeset/dirty-gifts-grin.md deleted file mode 100644 index 88cb0b7ace4..00000000000 --- a/.changeset/dirty-gifts-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Include entire RPC array for chainMetadataToViemChain diff --git a/.changeset/eleven-cows-dance.md b/.changeset/eleven-cows-dance.md deleted file mode 100644 index adc16422bd2..00000000000 --- a/.changeset/eleven-cows-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Added ZKSync specific deployment logic and artifact related utils diff --git a/.changeset/famous-taxis-dream.md b/.changeset/famous-taxis-dream.md deleted file mode 100644 index 5471470bf31..00000000000 --- a/.changeset/famous-taxis-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/core': minor ---- - -Refactor ZKsync artifact generation and validation logic diff --git a/.changeset/tame-sheep-retire.md b/.changeset/tame-sheep-retire.md deleted file mode 100644 index 0b0b65b0f86..00000000000 --- a/.changeset/tame-sheep-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Adds the proxyAdmin.owner to the Checker ownerOverrides such that it checks proxyAdmin.owner instead of always using the top-level owner diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index d38c530f1ef..162ffdadb95 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/core +## 7.1.0 + +### Minor Changes + +- e6f6d61a0: Refactor ZKsync artifact generation and validation logic + +### Patch Changes + +- @hyperlane-xyz/utils@12.1.0 + ## 7.0.0 ### Major Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index b81271a7be6..b479179c235 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.0.0"; + string public constant PACKAGE_VERSION = "7.1.0"; } diff --git a/solidity/package.json b/solidity/package.json index 23ebb4e43a7..47e6db51788 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.0.0", + "version": "7.1.0", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.0.0", + "@hyperlane-xyz/utils": "12.1.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 2f574ead2ef..873cf4885b0 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 12.1.0 + ## 12.0.0 ## 11.0.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 866d1a886f3..d01f5485f55 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.0.0", + "version": "12.1.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 9663226d351..38a58bb2fd5 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @hyperlane-xyz/cli +## 12.1.0 + +### Patch Changes + +- Updated dependencies [acbf5936a] +- Updated dependencies [c757b6a18] +- Updated dependencies [a646f9ca1] +- Updated dependencies [3b615c892] + - @hyperlane-xyz/sdk@12.1.0 + - @hyperlane-xyz/utils@12.1.0 + ## 12.0.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index b8c5e0b72a9..1191e5ec4c5 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.0.0", + "version": "12.1.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": "11.1.0", - "@hyperlane-xyz/sdk": "12.0.0", - "@hyperlane-xyz/utils": "12.0.0", + "@hyperlane-xyz/sdk": "12.1.0", + "@hyperlane-xyz/utils": "12.1.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 5c7e7312f15..e04a4acc82b 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.0.0'; +export const VERSION = '12.1.0'; diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index 5c60ac4e800..7933757440f 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 12.1.0 + ## 12.0.0 ## 11.0.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index 760821a2137..c5afc67488c 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.0.0", + "version": "12.1.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 2fbc078302e..d62f1670bb1 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,17 @@ # @hyperlane-xyz/helloworld +## 12.1.0 + +### Patch Changes + +- Updated dependencies [acbf5936a] +- Updated dependencies [c757b6a18] +- Updated dependencies [a646f9ca1] +- Updated dependencies [e6f6d61a0] +- Updated dependencies [3b615c892] + - @hyperlane-xyz/sdk@12.1.0 + - @hyperlane-xyz/core@7.1.0 + ## 12.0.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index b716d546b40..b16fc5d4472 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": "12.0.0", + "version": "12.1.0", "dependencies": { - "@hyperlane-xyz/core": "7.0.0", + "@hyperlane-xyz/core": "7.1.0", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.0.0", + "@hyperlane-xyz/sdk": "12.1.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 761daf41e1e..7cd8eab6e2e 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,24 @@ # @hyperlane-xyz/infra +## 12.1.0 + +### Minor Changes + +- acbf5936a: New check: HyperlaneRouterChecker now compares the list of domains + the Router is enrolled with against the warp route expectations. + It will raise a violation for missing remote domains. + `check-deploy` and `check-warp-deploy` scripts use this new check. + +### Patch Changes + +- Updated dependencies [acbf5936a] +- Updated dependencies [c757b6a18] +- Updated dependencies [a646f9ca1] +- Updated dependencies [3b615c892] + - @hyperlane-xyz/sdk@12.1.0 + - @hyperlane-xyz/helloworld@12.1.0 + - @hyperlane-xyz/utils@12.1.0 + ## 12.0.0 ### Minor Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 8bd75a96236..125b2247328 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": "12.0.0", + "version": "12.1.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.0.0", + "@hyperlane-xyz/helloworld": "12.1.0", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.0.0", - "@hyperlane-xyz/utils": "12.0.0", + "@hyperlane-xyz/sdk": "12.1.0", + "@hyperlane-xyz/utils": "12.1.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 49188b02259..d5415ce0e52 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,23 @@ # @hyperlane-xyz/sdk +## 12.1.0 + +### Minor Changes + +- acbf5936a: New check: HyperlaneRouterChecker now compares the list of domains + the Router is enrolled with against the warp route expectations. + It will raise a violation for missing remote domains. + `check-deploy` and `check-warp-deploy` scripts use this new check. +- c757b6a18: Include entire RPC array for chainMetadataToViemChain +- a646f9ca1: Added ZKSync specific deployment logic and artifact related utils +- 3b615c892: Adds the proxyAdmin.owner to the Checker ownerOverrides such that it checks proxyAdmin.owner instead of always using the top-level owner + +### Patch Changes + +- Updated dependencies [e6f6d61a0] + - @hyperlane-xyz/core@7.1.0 + - @hyperlane-xyz/utils@12.1.0 + ## 12.0.0 ### Major Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index ef9c5c59532..a1a7da696dd 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,15 +1,15 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.0.0", + "version": "12.1.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.14", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.0.0", - "@hyperlane-xyz/utils": "12.0.0", + "@hyperlane-xyz/core": "7.1.0", + "@hyperlane-xyz/utils": "12.1.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 21dbe797841..4aa277d315b 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/utils +## 12.1.0 + ## 12.0.0 ## 11.0.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 780df82ebb4..07adc835b50 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": "12.0.0", + "version": "12.1.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index eba5931d20d..eb882f68d7e 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,16 @@ # @hyperlane-xyz/widgets +## 12.1.0 + +### Patch Changes + +- Updated dependencies [acbf5936a] +- Updated dependencies [c757b6a18] +- Updated dependencies [a646f9ca1] +- Updated dependencies [3b615c892] + - @hyperlane-xyz/sdk@12.1.0 + - @hyperlane-xyz/utils@12.1.0 + ## 12.0.0 ### Minor Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 9d0e31de397..57d067f40e5 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.0.0", + "version": "12.1.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -9,8 +9,8 @@ "dependencies": { "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/sdk": "12.0.0", - "@hyperlane-xyz/utils": "12.0.0", + "@hyperlane-xyz/sdk": "12.1.0", + "@hyperlane-xyz/utils": "12.1.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index 57d4dcdc282..2bc8f335da3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7651,8 +7651,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.0.0" - "@hyperlane-xyz/utils": "npm:12.0.0" + "@hyperlane-xyz/sdk": "npm:12.1.0" + "@hyperlane-xyz/utils": "npm:12.1.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7691,14 +7691,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.0.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.0, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.0.0" + "@hyperlane-xyz/utils": "npm:12.1.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7794,14 +7794,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.0.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:12.1.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.0.0" + "@hyperlane-xyz/core": "npm:7.1.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.0.0" + "@hyperlane-xyz/sdk": "npm:12.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7850,10 +7850,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.0.0" + "@hyperlane-xyz/helloworld": "npm:12.1.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.0.0" - "@hyperlane-xyz/utils": "npm:12.0.0" + "@hyperlane-xyz/sdk": "npm:12.1.0" + "@hyperlane-xyz/utils": "npm:12.1.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7924,7 +7924,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.0.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:12.1.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7934,8 +7934,8 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.0.0" - "@hyperlane-xyz/utils": "npm:12.0.0" + "@hyperlane-xyz/core": "npm:7.1.0" + "@hyperlane-xyz/utils": "npm:12.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -7980,7 +7980,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.0.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:12.1.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8022,8 +8022,8 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.0.0" - "@hyperlane-xyz/utils": "npm:12.0.0" + "@hyperlane-xyz/sdk": "npm:12.1.0" + "@hyperlane-xyz/utils": "npm:12.1.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From 3e8596bab02e0d60969df8ece73d21810623f8a7 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 17 Apr 2025 17:08:48 +0100 Subject: [PATCH 023/223] chore: log eip1559 price estimation error (#5958) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/chains/hyperlane-ethereum/src/tx.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index 788dee4ab4a..9b509d2f53c 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -166,9 +166,13 @@ where Some(fee) => Ok(fee), None => estimate_eip1559_fees(provider.clone(), None, &latest_block, domain, &tx.tx).await, }; - let Ok((base_fee, max_fee, max_priority_fee)) = eip1559_fee_result else { - // Is not EIP 1559 chain - return Ok(tx.gas(gas_limit)); + let (base_fee, max_fee, max_priority_fee) = match eip1559_fee_result { + Ok(result) => result, + Err(err) => { + warn!(?err, "Failed to estimate EIP-1559 fees"); + // Assume it's not an EIP 1559 chain + return Ok(tx.gas(gas_limit)); + } }; // If the base fee is zero, just treat the chain as a non-EIP-1559 chain. From e1567ab978d597a6320ff7cbcf756d9da883fb0b Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 17 Apr 2025 18:58:48 +0100 Subject: [PATCH 024/223] feat: apply prepare queue backpressure (#5956) ### Description - Applies backpressure to the prepare queue based off the value of the env var `HYP_MAXSUBMITQUEUELENGTH` - Drive-by to lower the bsctestnet gas price to 1 gwei instead of 8, which is far higher and seemingly a relic of past times - make IGP indexing optional - make tx id indexing optional ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> --- rust/main/agents/relayer/src/msg/op_queue.rs | 5 ++ .../agents/relayer/src/msg/op_submitter.rs | 29 +++++++- rust/main/agents/relayer/src/relayer.rs | 66 +++++++++++-------- rust/main/agents/relayer/src/settings/mod.rs | 18 +++++ rust/main/agents/scraper/src/agent.rs | 3 + rust/main/agents/validator/src/validator.rs | 1 + rust/main/config/testnet_config.json | 2 +- .../hyperlane-base/src/contract_sync/mod.rs | 8 ++- rust/main/hyperlane-base/src/settings/base.rs | 33 +++++++++- .../config/environments/testnet4/chains.ts | 2 +- 10 files changed, 134 insertions(+), 33 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_queue.rs b/rust/main/agents/relayer/src/msg/op_queue.rs index 7ca5452d841..9cdabfc8eb8 100644 --- a/rust/main/agents/relayer/src/msg/op_queue.rs +++ b/rust/main/agents/relayer/src/msg/op_queue.rs @@ -153,6 +153,11 @@ impl OpQueue { queue.append(&mut reprioritized_queue); retry_responses } + + pub async fn len(&self) -> usize { + let queue = self.queue.lock().await; + queue.len() + } } #[cfg(test)] diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index 47d83e4da20..79969c36632 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -2,7 +2,7 @@ #![allow(clippy::doc_lazy_continuation)] // TODO: `rustc` 1.80.1 clippy issue use std::fmt::Debug; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::time::Duration; use futures::future::join_all; @@ -37,6 +37,19 @@ use super::op_queue::OperationPriorityQueue; /// update the number of queues an OpSubmitter has. pub const SUBMITTER_QUEUE_COUNT: usize = 3; +/// Target max length of the submit queue. The prepare queue will +/// hold off on pushing new messages to the submit queue until +/// the submit queue is below this length. +pub static MAX_SUBMIT_QUEUE_LEN: OnceLock> = OnceLock::new(); + +fn max_submit_queue_len() -> Option { + *MAX_SUBMIT_QUEUE_LEN.get_or_init(|| { + std::env::var("HYP_MAXSUBMITQUEUELENGTH") + .ok() + .and_then(|s| s.parse::().ok()) + }) +} + /// SerialSubmitter accepts operations over a channel. It is responsible for /// executing the right strategy to deliver those messages to the destination /// chain. It is designed to be used in a scenario allowing only one @@ -339,6 +352,20 @@ async fn prepare_task( // Prepare at most `max_batch_size` ops at a time to avoid getting rate-limited let ops_to_prepare = max_batch_size as usize; loop { + // Apply backpressure to the prepare queue if the submit queue is too long. + if let Some(max_len) = max_submit_queue_len() { + let submit_queue_len = submit_queue.len().await; + if submit_queue_len >= max_len { + debug!( + %submit_queue_len, + max_submit_queue_len=%max_len, + "Submit queue is too long, waiting to prepare more ops" + ); + // The submit queue is too long, so give some time before checking again + sleep(Duration::from_millis(150)).await; + continue; + } + } // Pop messages here according to the configured batch. let mut batch = prepare_queue.pop_many(ops_to_prepare).await; if batch.is_empty() { diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index 66b96f94e29..1995e874719 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -80,7 +80,7 @@ pub struct Relayer { core: HyperlaneAgentCore, message_syncs: HashMap>>, interchain_gas_payment_syncs: - HashMap>>, + Option>>>, /// Context data for each (origin, destination) chain pair a message can be /// sent between msg_ctxs: HashMap>, @@ -220,6 +220,7 @@ impl BaseAgent for Relayer { .map(|(d, db)| (d.clone(), Arc::new(db.clone()))) .collect(), false, + settings.tx_id_indexing_enabled, ) .await? .into_iter() @@ -228,20 +229,27 @@ impl BaseAgent for Relayer { debug!(elapsed = ?start_entity_init.elapsed(), event = "initialized message syncs", "Relayer startup duration measurement"); start_entity_init = Instant::now(); - let interchain_gas_payment_syncs = settings - .contract_syncs::( - settings.origin_chains.iter(), - &core_metrics, - &contract_sync_metrics, - dbs.iter() - .map(|(d, db)| (d.clone(), Arc::new(db.clone()))) + let interchain_gas_payment_syncs = if settings.igp_indexing_enabled { + Some( + settings + .contract_syncs::( + settings.origin_chains.iter(), + &core_metrics, + &contract_sync_metrics, + dbs.iter() + .map(|(d, db)| (d.clone(), Arc::new(db.clone()))) + .collect(), + false, + settings.tx_id_indexing_enabled, + ) + .await? + .into_iter() + .map(|(k, v)| (k, v as _)) .collect(), - false, ) - .await? - .into_iter() - .map(|(k, v)| (k, v as _)) - .collect(); + } else { + None + }; debug!(elapsed = ?start_entity_init.elapsed(), event = "initialized IGP syncs", "Relayer startup duration measurement"); start_entity_init = Instant::now(); @@ -254,6 +262,7 @@ impl BaseAgent for Relayer { .map(|(d, db)| (d.clone(), Arc::new(db.clone()))) .collect(), false, + settings.tx_id_indexing_enabled, ) .await? .into_iter() @@ -494,14 +503,17 @@ impl BaseAgent for Relayer { .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, - BroadcastMpscSender::map_get_receiver(maybe_broadcaster.as_ref()).await, - task_monitor.clone(), - ) - .await, - ); + if let Some(interchain_gas_payment_syncs) = &self.interchain_gas_payment_syncs { + tasks.push( + self.run_interchain_gas_payment_sync( + origin, + interchain_gas_payment_syncs, + BroadcastMpscSender::map_get_receiver(maybe_broadcaster.as_ref()).await, + task_monitor.clone(), + ) + .await, + ); + } tasks.push( self.run_merkle_tree_hook_sync( origin, @@ -628,16 +640,16 @@ impl Relayer { async fn run_interchain_gas_payment_sync( &self, origin: &HyperlaneDomain, + interchain_gas_payment_syncs: &HashMap< + HyperlaneDomain, + Arc>, + >, tx_id_receiver: Option>, task_monitor: TaskMonitor, ) -> JoinHandle<()> { let origin = origin.clone(); let index_settings = self.as_ref().settings.chains[origin.name()].index_settings(); - let contract_sync = self - .interchain_gas_payment_syncs - .get(&origin) - .unwrap() - .clone(); + let contract_sync = interchain_gas_payment_syncs.get(&origin).unwrap().clone(); let chain_metrics = self.chain_metrics.clone(); let name = Self::contract_sync_task_name("gas_payment::", origin.name()); @@ -1135,6 +1147,8 @@ mod test { allow_contract_call_caching: true, ism_cache_configs: Default::default(), max_retries: 1, + tx_id_indexing_enabled: true, + igp_indexing_enabled: true, } } diff --git a/rust/main/agents/relayer/src/settings/mod.rs b/rust/main/agents/relayer/src/settings/mod.rs index e3f4e259f14..91c8535213e 100644 --- a/rust/main/agents/relayer/src/settings/mod.rs +++ b/rust/main/agents/relayer/src/settings/mod.rs @@ -70,6 +70,10 @@ pub struct RelayerSettings { pub ism_cache_configs: Vec, /// Maximum number of retries per operation pub max_retries: u32, + /// Whether to enable indexing of hook events given tx ids from indexed messages. + pub tx_id_indexing_enabled: bool, + /// Whether to enable IGP indexing. + pub igp_indexing_enabled: bool, } /// Config for gas payment enforcement @@ -341,6 +345,18 @@ impl FromRawConf for RelayerSettings { .parse_u32() .unwrap_or(DEFAULT_MAX_MESSAGE_RETRIES); + let tx_id_indexing_enabled = p + .chain(&mut err) + .get_opt_key("txIdIndexingEnabled") + .parse_bool() + .unwrap_or(true); + + let igp_indexing_enabled = p + .chain(&mut err) + .get_opt_key("igpIndexingEnabled") + .parse_bool() + .unwrap_or(true); + err.into_result(RelayerSettings { base, db, @@ -357,6 +373,8 @@ impl FromRawConf for RelayerSettings { allow_contract_call_caching, ism_cache_configs, max_retries: max_message_retries, + tx_id_indexing_enabled, + igp_indexing_enabled, }) } } diff --git a/rust/main/agents/scraper/src/agent.rs b/rust/main/agents/scraper/src/agent.rs index be28db8f6f1..507d877523a 100644 --- a/rust/main/agents/scraper/src/agent.rs +++ b/rust/main/agents/scraper/src/agent.rs @@ -273,6 +273,7 @@ impl Scraper { &contract_sync_metrics.clone(), store.into(), true, + true, ) .await .map_err(|err| { @@ -309,6 +310,7 @@ impl Scraper { &contract_sync_metrics.clone(), Arc::new(store.clone()) as _, true, + true, ) .await .map_err(|err| { @@ -347,6 +349,7 @@ impl Scraper { &contract_sync_metrics.clone(), Arc::new(store.clone()) as _, true, + true, ) .await .map_err(|err| { diff --git a/rust/main/agents/validator/src/validator.rs b/rust/main/agents/validator/src/validator.rs index 101311632df..0262e0e5b9e 100644 --- a/rust/main/agents/validator/src/validator.rs +++ b/rust/main/agents/validator/src/validator.rs @@ -155,6 +155,7 @@ impl BaseAgent for Validator { &contract_sync_metrics, msg_db.clone().into(), false, + false, ) .await?; diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index ffdfd5e3723..60759034e01 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -269,7 +269,7 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1", "gasCurrencyCoinGeckoId": "binancecoin", "transactionOverrides": { - "gasPrice": 8000000000 + "gasPrice": 1000000000 } }, "connextsepolia": { diff --git a/rust/main/hyperlane-base/src/contract_sync/mod.rs b/rust/main/hyperlane-base/src/contract_sync/mod.rs index d79b9a26160..fb95433bdf5 100644 --- a/rust/main/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/main/hyperlane-base/src/contract_sync/mod.rs @@ -60,13 +60,19 @@ impl, I: Indexer> ContractSync store: S, indexer: I, metrics: ContractSyncMetrics, + broadcast_sender_enabled: bool, ) -> Self { + let broadcast_sender = if broadcast_sender_enabled { + T::broadcast_channel_size().map(BroadcastMpscSender::new) + } else { + None + }; Self { domain, store, indexer, metrics, - broadcast_sender: T::broadcast_channel_size().map(BroadcastMpscSender::new), + broadcast_sender, _phantom: PhantomData, } } diff --git a/rust/main/hyperlane-base/src/settings/base.rs b/rust/main/hyperlane-base/src/settings/base.rs index 0fad293caed..54ddf810872 100644 --- a/rust/main/hyperlane-base/src/settings/base.rs +++ b/rust/main/hyperlane-base/src/settings/base.rs @@ -162,6 +162,7 @@ impl Settings { sync_metrics: &ContractSyncMetrics, store: Arc, advanced_log_meta: bool, + broadcast_sender_enabled: bool, ) -> eyre::Result>> where T: Indexable + Debug, @@ -177,6 +178,7 @@ impl Settings { store.clone() as SequenceAwareLogStore<_>, indexer, sync_metrics.clone(), + broadcast_sender_enabled, ))) } @@ -188,6 +190,7 @@ impl Settings { sync_metrics: &ContractSyncMetrics, store: Arc, advanced_log_meta: bool, + broadcast_sender_enabled: bool, ) -> eyre::Result>> where T: Indexable + Debug, @@ -203,6 +206,7 @@ impl Settings { store.clone() as WatermarkLogStore<_>, indexer, sync_metrics.clone(), + broadcast_sender_enabled, ))) } @@ -216,6 +220,7 @@ impl Settings { sync_metrics: &ContractSyncMetrics, stores: HashMap>, advanced_log_meta: bool, + broadcast_sender_enabled: bool, ) -> Result>>> where T: Indexable + Debug + Send + Sync + Clone + Eq + Hash + 'static, @@ -230,7 +235,14 @@ impl Settings { for domain in domains { let store = stores.get(domain).unwrap().clone(); let sync = self - .contract_sync(domain, metrics, sync_metrics, store, advanced_log_meta) + .contract_sync( + domain, + metrics, + sync_metrics, + store, + advanced_log_meta, + broadcast_sender_enabled, + ) .await?; syncs.push(sync); } @@ -251,6 +263,7 @@ impl Settings { sync_metrics: &ContractSyncMetrics, store: Arc, advanced_log_meta: bool, + broadcast_sender_enabled: bool, ) -> Result>> where T: Indexable + Debug + Send + Sync + Clone + Eq + Hash + 'static, @@ -262,11 +275,25 @@ impl Settings { { let sync = match T::indexing_cursor(domain.domain_protocol()) { CursorType::SequenceAware => self - .sequenced_contract_sync(domain, metrics, sync_metrics, store, advanced_log_meta) + .sequenced_contract_sync( + domain, + metrics, + sync_metrics, + store, + advanced_log_meta, + broadcast_sender_enabled, + ) .await .map(|r| r as Arc>)?, CursorType::RateLimited => self - .watermark_contract_sync(domain, metrics, sync_metrics, store, advanced_log_meta) + .watermark_contract_sync( + domain, + metrics, + sync_metrics, + store, + advanced_log_meta, + broadcast_sender_enabled, + ) .await .map(|r| r as Arc>)?, }; diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 8d0251beb84..20e17ef9949 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -15,7 +15,7 @@ export const ethereumChainNames = supportedChainNames.filter( export const chainMetadataOverrides: ChainMap> = { bsctestnet: { transactionOverrides: { - gasPrice: 8 * 10 ** 9, // 8 gwei + gasPrice: 1 * 10 ** 9, // 1 gwei }, }, // deploy-only overrides From 5dc2cf441d729a6584f51d56d9b28cbab5c1683d Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 17 Apr 2025 19:34:34 +0100 Subject: [PATCH 025/223] fix: zeronetwork igp check (#5964) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 3a3eafca1f5..571b3214866 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -580,6 +580,8 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ // Infinity VM is gasless, so enforcing min 1 wei here ensures outbound txs // outside of Solana are ignored. { originDomain: getDomainId('infinityvm') }, + // Temporary workaround due to funky Zeronetwork gas amounts. + { destinationDomain: getDomainId('zeronetwork') }, // Temporary workaround for some high gas amount estimates on Treasure ...warpRouteMatchingList(WarpRouteIds.ArbitrumTreasureMAGIC), ], From 0c47163281fa5a9381f4a38fb5b6995c8e9a382d Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:48:49 +0100 Subject: [PATCH 026/223] chore: increase igp overhead to zeronetwork (#5965) ### Description chore: increase igp overhead to zeronetwork - zeronetwork gas usage has changed recently and now requires another 3x multiplier on top of the ZKSync overhead. - context https://hyperlaneworkspace.slack.com/archives/C08GHFABJQ6/p1744912056132509 ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/igp.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index 7099d6aa712..23ef3b6ba83 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -39,6 +39,12 @@ export function getOverheadWithOverrides(local: ChainName, remote: ChainName) { // estimates. We double the overhead to help account for this. if (getChain(remote).technicalStack === ChainTechnicalStack.ZkSync) { overhead *= 2; + + // Zero Network gas usage has changed recently and now requires + // another 3x multiplier on top of the ZKSync overhead. + if (remote === 'zeronetwork') { + overhead *= 3; + } } return overhead; } From f49ef5639a6f456fdaee4a005d2e0e815a49aa29 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:44:08 +0100 Subject: [PATCH 027/223] feat: escalator gas syncing (#5970) uses new escalator that checks onchain gas price before escalating https://github.com/hyperlane-xyz/ethers-rs/pull/37 --- rust/main/Cargo.lock | 21 +++++++++++---------- rust/main/Cargo.toml | 10 +++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 1b4422a28a2..18323263f60 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -3485,7 +3485,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3499,7 +3499,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "ethers-core", "once_cell", @@ -3510,7 +3510,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -3528,7 +3528,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "Inflector", "cfg-if", @@ -3552,7 +3552,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -3566,7 +3566,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "arrayvec", "bytes", @@ -3596,7 +3596,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3612,7 +3612,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3621,6 +3621,7 @@ dependencies = [ "ethers-etherscan", "ethers-providers", "ethers-signers", + "eyre", "futures-locks", "futures-util", "instant", @@ -3662,7 +3663,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3698,7 +3699,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-14-2#8e090d7571467af5b410cfd10cf33451c3cd5586" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" dependencies = [ "async-trait", "coins-bip32 0.7.0", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index d99cd3c5884..70428cc8fcd 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -218,27 +218,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-14-2" +tag = "2025-04-18" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-14-2" +tag = "2025-04-18" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-14-2" +tag = "2025-04-18" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-14-2" +tag = "2025-04-18" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-14-2" +tag = "2025-04-18" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" From 7b0f5a03ccdf045b827aa73b3222c644e46843e3 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:54:25 +0100 Subject: [PATCH 028/223] feat: gas price improvements (#5962) ### Description adds: - hardcoded gas price multiplier on EVM of 10% (previously there was none). Lmk if you'd rather have this configurable - min gas price: configs are added under the `transactionOverrides` config, for all legacy and eip1559 fields - higher hardcoded EVM escalator multiplier: it's now 1.25 (from a previous 1.125) - lower tx inclusion timeout: 90 seconds (from a previous 150s), to match how long it takes to escalate, since escalation changes the txhash, causing the originally tracked one to be dropped ### Testing None --- rust/main/agents/relayer/src/relayer.rs | 1 + rust/main/agents/scraper/src/agent.rs | 1 + .../chains/hyperlane-ethereum/src/config.rs | 17 +++ .../src/rpc_clients/trait_builder.rs | 14 ++- rust/main/chains/hyperlane-ethereum/src/tx.rs | 114 +++++++++++++++--- .../hyperlane-base/src/settings/chains.rs | 3 + .../src/settings/parser/connection_parser.rs | 38 ++++++ 7 files changed, 164 insertions(+), 24 deletions(-) diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index 1995e874719..227fc4320e7 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -1096,6 +1096,7 @@ mod test { gas_limit: None, max_fee_per_gas: None, max_priority_fee_per_gas: None, + ..Default::default() }, operation_batch: OperationBatchConfig { batch_contract_address: None, diff --git a/rust/main/agents/scraper/src/agent.rs b/rust/main/agents/scraper/src/agent.rs index 507d877523a..9d3d067b1dd 100644 --- a/rust/main/agents/scraper/src/agent.rs +++ b/rust/main/agents/scraper/src/agent.rs @@ -444,6 +444,7 @@ mod test { gas_limit: None, max_fee_per_gas: None, max_priority_fee_per_gas: None, + ..Default::default() }, operation_batch: OperationBatchConfig { batch_contract_address: None, diff --git a/rust/main/chains/hyperlane-ethereum/src/config.rs b/rust/main/chains/hyperlane-ethereum/src/config.rs index e1375e790d8..d0dc74b75dd 100644 --- a/rust/main/chains/hyperlane-ethereum/src/config.rs +++ b/rust/main/chains/hyperlane-ethereum/src/config.rs @@ -55,6 +55,23 @@ pub struct TransactionOverrides { pub max_fee_per_gas: Option, /// Max priority fee per gas to use for EIP-1559 transactions. pub max_priority_fee_per_gas: Option, + + /// Min gas price to use for Legacy transactions, in wei. + pub min_gas_price: Option, + /// Min fee per gas to use for EIP-1559 transactions. + pub min_fee_per_gas: Option, + /// Min priority fee per gas to use for EIP-1559 transactions. + pub min_priority_fee_per_gas: Option, + + /// Gas limit multiplier denominator to use for transactions, eg 110 + pub gas_limit_multiplier_denominator: Option, + /// Gas limit multiplier numerator to use for transactions, eg 100 + pub gas_limit_multiplier_numerator: Option, + + /// Gas price multiplier denominator to use for transactions, eg 110 + pub gas_price_multiplier_denominator: Option, + /// Gas price multiplier numerator to use for transactions, eg 100 + pub gas_price_multiplier_numerator: Option, } /// Ethereum reorg period diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs index 98138a9e102..b135b382249 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs @@ -33,6 +33,7 @@ use hyperlane_metric::prometheus_metric::{ use tracing::instrument; use crate::signer::Signers; +use crate::tx::PENDING_TX_TIMEOUT_SECS; use crate::{ConnectionConf, EthereumFallbackProvider, RetryingProvider, RpcConnectionConf}; // This should be whatever the prometheus scrape interval is @@ -283,11 +284,14 @@ fn wrap_with_gas_escalator(provider: M) -> GasEscalatorMiddleware where M: Middleware + 'static, { - // Increase the gas price by 12.5% every 90 seconds - // (These are the default values from ethers doc comments) - const COEFFICIENT: f64 = 1.125; - const EVERY_SECS: u64 = 90u64; - // a 3k gwei limit is chosen to account for `treasure` chain, where the highest gas price observed is 1.2k gwei + // Increase the gas price by 25% every 90 seconds + const COEFFICIENT: f64 = 1.25; + + // escalating creates a new tx hash, and the submitter tracks each tx hash for at most + // `PENDING_TX_TIMEOUT_SECS`. So the escalator will send a new tx when the initial + // tx hash stops being tracked. + const EVERY_SECS: u64 = PENDING_TX_TIMEOUT_SECS; + // a 50k gwei limit is chosen to account for `treasure` chain, where the highest gas price observed is 1.2k gwei const MAX_GAS_PRICE: u128 = 3_000 * 10u128.pow(9); let escalator = GeometricGasPrice::new(COEFFICIENT, EVERY_SECS, MAX_GAS_PRICE.into()); // Check the status of sent txs every eth block or so. The alternative is to subscribe to new blocks and check then, diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index 9b509d2f53c..c44fa7e23b3 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -26,20 +26,26 @@ use tracing::{debug, error, info, instrument, warn}; use crate::{EthereumMailboxCache, EthereumReorgPeriod, Middleware, TransactionOverrides}; -/// An amount of gas to add to the estimated gas -pub const GAS_ESTIMATE_BUFFER: u32 = 75_000; +/// An amount of gas to add to the estimated gas limit +pub const GAS_LIMIT_BUFFER: u32 = 75_000; -// A multiplier to apply to the estimated gas, i.e. 10%. -pub const GAS_ESTIMATE_MULTIPLIER_NUMERATOR: u32 = 11; -pub const GAS_ESTIMATE_MULTIPLIER_DENOMINATOR: u32 = 10; +// A multiplier to apply to the estimated gas limit, i.e. 10%. +pub const DEFAULT_GAS_LIMIT_MULTIPLIER_NUMERATOR: u32 = 11; +pub const DEFAULT_GAS_LIMIT_MULTIPLIER_DENOMINATOR: u32 = 10; + +// A multiplier to apply to the estimated gas price, i.e. 10%. +pub const DEFAULT_GAS_PRICE_MULTIPLIER_NUMERATOR: u32 = 11; +pub const DEFAULT_GAS_PRICE_MULTIPLIER_DENOMINATOR: u32 = 10; + +pub const PENDING_TX_TIMEOUT_SECS: u64 = 90; pub fn apply_gas_estimate_buffer(gas: U256, domain: &HyperlaneDomain) -> ChainResult { // Arbitrum Nitro chains use 2d fees are especially prone to costs increasing // by the time the transaction lands on chain, requiring a higher gas limit. // In this case, we apply a multiplier to the gas estimate. let gas = if domain.is_arbitrum_nitro() { - gas.saturating_mul(GAS_ESTIMATE_MULTIPLIER_NUMERATOR.into()) - .checked_div(GAS_ESTIMATE_MULTIPLIER_DENOMINATOR.into()) + gas.saturating_mul(DEFAULT_GAS_LIMIT_MULTIPLIER_NUMERATOR.into()) + .checked_div(DEFAULT_GAS_LIMIT_MULTIPLIER_DENOMINATOR.into()) .ok_or_else(|| { ChainCommunicationError::from_other_str("Gas estimate buffer divide by zero") })? @@ -48,7 +54,30 @@ pub fn apply_gas_estimate_buffer(gas: U256, domain: &HyperlaneDomain) -> ChainRe }; // Always add a flat buffer - Ok(gas.saturating_add(GAS_ESTIMATE_BUFFER.into())) + Ok(gas.saturating_add(GAS_LIMIT_BUFFER.into())) +} + +pub fn apply_gas_price_multiplier( + gas_price: EthersU256, + transaction_overrides: &TransactionOverrides, +) -> EthersU256 { + let numerator: EthersU256 = transaction_overrides + .gas_price_multiplier_numerator + .map(Into::into) + .unwrap_or(DEFAULT_GAS_PRICE_MULTIPLIER_NUMERATOR.into()); + let denominator: EthersU256 = transaction_overrides + .gas_price_multiplier_denominator + .map(Into::into) + .unwrap_or(DEFAULT_GAS_PRICE_MULTIPLIER_DENOMINATOR.into()); + let multiplied = gas_price.saturating_mul(numerator).checked_div(denominator); + let Some(multiplied) = multiplied else { + warn!( + ?gas_price, + "Gas price multiplier divide by zero, using original gas price" + ); + return gas_price; + }; + multiplied } const PENDING_TRANSACTION_POLLING_INTERVAL: Duration = Duration::from_secs(2); @@ -82,7 +111,7 @@ pub(crate) async fn track_pending_tx( info!(?tx_hash, "Dispatched tx"); - match tokio::time::timeout(Duration::from_secs(150), pending_tx).await { + match tokio::time::timeout(Duration::from_secs(PENDING_TX_TIMEOUT_SECS), pending_tx).await { // all good Ok(Ok(Some(receipt))) => { info!(?tx_hash, "confirmed transaction"); @@ -171,7 +200,7 @@ where Err(err) => { warn!(?err, "Failed to estimate EIP-1559 fees"); // Assume it's not an EIP 1559 chain - return Ok(tx.gas(gas_limit)); + return Ok(apply_legacy_min_gas_price(tx, transaction_overrides).gas(gas_limit)); } }; @@ -181,18 +210,12 @@ where // fee lower than 3 gwei because of privileged transactions being included by block // producers that have a lower priority fee. if base_fee.is_zero() { - return Ok(tx.gas(gas_limit)); + return Ok(apply_legacy_min_gas_price(tx, transaction_overrides).gas(gas_limit)); } // Apply overrides for EIP 1559 tx params if they exist. - let max_fee = transaction_overrides - .max_fee_per_gas - .map(Into::into) - .unwrap_or(max_fee); - let max_priority_fee = transaction_overrides - .max_priority_fee_per_gas - .map(Into::into) - .unwrap_or(max_priority_fee); + let (max_fee, max_priority_fee) = + apply_1559_multipliers_and_overrides(max_fee, max_priority_fee, transaction_overrides); // Is EIP 1559 chain let mut request = Eip1559TransactionRequest::new(); @@ -215,6 +238,59 @@ where Ok(eip_1559_tx.gas(gas_limit)) } +fn apply_legacy_min_gas_price( + tx: ContractCall, + transaction_overrides: &TransactionOverrides, +) -> ContractCall +where + M: Middleware + 'static, + D: Detokenize, +{ + let gas_price = max_between_options( + tx.tx.gas_price(), + transaction_overrides.min_gas_price.map(Into::into), + ); + if let Some(gas_price) = gas_price { + return tx.gas_price(gas_price); + } + tx +} + +fn max_between_options(a: Option, b: Option) -> Option { + match (a, b) { + (Some(a), Some(b)) => Some(a.max(b)), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (None, None) => None, + } +} + +fn apply_1559_multipliers_and_overrides( + max_fee: EthersU256, + max_priority_fee: EthersU256, + transaction_overrides: &TransactionOverrides, +) -> (EthersU256, EthersU256) { + let max_fee = transaction_overrides + .max_fee_per_gas + .map(Into::into) + .unwrap_or(max_fee); + let mut max_fee = apply_gas_price_multiplier(max_fee, transaction_overrides); + if let Some(min_fee) = transaction_overrides.min_fee_per_gas { + max_fee = max_fee.max(min_fee.into()); + } + + let max_priority_fee = transaction_overrides + .max_priority_fee_per_gas + .map(Into::into) + .unwrap_or(max_priority_fee); + let mut max_priority_fee = apply_gas_price_multiplier(max_priority_fee, transaction_overrides); + + if let Some(min_priority_fee) = transaction_overrides.min_priority_fee_per_gas { + max_priority_fee = max_priority_fee.max(min_priority_fee.into()); + } + (max_fee, max_priority_fee) +} + type FeeEstimator = fn(EthersU256, Vec>) -> (EthersU256, EthersU256); pub type Eip1559Fee = ( diff --git a/rust/main/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs index b304f1e4cba..6a7241a3112 100644 --- a/rust/main/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -155,6 +155,9 @@ impl TryFromWithMetrics for MerkleTreeHookIndexer { /// A connection to _some_ blockchain. #[derive(Clone, Debug)] +// TODO: re-enable this clippy check once the new submitter is shipped, +// since it might take in configs in a different way +#[allow(clippy::large_enum_variant)] pub enum ChainConnectionConf { /// Ethereum configuration Ethereum(h_eth::ConnectionConf), diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index fa73094f2a2..f96aa2a521e 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -68,6 +68,44 @@ pub fn build_ethereum_connection_conf( .get_opt_key("maxPriorityFeePerGas") .parse_u256() .end(), + + min_gas_price: value_parser + .chain(err) + .get_opt_key("minGasPrice") + .parse_u256() + .end(), + min_fee_per_gas: value_parser + .chain(err) + .get_opt_key("minFeePerGas") + .parse_u256() + .end(), + min_priority_fee_per_gas: value_parser + .chain(err) + .get_opt_key("minPriorityFeePerGas") + .parse_u256() + .end(), + + gas_limit_multiplier_denominator: value_parser + .chain(err) + .get_opt_key("gasLimitMultiplierDenominator") + .parse_u256() + .end(), + gas_limit_multiplier_numerator: value_parser + .chain(err) + .get_opt_key("gasLimitMultiplierNumerator") + .parse_u256() + .end(), + + gas_price_multiplier_denominator: value_parser + .chain(err) + .get_opt_key("gasPriceMultiplierDenominator") + .parse_u256() + .end(), + gas_price_multiplier_numerator: value_parser + .chain(err) + .get_opt_key("gasPriceMultiplierNumerator") + .parse_u256() + .end(), }) .unwrap_or_default(); From 7dda44639fa9c26eeda300a492b72b00d1eea11b Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 18 Apr 2025 13:52:53 +0100 Subject: [PATCH 029/223] fix: Add simulation back in parallel with refreshing cache (#5968) ### Description Add simulation back in parallel with refreshing cache so that we can see which calls to Mailbox.process failed in the batch. ### Backward compatibility Yes ### Testing Unit test with real RPC calls --------- Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/agents/relayer/src/msg/op_batch.rs | 15 ++++--- .../src/contracts/mailbox.rs | 45 +++++++------------ 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index 30abad29647..096f8568201 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -4,7 +4,7 @@ use derive_new::new; use hyperlane_core::{ rpc_clients::DEFAULT_MAX_RPC_RETRIES, total_estimated_cost, BatchResult, ChainCommunicationError, ChainResult, ConfirmReason, HyperlaneDomain, Mailbox, - PendingOperation, PendingOperationStatus, QueueOperation, TxOutcome, + PendingOperation, PendingOperationStatus, QueueOperation, ReprepareReason, TxOutcome, }; use itertools::{Either, Itertools}; use tokio::time::sleep; @@ -44,10 +44,13 @@ impl OperationBatch { }; if !excluded_ops.is_empty() { - warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Falling back to serial submission."); - OperationBatch::new(excluded_ops, self.domain) - .submit_serially(prepare_queue, confirm_queue, metrics) - .await; + warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Sending them back to prepare queue."); + let reason = ReprepareReason::ErrorSubmitting; + let status = Some(PendingOperationStatus::Retry(reason.clone())); + for mut op in excluded_ops.into_iter() { + op.on_reprepare(None, reason.clone()); + prepare_queue.push(op, status.clone()).await; + } } } @@ -142,7 +145,7 @@ impl OperationBatch { } } - async fn submit_serially( + async fn _submit_serially( self, prepare_queue: &mut OpQueue, confirm_queue: &mut OpQueue, diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index a0fec99e02c..dd3ff9ed79a 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -14,10 +14,10 @@ use ethers::types::{Block, BlockNumber, H256 as TxHash}; use ethers_contract::builders::ContractCall; use ethers_contract::{Multicall, MulticallResult}; use ethers_core::utils::WEI_IN_ETHER; -use futures_util::future::join_all; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512}; use itertools::Itertools; +use tokio::join; use tokio::sync::Mutex; use tracing::instrument; @@ -407,7 +407,7 @@ where } } - async fn submit_multicall( + async fn _submit_multicall( &self, multicall: &mut Multicall, contract_calls: Vec>, @@ -427,7 +427,7 @@ where Ok(BatchResult::new(Some(outcome.into()), vec![])) } - async fn simulate_and_submit_batch( + async fn _simulate_and_submit_batch( &self, multicall: &mut Multicall, contract_calls: Vec>, @@ -611,32 +611,21 @@ where ) }) .collect::>>()?; - if let Some(contract_call) = contract_calls.first() { - self.refresh_block_and_fee_cache(&contract_call.tx).await?; - } - let filled_tx_params_futures = contract_calls.iter().map(|tx| { - fill_tx_gas_params( - tx.clone(), - self.provider.clone(), - &self.conn.transaction_overrides, - &self.domain, - true, - self.cache.clone(), - ) - }); - let contract_calls = join_all(filled_tx_params_futures) - .await - .into_iter() - .collect::>>()?; - if self.conn.operation_batch.bypass_batch_simulation { - // submit the tx without checking if subcalls would revert - self.submit_multicall(&mut multicall, contract_calls, self.cache.clone()) - .await - } else { - self.simulate_and_submit_batch(&mut multicall, contract_calls, self.cache.clone()) - .await - } + let simulate_future = self.simulate_batch(&mut multicall, contract_calls.clone()); + let refresh_cache_future = async { + if let Some(contract_call) = contract_calls.first() { + self.refresh_block_and_fee_cache(&contract_call.tx).await + } else { + Ok(()) + } + }; + + let (simulate_result, refresh_result) = join!(simulate_future, refresh_cache_future); + + let (simulation, _) = (simulate_result?, refresh_result?); + + simulation.try_submit(self.cache.clone()).await } #[instrument(skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] From c7a4c3f6a5ca38d087b9c186f0f662ea35dd9ee7 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 18 Apr 2025 15:48:17 +0100 Subject: [PATCH 030/223] fix: request latest block and fee history in parallel and filter out failed calls and estimate only successful calls (#5972) ### Description Request latest block and fee history in parallel so that the latency to update cache with block number and fees is reduced. We filter out failed calls from the batch so that the batch is smaller and we don't waist resources. ### Backward compatibility Yes ### Testing Unit test with real RPC calls --------- Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../src/contracts/mailbox.rs | 126 +++++++++++------- .../src/contracts/multicall.rs | 52 ++++++-- rust/main/chains/hyperlane-ethereum/src/tx.rs | 73 ++++++---- 3 files changed, 171 insertions(+), 80 deletions(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index dd3ff9ed79a..b0b6605b77c 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -8,15 +8,14 @@ use std::sync::Arc; use async_trait::async_trait; use derive_new::new; use ethers::prelude::Middleware; -use ethers::providers::ProviderError; use ethers::types::transaction::eip2718::TypedTransaction; -use ethers::types::{Block, BlockNumber, H256 as TxHash}; +use ethers::types::{Block, H256 as TxHash}; use ethers_contract::builders::ContractCall; use ethers_contract::{Multicall, MulticallResult}; use ethers_core::utils::WEI_IN_ETHER; +use futures_util::future::join_all; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512}; -use itertools::Itertools; use tokio::join; use tokio::sync::Mutex; use tracing::instrument; @@ -364,34 +363,27 @@ where multicall: &mut Multicall, contract_calls: Vec>, ) -> ChainResult> { - let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()).await?; + let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()); let call_results = batch.call().await?; - let failed_calls = contract_calls - .iter() - .zip(call_results.iter()) - .enumerate() - .filter_map( - |(index, (_, result))| { - if !result.success { - Some(index) - } else { - None - } - }, - ) - .collect_vec(); + let (successful, failed) = multicall::filter_failed(contract_calls, call_results); + + if successful.is_empty() { + return Ok(BatchSimulation::failed(failed.len())); + } + + let successful_calls_len = successful.len(); + let successful_batch = multicall::batch::<_, ()>(multicall, successful.clone()); // only send a batch if there are at least two successful calls - let call_count = contract_calls.len(); - let successful_calls = call_count - failed_calls.len(); - if successful_calls >= 2 { + if successful_calls_len >= 2 { Ok(BatchSimulation::new( - Some(self.submittable_batch(batch)), - failed_calls, + Some(self.submittable_batch(successful_batch)), + successful, + failed, )) } else { - Ok(BatchSimulation::failed(call_count)) + Ok(BatchSimulation::failed(failed.len())) } } @@ -413,7 +405,7 @@ where contract_calls: Vec>, cache: Arc>, ) -> ChainResult { - let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()).await?; + let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()); let call_with_gas_overrides = fill_tx_gas_params( batch, self.provider.clone(), @@ -437,27 +429,25 @@ where batch_simulation.try_submit(cache).await } - async fn refresh_block_and_fee_cache(&self, tx: &TypedTransaction) -> ChainResult<()> { - let latest_block = self - .provider - .get_block(BlockNumber::Latest) - .await - .map_err(ChainCommunicationError::from_other)? - .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?; - let eip1559_fee = - estimate_eip1559_fees(self.provider.clone(), None, &latest_block, &self.domain, tx) + async fn refresh_block_and_fee_cache(&self, tx: &TypedTransaction) { + let Some((eip1559_fee, latest_block)) = + estimate_eip1559_fees(self.provider.clone(), None, &self.domain, tx) .await - .ok(); + .ok() + else { + return; + }; let mut cache = self.cache.lock().await; cache.latest_block = Some(latest_block); - cache.eip1559_fee = eip1559_fee; - Ok(()) + cache.eip1559_fee = Some(eip1559_fee); } } #[derive(new)] pub struct BatchSimulation { pub call: Option>, + /// Successful individual calls + pub successful: Vec>, /// Indexes of excluded calls in the batch (because they either failed the simulation /// or they were the only successful call) pub excluded_call_indexes: Vec, @@ -465,7 +455,7 @@ pub struct BatchSimulation { impl BatchSimulation { pub fn failed(ops_count: usize) -> Self { - Self::new(None, (0..ops_count).collect()) + Self::new(None, vec![], (0..ops_count).collect()) } } @@ -474,7 +464,9 @@ impl BatchSimulation { self, cache: Arc>, ) -> ChainResult { - if let Some(submittable_batch) = self.call { + if let Some(mut submittable_batch) = self.call { + let estimated = multicall::estimate(submittable_batch.call, self.successful).await?; + submittable_batch.call = estimated; let batch_outcome = submittable_batch.submit(cache).await?; Ok(BatchResult::new( Some(batch_outcome), @@ -616,14 +608,28 @@ where let refresh_cache_future = async { if let Some(contract_call) = contract_calls.first() { self.refresh_block_and_fee_cache(&contract_call.tx).await - } else { - Ok(()) } }; - let (simulate_result, refresh_result) = join!(simulate_future, refresh_cache_future); + let (simulate_result, _) = join!(simulate_future, refresh_cache_future); + let mut simulation = simulate_result?; + + let filled_tx_params_futures = simulation.successful.iter().map(|tx| { + fill_tx_gas_params( + tx.clone(), + self.provider.clone(), + &self.conn.transaction_overrides, + &self.domain, + false, + self.cache.clone(), + ) + }); + let contract_calls = join_all(filled_tx_params_futures) + .await + .into_iter() + .collect::>>()?; - let (simulation, _) = (simulate_result?, refresh_result?); + simulation.successful = contract_calls; simulation.try_submit(self.cache.clone()).await } @@ -708,7 +714,7 @@ mod test { providers::{MockProvider, Provider}, types::{Block, Transaction, U256 as EthersU256}, }; - + use ethers_core::types::FeeHistory; use hyperlane_core::{ ContractLocator, HyperlaneDomain, HyperlaneMessage, KnownHyperlaneDomain, Mailbox, TxCostEstimate, H160, H256, U256, @@ -770,14 +776,28 @@ mod test { EthersU256::from(ethers::utils::parse_units("15", "gwei").unwrap()).into(); mock_provider.push(gas_price).unwrap(); - // RPC 4: eth_estimateGas to the ArbitrumNodeInterface's estimateRetryableTicket function by process_estimate_costs + // RPC 6: eth_estimateGas to the ArbitrumNodeInterface's estimateRetryableTicket function by process_estimate_costs let l2_gas_limit = U256::from(200000); // 200k gas mock_provider.push(l2_gas_limit).unwrap(); + let fee_history = FeeHistory { + oldest_block: ethers::types::U256::zero(), + base_fee_per_gas: vec![], + gas_used_ratio: vec![], + reward: vec![vec![]], + }; + + // RPC 5: eth_feeHistory from the estimate_eip1559_fees_default + mock_provider.push(fee_history).unwrap(); + let latest_block: Block = Block { gas_limit: ethers::types::U256::MAX, ..Block::::default() }; + + // RPC 4: eth_getBlockByNumber from the estimate_eip1559_fees_default + mock_provider.push(latest_block.clone()).unwrap(); + // RPC 3: eth_getBlockByNumber from the fill_tx_gas_params call in process_contract_call // to get the latest block gas limit and for eip 1559 fee estimation mock_provider.push(latest_block).unwrap(); @@ -814,17 +834,31 @@ mod test { // order, so we start with the final RPCs and work toward the first // RPCs - // RPC 4: eth_gasPrice by process_estimate_costs + // RPC 6: eth_gasPrice by process_estimate_costs // Return 15 gwei let gas_price: U256 = EthersU256::from(ethers::utils::parse_units("15", "gwei").unwrap()).into(); mock_provider.push(gas_price).unwrap(); + let fee_history = FeeHistory { + oldest_block: ethers::types::U256::zero(), + base_fee_per_gas: vec![], + gas_used_ratio: vec![], + reward: vec![vec![]], + }; + + // RPC 5: eth_feeHistory from the estimate_eip1559_fees_default + mock_provider.push(fee_history).unwrap(); + let latest_block_gas_limit = U256::from(12345u32); let latest_block: Block = Block { gas_limit: latest_block_gas_limit.into(), ..Block::::default() }; + + // RPC 4: eth_getBlockByNumber from the estimate_eip1559_fees_default + mock_provider.push(latest_block.clone()).unwrap(); + // RPC 3: eth_getBlockByNumber from the fill_tx_gas_params call in process_contract_call // to get the latest block gas limit and for eip 1559 fee estimation mock_provider.push(latest_block).unwrap(); diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs index 1011f9456c0..d840f64afec 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs @@ -2,11 +2,13 @@ use std::sync::Arc; use ethers::{abi::Detokenize, providers::Middleware}; use ethers_contract::{builders::ContractCall, Multicall, MulticallResult, MulticallVersion}; +use itertools::{Either, Itertools}; +use tokio::sync::Mutex; +use tracing::warn; + use hyperlane_core::{ utils::hex_or_base58_to_h256, ChainResult, HyperlaneDomain, HyperlaneProvider, U256, }; -use tokio::sync::Mutex; -use tracing::warn; use crate::{ConnectionConf, EthereumProvider}; @@ -60,17 +62,49 @@ pub async fn build_multicall( Ok(multicall) } -pub async fn batch( +pub fn batch( multicall: &mut Multicall, calls: Vec>, -) -> ChainResult>> +) -> ContractCall> where - M: Middleware + 'static, + M: Middleware, D: Detokenize, { // clear any calls that were in the multicall beforehand multicall.clear_calls(); + calls.into_iter().for_each(|call| { + multicall.add_call(call, ALLOW_BATCH_FAILURES); + }); + + multicall.as_aggregate_3_value() +} + +pub fn filter_failed( + calls: Vec>, + results: Vec, +) -> (Vec>, Vec) { + calls + .into_iter() + .zip(results) + .enumerate() + .partition_map(|(index, (call, result))| { + if result.success { + Either::Left(call) + } else { + Either::Right(index) + } + }) +} + +pub async fn estimate( + batch: ContractCall, + calls: Vec>, +) -> ChainResult> +where + M: Middleware + 'static, + D: Detokenize, +{ let mut individual_estimates_sum = Some(U256::zero()); let overhead_per_call: U256 = MULTICALL_OVERHEAD_PER_CALL.into(); @@ -85,15 +119,13 @@ where None } }; - multicall.add_call(call, ALLOW_BATCH_FAILURES); }); - let mut batch_call = multicall.as_aggregate_3_value(); - let mut gas_limit: U256 = batch_call.estimate_gas().await?.into(); + let mut gas_limit: U256 = batch.estimate_gas().await?.into(); // Use the max of the sum of individual estimates and the estimate for the entire batch if let Some(gas_sum) = individual_estimates_sum { gas_limit = gas_limit.max(gas_sum) } - batch_call = batch_call.gas(gas_limit); - Ok(batch_call) + + Ok(batch.gas(gas_limit)) } diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index c44fa7e23b3..f9792410e53 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -18,11 +18,13 @@ use ethers_core::{ EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE, }, }; +use tokio::sync::Mutex; +use tokio::try_join; +use tracing::{debug, error, info, instrument, warn}; + use hyperlane_core::{ ChainCommunicationError, ChainResult, HyperlaneDomain, ReorgPeriod, H256, U256, }; -use tokio::sync::Mutex; -use tracing::{debug, error, info, instrument, warn}; use crate::{EthereumMailboxCache, EthereumReorgPeriod, Middleware, TransactionOverrides}; @@ -192,10 +194,10 @@ where } let eip1559_fee_result = match cached_eip1559_fee { - Some(fee) => Ok(fee), - None => estimate_eip1559_fees(provider.clone(), None, &latest_block, domain, &tx.tx).await, + Some(fee) => Ok((fee, latest_block)), + None => estimate_eip1559_fees(provider.clone(), None, domain, &tx.tx).await, }; - let (base_fee, max_fee, max_priority_fee) = match eip1559_fee_result { + let ((base_fee, max_fee, max_priority_fee), _) = match eip1559_fee_result { Ok(result) => result, Err(err) => { warn!(?err, "Failed to estimate EIP-1559 fees"); @@ -299,32 +301,44 @@ pub type Eip1559Fee = ( EthersU256, // max priority fee ); +async fn latest_block(provider: Arc) -> ChainResult> +where + M: Middleware + 'static, +{ + let latest_block = provider + .get_block(BlockNumber::Latest) + .await + .map_err(ChainCommunicationError::from_other)? + .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?; + Ok(latest_block) +} + /// Use this to estimate EIP 1559 fees with some chain-specific logic. pub(crate) async fn estimate_eip1559_fees( provider: Arc, estimator: Option, - latest_block: &Block, domain: &HyperlaneDomain, tx: &TypedTransaction, -) -> ChainResult +) -> ChainResult<(Eip1559Fee, Block)> where M: Middleware + 'static, { if domain.is_zksync_stack() { - estimate_eip1559_fees_zksync(provider, latest_block, tx).await + estimate_eip1559_fees_zksync(provider, tx).await } else { - estimate_eip1559_fees_default(provider, estimator, latest_block).await + estimate_eip1559_fees_default(provider, estimator).await } } async fn estimate_eip1559_fees_zksync( provider: Arc, - latest_block: &Block, tx: &TypedTransaction, -) -> ChainResult +) -> ChainResult<(Eip1559Fee, Block)> where M: Middleware + 'static, { + let latest_block = latest_block(provider.clone()).await?; + let base_fee_per_gas = latest_block .base_fee_per_gas .ok_or_else(|| ProviderError::CustomError("EIP-1559 not activated".into()))?; @@ -333,7 +347,10 @@ where let max_fee_per_gas = response.max_fee_per_gas; let max_priority_fee_per_gas = response.max_priority_fee_per_gas; - Ok((base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas)) + Ok(( + (base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas), + latest_block, + )) } async fn zksync_estimate_fee( @@ -380,24 +397,29 @@ struct ZksyncEstimateFeeResponse { async fn estimate_eip1559_fees_default( provider: Arc, estimator: Option, - latest_block: &Block, -) -> ChainResult<(EthersU256, EthersU256, EthersU256)> +) -> ChainResult<((EthersU256, EthersU256, EthersU256), Block)> where M: Middleware + 'static, { + let latest_block = latest_block(provider.clone()); + + let fee_history = async { + provider + .fee_history( + EIP1559_FEE_ESTIMATION_PAST_BLOCKS, + BlockNumber::Latest, + &[EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE], + ) + .await + .map_err(ChainCommunicationError::from_other) + }; + + let (latest_block, fee_history) = try_join!(latest_block, fee_history)?; + let base_fee_per_gas = latest_block .base_fee_per_gas .ok_or_else(|| ProviderError::CustomError("EIP-1559 not activated".into()))?; - let fee_history = provider - .fee_history( - EIP1559_FEE_ESTIMATION_PAST_BLOCKS, - BlockNumber::Latest, - &[EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE], - ) - .await - .map_err(ChainCommunicationError::from_other)?; - // use the provided fee estimator function, or fallback to the default implementation. let (max_fee_per_gas, max_priority_fee_per_gas) = if let Some(es) = estimator { es(base_fee_per_gas, fee_history.reward) @@ -405,7 +427,10 @@ where eip1559_default_estimator(base_fee_per_gas, fee_history.reward) }; - Ok((base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas)) + Ok(( + (base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas), + latest_block, + )) } pub(crate) async fn call_with_reorg_period( From 385b3078e93f44fd8230b31ccfbfb159c3c3bf2a Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 18 Apr 2025 15:53:15 +0100 Subject: [PATCH 031/223] feat: optimizing the validator (#5966) ### Description - uses multithreaded tokio runtime, this helped a little - moves away from the singleton signer, which has some historical baggage so it's still used as a fallback - performs chunks of checkpoints concurrently. this is configurable, and defaults to chunks of 50 ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/agents/validator/src/main.rs | 2 +- rust/main/agents/validator/src/settings.rs | 9 + rust/main/agents/validator/src/submit.rs | 185 ++++++++++++++---- rust/main/agents/validator/src/validator.rs | 13 +- .../config/environments/testnet4/agent.ts | 2 +- 5 files changed, 174 insertions(+), 37 deletions(-) diff --git a/rust/main/agents/validator/src/main.rs b/rust/main/agents/validator/src/main.rs index 14056046e0c..7f9923d679b 100644 --- a/rust/main/agents/validator/src/main.rs +++ b/rust/main/agents/validator/src/main.rs @@ -14,7 +14,7 @@ mod settings; mod submit; mod validator; -#[tokio::main(flavor = "current_thread")] +#[tokio::main(flavor = "multi_thread")] async fn main() -> Result<()> { // Logging is not initialised at this point, so, using `println!` println!("Validator starting up..."); diff --git a/rust/main/agents/validator/src/settings.rs b/rust/main/agents/validator/src/settings.rs index 07d0e57692f..ddf23dd7560 100644 --- a/rust/main/agents/validator/src/settings.rs +++ b/rust/main/agents/validator/src/settings.rs @@ -55,6 +55,8 @@ pub struct ValidatorSettings { pub rpcs: Vec, /// If the validator oped into public RPCs pub allow_public_rpcs: bool, + /// Max sign concurrency + pub max_sign_concurrency: usize, } #[derive(Debug, Deserialize)] @@ -153,6 +155,12 @@ impl FromRawConf for ValidatorSettings { .end() .unwrap(); + let max_sign_concurrency = p + .chain(&mut err) + .get_opt_key("maxSignConcurrency") + .parse_u64() + .unwrap_or(50) as usize; + let mut rpcs = get_rpc_urls(&chain, "rpcUrls", "customRpcUrls", &mut err); // this is only relevant for cosmos rpcs.extend(get_rpc_urls(&chain, "grpcUrls", "customGrpcUrls", &mut err)); @@ -177,6 +185,7 @@ impl FromRawConf for ValidatorSettings { interval, rpcs, allow_public_rpcs, + max_sign_concurrency, }) } } diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs index 2f75b51903d..f7ce326f036 100644 --- a/rust/main/agents/validator/src/submit.rs +++ b/rust/main/agents/validator/src/submit.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use std::vec; +use futures::future::join_all; use prometheus::IntGauge; use tokio::time::sleep; use tracing::{debug, error, info}; @@ -13,38 +14,46 @@ use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, Checkpoint, CheckpointWithMessageId, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSignerExt, }; -use hyperlane_core::{ChainResult, MerkleTreeHook, ReorgEvent, ReorgPeriod}; -use hyperlane_ethereum::SingletonSignerHandle; +use hyperlane_core::{ChainResult, MerkleTreeHook, ReorgEvent, ReorgPeriod, SignedType}; +use hyperlane_ethereum::{Signers, SingletonSignerHandle}; #[derive(Clone)] pub(crate) struct ValidatorSubmitter { interval: Duration, reorg_period: ReorgPeriod, - signer: SingletonSignerHandle, + #[allow(unused)] + singleton_signer: SingletonSignerHandle, + signer: Signers, merkle_tree_hook: Arc, checkpoint_syncer: Arc, db: Arc, metrics: ValidatorSubmitterMetrics, + max_sign_concurrency: usize, } impl ValidatorSubmitter { + #[allow(clippy::too_many_arguments)] pub(crate) fn new( interval: Duration, reorg_period: ReorgPeriod, merkle_tree_hook: Arc, - signer: SingletonSignerHandle, + singleton_signer: SingletonSignerHandle, + signer: Signers, checkpoint_syncer: Arc, db: Arc, metrics: ValidatorSubmitterMetrics, + max_sign_concurrency: usize, ) -> Self { Self { reorg_period, interval, merkle_tree_hook, + singleton_signer, signer, checkpoint_syncer, db, metrics, + max_sign_concurrency, } } @@ -148,6 +157,7 @@ impl ValidatorSubmitter { tree: &mut IncrementalMerkle, correctness_checkpoint: &Checkpoint, ) { + let start = Instant::now(); // This should never be called with a tree that is ahead of the correctness checkpoint. assert!( !tree_exceeds_checkpoint(correctness_checkpoint, tree), @@ -240,6 +250,12 @@ impl ValidatorSubmitter { panic!("{panic_message}"); } + tracing::info!( + elapsed=?start.elapsed(), + checkpoint_queue_len = checkpoint_queue.len(), + "Checkpoint submitter reached correctness checkpoint" + ); + if !checkpoint_queue.is_empty() { info!( index = checkpoint.index, @@ -255,22 +271,73 @@ impl ValidatorSubmitter { } } + async fn sign_checkpoint( + &self, + checkpoint: CheckpointWithMessageId, + ) -> ChainResult> { + let signer_retries = 5; + + for i in 0..signer_retries { + match self.signer.sign(checkpoint).await { + Ok(signed_checkpoint) => return Ok(signed_checkpoint), + Err(err) => { + tracing::warn!( + ?checkpoint, + attempt = i, + retries = signer_retries, + ?err, + "Error signing checkpoint with direct signer" + ); + sleep(Duration::from_millis(100)).await; + } + } + } + + tracing::warn!( + ?checkpoint, + retries = signer_retries, + "Error signing checkpoint with direct signer after all retries, falling back to singleton signer" + ); + + // Now try the singleton signer as a last resort + Ok(self.singleton_signer.sign(checkpoint).await?) + } + async fn sign_and_submit_checkpoint( &self, checkpoint: CheckpointWithMessageId, ) -> ChainResult<()> { + let start = Instant::now(); let existing = self .checkpoint_syncer .fetch_checkpoint(checkpoint.index) .await?; + tracing::trace!( + elapsed=?start.elapsed(), + "Fetched checkpoint from checkpoint storage", + ); + if existing.is_some() { debug!(index = checkpoint.index, "Checkpoint already submitted"); return Ok(()); } - let signed_checkpoint = self.signer.sign(checkpoint).await?; + + let start = Instant::now(); + let signed_checkpoint = self.sign_checkpoint(checkpoint).await?; + tracing::trace!( + elapsed=?start.elapsed(), + "Signed checkpoint", + ); + + let start = Instant::now(); self.checkpoint_syncer .write_checkpoint(&signed_checkpoint) .await?; + tracing::trace!( + elapsed=?start.elapsed(), + "Stored checkpoint", + ); + debug!(index = checkpoint.index, "Signed and submitted checkpoint"); // TODO: move these into S3 implementations @@ -280,36 +347,81 @@ impl ValidatorSubmitter { } /// Signs and submits any previously unsubmitted checkpoints. - async fn sign_and_submit_checkpoints(&self, checkpoints: Vec) { - let last_checkpoint = checkpoints.as_slice()[checkpoints.len() - 1]; - // Submits checkpoints to the store in reverse order. This speeds up processing historic checkpoints (those before the validator is spun up), - // since those are the most likely to make messages become processable. - // A side effect is that new checkpoints will also be submitted in reverse order. - for queued_checkpoint in checkpoints.into_iter().rev() { - // certain checkpoint stores rate limit very aggressively, so we retry indefinitely - call_and_retry_indefinitely(|| { - let self_clone = self.clone(); - Box::pin(async move { - self_clone - .sign_and_submit_checkpoint(queued_checkpoint) - .await?; - Ok(()) + async fn sign_and_submit_checkpoints(&self, mut checkpoints: Vec) { + // The checkpoints are ordered by index, so the last one is the highest index. + let last_checkpoint_index = checkpoints[checkpoints.len() - 1].index; + + let arc_self = Arc::new(self.clone()); + + let mut first_chunk = true; + + while !checkpoints.is_empty() { + let start = Instant::now(); + + // Take a chunk of checkpoints, starting with the highest index. + // This speeds up processing historic checkpoints (those before the validator is spun up), + // since those are the most likely to make messages become processable. + // A side effect is that new checkpoints will also be submitted in reverse order. + + // This logic is a bit awkward, but we want control over the chunks so we can also + // write the latest index to the checkpoint storage after the first chunk is successful. + let mut chunk = Vec::with_capacity(self.max_sign_concurrency); + for _ in 0..self.max_sign_concurrency { + if let Some(cp) = checkpoints.pop() { + chunk.push(cp); + } else { + break; + } + } + + let chunk_len = chunk.len(); + + let futures = chunk.into_iter().map(|checkpoint| { + let self_clone = arc_self.clone(); + call_and_retry_indefinitely(move || { + let self_clone = self_clone.clone(); + Box::pin(async move { + let start = Instant::now(); + self_clone.sign_and_submit_checkpoint(checkpoint).await?; + tracing::info!( + elapsed=?start.elapsed(), + "Signed and submitted checkpoint", + ); + Ok(()) + }) }) - }) - .await; - } + }); - call_and_retry_indefinitely(|| { - let self_clone = self.clone(); - Box::pin(async move { - self_clone - .checkpoint_syncer - .update_latest_index(last_checkpoint.index) - .await?; - Ok(()) - }) - }) - .await; + join_all(futures).await; + + tracing::info!( + elapsed=?start.elapsed(), + chunk_len, + remaining_checkpoints = checkpoints.len(), + "Signed and submitted checkpoint chunk", + ); + + // If it's the first chunk, update the latest index + if first_chunk { + call_and_retry_indefinitely(|| { + let self_clone = self.clone(); + Box::pin(async move { + let start = Instant::now(); + self_clone + .checkpoint_syncer + .update_latest_index(last_checkpoint_index) + .await?; + tracing::trace!( + elapsed=?start.elapsed(), + "Updated latest index", + ); + Ok(()) + }) + }) + .await; + first_chunk = false; + } + } } } @@ -633,15 +745,22 @@ mod test { Ok(()) }); + let signer: Signers = "1111111111111111111111111111111111111111111111111111111111111111" + .parse::() + .unwrap() + .into(); + // instantiate the validator submitter let validator_submitter = ValidatorSubmitter::new( Duration::from_secs(1), ReorgPeriod::from_blocks(expected_reorg_period), Arc::new(mock_merkle_tree_hook), dummy_singleton_handle(), + signer, Arc::new(mock_checkpoint_syncer), Arc::new(db), dummy_metrics(), + 50, ); // mock the correctness checkpoint response diff --git a/rust/main/agents/validator/src/validator.rs b/rust/main/agents/validator/src/validator.rs index 0262e0e5b9e..5dd1c316377 100644 --- a/rust/main/agents/validator/src/validator.rs +++ b/rust/main/agents/validator/src/validator.rs @@ -26,7 +26,7 @@ use hyperlane_core::{ HyperlaneSignerExt, Mailbox, MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, TxOutcome, ValidatorAnnounce, H256, U256, }; -use hyperlane_ethereum::{SingletonSigner, SingletonSignerHandle}; +use hyperlane_ethereum::{Signers, SingletonSigner, SingletonSignerHandle}; use crate::{ settings::ValidatorSettings, @@ -46,6 +46,7 @@ pub struct Validator { merkle_tree_hook: Arc, validator_announce: Arc, signer: SingletonSignerHandle, + raw_signer: Signers, // temporary holder until `run` is called signer_instance: Option>, reorg_period: ReorgPeriod, @@ -56,6 +57,7 @@ pub struct Validator { chain_metrics: ChainMetrics, runtime_metrics: RuntimeMetrics, agent_metadata: ValidatorMetadata, + max_sign_concurrency: usize, } /// Metadata for `validator` @@ -115,8 +117,10 @@ impl BaseAgent for Validator { let db = DB::from_path(&settings.db)?; let msg_db = HyperlaneRocksDB::new(&settings.origin_chain, db); + let raw_signer: Signers = settings.validator.build().await?; + // Intentionally using hyperlane_ethereum for the validator's signer - let (signer_instance, signer) = SingletonSigner::new(settings.validator.build().await?); + let (signer_instance, signer) = SingletonSigner::new(raw_signer.clone()); let core = settings.build_hyperlane_core(metrics.clone()); // Be extra sure to panic checkpoint syncer fails, which indicates @@ -169,6 +173,7 @@ impl BaseAgent for Validator { merkle_tree_hook_sync, validator_announce: validator_announce.into(), signer, + raw_signer, signer_instance: Some(Box::new(signer_instance)), reorg_period: settings.reorg_period, interval: settings.interval, @@ -178,6 +183,7 @@ impl BaseAgent for Validator { core_metrics: metrics, runtime_metrics, agent_metadata, + max_sign_concurrency: settings.max_sign_concurrency, }) } @@ -295,9 +301,11 @@ impl Validator { self.reorg_period.clone(), self.merkle_tree_hook.clone(), self.signer.clone(), + self.raw_signer.clone(), self.checkpoint_syncer.clone(), Arc::new(self.db.clone()) as Arc, ValidatorSubmitterMetrics::new(&self.core.metrics, &self.origin_chain), + self.max_sign_concurrency, ); let tip_tree = self @@ -305,6 +313,7 @@ impl Validator { .tree(&self.reorg_period) .await .expect("failed to get merkle tree"); + // This function is only called after we have already checked that the // merkle tree hook has count > 0, but we assert to be extra sure this is // the case. diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index d76efb607a4..66a8892ed0e 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -336,7 +336,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '45739bd-20250401-014114', + tag: 'f6ac77a-20250418-001005', }, chains: validatorChainConfig(Contexts.Hyperlane), resources: validatorResources, From b598c48941b14b96562f0d5bc0c7debeedfc1d57 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 18 Apr 2025 18:19:56 +0100 Subject: [PATCH 032/223] chore: Upgrade mainnet validators to latest image (#5976) ### Description Upgrade mainnet validators to latest image 385b307-20250418-150728 ### Backward compatibility Yes ### Testing Testnet Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- typescript/infra/config/environments/mainnet3/agent.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 571b3214866..93980e0ee55 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -783,7 +783,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: '8ab7c80-20250326-191115', + tag: '385b307-20250418-150728', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -824,7 +824,7 @@ const releaseCandidate: RootAgentConfig = { validators: { docker: { repo, - tag: '11a4e95-20250116-145528', + tag: '385b307-20250418-150728', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.ReleaseCandidate), From 78bc7cb78adb900eb271faf37d31950b2da9f302 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:13:11 +0100 Subject: [PATCH 033/223] feat: gas price cap and batch gas limit optimization (#5977) ### Description - adds a gas price cap tx override - scales down the batch gas limit to 80% of the estimate, since in practice we use no more than 65% of the estimate ### Testing e2e --- .../chains/hyperlane-ethereum/src/config.rs | 3 ++ .../src/contracts/multicall.rs | 10 +++- rust/main/chains/hyperlane-ethereum/src/tx.rs | 52 ++++++++++++------- .../src/settings/parser/connection_parser.rs | 6 +++ 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/config.rs b/rust/main/chains/hyperlane-ethereum/src/config.rs index d0dc74b75dd..a2d0267639d 100644 --- a/rust/main/chains/hyperlane-ethereum/src/config.rs +++ b/rust/main/chains/hyperlane-ethereum/src/config.rs @@ -72,6 +72,9 @@ pub struct TransactionOverrides { pub gas_price_multiplier_denominator: Option, /// Gas price multiplier numerator to use for transactions, eg 100 pub gas_price_multiplier_numerator: Option, + + /// Gas price cap, in wei. + pub gas_price_cap: Option, } /// Ethereum reorg period diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs index d840f64afec..f42a5368f12 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs @@ -14,6 +14,8 @@ use crate::{ConnectionConf, EthereumProvider}; use super::EthereumMailboxCache; +const MULTICALL_GAS_LIMIT_MULTIPLIER_DENOMINATOR: u64 = 80; +const MULTICALL_GAS_LIMIT_MULTIPLIER_NUMERATOR: u64 = 100; const ALLOW_BATCH_FAILURES: bool = true; /// Conservative estimate picked by subtracting the gas used by individual calls from the total cost of `aggregate3` @@ -127,5 +129,11 @@ where gas_limit = gas_limit.max(gas_sum) } - Ok(batch.gas(gas_limit)) + // in practice, even when the full batch lands, no more than 65% of the gas limit is used. + // this sets the limit lower, but still allows for some overhead, to make it more likely + // that the tx gets included (due to the lower gas limit) + let scaled_down_gas_limit = gas_limit * MULTICALL_GAS_LIMIT_MULTIPLIER_DENOMINATOR + / MULTICALL_GAS_LIMIT_MULTIPLIER_NUMERATOR; + + Ok(batch.gas(scaled_down_gas_limit)) } diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index f9792410e53..ce17acaf23f 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -202,7 +202,7 @@ where Err(err) => { warn!(?err, "Failed to estimate EIP-1559 fees"); // Assume it's not an EIP 1559 chain - return Ok(apply_legacy_min_gas_price(tx, transaction_overrides).gas(gas_limit)); + return Ok(apply_legacy_overrides(tx, transaction_overrides).gas(gas_limit)); } }; @@ -212,7 +212,7 @@ where // fee lower than 3 gwei because of privileged transactions being included by block // producers that have a lower priority fee. if base_fee.is_zero() { - return Ok(apply_legacy_min_gas_price(tx, transaction_overrides).gas(gas_limit)); + return Ok(apply_legacy_overrides(tx, transaction_overrides).gas(gas_limit)); } // Apply overrides for EIP 1559 tx params if they exist. @@ -240,7 +240,7 @@ where Ok(eip_1559_tx.gas(gas_limit)) } -fn apply_legacy_min_gas_price( +fn apply_legacy_overrides( tx: ContractCall, transaction_overrides: &TransactionOverrides, ) -> ContractCall @@ -248,23 +248,19 @@ where M: Middleware + 'static, D: Detokenize, { - let gas_price = max_between_options( - tx.tx.gas_price(), - transaction_overrides.min_gas_price.map(Into::into), - ); - if let Some(gas_price) = gas_price { - return tx.gas_price(gas_price); - } - tx -} + let gas_price = tx.tx.gas_price(); + // if no gas price was set in the tx, leave the tx as is and return early + let Some(mut gas_price) = gas_price else { + return tx; + }; -fn max_between_options(a: Option, b: Option) -> Option { - match (a, b) { - (Some(a), Some(b)) => Some(a.max(b)), - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), - (None, None) => None, - } + let min_price_override = transaction_overrides + .min_gas_price + .map(Into::into) + .unwrap_or(0.into()); + gas_price = gas_price.max(min_price_override); + gas_price = apply_gas_price_cap(gas_price, transaction_overrides); + tx.gas_price(gas_price) } fn apply_1559_multipliers_and_overrides( @@ -290,9 +286,27 @@ fn apply_1559_multipliers_and_overrides( if let Some(min_priority_fee) = transaction_overrides.min_priority_fee_per_gas { max_priority_fee = max_priority_fee.max(min_priority_fee.into()); } + max_fee = apply_gas_price_cap(max_fee, transaction_overrides); (max_fee, max_priority_fee) } +fn apply_gas_price_cap( + gas_price: EthersU256, + transaction_overrides: &TransactionOverrides, +) -> EthersU256 { + if let Some(gas_price_cap) = transaction_overrides.gas_price_cap { + if gas_price > gas_price_cap.into() { + warn!( + ?gas_price, + ?gas_price_cap, + "Gas price for transaction is higher than the gas price cap. Capping it to the gas price cap." + ); + return gas_price_cap.into(); + } + } + gas_price +} + type FeeEstimator = fn(EthersU256, Vec>) -> (EthersU256, EthersU256); pub type Eip1559Fee = ( diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index f96aa2a521e..39597233296 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -106,6 +106,12 @@ pub fn build_ethereum_connection_conf( .get_opt_key("gasPriceMultiplierNumerator") .parse_u256() .end(), + + gas_price_cap: value_parser + .chain(err) + .get_opt_key("gasPriceCap") + .parse_u256() + .end(), }) .unwrap_or_default(); From 8daece7bba3b51902e09aa941d4438a72d9ca1d7 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 18 Apr 2025 21:52:58 +0100 Subject: [PATCH 034/223] feat(infra): support multiple governance types (#5938) ### Description feat(infra): support multiple governance types - add separate owners file for matrix of owner x governance types - [safe,ica] owner x [regular,irregular,exceptional,AW] - add reusable utils/types for reasoning with multiple governance types/owners - update any script dealing with safes/icas to correctly operate on the corresponding set for a given governance type - streamline owners.ts - fix warp config generation by manually configuring legacy-style owner/ownerOverrides ### Drive-by changes - switch logging from `console` to `rootLogger` in more places - refactor signers configuration - refactor old create-safes script ### Related issues https://linear.app/hyperlane-xyz/issue/AW-75 ### Backward compatibility ### Testing manual check-deploy --------- Signed-off-by: pbio <10051819+paulbalaji@users.noreply.github.com> Co-authored-by: Mohammed Hussan --- .../mainnet3/governance/ica/aw.ts | 169 +++++++++++++ .../mainnet3/governance/ica/regular.ts | 111 ++++++++ .../mainnet3/governance/safe/aw.ts | 51 ++++ .../mainnet3/governance/safe/irregular.ts | 6 + .../mainnet3/governance/safe/regular.ts | 44 ++-- .../mainnet3/governance/signers/aw.ts | 15 ++ .../mainnet3/governance/signers/irregular.ts | 13 + .../safeConfig.ts => signers/regular.ts} | 4 +- .../environments/mainnet3/governance/utils.ts | 68 +++++ .../config/environments/mainnet3/owners.ts | 239 +----------------- .../mainnet3/safe/safeSigners.json | 13 - typescript/infra/scripts/agent-utils.ts | 4 +- .../infra/scripts/check/check-owner-ica.ts | 42 +-- .../infra/scripts/keys/get-owner-ica.ts | 27 +- .../infra/scripts/print-mailbox-owner.ts | 53 +--- typescript/infra/scripts/safes/create-safe.ts | 79 +++--- .../infra/scripts/safes/delete-pending-txs.ts | 20 +- typescript/infra/scripts/safes/delete-tx.ts | 28 +- .../infra/scripts/safes/get-pending-txs.ts | 13 +- .../safes/governance/check-safe-signers.ts | 149 +++++++++++ .../safes/governance/update-signers.ts | 24 +- .../safes/migrate-to-typed-signatures.ts | 2 +- typescript/infra/scripts/safes/parse-txs.ts | 22 +- typescript/infra/src/config/environment.ts | 29 ++- .../infra/src/govern/HyperlaneAppGovernor.ts | 163 ++++++------ .../infra/src/govern/HyperlaneCoreGovernor.ts | 3 +- typescript/infra/src/governance.ts | 72 ++++++ .../infra/src/tx/govern-transaction-reader.ts | 10 +- typescript/infra/src/utils/safe.ts | 63 ++++- 29 files changed, 1036 insertions(+), 500 deletions(-) create mode 100644 typescript/infra/config/environments/mainnet3/governance/ica/aw.ts create mode 100644 typescript/infra/config/environments/mainnet3/governance/ica/regular.ts create mode 100644 typescript/infra/config/environments/mainnet3/governance/safe/aw.ts create mode 100644 typescript/infra/config/environments/mainnet3/governance/safe/irregular.ts create mode 100644 typescript/infra/config/environments/mainnet3/governance/signers/aw.ts create mode 100644 typescript/infra/config/environments/mainnet3/governance/signers/irregular.ts rename typescript/infra/config/environments/mainnet3/governance/{safe/safeConfig.ts => signers/regular.ts} (88%) create mode 100644 typescript/infra/config/environments/mainnet3/governance/utils.ts delete mode 100644 typescript/infra/config/environments/mainnet3/safe/safeSigners.json create mode 100644 typescript/infra/scripts/safes/governance/check-safe-signers.ts create mode 100644 typescript/infra/src/governance.ts diff --git a/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts b/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts new file mode 100644 index 00000000000..77c0e572b5a --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts @@ -0,0 +1,169 @@ +// Found by running: +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +// yarn tsx ./scripts/keys/get-owner-ica.ts -e mainnet3 --ownerChain ethereum --destinationChains ... +export const awIcas: ChainMap
= { + viction: '0x23ed65DE22ac29Ec1C16E75EddB0cE3A187357b4', + inevm: '0xFDF9EDcb2243D51f5f317b9CEcA8edD2bEEE036e', + + // Jul 26, 2024 batch + // ---------------------------------------------------------- + xlayer: '0x1571c482fe9E76bbf50829912b1c746792966369', + cheesechain: '0xEe2C5320BE9bC7A1492187cfb289953b53E3ff1b', + worldchain: '0x1996DbFcFB433737fE404F58D2c32A7f5f334210', + // zircuit: '0x0d67c56E818a02ABa58cd2394b95EF26db999aA3', // already has a safe + + // Aug 5, 2024 batch + // ---------------------------------------------------------- + cyber: '0x984Fe5a45Ac4aaeC4E4655b50f776aB79c9Be19F', + degenchain: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', + kroma: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', + lisk: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', + lukso: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', + merlin: '0xCf867cEaeeE8CBe65C680c734D29d26440931D5b', + metis: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', + mint: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', + proofofplay: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', + real: '0xc761e68BF3A94326FD0D305e3ccb4cdaab2edA19', + sanko: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752', + tangle: '0xCC2aeb692197C7894E561d31ADFE8F79746f7d9F', + xai: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', + // taiko: '0x483D218D2FEe7FC7204ba15F00C7901acbF9697D', // renzo chain + + // Aug 26, 2024 batch + // ---------------------------------------------------------- + astar: '0x6b241544eBa7d89B51b72DF85a0342dAa37371Ca', + bitlayer: '0xe6239316cA60814229E801fF0B9DD71C9CA29008', + coredao: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', + dogechain: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', + flare: '0x689b8DaBBF2f9Fd83D37427A062B30edF463e20b', + molten: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', + shibarium: '0x6348FAe3a8374dbAAaE84EEe5458AE4063Fe2be7', + + // Sep 9, 2024 batch + // ---------------------------------------------------------- + everclear: '0x63B2075372D6d0565F51210D0A296D3c8a773aB6', + oortmainnet: '0x7021D11F9fAe455AB2f45D72dbc2C64d116Cb657', + + // Sep 19, 2024 SAFE --> ICA v1 Migration + // ---------------------------------------------------------- + celo: '0x3fA264c58E1365f1d5963B831b864EcdD2ddD19b', + avalanche: '0x8c8695cD9905e22d84E466804ABE55408A87e595', + polygon: '0xBDD25dd5203fedE33FD631e30fEF9b9eF2598ECE', + moonbeam: '0x480e5b5De6a29F07fe8295C60A1845d36b7BfdE6', + gnosis: '0xD42125a4889A7A36F32d7D12bFa0ae52B0AD106b', + scroll: '0x2a3fe2513F4A7813683d480724AB0a3683EfF8AC', + polygonzkevm: '0x66037De438a59C966214B78c1d377c4e93a5C7D1', + ancient8: '0xA9FD5BeB556AB1859D7625B381110a257f56F98C', + redstone: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752', + mantle: '0x08C880b88335CA3e85Ebb4E461245a7e899863c9', + bob: '0xc99e58b9A4E330e2E4d09e2c94CD3c553904F588', + zetachain: '0xc876B8e63c3ff5b636d9492715BE375644CaD345', + zoramainnet: '0x84977Eb15E0ff5824a6129c789F70e88352C230b', + fusemainnet: '0xbBdb1682B2922C282b56DD716C29db5EFbdb5632', + endurance: '0x470E04D8a3b7938b385093B93CeBd8Db7A1E557C', + // sei: '0xabad187003EdeDd6C720Fc633f929EA632996567', // renzo chain + + // Oct 16, 2024 batch + // ---------------------------------------------------------- + // lumia: '0x418E10Ac9e0b84022d0636228d05bc74172e0e41', + + // Oct 30, 2024 batch + // ---------------------------------------------------------- + apechain: '0xe68b0aB6BB8c11D855556A5d3539524f6DB3bdc6', + arbitrumnova: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + b3: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + fantom: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + gravity: '0x3104ADE26e21AEbdB325321433541DfE8B5dCF23', + harmony: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + kaia: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + morph: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + orderly: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + snaxchain: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', + + // Nov 8, 2024 batch + // ---------------------------------------------------------- + alephzeroevmmainnet: '0xDE91AC081E12107a033728A287b06B1Fc640A637', + chilizmainnet: '0x54AF0FCDCD58428f8dF3f825267DfB58f2C710eb', + flowmainnet: '0x65528D447C93CC1A1A7186CB4449d9fE0d5C1928', + immutablezkevmmainnet: '0x54AF0FCDCD58428f8dF3f825267DfB58f2C710eb', + metal: '0xf1d25462e1f82BbF25b3ef7A4C94F738a30a968B', + polynomialfi: '0x6ACa36E710dC0C80400090EA0bC55dA913a3D20D', + rarichain: '0xD0A4Ad2Ca0251BBc6541f8c2a594F1A82b67F114', + rootstockmainnet: '0x0C15f7479E0B46868693568a3f1C747Fdec9f17d', + superpositionmainnet: '0x5F17Dc2e1fd1371dc6e694c51f22aBAF8E27667B', + flame: '0x4F3d85360840497Cd1bc34Ca55f27629eee2AA2e', + prom: '0x1cDd3C143387cD1FaE23e2B66bc3F409D073aC3D', + + // Nov 21, 2024 batch + // ---------------------------------------------------------- + boba: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', + duckchain: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', + unichain: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', + vana: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', + bsquared: '0xd9564EaaA68A327933f758A54450D3A0531E60BB', + superseed: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', + + // Dec 4, 2024 batch + // ---------------------------------------------------------- + // swell: '0xff8326468e7AaB51c53D3569cf7C45Dd54c11687', // already has a safe + lumiaprism: '0xAFfA863646D1bC74ecEC0dB1070f069Af065EBf5', + appchain: '0x4F25DFFd10A6D61C365E1a605d07B2ab0E82A7E6', + + // Dec 13, 2024 batch + // ---------------------------------------------------------- + arthera: '0x962e4E5F5e47e1Ab5361eE0B5108Ebeb9Fa5c99B', + aurora: '0x853f40c807cbb08EDd19B326b9b6A669bf3c274c', + conflux: '0xac8f0e306A126312C273080d149ca01d461603FE', + conwai: '0x5926599B8Aff45f1708b804B30213babdAD78C83', + corn: '0x5926599B8Aff45f1708b804B30213babdAD78C83', + evmos: '0x5926599B8Aff45f1708b804B30213babdAD78C83', + form: '0x5926599B8Aff45f1708b804B30213babdAD78C83', + ink: '0xDde4Ce691d1c0579d48BCdd3491aA71472b6cC38', + rivalz: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', + soneium: '0x5926599B8Aff45f1708b804B30213babdAD78C83', + sonic: '0x5926599B8Aff45f1708b804B30213babdAD78C83', + telos: '0xDde4Ce691d1c0579d48BCdd3491aA71472b6cC38', + + // Jan 13, 2025 batch + // ---------------------------------------------------------- + artela: '0x745CEA119757ea3e27093da590bC91f408bD4448', + guru: '0x825cF3d703F384E4aA846BA72eCf70f1985C91b6', + hemi: '0x8D18CBB212920e5ef070b23b813d82F8981cC276', + nero: '0xbBdb1682B2922C282b56DD716C29db5EFbdb5632', + torus: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', + xpla: '0x24832680dF0468967F413be1C83acfE24154F88D', + + // Feb 3, 2025 batch + // ---------------------------------------------------------- + glue: '0x24832680dF0468967F413be1C83acfE24154F88D', + matchain: '0x66af72e46b3e8DFc19992A2A88C05d9EEFE01ffB', + unitzero: '0x66af72e46b3e8DFc19992A2A88C05d9EEFE01ffB', + trumpchain: '0x56895bFa7f7dFA5743b2A0994B5B0f88b88350F9', + + // Q5, 2024 batch + // ---------------------------------------------------------- + // berachain: '0x56895bFa7f7dFA5743b2A0994B5B0f88b88350F9', + + // Feb 17, 2025 batch + // ---------------------------------------------------------- + bouncebit: '0x8768A14AA6eD2A62C77155501E742376cbE97981', + arcadia: '0xD2344a364b6Dc6B2Fe0f7D836fa344d83056cbaD', + ronin: '0x8768A14AA6eD2A62C77155501E742376cbE97981', + story: '0x8768A14AA6eD2A62C77155501E742376cbE97981', + subtensor: '0x61BFbb5FEC57f5470388A80946F0415138630b9c', + + // Mar 14, 2025 batch + // ---------------------------------------------------------- + infinityvm: '0x35460c519b7C71d49C64F060dF89AbAE463F3b9a', + plume: '0x61BFbb5FEC57f5470388A80946F0415138630b9c', + + // Mar 31, 2025 batch + // ---------------------------------------------------------- + coti: '0x294589E4913A132A49F7830a2A219363A25c0529', + deepbrainchain: '0xeFb7D10Da69A0a913485851ccec6B85cF98d9cab', + // nibiru: '0x40cD75e80d04663FAe0CE30687504074F163C346', // temporary while looking into decimals + opbnb: '0xeFb7D10Da69A0a913485851ccec6B85cF98d9cab', + reactive: '0x9312B04076efA12D69b95bcE7F4F0EA847073E6a', +} as const; diff --git a/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts b/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts new file mode 100644 index 00000000000..cecfe179e26 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts @@ -0,0 +1,111 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +export const regularIcas: ChainMap
= { + // PREDEPLOY ALL THE ICAS + // ancient8: '0xf789D8a609247c448E28d3af5b8EFC6Cb786C4ee', + // alephzeroevmmainnet: '0xA3dfBa2447F0A50706F22e38f2785e0Bf30BC069', + // apechain: '0x9422838f6fA763354756a3Aba18f34015cB4bF74', + // appchain: '0x8A4E9b5B445504727D7a9377eA329Bb38700F8FA', + // arbitrumnova: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // arcadia: '0x32879753603c140E34D46375F43Db8BdC9a8c545', + // artela: '0x2754282484EBC6103042BE6c2f17acFA96B2546a', + // arthera: '0x33fab5e0290b7b7a0f19Ba9Db8b97C6e7f7a8848', + // astar: '0x87433Bf4d46fB11C6c39c6F1876b95c9001D06E1', + // aurora: '0x793C99f45CF63dd101D7D6819e02333Fb6cFd57f', + // bouncebit: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', + // flame: '0x355e54B6D423db49735Cb701452bd98434A90BAd', + // avalanche: '0x3a1014df0202477a1222999c72bD36395904e8AB', + // b3: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // bitlayer: '0x39EBb0D2b62D623BBEB874505079a21d05A7Ab9d', + // bob: '0x4FB145Da6407F4F485A209332a38A5327B61f83e', + // boba: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + // bsquared: '0x7C475877fc180f5aAB628Ae1B506316a0F3ADE5A', + // celo: '0x20D701Ac137BB131e735B403e0471b101423dDeC', + // cheesechain: '0x5e4277969e2EEEbe04091dc270c2363b6e694F8d', + // chilizmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', + // conflux: '0x04b7cBD37eeFe304655c7e8638BbE4ddEff576E8', + // conwai: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + // coredao: '0x100D940B953b20378242Fe0FbC45792805d50556', + // corn: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + // coti: '0x54b625734673868027c70F656B807588910f3a6f', + // cyber: '0x162F8F6B70f9EE57fED16C4337B90e939851ECA1', + // deepbrainchain: '0x1fbE174fc6B1d3123B890f34aBd982577377Bec8', + // degenchain: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', + // dogechain: '0x100D940B953b20378242Fe0FbC45792805d50556', + // duckchain: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + // endurance: '0xd8b01D2fA7F50889eE0f51114D00ab4c8581A5F4', + // everclear: '0x975866f773aC8f6d816089B0A5Ab9DC8A01c9462', + // evmos: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + // fantom: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // flare: '0x68be5bC1b1E373f3c6a278D290F39461DDD21968', + // flowmainnet: '0x8e9558D9bA4d3FB52ea75D18B337f778589AB1aF', + // form: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + // fusemainnet: '0x4601d6260C532E73187248D0608cB88D446149AD', + // glue: '0x0Fb0635eAbB9332eDc2544A57e7C15fBc5204C0B', + // gnosis: '0x2984A89A1A0331ae200FE2Cb46cCa190e795672E', + // gravity: '0x4Ce5ecE7349E467EBd77F39Eb4232Dd8C6a7314D', + // guru: '0x562FF51582Ec53466c4B8506d3a2FBD3B2F675f4', + // harmony: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // hemi: '0xa16C2860414e52945Bae3eb957cC0897A21f04a6', + // immutablezkevmmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', + // inevm: '0x8427F6021a84a992008DA6a949cad986CEB0d0b3', + // infinityvm: '0xf0c8600D83dC8B517C347cb1D45e02B1F95057bb', + // ink: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', + // kaia: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // kroma: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + // lisk: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', + // lukso: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + // lumiaprism: '0x60a51cB66CF6012A2adF27090fb7D51aE17369CF', + // mantle: '0x9652721a385AF5B410AA0865Df959255a798F761', + // matchain: '0x262E9A3eDA180fCb84846BaBa2aEB9EDa88e9BeF', + // merlin: '0xe4a82d029b3aB2D8Ed12b6800e86452E979D8c5c', + // metal: '0x9E359Bd54B59D28218a47E4C9a1e84568f09aFb4', + // metis: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', + // mint: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', + // molten: '0x100D940B953b20378242Fe0FbC45792805d50556', + // moonbeam: '0xb3178E470E959340206AD9d9eE4E53d0911b2ba9', + // morph: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // nero: '0x4601d6260C532E73187248D0608cB88D446149AD', + // nibiru: '0x966A57078204CA6cA13f091eE66eE61Ada5D2318', + // oortmainnet: '0xBc2cc7372C018C2a0b1F78E29D8B49d21bc5f3bA', + // opbnb: '0x1fbE174fc6B1d3123B890f34aBd982577377Bec8', + // orderly: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // plume: '0xB9d20ea5f3bB574C923F0af67b06b8D87F111819', + // polygon: '0xff5b2F0Ae1FFBf6b92Ea9B5851D7643C81102064', + // polygonzkevm: '0x258B14038C610f7889C74162760A3A70Ce526CD5', + // polynomialfi: '0x45EFBEAD9009C2662a633ee6F307C90B647B7793', + // prom: '0x9123625687807F02FC92A9817cFb8db13A9a8B4d', + // proofofplay: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', + // rarichain: '0x80c7c5fF8F84BEeDEd18382eb45aA3246063fbc5', + // reactive: '0x97B3BE8f063E98Da6ac254716DA194da4634aC80', + // real: '0xdc13fF36978e860d2FC98EaFBA4fDeB64E1D864A', + // redstone: '0x27C06C13E512Cdd80A2E49fc9c803cFd0de7Ba9e', + // rivalz: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + // ronin: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', + // rootstockmainnet: '0x77d190423A18e78C95269f4D1349141D15E433f3', + // sanko: '0x27C06C13E512Cdd80A2E49fc9c803cFd0de7Ba9e', + // scroll: '0xDD2e306EE337952b4f7C0c4Eb44A8B5b9925Bc76', + // shibarium: '0x778f0e4CD1434258A811ab493217f3DC8d501C1b', + // snaxchain: '0x83be90021870A93ff794E00E8CFe454547877E3E', + // soneium: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + // sonic: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + // story: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', + // subtensor: '0xB9d20ea5f3bB574C923F0af67b06b8D87F111819', + // superseed: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + // superpositionmainnet: '0x9f921e87309829ecc61F6df0F2016fD08B80CDD1', + // tangle: '0x60109e724a30E89fEB1877fAd006317f8b81768a', + // telos: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', + // torus: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + // trumpchain: '0x3FAA6Ff31e4FC67957792303bA35092Ee0EBd367', + // unichain: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + // unitzero: '0x262E9A3eDA180fCb84846BaBa2aEB9EDa88e9BeF', + // vana: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + // viction: '0x426FC4C5CC60E5e47101fe30d4f8B94F1b7C1C70', + // worldchain: '0x51E4E4b7317043A1D6e2fe6ccd9dA51844C385f0', + // xai: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', + // xlayer: '0x73581C1c03E64BDE5775b7236B57d78155B75fA7', + // xpla: '0x0Fb0635eAbB9332eDc2544A57e7C15fBc5204C0B', + // zetachain: '0x33d8605D4dF6661259640268DD32BC99A2F55a83', + // zoramainnet: '0xdbB6E8FDe96dE3a1604b93884d8fF9Cb521059fC', +}; diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts b/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts new file mode 100644 index 00000000000..f5f96e90736 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts @@ -0,0 +1,51 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +export const awSafes: ChainMap
= { + mantapacific: '0x03ed2D65f2742193CeD99D48EbF1F1D6F12345B6', // does not have a UI + celo: '0x879038d6Fc9F6D5e2BA73188bd078486d77e1156', + ethereum: '0x3965AC3D295641E452E0ea896a086A9cD7C6C5b6', + avalanche: '0x5bE94B17112B8F18eA9Ac8e559377B467556a3c3', + polygon: '0xf9cFD440CfBCfAB8473cc156485B7eE753b2913E', + bsc: '0x7bB2ADeDdC342ffb611dDC073095cc4B8C547170', + arbitrum: '0x03fD5BE9DF85F0017dC7F4DC3068dDF64fffF25e', + optimism: '0xbd7db3821806bc72D223F0AE521Bf82FcBd6Ef4d', + moonbeam: '0x594203849E52BF6ee0E511cD807Ca2D658893e37', + gnosis: '0x0Ac72fBc82c9c39F81242229631dfC38aA13031B', + inevm: '0x77F3863ea99F2360D84d4BA1A2E441857D0357fa', // caldera + injective + base: '0x3949eD0CD036D9FF662d97BD7aC1686051c4aeBF', + scroll: '0x6EeEbB9F7FB18DD5E59F82658c59B846715eD4F7', + polygonzkevm: '0x1610f578D4d77Fc4ae7ce2DD9AA0b98A5Cd0a9b2', + // injective: 'inj1632x8j35kenryam3mkrsez064sqg2y2fr0frzt', + // solana: 'EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3', + blast: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', + linea: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', + mode: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', + ancient8: '0xD2BFA0F0654E3f2139b8cDC56c32eeC54D32b133', + taiko: '0xa4864301d3fa2a3e68256309F9F0F570270a1BD0', + fraxtal: '0x66e9f52800E9F89F0569fddc594Acd5EE609f762', + sei: '0xCed197FBc360C26C19889745Cf73511b71D03d5D', + redstone: '0xa1a50ff5FD859558E1899fEC5C3064483177FA23', + mantle: '0x8aFE6EECc6CcB02aA20DA8Fff7d29aadEBbc2DCd', + bob: '0x9e2fe7723b018d02cDE4f5cC1A9bC9C65b922Fc8', + zetachain: '0x9d399876522Fc5C044D048594de399A2349d6026', + zoramainnet: '0xF87018025575552889062De4b05bBC3DAe35Cd96', + fusemainnet: '0x29a526227CB864C90Cf078d03872da913B473139', + endurance: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', + zircuit: '0x9e2fe7723b018d02cDE4f5cC1A9bC9C65b922Fc8', + swell: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', + + // Q5, 2024 batch + berachain: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', + + // HyperEVM + hyperevm: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', + + // zksync chains + zeronetwork: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', + abstract: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', + zksync: '0x9C81aA0cC233e9BddeA426F5d395Ab5B65135450', + zklink: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', + treasure: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', + sophon: '0x3D1baf8cA4935f416671640B1Aa9E17E005986eE', +}; diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/irregular.ts b/typescript/infra/config/environments/mainnet3/governance/safe/irregular.ts new file mode 100644 index 00000000000..2b00e65a1ee --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/safe/irregular.ts @@ -0,0 +1,6 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +export const irregularSafes: ChainMap
= { + ethereum: '0xec2EdC01a2Fbade68dBcc80947F43a5B408cC3A0', +}; diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts b/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts index acb47bbc64f..2250f7d771f 100644 --- a/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts +++ b/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts @@ -2,26 +2,26 @@ import { ChainMap } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; export const regularSafes: ChainMap
= { - abstract: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - arbitrum: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - base: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - berachain: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - blast: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - bsc: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - ethereum: '0x562Dfaac27A84be6C96273F5c9594DA1681C0DA7', - fraxtal: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - hyperevm: '0x290Eb7bbf939A36B2c350a668c04815E49757eDC', - linea: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - mantapacific: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - mode: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - optimism: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - sei: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - sophon: '0x113d3a19031Fe5DB58884D6aa54545dD4De499c0', - swell: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - taiko: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - treasure: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - zeronetwork: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - zksync: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - zklink: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - zircuit: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // abstract: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + // arbitrum: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // base: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + // berachain: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // blast: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // bsc: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // ethereum: '0x562Dfaac27A84be6C96273F5c9594DA1681C0DA7', + // fraxtal: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + // hyperevm: '0x290Eb7bbf939A36B2c350a668c04815E49757eDC', + // linea: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // mantapacific: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // mode: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // optimism: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + // sei: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // sophon: '0x113d3a19031Fe5DB58884D6aa54545dD4De499c0', + // swell: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + // taiko: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + // treasure: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + // zeronetwork: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + // zksync: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + // zklink: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + // zircuit: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', }; diff --git a/typescript/infra/config/environments/mainnet3/governance/signers/aw.ts b/typescript/infra/config/environments/mainnet3/governance/signers/aw.ts new file mode 100644 index 00000000000..4f98960731f --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/signers/aw.ts @@ -0,0 +1,15 @@ +import { Address } from '@hyperlane-xyz/utils'; + +export const awSigners: Address[] = [ + '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', // 1 + '0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA', // 2 + '0x3b7f8f68A4FD0420FeA2F42a1eFc53422f205599', // 3 + '0x478be6076f31E9666123B9721D0B6631baD944AF', // 4 + '0x003DDD9eEAb62013b7332Ab4CC6B10077a8ca961', // 5 + '0xd00d6A31485C93c597D1d8231eeeE0ed17B9844B', // 6 + '0x483fd7284A696343FEc0819DDF2cf7E06E8A06E5', // 7 + '0x5b73A98165778BCCE72979B4EE3faCdb31728b8E', // 8 + '0x5dd9a0814022A61777938263308EBB336174f13D', // 9 +]; + +export const awThreshold = 4; diff --git a/typescript/infra/config/environments/mainnet3/governance/signers/irregular.ts b/typescript/infra/config/environments/mainnet3/governance/signers/irregular.ts new file mode 100644 index 00000000000..a8bfd5d5053 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/signers/irregular.ts @@ -0,0 +1,13 @@ +import { Address } from '@hyperlane-xyz/utils'; + +export const irregularSigners: Address[] = [ + '0x9f500df92175b2ac36f8d443382b219d211d354a', + '0x82950a6356316272dF1928C72F5F0A44D9673c88', + '0x861FC61a961F8AFDf115B8DE274101B9ECea2F26', + '0x3b548E88BA3259A6f45DEeA91449cdda5cF164b3', + '0xD5c0D17cCb9071D27a4F7eD8255F59989b9aee0d', + '0x3965AC3D295641E452E0ea896a086A9cD7C6C5b6', + '0x87fcEcb180E0275C22CEF213FF301816bB24E74B', +]; + +export const irregularThreshold = 5; diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts b/typescript/infra/config/environments/mainnet3/governance/signers/regular.ts similarity index 88% rename from typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts rename to typescript/infra/config/environments/mainnet3/governance/signers/regular.ts index 277bd56b055..f06ddd4f8fd 100644 --- a/typescript/infra/config/environments/mainnet3/governance/safe/safeConfig.ts +++ b/typescript/infra/config/environments/mainnet3/governance/signers/regular.ts @@ -1,6 +1,6 @@ import { Address } from '@hyperlane-xyz/utils'; -export const SIGNERS: Address[] = [ +export const regularSigners: Address[] = [ '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', // 1 '0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA', // 2 '0x2f43Ac3cD6A22E4Ba20d3d18d116b1f9420eD84B', // 3 @@ -13,4 +13,4 @@ export const SIGNERS: Address[] = [ '0xD5c0D17cCb9071D27a4F7eD8255F59989b9aee0d', // 10 ]; -export const THRESHOLD = 1; +export const regularThreshold = 6; diff --git a/typescript/infra/config/environments/mainnet3/governance/utils.ts b/typescript/infra/config/environments/mainnet3/governance/utils.ts new file mode 100644 index 00000000000..e62602711a4 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/governance/utils.ts @@ -0,0 +1,68 @@ +import { ChainName } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { GovernanceType } from '../../../../src/governance.js'; + +import { awIcas } from './ica/aw.js'; +import { regularIcas } from './ica/regular.js'; +import { awSafes } from './safe/aw.js'; +import { irregularSafes } from './safe/irregular.js'; +import { regularSafes } from './safe/regular.js'; +import { awSigners, awThreshold } from './signers/aw.js'; +import { irregularSigners, irregularThreshold } from './signers/irregular.js'; +import { regularSigners, regularThreshold } from './signers/regular.js'; + +export function getGovernanceSafes(governanceType: GovernanceType) { + switch (governanceType) { + case GovernanceType.Regular: + return regularSafes; + case GovernanceType.AbacusWorks: + return awSafes; + case GovernanceType.Irregular: + return irregularSafes; + default: + throw new Error(`Unknown governance type: ${governanceType}`); + } +} + +export function getGovernanceIcas(governanceType: GovernanceType) { + switch (governanceType) { + case GovernanceType.Regular: + return regularIcas; + case GovernanceType.AbacusWorks: + return awIcas; + case GovernanceType.Irregular: + return {}; + default: + throw new Error(`Unknown governance type: ${governanceType}`); + } +} + +export function getGovernanceSigners(governanceType: GovernanceType): { + signers: Address[]; + threshold: number; +} { + switch (governanceType) { + case GovernanceType.Regular: + return { + signers: regularSigners, + threshold: regularThreshold, + }; + case GovernanceType.AbacusWorks: + return { + signers: awSigners, + threshold: awThreshold, + }; + case GovernanceType.Irregular: + return { + signers: irregularSigners, + threshold: irregularThreshold, + }; + } +} +export function getSafeChains(): Set { + return new Set( + ...Object.keys(getGovernanceSafes(GovernanceType.AbacusWorks)), + ...Object.keys(getGovernanceSafes(GovernanceType.Regular)), + ); +} diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index 8a551948958..cbceba86074 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -4,7 +4,10 @@ import { Address, objFilter, objMap } from '@hyperlane-xyz/utils'; import { getMainnetAddresses } from '../../registry.js'; import { ethereumChainNames } from './chains.js'; -import { supportedChainNames } from './supportedChainNames.js'; +import { awIcas } from './governance/ica/aw.js'; +import { regularIcas } from './governance/ica/regular.js'; +import { awSafes } from './governance/safe/aw.js'; +import { regularSafes } from './governance/safe/regular.js'; export const upgradeTimelocks: ChainMap
= { arbitrum: '0xAC98b0cD1B64EA4fe133C6D2EDaf842cE5cF4b01', @@ -28,247 +31,27 @@ export function localAccountRouters(): ChainMap
{ ); } -export const safes: ChainMap
= { - mantapacific: '0x03ed2D65f2742193CeD99D48EbF1F1D6F12345B6', // does not have a UI - celo: '0x879038d6Fc9F6D5e2BA73188bd078486d77e1156', - ethereum: '0x3965AC3D295641E452E0ea896a086A9cD7C6C5b6', - avalanche: '0x5bE94B17112B8F18eA9Ac8e559377B467556a3c3', - polygon: '0xf9cFD440CfBCfAB8473cc156485B7eE753b2913E', - bsc: '0x7bB2ADeDdC342ffb611dDC073095cc4B8C547170', - arbitrum: '0x03fD5BE9DF85F0017dC7F4DC3068dDF64fffF25e', - optimism: '0xbd7db3821806bc72D223F0AE521Bf82FcBd6Ef4d', - moonbeam: '0x594203849E52BF6ee0E511cD807Ca2D658893e37', - gnosis: '0x0Ac72fBc82c9c39F81242229631dfC38aA13031B', - inevm: '0x77F3863ea99F2360D84d4BA1A2E441857D0357fa', // caldera + injective - base: '0x3949eD0CD036D9FF662d97BD7aC1686051c4aeBF', - scroll: '0x6EeEbB9F7FB18DD5E59F82658c59B846715eD4F7', - polygonzkevm: '0x1610f578D4d77Fc4ae7ce2DD9AA0b98A5Cd0a9b2', - // injective: 'inj1632x8j35kenryam3mkrsez064sqg2y2fr0frzt', - // solana: 'EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3', - blast: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', - linea: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', - mode: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', - ancient8: '0xD2BFA0F0654E3f2139b8cDC56c32eeC54D32b133', - taiko: '0xa4864301d3fa2a3e68256309F9F0F570270a1BD0', - fraxtal: '0x66e9f52800E9F89F0569fddc594Acd5EE609f762', - sei: '0xCed197FBc360C26C19889745Cf73511b71D03d5D', - redstone: '0xa1a50ff5FD859558E1899fEC5C3064483177FA23', - mantle: '0x8aFE6EECc6CcB02aA20DA8Fff7d29aadEBbc2DCd', - bob: '0x9e2fe7723b018d02cDE4f5cC1A9bC9C65b922Fc8', - zetachain: '0x9d399876522Fc5C044D048594de399A2349d6026', - zoramainnet: '0xF87018025575552889062De4b05bBC3DAe35Cd96', - fusemainnet: '0x29a526227CB864C90Cf078d03872da913B473139', - endurance: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5', - zircuit: '0x9e2fe7723b018d02cDE4f5cC1A9bC9C65b922Fc8', - swell: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', - - // Q5, 2024 batch - berachain: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', - - // HyperEVM - hyperevm: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', - - // zksync chains - zeronetwork: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', - abstract: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', - zksync: '0x9C81aA0cC233e9BddeA426F5d395Ab5B65135450', - zklink: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', - treasure: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', - sophon: '0x3D1baf8cA4935f416671640B1Aa9E17E005986eE', -}; - export const icaOwnerChain = 'ethereum'; - -// Found by running: -// yarn tsx ./scripts/keys/get-owner-ica.ts -e mainnet3 --ownerChain ethereum --destinationChains ... -export const icas: Partial< - Record<(typeof supportedChainNames)[number], Address> -> = { - viction: '0x23ed65DE22ac29Ec1C16E75EddB0cE3A187357b4', - inevm: '0xFDF9EDcb2243D51f5f317b9CEcA8edD2bEEE036e', - - // Jul 26, 2024 batch - // ---------------------------------------------------------- - xlayer: '0x1571c482fe9E76bbf50829912b1c746792966369', - cheesechain: '0xEe2C5320BE9bC7A1492187cfb289953b53E3ff1b', - worldchain: '0x1996DbFcFB433737fE404F58D2c32A7f5f334210', - // zircuit: '0x0d67c56E818a02ABa58cd2394b95EF26db999aA3', // already has a safe - - // Aug 5, 2024 batch - // ---------------------------------------------------------- - cyber: '0x984Fe5a45Ac4aaeC4E4655b50f776aB79c9Be19F', - degenchain: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', - kroma: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', - lisk: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', - lukso: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', - merlin: '0xCf867cEaeeE8CBe65C680c734D29d26440931D5b', - metis: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', - mint: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', - proofofplay: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', - real: '0xc761e68BF3A94326FD0D305e3ccb4cdaab2edA19', - sanko: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752', - tangle: '0xCC2aeb692197C7894E561d31ADFE8F79746f7d9F', - xai: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', - // taiko: '0x483D218D2FEe7FC7204ba15F00C7901acbF9697D', // renzo chain - - // Aug 26, 2024 batch - // ---------------------------------------------------------- - astar: '0x6b241544eBa7d89B51b72DF85a0342dAa37371Ca', - bitlayer: '0xe6239316cA60814229E801fF0B9DD71C9CA29008', - coredao: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', - dogechain: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', - flare: '0x689b8DaBBF2f9Fd83D37427A062B30edF463e20b', - molten: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', - shibarium: '0x6348FAe3a8374dbAAaE84EEe5458AE4063Fe2be7', - - // Sep 9, 2024 batch - // ---------------------------------------------------------- - everclear: '0x63B2075372D6d0565F51210D0A296D3c8a773aB6', - oortmainnet: '0x7021D11F9fAe455AB2f45D72dbc2C64d116Cb657', - - // Sep 19, 2024 SAFE --> ICA v1 Migration - // ---------------------------------------------------------- - celo: '0x3fA264c58E1365f1d5963B831b864EcdD2ddD19b', - avalanche: '0x8c8695cD9905e22d84E466804ABE55408A87e595', - polygon: '0xBDD25dd5203fedE33FD631e30fEF9b9eF2598ECE', - moonbeam: '0x480e5b5De6a29F07fe8295C60A1845d36b7BfdE6', - gnosis: '0xD42125a4889A7A36F32d7D12bFa0ae52B0AD106b', - scroll: '0x2a3fe2513F4A7813683d480724AB0a3683EfF8AC', - polygonzkevm: '0x66037De438a59C966214B78c1d377c4e93a5C7D1', - ancient8: '0xA9FD5BeB556AB1859D7625B381110a257f56F98C', - redstone: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752', - mantle: '0x08C880b88335CA3e85Ebb4E461245a7e899863c9', - bob: '0xc99e58b9A4E330e2E4d09e2c94CD3c553904F588', - zetachain: '0xc876B8e63c3ff5b636d9492715BE375644CaD345', - zoramainnet: '0x84977Eb15E0ff5824a6129c789F70e88352C230b', - fusemainnet: '0xbBdb1682B2922C282b56DD716C29db5EFbdb5632', - endurance: '0x470E04D8a3b7938b385093B93CeBd8Db7A1E557C', - // sei: '0xabad187003EdeDd6C720Fc633f929EA632996567', // renzo chain - - // Oct 16, 2024 batch - // ---------------------------------------------------------- - // lumia: '0x418E10Ac9e0b84022d0636228d05bc74172e0e41', - - // Oct 30, 2024 batch - // ---------------------------------------------------------- - apechain: '0xe68b0aB6BB8c11D855556A5d3539524f6DB3bdc6', - arbitrumnova: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - b3: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - fantom: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - gravity: '0x3104ADE26e21AEbdB325321433541DfE8B5dCF23', - harmony: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - kaia: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - morph: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - orderly: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - snaxchain: '0x8965d9f19336EB4e910d5f1B9070205FdBee6837', - - // Nov 8, 2024 batch - // ---------------------------------------------------------- - alephzeroevmmainnet: '0xDE91AC081E12107a033728A287b06B1Fc640A637', - chilizmainnet: '0x54AF0FCDCD58428f8dF3f825267DfB58f2C710eb', - flowmainnet: '0x65528D447C93CC1A1A7186CB4449d9fE0d5C1928', - immutablezkevmmainnet: '0x54AF0FCDCD58428f8dF3f825267DfB58f2C710eb', - metal: '0xf1d25462e1f82BbF25b3ef7A4C94F738a30a968B', - polynomialfi: '0x6ACa36E710dC0C80400090EA0bC55dA913a3D20D', - rarichain: '0xD0A4Ad2Ca0251BBc6541f8c2a594F1A82b67F114', - rootstockmainnet: '0x0C15f7479E0B46868693568a3f1C747Fdec9f17d', - superpositionmainnet: '0x5F17Dc2e1fd1371dc6e694c51f22aBAF8E27667B', - flame: '0x4F3d85360840497Cd1bc34Ca55f27629eee2AA2e', - prom: '0x1cDd3C143387cD1FaE23e2B66bc3F409D073aC3D', - - // Nov 21, 2024 batch - // ---------------------------------------------------------- - boba: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', - duckchain: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', - unichain: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', - vana: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', - bsquared: '0xd9564EaaA68A327933f758A54450D3A0531E60BB', - superseed: '0x29dfa34765e29ea353FC8aB70A19e32a5578E603', - - // Dec 4, 2024 batch - // ---------------------------------------------------------- - // swell: '0xff8326468e7AaB51c53D3569cf7C45Dd54c11687', // already has a safe - lumiaprism: '0xAFfA863646D1bC74ecEC0dB1070f069Af065EBf5', - appchain: '0x4F25DFFd10A6D61C365E1a605d07B2ab0E82A7E6', - - // Dec 13, 2024 batch - // ---------------------------------------------------------- - arthera: '0x962e4E5F5e47e1Ab5361eE0B5108Ebeb9Fa5c99B', - aurora: '0x853f40c807cbb08EDd19B326b9b6A669bf3c274c', - conflux: '0xac8f0e306A126312C273080d149ca01d461603FE', - conwai: '0x5926599B8Aff45f1708b804B30213babdAD78C83', - corn: '0x5926599B8Aff45f1708b804B30213babdAD78C83', - evmos: '0x5926599B8Aff45f1708b804B30213babdAD78C83', - form: '0x5926599B8Aff45f1708b804B30213babdAD78C83', - ink: '0xDde4Ce691d1c0579d48BCdd3491aA71472b6cC38', - rivalz: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', - soneium: '0x5926599B8Aff45f1708b804B30213babdAD78C83', - sonic: '0x5926599B8Aff45f1708b804B30213babdAD78C83', - telos: '0xDde4Ce691d1c0579d48BCdd3491aA71472b6cC38', - - // Jan 13, 2025 batch - // ---------------------------------------------------------- - artela: '0x745CEA119757ea3e27093da590bC91f408bD4448', - guru: '0x825cF3d703F384E4aA846BA72eCf70f1985C91b6', - hemi: '0x8D18CBB212920e5ef070b23b813d82F8981cC276', - nero: '0xbBdb1682B2922C282b56DD716C29db5EFbdb5632', - torus: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', - xpla: '0x24832680dF0468967F413be1C83acfE24154F88D', - - // Feb 3, 2025 batch - // ---------------------------------------------------------- - glue: '0x24832680dF0468967F413be1C83acfE24154F88D', - matchain: '0x66af72e46b3e8DFc19992A2A88C05d9EEFE01ffB', - unitzero: '0x66af72e46b3e8DFc19992A2A88C05d9EEFE01ffB', - trumpchain: '0x56895bFa7f7dFA5743b2A0994B5B0f88b88350F9', - - // Q5, 2024 batch - // ---------------------------------------------------------- - // berachain: '0x56895bFa7f7dFA5743b2A0994B5B0f88b88350F9', - - // Feb 17, 2025 batch - // ---------------------------------------------------------- - bouncebit: '0x8768A14AA6eD2A62C77155501E742376cbE97981', - arcadia: '0xD2344a364b6Dc6B2Fe0f7D836fa344d83056cbaD', - ronin: '0x8768A14AA6eD2A62C77155501E742376cbE97981', - story: '0x8768A14AA6eD2A62C77155501E742376cbE97981', - subtensor: '0x61BFbb5FEC57f5470388A80946F0415138630b9c', - - // Mar 14, 2025 batch - // ---------------------------------------------------------- - infinityvm: '0x35460c519b7C71d49C64F060dF89AbAE463F3b9a', - plume: '0x61BFbb5FEC57f5470388A80946F0415138630b9c', - - // Mar 31, 2025 batch - // ---------------------------------------------------------- - coti: '0x294589E4913A132A49F7830a2A219363A25c0529', - deepbrainchain: '0xeFb7D10Da69A0a913485851ccec6B85cF98d9cab', - // nibiru: '0x40cD75e80d04663FAe0CE30687504074F163C346', // temporary while looking into decimals - opbnb: '0xeFb7D10Da69A0a913485851ccec6B85cF98d9cab', - reactive: '0x9312B04076efA12D69b95bcE7F4F0EA847073E6a', -} as const; - export const DEPLOYER = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; export const ethereumChainOwners: ChainMap = Object.fromEntries( ethereumChainNames.map((local) => { - const owner = icas[local] ?? safes[local] ?? DEPLOYER; + const owner = + regularIcas[local] ?? + regularSafes[local] ?? + awIcas[local] ?? + awSafes[local] ?? + DEPLOYER; return [ local, { owner, ownerOverrides: { - proxyAdmin: upgradeTimelocks[local] ?? owner, + proxyAdmin: owner, validatorAnnounce: DEPLOYER, // unused testRecipient: DEPLOYER, fallbackRoutingHook: DEPLOYER, - // Because of the logic above of setting the owner to the Safe or ICA address, - // the checker/governor tooling does not know what type of owner it is. - // So we need to keep the Safe and ICA addresses somewhere in the config - // to be able to track down which addresses are SAFEs, ICAs, or standard SIGNERS. - ...(safes[local] && { _safeAddress: safes[local] }), - ...(icas[local] && { _icaAddress: icas[local] }), }, }, ]; diff --git a/typescript/infra/config/environments/mainnet3/safe/safeSigners.json b/typescript/infra/config/environments/mainnet3/safe/safeSigners.json deleted file mode 100644 index fca35815faa..00000000000 --- a/typescript/infra/config/environments/mainnet3/safe/safeSigners.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "signers": [ - "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba", - "0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA", - "0x3b7f8f68A4FD0420FeA2F42a1eFc53422f205599", - "0x478be6076f31E9666123B9721D0B6631baD944AF", - "0x003DDD9eEAb62013b7332Ab4CC6B10077a8ca961", - "0xd00d6A31485C93c597D1d8231eeeE0ed17B9844B", - "0x483fd7284A696343FEc0819DDF2cf7E06E8A06E5", - "0x5b73A98165778BCCE72979B4EE3faCdb31728b8E", - "0x5dd9a0814022A61777938263308EBB336174f13D" - ] -} diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index 2939c72a339..53483f0090a 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -383,7 +383,7 @@ export async function getWarpRouteIdsInteractive() { pageSize: 30, }); if (!selection.length) { - console.log('Please select at least one Warp Route ID'); + rootLogger.info('Please select at least one Warp Route ID'); } } @@ -682,7 +682,7 @@ export async function assertCorrectKubeContext(coreConfig: EnvironmentConfig) { !currentKubeContext.endsWith(`${coreConfig.infra.kubernetes.clusterName}`) ) { const cluster = coreConfig.infra.kubernetes.clusterName; - console.error( + rootLogger.error( `Cowardly refusing to deploy using current k8s context ${currentKubeContext}; are you sure you have the right k8s context active?`, `Want clusterName ${cluster}`, `Run gcloud container clusters get-credentials ${cluster} --zone us-east1-c`, diff --git a/typescript/infra/scripts/check/check-owner-ica.ts b/typescript/infra/scripts/check/check-owner-ica.ts index 99d13ed3a2e..f508b793502 100644 --- a/typescript/infra/scripts/check/check-owner-ica.ts +++ b/typescript/infra/scripts/check/check-owner-ica.ts @@ -6,16 +6,21 @@ import { configureRootLogger, eqAddress, isZeroishAddress, + rootLogger, } from '@hyperlane-xyz/utils'; -import { icas } from '../../config/environments/mainnet3/owners.js'; +import { + getGovernanceIcas, + getGovernanceSafes, +} from '../../config/environments/mainnet3/governance/utils.js'; import { chainsToSkip } from '../../src/config/chain.js'; +import { withGovernanceType } from '../../src/governance.js'; import { isEthereumProtocolChain } from '../../src/utils/utils.js'; import { getArgs as getEnvArgs, withChains } from '../agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; function getArgs() { - return withChains(getEnvArgs()).option('ownerChain', { + return withGovernanceType(withChains(getEnvArgs())).option('ownerChain', { type: 'string', description: 'Origin chain where the Safe owner lives', default: 'ethereum', @@ -25,20 +30,25 @@ function getArgs() { async function main() { configureRootLogger(LogFormat.Pretty, LogLevel.Info); - const { environment, ownerChain, chains } = await getArgs(); + const { environment, ownerChain, chains, governanceType } = await getArgs(); const config = getEnvironmentConfig(environment); const multiProvider = await config.getMultiProvider(); - const owner = config.owners[ownerChain].ownerOverrides?._safeAddress; + const owner = getGovernanceSafes(governanceType)[ownerChain]; if (!owner) { - console.error(`No Safe owner found for ${ownerChain}`); + rootLogger.error(`No Safe owner found for ${ownerChain}`); process.exit(1); } - console.log(`Safe owner on ${ownerChain}: ${owner}`); + rootLogger.info(`Safe owner on ${ownerChain}: ${owner}`); const { chainAddresses } = await getHyperlaneCore(environment, multiProvider); - const ica = InterchainAccount.fromAddressesMap(chainAddresses, multiProvider); + const interchainAccountApp = InterchainAccount.fromAddressesMap( + chainAddresses, + multiProvider, + ); + + const icas = getGovernanceIcas(governanceType); const checkOwnerIcaChains = ( chains?.length ? chains : Object.keys(icas) @@ -51,10 +61,11 @@ async function main() { owner: owner, }; const ownerChainInterchainAccountRouter = - ica.contractsMap[ownerChain].interchainAccountRouter.address; + interchainAccountApp.contractsMap[ownerChain].interchainAccountRouter + .address; if (isZeroishAddress(ownerChainInterchainAccountRouter)) { - console.error(`Interchain account router address is zero`); + rootLogger.error(`Interchain account router address is zero`); process.exit(1); } @@ -63,12 +74,12 @@ async function main() { { Expected: Address; Actual: Address } > = {}; for (const chain of checkOwnerIcaChains) { - const expectedAddress = icas[chain as keyof typeof icas]; + const expectedAddress = icas[chain]; if (!expectedAddress) { - console.error(`No expected address found for ${chain}`); + rootLogger.error(`No expected address found for ${chain}`); continue; } - const actualAccount = await ica.getAccount( + const actualAccount = await interchainAccountApp.getAccount( chain, ownerConfig, ownerChainInterchainAccountRouter, @@ -82,16 +93,17 @@ async function main() { } if (Object.keys(mismatchedResults).length > 0) { - console.error('\nMismatched ICAs found:'); + rootLogger.error('\nMismatched ICAs found:'); + // eslint-disable-next-line no-console console.table(mismatchedResults); process.exit(1); } else { - console.log('✅ All ICAs match the expected addresses.'); + rootLogger.info('✅ All ICAs match the expected addresses.'); } process.exit(0); } main().catch((err) => { - console.error('Error:', err); + rootLogger.error('Error:', err); process.exit(1); }); diff --git a/typescript/infra/scripts/keys/get-owner-ica.ts b/typescript/infra/scripts/keys/get-owner-ica.ts index 730e5e7bdda..7728dfbd20c 100644 --- a/typescript/infra/scripts/keys/get-owner-ica.ts +++ b/typescript/infra/scripts/keys/get-owner-ica.ts @@ -6,19 +6,24 @@ import { configureRootLogger, eqAddress, isZeroishAddress, + rootLogger, } from '@hyperlane-xyz/utils'; +import { getGovernanceSafes } from '../../config/environments/mainnet3/governance/utils.js'; +import { icaOwnerChain } from '../../config/environments/mainnet3/owners.js'; import { chainsToSkip } from '../../src/config/chain.js'; +import { withGovernanceType } from '../../src/governance.js'; import { isEthereumProtocolChain } from '../../src/utils/utils.js'; import { getArgs as getEnvArgs, withChains } from '../agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; function getArgs() { - return withChains(getEnvArgs()) + return withGovernanceType(withChains(getEnvArgs())) .option('ownerChain', { type: 'string', description: 'Origin chain where the governing owner lives', demandOption: true, + default: icaOwnerChain, }) .option('owner', { type: 'string', @@ -43,16 +48,19 @@ async function main() { chains, deploy, owner: ownerOverride, + governanceType, } = await getArgs(); const config = getEnvironmentConfig(environment); const multiProvider = await config.getMultiProvider(); - const originOwner = ownerOverride ?? config.owners[ownerChain]?.owner; + // Get the safe owner for the given governance type + const governanceOwner = getGovernanceSafes(governanceType)[ownerChain]; + const originOwner = ownerOverride ?? governanceOwner; if (!originOwner) { throw new Error(`No owner found for ${ownerChain}`); } - console.log(`Governance owner on ${ownerChain}: ${originOwner}`); + rootLogger.info(`Governance owner on ${ownerChain}: ${originOwner}`); const { chainAddresses } = await getHyperlaneCore(environment, multiProvider); const ica = InterchainAccount.fromAddressesMap(chainAddresses, multiProvider); @@ -65,7 +73,7 @@ async function main() { ica.contractsMap[ownerChain].interchainAccountRouter.address; if (isZeroishAddress(ownerChainInterchainAccountRouter)) { - console.error(`Interchain account router address is zero`); + rootLogger.error(`Interchain account router address is zero`); process.exit(1); } @@ -94,7 +102,7 @@ async function main() { ); result.Deployed = eqAddress(account, deployedAccount) ? '✅' : '❌'; if (result.Deployed === '❌') { - console.warn( + rootLogger.warn( `Mismatch between account and deployed account for ${chain}`, ); } @@ -102,7 +110,7 @@ async function main() { return { chain, result }; } catch (error) { - console.error(`Error processing chain ${chain}:`, error); + rootLogger.error(`Error processing chain ${chain}:`, error); return { chain, error }; } }), @@ -112,15 +120,16 @@ async function main() { if (settledResult.status === 'fulfilled') { const { chain, result, error } = settledResult.value; if (error || !result) { - console.error(`Failed to process ${chain}:`, error); + rootLogger.error(`Failed to process ${chain}:`, error); } else { results[chain] = result; } } else { - console.error(`Promise rejected:`, settledResult.reason); + rootLogger.error(`Promise rejected:`, settledResult.reason); } }); + // eslint-disable-next-line no-console console.table(results); process.exit(0); } @@ -128,6 +137,6 @@ async function main() { main() .then() .catch((err) => { - console.error('Error:', err); + rootLogger.error('Error:', err); process.exit(1); }); diff --git a/typescript/infra/scripts/print-mailbox-owner.ts b/typescript/infra/scripts/print-mailbox-owner.ts index 88b04598d3d..a59f56f0a40 100644 --- a/typescript/infra/scripts/print-mailbox-owner.ts +++ b/typescript/infra/scripts/print-mailbox-owner.ts @@ -1,5 +1,3 @@ -import { ethers } from 'ethers'; - import { ChainAddresses } from '@hyperlane-xyz/registry'; import { ChainMap, @@ -8,15 +6,14 @@ import { HyperlaneCore, } from '@hyperlane-xyz/sdk'; import { - Address, - eqAddressEvm, objFilter, objMap, promiseObjAll, + rootLogger, } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts.js'; -import { DeployEnvironment } from '../src/config/environment.js'; +import { Owner, determineGovernanceType } from '../src/governance.js'; import { Role } from '../src/roles.js'; import { filterRemoteDomainMetadata, @@ -33,19 +30,6 @@ import { } from './agent-utils.js'; import { getEnvironmentConfig } from './core-utils.js'; -const DEPLOYERS: Record = { - mainnet3: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - testnet4: '0xfaD1C94469700833717Fa8a3017278BC1cA8031C', - test: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', -}; - -export enum Owner { - ICA = 'ICA', - SAFE = 'SAFE', - DEPLOYER = 'DEPLOYER KEY', - UNKNOWN = 'UNKNOWN', -} - async function main() { const { context = Contexts.Hyperlane, @@ -80,47 +64,32 @@ async function main() { isEthereumProtocolChain(chain), ); - const deployer = DEPLOYERS[environment]; - const mailboxOwners = await promiseObjAll( objMap( evmContractsMap, async (chain: string, contracts: HyperlaneContracts) => { - // get possible owners from config - const ownerConfig = envConfig.owners[chain]; - const safeAddress = - ownerConfig.ownerOverrides?._safeAddress ?? - ethers.constants.AddressZero; - const icaAddress = - ownerConfig.ownerOverrides?._icaAddress ?? - ethers.constants.AddressZero; - // get actual onchain owner const ownerAddress = await contracts.mailbox.owner(); - - // determine owner type - let ownerType = Owner.UNKNOWN; - if (eqAddressEvm(ownerAddress, deployer)) { - ownerType = Owner.DEPLOYER; - } else if (eqAddressEvm(ownerAddress, safeAddress)) { - ownerType = Owner.SAFE; - } else if (eqAddressEvm(ownerAddress, icaAddress)) { - ownerType = Owner.ICA; - } - + const { ownerType, governanceType } = await determineGovernanceType( + chain, + ownerAddress, + ); return { owner: ownerAddress, type: ownerType, + governance: governanceType, }; }, ), ); + // eslint-disable-next-line no-console console.table(mailboxOwners); const totalChains = Object.keys(mailboxOwners).length; - console.log(`\nTotal chains: ${totalChains}`); + rootLogger.info(`\nTotal chains: ${totalChains}`); + // eslint-disable-next-line no-console console.table( Object.values(Owner).map((ownerType) => ({ 'Owner Type': ownerType, @@ -134,6 +103,6 @@ async function main() { main() .then() .catch((e) => { - console.error(e); + rootLogger.error(e); process.exit(1); }); diff --git a/typescript/infra/scripts/safes/create-safe.ts b/typescript/infra/scripts/safes/create-safe.ts index febf00cb570..f21ae84f86f 100644 --- a/typescript/infra/scripts/safes/create-safe.ts +++ b/typescript/infra/scripts/safes/create-safe.ts @@ -1,35 +1,42 @@ -import { EthersAdapter, SafeFactory } from '@safe-global/protocol-kit'; -import { SafeAccountConfig } from '@safe-global/protocol-kit'; +import { + EthersAdapter, + SafeAccountConfig, + SafeFactory, +} from '@safe-global/protocol-kit'; import { ethers } from 'ethers'; +import { rootLogger } from '@hyperlane-xyz/utils'; + import { Contexts } from '../../config/contexts.js'; -import { getChain } from '../../config/registry.js'; +import { getGovernanceSigners } from '../../config/environments/mainnet3/governance/utils.js'; +import { withGovernanceType } from '../../src/governance.js'; import { Role } from '../../src/roles.js'; -import { readJSONAtPath } from '../../src/utils/utils.js'; import { getArgs, - getKeyForRole, withChainRequired, withSafeHomeUrlRequired, withThreshold, } from '../agent-utils.js'; - -const OWNERS_FILE_PATH = 'config/environments/mainnet3/safe/safeSigners.json'; +import { getEnvironmentConfig } from '../core-utils.js'; async function main() { - const { chain, safeHomeUrl, threshold } = await withThreshold( - withSafeHomeUrlRequired(withChainRequired(getArgs())), - ).argv; - - const chainMetadata = await getChain(chain); - const rpcUrls = chainMetadata.rpcUrls; - const deployerPrivateKey = await getDeployerPrivateKey(); - - // Create ethers signer with deployer key - const provider = new ethers.providers.JsonRpcProvider(rpcUrls[0].http); + const { chain, safeHomeUrl, threshold, governanceType } = + await withGovernanceType( + withThreshold(withSafeHomeUrlRequired(withChainRequired(getArgs()))), + ).argv; + + const envConfig = getEnvironmentConfig('mainnet3'); + const multiProvider = await envConfig.getMultiProvider( + Contexts.Hyperlane, + Role.Deployer, + true, + [chain], + ); + + const signer = multiProvider.getSigner(chain); const ethAdapter = new EthersAdapter({ ethers, - signerOrProvider: new ethers.Wallet(deployerPrivateKey, provider), + signerOrProvider: signer, }); let safeFactory; @@ -38,31 +45,30 @@ async function main() { ethAdapter, }); } catch (e) { - console.error(`Error initializing SafeFactory: ${e}`); + rootLogger.error(`Error initializing SafeFactory: ${e}`); process.exit(1); } - const ownersConfig = readJSONAtPath(OWNERS_FILE_PATH); - const owners = ownersConfig.signers; - + const { signers, threshold: defaultThreshold } = + getGovernanceSigners(governanceType); const safeAccountConfig: SafeAccountConfig = { - owners, - threshold, + owners: signers, + threshold: threshold ?? defaultThreshold, }; let safe; try { safe = await safeFactory.deploySafe({ safeAccountConfig }); } catch (e) { - console.error(`Error deploying Safe: ${e}`); + rootLogger.error(`Error deploying Safe: ${e}`); process.exit(1); } const safeAddress = await safe.getAddress(); - console.log(`Safe address: ${safeAddress}`); - console.log(`Safe url: ${safeHomeUrl}/home?safe=${chain}:${safeAddress}`); - console.log('url may not be correct, please check by following the link'); + rootLogger.info(`Safe address: ${safeAddress}`); + rootLogger.info(`Safe url: ${safeHomeUrl}/home?safe=${chain}:${safeAddress}`); + rootLogger.info('url may not be correct, please check by following the link'); try { // TODO: check https://app.safe.global for officially supported chains, filter by chain id @@ -70,31 +76,24 @@ async function main() { 'https://', 'https://gateway.', )}/v1/chains`; - console.log(`Fetching chain data from ${chainsUrl}`); + rootLogger.info(`Fetching chain data from ${chainsUrl}`); const response = await fetch(chainsUrl); const resultsJson = await response.json(); const transactionService = resultsJson.results[0].transactionService; - console.log(`Chains: ${JSON.stringify(transactionService)}`); - console.log( + rootLogger.info(`Chains: ${JSON.stringify(transactionService)}`); + rootLogger.info( `Add the transaction service url ${transactionService} as gnosisSafeTransactionServiceUrl to the metadata.yml in the registry`, ); } catch (e) { - console.error(`Could not fetch safe tx service url: ${e}`); + rootLogger.error(`Could not fetch safe tx service url: ${e}`); } } -const getDeployerPrivateKey = async () => { - const key = getKeyForRole('mainnet3', Contexts.Hyperlane, Role.Deployer); - await key.fetch(); - - return key.privateKey; -}; - main() .then() .catch((e) => { - console.error(e); + rootLogger.error(e); process.exit(1); }); diff --git a/typescript/infra/scripts/safes/delete-pending-txs.ts b/typescript/infra/scripts/safes/delete-pending-txs.ts index 8fe9187a589..f452d76a47e 100644 --- a/typescript/infra/scripts/safes/delete-pending-txs.ts +++ b/typescript/infra/scripts/safes/delete-pending-txs.ts @@ -1,17 +1,22 @@ import yargs from 'yargs'; +import { rootLogger } from '@hyperlane-xyz/utils'; + import { Contexts } from '../../config/contexts.js'; -import { safes } from '../../config/environments/mainnet3/owners.js'; +import { getGovernanceSafes } from '../../config/environments/mainnet3/governance/utils.js'; +import { withGovernanceType } from '../../src/governance.js'; import { Role } from '../../src/roles.js'; import { deleteAllPendingSafeTxs } from '../../src/utils/safe.js'; import { withChains } from '../agent-utils.js'; import { getEnvironmentConfig } from '../core-utils.js'; async function main() { - const { chains } = await withChains(yargs(process.argv.slice(2))).argv; + const { chains, governanceType } = await withGovernanceType( + withChains(yargs(process.argv.slice(2))), + ).argv; if (!chains || chains.length === 0) { - console.error('No chains provided'); + rootLogger.error('No chains provided'); process.exit(1); } @@ -23,11 +28,16 @@ async function main() { chains, ); + const safes = getGovernanceSafes(governanceType); + for (const chain of chains) { try { await deleteAllPendingSafeTxs(chain, multiProvider, safes[chain]); } catch (error) { - console.error(`Error deleting pending transactions for ${chain}:`, error); + rootLogger.error( + `Error deleting pending transactions for ${chain}:`, + error, + ); } } } @@ -35,6 +45,6 @@ async function main() { main() .then() .catch((e) => { - console.error(e); + rootLogger.error(e); process.exit(1); }); diff --git a/typescript/infra/scripts/safes/delete-tx.ts b/typescript/infra/scripts/safes/delete-tx.ts index bca35efcbe3..0ecfe2f4483 100644 --- a/typescript/infra/scripts/safes/delete-tx.ts +++ b/typescript/infra/scripts/safes/delete-tx.ts @@ -1,28 +1,33 @@ import yargs from 'yargs'; +import { rootLogger } from '@hyperlane-xyz/utils'; + import { Contexts } from '../../config/contexts.js'; -import { safes } from '../../config/environments/mainnet3/owners.js'; +import { getGovernanceSafes } from '../../config/environments/mainnet3/governance/utils.js'; +import { withGovernanceType } from '../../src/governance.js'; import { Role } from '../../src/roles.js'; import { deleteSafeTx } from '../../src/utils/safe.js'; import { withChains } from '../agent-utils.js'; import { getEnvironmentConfig } from '../core-utils.js'; async function main() { - const { chains, tx } = await withChains( - yargs(process.argv.slice(2)).option('tx', { - type: 'string', - description: 'Transaction hash to delete', - demandOption: true, - }), + const { chains, tx, governanceType } = await withGovernanceType( + withChains( + yargs(process.argv.slice(2)).option('tx', { + type: 'string', + description: 'Transaction hash to delete', + demandOption: true, + }), + ), ).argv; if (!chains || chains.length === 0) { - console.error('No chains provided'); + rootLogger.error('No chains provided'); process.exit(1); } if (!tx) { - console.error('No transaction hash provided'); + rootLogger.error('No transaction hash provided'); process.exit(1); } @@ -34,11 +39,12 @@ async function main() { chains, ); + const safes = getGovernanceSafes(governanceType); for (const chain of chains) { try { await deleteSafeTx(chain, multiProvider, safes[chain], tx); } catch (error) { - console.error(`Error deleting transaction ${tx} for ${chain}:`, error); + rootLogger.error(`Error deleting transaction ${tx} for ${chain}:`, error); } } } @@ -46,6 +52,6 @@ async function main() { main() .then() .catch((e) => { - console.error(e); + rootLogger.error(e); process.exit(1); }); diff --git a/typescript/infra/scripts/safes/get-pending-txs.ts b/typescript/infra/scripts/safes/get-pending-txs.ts index ead1b69c74c..89ce203eddb 100644 --- a/typescript/infra/scripts/safes/get-pending-txs.ts +++ b/typescript/infra/scripts/safes/get-pending-txs.ts @@ -10,7 +10,8 @@ import { } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; -import { safes } from '../../config/environments/mainnet3/owners.js'; +import { getGovernanceSafes } from '../../config/environments/mainnet3/governance/utils.js'; +import { withGovernanceType } from '../../src/governance.js'; import { Role } from '../../src/roles.js'; import { SafeTxStatus, @@ -21,11 +22,10 @@ import { withChains } from '../agent-utils.js'; import { getEnvironmentConfig } from '../core-utils.js'; async function main() { - const safeChains = Object.keys(safes); configureRootLogger(LogFormat.Pretty, LogLevel.Info); - const { chains, fullTxHash } = await withChains( - yargs(process.argv.slice(2)), - safeChains, + + const { chains, fullTxHash, governanceType } = await withGovernanceType( + withChains(yargs(process.argv.slice(2))), ) .describe( 'fullTxHash', @@ -34,6 +34,9 @@ async function main() { .boolean('fullTxHash') .default('fullTxHash', false).argv; + const safes = getGovernanceSafes(governanceType); + const safeChains = Object.keys(safes); + const chainsToCheck = chains || safeChains; if (chainsToCheck.length === 0) { rootLogger.error('No chains provided'); diff --git a/typescript/infra/scripts/safes/governance/check-safe-signers.ts b/typescript/infra/scripts/safes/governance/check-safe-signers.ts new file mode 100644 index 00000000000..d0fdb2630cb --- /dev/null +++ b/typescript/infra/scripts/safes/governance/check-safe-signers.ts @@ -0,0 +1,149 @@ +import Safe from '@safe-global/protocol-kit'; +import yargs from 'yargs'; + +import { rootLogger } from '@hyperlane-xyz/utils'; + +import { Contexts } from '../../../config/contexts.js'; +import { + getGovernanceSafes, + getGovernanceSigners, +} from '../../../config/environments/mainnet3/governance/utils.js'; +import { GovernanceType, withGovernanceType } from '../../../src/governance.js'; +import { Role } from '../../../src/roles.js'; +import { getOwnerChanges, getSafeAndService } from '../../../src/utils/safe.js'; +import { getEnvironmentConfig } from '../../core-utils.js'; + +enum SafeConfigViolationType { + missingOwners = 'missingOwners', + unexpectedOwners = 'unexpectedOwners', + thresholdMismatch = 'thresholdMismatch', +} + +interface SafeConfigViolation { + type: SafeConfigViolationType; + chain: string; + safeAddress: string; + owners?: string[]; + expected?: string; + actual?: string; +} + +async function main() { + const { governanceType = GovernanceType.Regular } = await withGovernanceType( + yargs(process.argv.slice(2)), + ).argv; + + const violations: SafeConfigViolation[] = []; + + const { signers, threshold } = getGovernanceSigners(governanceType); + const safes = getGovernanceSafes(governanceType); + + const multiProvider = await getEnvironmentConfig('mainnet3').getMultiProvider( + Contexts.Hyperlane, + Role.Deployer, + true, + Object.keys(safes), + ); + + for (const [chain, safeAddress] of Object.entries(safes)) { + let safeSdk: Safe.default; + try { + ({ safeSdk } = await getSafeAndService( + chain, + multiProvider, + safeAddress, + )); + } catch (error) { + rootLogger.error(`[${chain}] could not get safe: ${error}`); + continue; + } + + const currentOwners = await safeSdk.getOwners(); + const currentThreshold = await safeSdk.getThreshold(); + const expectedOwners = signers; + const { ownersToRemove, ownersToAdd } = await getOwnerChanges( + currentOwners, + expectedOwners, + ); + + if (ownersToRemove.length > 0) { + violations.push({ + type: SafeConfigViolationType.unexpectedOwners, + chain, + safeAddress, + owners: ownersToRemove, + }); + } + + if (ownersToAdd.length > 0) { + violations.push({ + type: SafeConfigViolationType.missingOwners, + chain, + safeAddress, + owners: ownersToAdd, + }); + } + + if (threshold !== currentThreshold) { + violations.push({ + type: SafeConfigViolationType.thresholdMismatch, + chain, + safeAddress, + expected: threshold.toString(), + actual: currentThreshold.toString(), + }); + } + } + + if (violations.length > 0) { + // Display threshold mismatches in a table + const thresholdViolations = violations.filter( + (v) => v.type === SafeConfigViolationType.thresholdMismatch, + ); + if (thresholdViolations.length > 0) { + console.table(thresholdViolations, [ + 'chain', + 'safeAddress', + 'expected', + 'actual', + ]); + } + + // Group other violations by chain + const violationsByChain = violations + .filter((v) => v.type !== SafeConfigViolationType.thresholdMismatch) + .reduce((acc, v) => { + if (!acc[v.chain]) acc[v.chain] = []; + acc[v.chain].push(v); + return acc; + }, {} as Record); + + // Display chain-specific violations as bulleted lists + for (const [chain, chainViolations] of Object.entries(violationsByChain)) { + rootLogger.info(`\nChain: ${chain}`); + + const missingSigs = chainViolations.find( + (v) => v.type === SafeConfigViolationType.missingOwners, + ); + if (missingSigs?.owners?.length) { + rootLogger.info('Missing signers:'); + missingSigs.owners.forEach((owner) => rootLogger.info(` • ${owner}`)); + } + + const extraSigs = chainViolations.find( + (v) => v.type === SafeConfigViolationType.unexpectedOwners, + ); + if (extraSigs?.owners?.length) { + rootLogger.info('Extraneous signers:'); + extraSigs.owners.forEach((owner) => rootLogger.info(` • ${owner}`)); + } + } + } else { + rootLogger.info('No violations found'); + } +} + +main().catch((error) => { + rootLogger.error(error); + process.exit(1); +}); diff --git a/typescript/infra/scripts/safes/governance/update-signers.ts b/typescript/infra/scripts/safes/governance/update-signers.ts index c2cd9da8402..d51025d0af7 100644 --- a/typescript/infra/scripts/safes/governance/update-signers.ts +++ b/typescript/infra/scripts/safes/governance/update-signers.ts @@ -5,30 +5,34 @@ import { ChainName } from '@hyperlane-xyz/sdk'; import { rootLogger } from '@hyperlane-xyz/utils'; import { Contexts } from '../../../config/contexts.js'; -import { regularSafes } from '../../../config/environments/mainnet3/governance/safe/regular.js'; import { - SIGNERS, - THRESHOLD, -} from '../../../config/environments/mainnet3/governance/safe/safeConfig.js'; + getGovernanceSafes, + getGovernanceSigners, +} from '../../../config/environments/mainnet3/governance/utils.js'; import { AnnotatedCallData } from '../../../src/govern/HyperlaneAppGovernor.js'; import { SafeMultiSend } from '../../../src/govern/multisend.js'; +import { GovernanceType, withGovernanceType } from '../../../src/governance.js'; import { Role } from '../../../src/roles.js'; import { getSafeAndService, updateSafeOwner } from '../../../src/utils/safe.js'; import { withPropose } from '../../agent-utils.js'; import { getEnvironmentConfig } from '../../core-utils.js'; async function main() { - const { propose } = await withPropose(yargs(process.argv.slice(2))).argv; + const { propose, governanceType = GovernanceType.Regular } = + await withGovernanceType(withPropose(yargs(process.argv.slice(2)))).argv; + + const { signers, threshold } = getGovernanceSigners(governanceType); + const safes = getGovernanceSafes(governanceType); const envConfig = getEnvironmentConfig('mainnet3'); const multiProvider = await envConfig.getMultiProvider( Contexts.Hyperlane, Role.Deployer, true, - Object.keys(regularSafes), + Object.keys(safes), ); - for (const [chain, safeAddress] of Object.entries(regularSafes)) { + for (const [chain, safeAddress] of Object.entries(safes)) { let safeSdk: Safe.default; try { ({ safeSdk } = await getSafeAndService( @@ -55,7 +59,11 @@ async function main() { let transactions: AnnotatedCallData[]; try { - transactions = await updateSafeOwner(safeSdk, SIGNERS, THRESHOLD); + transactions = await updateSafeOwner({ + safeSdk, + owners: signers, + threshold, + }); } catch (error) { rootLogger.error(`[${chain}] could not update safe owner: ${error}`); continue; diff --git a/typescript/infra/scripts/safes/migrate-to-typed-signatures.ts b/typescript/infra/scripts/safes/migrate-to-typed-signatures.ts index d2f7dd5540d..06a455980dc 100644 --- a/typescript/infra/scripts/safes/migrate-to-typed-signatures.ts +++ b/typescript/infra/scripts/safes/migrate-to-typed-signatures.ts @@ -13,7 +13,7 @@ import { } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; -import { safes } from '../../config/environments/mainnet3/owners.js'; +import { awSafes as safes } from '../../config/environments/mainnet3/governance/safe/aw.js'; import { Role } from '../../src/roles.js'; import { createSafeTransaction, diff --git a/typescript/infra/scripts/safes/parse-txs.ts b/typescript/infra/scripts/safes/parse-txs.ts index 4483dc271ae..f9bdbc40f66 100644 --- a/typescript/infra/scripts/safes/parse-txs.ts +++ b/typescript/infra/scripts/safes/parse-txs.ts @@ -11,7 +11,11 @@ import { stringifyObject, } from '@hyperlane-xyz/utils'; -import { safes } from '../../config/environments/mainnet3/owners.js'; +import { + getGovernanceIcas, + getGovernanceSafes, +} from '../../config/environments/mainnet3/governance/utils.js'; +import { withGovernanceType } from '../../src/governance.js'; import { GovernTransactionReader } from '../../src/tx/govern-transaction-reader.js'; import { getPendingTxsForChains, getSafeTx } from '../../src/utils/safe.js'; import { writeYamlAtPath } from '../../src/utils/utils.js'; @@ -19,11 +23,11 @@ import { withChains } from '../agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; const environment = 'mainnet3'; -const safeChains = Object.keys(safes); async function main() { - const { chains } = await withChains(yargs(process.argv.slice(2)), safeChains) - .argv; + const { chains, governanceType } = await withGovernanceType( + withChains(yargs(process.argv.slice(2))), + ).argv; configureRootLogger(LogFormat.Pretty, LogLevel.Info); const config = getEnvironmentConfig(environment); @@ -33,16 +37,24 @@ async function main() { const registry = await config.getRegistry(); const warpRoutes = await registry.getWarpRoutes(); + // Get the relevant set of governance safes and icas + const safes = getGovernanceSafes(governanceType); + const icas = getGovernanceIcas(governanceType); + + // Initialize the transaction reader with the relevant safes and icas const reader = new GovernTransactionReader( environment, multiProvider, chainAddresses, config.core, warpRoutes, + safes, + icas, ); + // Get the pending transactions for the relevant chains, for the chosen governance type const pendingTxs = await getPendingTxsForChains( - !chains || chains.length === 0 ? safeChains : chains, + !chains || chains.length === 0 ? Object.keys(safes) : chains, multiProvider, safes, ); diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index 90761e1adc2..4166cdb5eaa 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -14,6 +14,12 @@ import { mustGet, objKeys, objMap, objMerge } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { environments } from '../../config/environments/index.js'; +import { awIcas } from '../../config/environments/mainnet3/governance/ica/aw.js'; +import { awSafes } from '../../config/environments/mainnet3/governance/safe/aw.js'; +import { + DEPLOYER, + upgradeTimelocks, +} from '../../config/environments/mainnet3/owners.js'; import { getHyperlaneCore } from '../../scripts/core-utils.js'; import { CloudAgentKey } from '../agents/keys.js'; import { Role } from '../roles.js'; @@ -89,7 +95,28 @@ export async function getRouterConfigsForAllVms( envConfig.environment, multiProvider, ); - const evmRouterConfig = core.getRouterConfig(envConfig.owners); + + // Core deployment governance is changing. + // For now stick with the previous ownership setup. + const ownerConfigs: ChainMap = objMap( + envConfig.owners, + (chain, _) => { + const owner = awIcas[chain] ?? awSafes[chain] ?? DEPLOYER; + return { + owner, + ownerOverrides: { + proxyAdmin: upgradeTimelocks[chain] ?? owner, + validatorAnnounce: DEPLOYER, + testRecipient: DEPLOYER, + fallbackRoutingHook: DEPLOYER, + ...(awSafes[chain] && { _safeAddress: awSafes[chain] }), + ...(awIcas[chain] && { _icaAddress: awIcas[chain] }), + }, + }; + }, + ); + + const evmRouterConfig = core.getRouterConfig(ownerConfigs); const allRouterConfigs: ChainMap = objMap( chainAddresses, diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index ecbd9c9ddc4..a9f90cf5ec7 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -2,8 +2,7 @@ import chalk from 'chalk'; import { BigNumber } from 'ethers'; import prompts from 'prompts'; -import { ProxyAdmin__factory } from '@hyperlane-xyz/core'; -import { Ownable__factory } from '@hyperlane-xyz/core'; +import { Ownable__factory, ProxyAdmin__factory } from '@hyperlane-xyz/core'; import { ChainMap, ChainName, @@ -14,9 +13,8 @@ import { OwnableConfig, OwnerViolation, ProxyAdminViolation, + canProposeSafeTransactions, } from '@hyperlane-xyz/sdk'; -// @ts-ignore -import { canProposeSafeTransactions } from '@hyperlane-xyz/sdk'; import { Address, CallData, @@ -24,9 +22,11 @@ import { eqAddress, objMap, retryAsync, + rootLogger, } from '@hyperlane-xyz/utils'; -import { getSafeAndService, updateSafeOwner } from '../utils/safe.js'; +import { getGovernanceSafes } from '../../config/environments/mainnet3/governance/utils.js'; +import { GovernanceType, determineGovernanceType } from '../governance.js'; import { ManualMultiSend, @@ -46,6 +46,7 @@ export type AnnotatedCallData = CallData & { description: string; expandedDescription?: string; icaTargetChain?: ChainName; + governanceType?: GovernanceType; }; export type InferredCall = { @@ -106,9 +107,17 @@ export abstract class HyperlaneAppGovernor< protected async sendCalls(chain: ChainName, requestConfirmation: boolean) { const calls = this.calls[chain] || []; - console.log(`\nFound ${calls.length} transactions for ${chain}`); - const filterCalls = (submissionType: SubmissionType) => - calls.filter((call) => call.submissionType == submissionType); + rootLogger.info(`\nFound ${calls.length} transactions for ${chain}`); + const filterCalls = ( + submissionType: SubmissionType, + governanceType?: GovernanceType, + ) => + calls.filter( + (call) => + call.submissionType == submissionType && + (governanceType === undefined || + call.governanceType == governanceType), + ); const summarizeCalls = async ( submissionType: SubmissionType, callsForSubmissionType: AnnotatedCallData[], @@ -117,17 +126,17 @@ export abstract class HyperlaneAppGovernor< return false; } - console.log( + rootLogger.info( `${SubmissionType[submissionType]} calls: ${callsForSubmissionType.length}`, ); callsForSubmissionType.map( ({ icaTargetChain, description, expandedDescription, ...call }) => { // Print a blank line to separate calls - console.log(''); + rootLogger.info(''); // Print the ICA call header if it exists if (icaTargetChain) { - console.log( + rootLogger.info( chalk.bold( `> INTERCHAIN ACCOUNT CALL: ${chain} -> ${icaTargetChain}`, ), @@ -135,14 +144,14 @@ export abstract class HyperlaneAppGovernor< } // Print the call details - console.log(chalk.bold(`> ${description.trimEnd()}`)); + rootLogger.info(chalk.bold(`> ${description.trimEnd()}`)); if (expandedDescription) { - console.info(chalk.gray(`${expandedDescription.trimEnd()}`)); + rootLogger.info(chalk.gray(`${expandedDescription.trimEnd()}`)); } - console.info(chalk.gray(`to: ${call.to}`)); - console.info(chalk.gray(`data: ${call.data}`)); - console.info(chalk.gray(`value: ${call.value}`)); + rootLogger.info(chalk.gray(`to: ${call.to}`)); + rootLogger.info(chalk.gray(`data: ${call.data}`)); + rootLogger.info(chalk.gray(`value: ${call.value}`)); }, ); if (!requestConfirmation) return true; @@ -160,25 +169,10 @@ export abstract class HyperlaneAppGovernor< const sendCallsForType = async ( submissionType: SubmissionType, multiSend: MultiSend, + governanceType?: GovernanceType, ) => { const callsForSubmissionType = []; - const filteredCalls = filterCalls(submissionType); - - // If calls are being submitted via a safe, we need to check for any safe owner changes first - if (submissionType === SubmissionType.SAFE) { - try { - const { safeSdk } = await getSafeAndService( - chain, - this.checker.multiProvider, - (multiSend as SafeMultiSend).safeAddress, - ); - const updateOwnerCalls = await updateSafeOwner(safeSdk); - callsForSubmissionType.push(...updateOwnerCalls); - } catch (error) { - // Catch but don't throw because we want to try submitting any remaining calls - console.error(`Error updating safe owner: ${error}`); - } - } + const filteredCalls = filterCalls(submissionType, governanceType); // Add the filtered calls to the calls for submission type callsForSubmissionType.push(...filteredCalls); @@ -190,7 +184,7 @@ export abstract class HyperlaneAppGovernor< callsForSubmissionType, ); if (confirmed) { - console.info( + rootLogger.info( chalk.italic( `Submitting calls on ${chain} via ${SubmissionType[submissionType]}`, ), @@ -204,12 +198,12 @@ export abstract class HyperlaneAppGovernor< })), ); } catch (error) { - console.error( + rootLogger.error( chalk.red(`Error submitting calls on ${chain}: ${error}`), ); } } else { - console.info( + rootLogger.info( chalk.italic( `Skipping submission of calls on ${chain} via ${SubmissionType[submissionType]}`, ), @@ -218,31 +212,36 @@ export abstract class HyperlaneAppGovernor< } }; + // Do all SIGNER calls first await sendCallsForType( SubmissionType.SIGNER, new SignerMultiSend(this.checker.multiProvider, chain), ); - const safeOwner = - this.checker.configMap[chain].ownerOverrides?._safeAddress; - if (safeOwner) { - await retryAsync( - () => - sendCallsForType( - SubmissionType.SAFE, - new SafeMultiSend(this.checker.multiProvider, chain, safeOwner), - ), - 10, - ); + // Then propose transactions on safes for all governance types + for (const governanceType of Object.values(GovernanceType)) { + const safeOwner = getGovernanceSafes(governanceType)[chain]; + if (safeOwner) { + await retryAsync( + () => + sendCallsForType( + SubmissionType.SAFE, + new SafeMultiSend(this.checker.multiProvider, chain, safeOwner), + governanceType, + ), + 10, + ); + } } + // Then finally submit remaining calls manually await sendCallsForType(SubmissionType.MANUAL, new ManualMultiSend(chain)); this.printSeparator(); } private printSeparator() { - console.log( + rootLogger.info( `-------------------------------------------------------------------------------------------------------------------`, ); } @@ -299,7 +298,7 @@ export abstract class HyperlaneAppGovernor< ), ); } catch (error) { - console.error( + rootLogger.error( chalk.red( `Error inferring call submission types for chain ${chain}: ${error}`, ), @@ -354,7 +353,7 @@ export abstract class HyperlaneAppGovernor< // If the account's owner is not the ICA router, default to manual submission if (!eqAddress(localOwner, this.interchainAccount.routerAddress(chain))) { - console.info( + rootLogger.info( chalk.gray( `Account's owner ${localOwner} is not ICA router. Defaulting to manual submission.`, ), @@ -374,7 +373,7 @@ export abstract class HyperlaneAppGovernor< const origin = this.interchainAccount.multiProvider.getChainName( accountConfig.origin, ); - console.info( + rootLogger.info( chalk.gray( `Inferred call for ICA remote owner ${bytes32ToAddress( accountConfig.owner, @@ -405,6 +404,11 @@ export abstract class HyperlaneAppGovernor< }; } + const { governanceType } = await determineGovernanceType( + origin, + accountConfig.owner, + ); + // If the call to the remote ICA is valid, infer the submission type const { description, expandedDescription } = call; const encodedCall: AnnotatedCallData = { @@ -413,6 +417,7 @@ export abstract class HyperlaneAppGovernor< value: callRemote.value, description, expandedDescription, + governanceType, }; // Try to infer the submission type for the ICA call @@ -512,42 +517,54 @@ export abstract class HyperlaneAppGovernor< try { await multiProvider.estimateGas(chain, call, submitterAddress); return true; - } catch (e) { + } catch (_) { return false; } }; // Check if the transaction will succeed with the SIGNER if (await checkTransactionSuccess(chain, signerAddress)) { - return { type: SubmissionType.SIGNER, chain, call }; + return { + type: SubmissionType.SIGNER, + chain, + call, + }; } // Check if the transaction will succeed with a SAFE - const safeAddress = - this.checker.configMap[chain].ownerOverrides?._safeAddress; - if (typeof safeAddress === 'string') { - // Check if the safe can propose transactions - const canProposeSafe = await this.checkSafeProposalEligibility( - chain, - signerAddress, - safeAddress, - ); - if ( - canProposeSafe && - (await checkTransactionSuccess(chain, safeAddress)) - ) { - // If the transaction will succeed with the safe, return the inferred call - return { type: SubmissionType.SAFE, chain, call }; + // Need to check all governance types because the safe address is different for each type + for (const governanceType of Object.values(GovernanceType)) { + const safeAddress = getGovernanceSafes(governanceType)[chain]; + if (typeof safeAddress === 'string') { + // Check if the safe can propose transactions + const canProposeSafe = await this.checkSafeProposalEligibility( + chain, + signerAddress, + safeAddress, + ); + if ( + canProposeSafe && + (await checkTransactionSuccess(chain, safeAddress)) + ) { + call.governanceType = governanceType; + // If the transaction will succeed with the safe, return the inferred call + return { type: SubmissionType.SAFE, chain, call }; + } } } // If we're not already an ICA call, try to infer an ICA call + // We'll also infer the governance type for the ICA call if (!isICACall) { return this.inferICAEncodedSubmissionType(chain, call); } // If the transaction will not succeed with SIGNER, SAFE or ICA, default to MANUAL submission - return { type: SubmissionType.MANUAL, chain, call }; + return { + type: SubmissionType.MANUAL, + chain, + call, + }; } private async checkSubmitterBalance( @@ -559,7 +576,7 @@ export abstract class HyperlaneAppGovernor< .getProvider(chain) .getBalance(submitterAddress); if (submitterBalance.lt(requiredValue)) { - console.warn( + rootLogger.warn( chalk.yellow( `Submitter ${submitterAddress} has an insufficient balance for the call and is likely to fail. Balance: ${submitterBalance}, Balance required: ${requiredValue}`, ), @@ -590,7 +607,7 @@ export abstract class HyperlaneAppGovernor< errorMessage.includes('invalid multisend contract address') || errorMessage.includes('invalid multisendcallonly contract address') ) { - console.warn(chalk.yellow(`Invalid contract: ${errorMessage}.`)); + rootLogger.warn(chalk.yellow(`Invalid contract: ${errorMessage}.`)); return false; } @@ -599,7 +616,7 @@ export abstract class HyperlaneAppGovernor< errorMessage.includes('service unavailable') || errorMessage.includes('too many requests') ) { - console.warn( + rootLogger.warn( chalk.yellow( `Safe service error for ${safeAddress} on ${chain}: ${errorMessage}. ${retries} retries left.`, ), @@ -618,7 +635,7 @@ export abstract class HyperlaneAppGovernor< } // Handle all other errors - console.error( + rootLogger.error( chalk.red( `Failed to determine if signer can propose safe transactions on ${chain}. Error: ${error}`, ), diff --git a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts index f4b9c745591..cd0667b52e3 100644 --- a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts @@ -15,6 +15,7 @@ import { ProxyAdminViolation, ViolationType, } from '@hyperlane-xyz/sdk'; +import { rootLogger } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from '../govern/HyperlaneAppGovernor.js'; @@ -77,7 +78,7 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor< return this.handleMailboxViolation(violation as MailboxViolation); } case CoreViolationType.ValidatorAnnounce: { - console.warn(chalk.yellow('Ignoring ValidatorAnnounce violation')); + rootLogger.warn(chalk.yellow('Ignoring ValidatorAnnounce violation')); return undefined; } case ViolationType.ProxyAdmin: { diff --git a/typescript/infra/src/governance.ts b/typescript/infra/src/governance.ts new file mode 100644 index 00000000000..19a7424e9db --- /dev/null +++ b/typescript/infra/src/governance.ts @@ -0,0 +1,72 @@ +import { Argv } from 'yargs'; + +import { ChainName } from '@hyperlane-xyz/sdk'; +import { Address, eqAddressEvm } from '@hyperlane-xyz/utils'; + +import { + getGovernanceIcas, + getGovernanceSafes, +} from '../config/environments/mainnet3/governance/utils.js'; + +import { DeployEnvironment } from './config/environment.js'; + +export enum GovernanceType { + AbacusWorks = 'abacusWorks', + Regular = 'regular', + Irregular = 'irregular', +} + +export enum Owner { + ICA = 'ICA', + SAFE = 'SAFE', + DEPLOYER = 'DEPLOYER KEY', + UNKNOWN = 'UNKNOWN', +} + +export const DEPLOYERS: Record = { + mainnet3: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', + testnet4: '0xfaD1C94469700833717Fa8a3017278BC1cA8031C', + test: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +}; + +export function withGovernanceType(args: Argv) { + return args.option('governanceType', { + type: 'string', + description: 'Type of governance to use', + choices: Object.values(GovernanceType), + default: GovernanceType.AbacusWorks, + }); +} + +export async function determineGovernanceType( + chain: ChainName, + address: Address, +): Promise<{ + ownerType: Owner | null; + governanceType: GovernanceType; +}> { + if ( + Object.values(DEPLOYERS).some((deployer) => eqAddressEvm(deployer, address)) + ) { + return { + ownerType: Owner.DEPLOYER, + governanceType: GovernanceType.AbacusWorks, + }; + } + + for (const governanceType of Object.values(GovernanceType)) { + const icas = getGovernanceIcas(governanceType); + if (icas[chain] && icas[chain].includes(address)) { + return { ownerType: Owner.ICA, governanceType }; + } + const safes = getGovernanceSafes(governanceType); + if (safes[chain] && safes[chain].includes(address)) { + return { ownerType: Owner.SAFE, governanceType }; + } + } + + return { + ownerType: Owner.UNKNOWN, + governanceType: GovernanceType.AbacusWorks, + }; +} diff --git a/typescript/infra/src/tx/govern-transaction-reader.ts b/typescript/infra/src/tx/govern-transaction-reader.ts index cb63eb97daf..c5892d5f050 100644 --- a/typescript/infra/src/tx/govern-transaction-reader.ts +++ b/typescript/infra/src/tx/govern-transaction-reader.ts @@ -38,8 +38,6 @@ import { import { icaOwnerChain, - icas, - safes, timelocks, } from '../../config/environments/mainnet3/owners.js'; import { DeployEnvironment } from '../config/environment.js'; @@ -102,6 +100,8 @@ export class GovernTransactionReader { readonly chainAddresses: ChainMap>, readonly coreConfig: ChainMap, warpRoutes: Record, + readonly safes: ChainMap, + readonly icas: ChainMap, ) { // Populate maps with warp route addresses and additional token details for (const warpRoute of Object.values(warpRoutes)) { @@ -640,12 +640,12 @@ export class GovernTransactionReader { this.chainAddresses, this.multiProvider, ).getAccount(remoteChainName, { - owner: safes[icaOwnerChain], + owner: this.safes[icaOwnerChain], origin: icaOwnerChain, routerOverride: router, ismOverride: ism, }); - const expectedRemoteIcaAddress = icas[remoteChainName as keyof typeof icas]; + const expectedRemoteIcaAddress = this.icas[remoteChainName]; let remoteIcaInsight = '✅ matches expected ICA'; if ( !expectedRemoteIcaAddress || @@ -837,7 +837,7 @@ export class GovernTransactionReader { return this.multiSendCallOnlyAddressCache[chain]; } - const safe = safes[chain]; + const safe = this.safes[chain]; if (!safe) { return undefined; } diff --git a/typescript/infra/src/utils/safe.ts b/typescript/infra/src/utils/safe.ts index 04fea612177..f3fb37a35e1 100644 --- a/typescript/infra/src/utils/safe.ts +++ b/typescript/infra/src/utils/safe.ts @@ -23,7 +23,6 @@ import { rootLogger, } from '@hyperlane-xyz/utils'; -import safeSigners from '../../config/environments/mainnet3/safe/safeSigners.json' assert { type: 'json' }; // eslint-disable-next-line import/no-cycle import { AnnotatedCallData } from '../govern/HyperlaneAppGovernor.js'; @@ -308,22 +307,41 @@ export async function deleteSafeTx( } } -export async function updateSafeOwner( - safeSdk: Safe.default, - owners?: Address[], - threshold?: number, -): Promise { +export async function getOwnerChanges( + currentOwners: Address[], + expectedOwners: Address[], +): Promise<{ + ownersToRemove: Address[]; + ownersToAdd: Address[]; +}> { + const ownersToRemove = currentOwners.filter( + (owner) => !expectedOwners.some((newOwner) => eqAddress(owner, newOwner)), + ); + const ownersToAdd = expectedOwners.filter( + (newOwner) => !currentOwners.some((owner) => eqAddress(newOwner, owner)), + ); + + return { ownersToRemove, ownersToAdd }; +} + +export async function updateSafeOwner({ + safeSdk, + owners, + threshold, +}: { + safeSdk: Safe.default; + owners?: Address[]; + threshold?: number; +}): Promise { const currentThreshold = await safeSdk.getThreshold(); const newThreshold = threshold ?? currentThreshold; const currentOwners = await safeSdk.getOwners(); - const newOwners = owners ?? safeSigners.signers; + const expectedOwners = owners ?? currentOwners; - const ownersToRemove = currentOwners.filter( - (owner) => !newOwners.some((newOwner) => eqAddress(owner, newOwner)), - ); - const ownersToAdd = newOwners.filter( - (newOwner) => !currentOwners.some((owner) => eqAddress(newOwner, owner)), + const { ownersToRemove, ownersToAdd } = await getOwnerChanges( + currentOwners, + expectedOwners, ); rootLogger.info(chalk.magentaBright('Owners to remove:', ownersToRemove)); @@ -357,6 +375,27 @@ export async function updateSafeOwner( }); } + if ( + ownersToRemove.length === 0 && + ownersToAdd.length === 0 && + currentThreshold !== newThreshold + ) { + rootLogger.info( + chalk.magentaBright( + `Threshold change ${currentThreshold} => ${newThreshold}`, + ), + ); + const { data: thresholdTxData } = await safeSdk.createChangeThresholdTx( + newThreshold, + ); + transactions.push({ + to: thresholdTxData.to, + data: thresholdTxData.data, + value: BigNumber.from(thresholdTxData.value), + description: `Change safe threshold to ${newThreshold}`, + }); + } + return transactions; } From f297a0994a1446141b5c1664331c21a58dffb299 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 18 Apr 2025 22:51:15 +0100 Subject: [PATCH 035/223] feat: chain-specific maxsubmitqueues (#5978) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/agents/relayer/src/msg/op_batch.rs | 5 ++-- .../agents/relayer/src/msg/op_submitter.rs | 24 +++++++------------ rust/main/agents/relayer/src/msg/processor.rs | 2 +- rust/main/agents/relayer/src/relayer.rs | 12 ++++++---- rust/main/agents/scraper/src/agent.rs | 4 ++-- .../src/trait_builder.rs | 6 ++--- .../src/providers/grpc/tests.rs | 4 ++-- .../hyperlane-cosmos/src/trait_builder.rs | 8 +++---- .../chains/hyperlane-ethereum/src/config.rs | 4 ++-- .../src/contracts/mailbox.rs | 2 +- .../src/contracts/multicall.rs | 2 +- .../hyperlane-sealevel/src/trait_builder.rs | 4 ++-- .../hyperlane-base/src/settings/chains.rs | 10 ++++---- .../src/settings/parser/connection_parser.rs | 16 ++++++------- .../hyperlane-base/src/settings/parser/mod.rs | 9 ++++++- rust/main/hyperlane-core/src/config/mod.rs | 5 +++- .../chains/sealevel/adapter.rs | 2 +- .../chains/sealevel/adapter/tests/config.rs | 4 ++-- 18 files changed, 65 insertions(+), 58 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index 096f8568201..32f4f2b6c24 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -188,7 +188,7 @@ mod tests { CoreMetrics, }; use hyperlane_core::{ - config::OperationBatchConfig, Decode, HyperlaneMessage, KnownHyperlaneDomain, + config::OpSubmissionConfig, Decode, HyperlaneMessage, KnownHyperlaneDomain, MessageSubmissionData, ReorgPeriod, SubmitterType, H160, U256, }; use hyperlane_ethereum::{ConnectionConf, RpcConnectionConf}; @@ -347,10 +347,11 @@ mod tests { ], }, transaction_overrides: Default::default(), - operation_batch: OperationBatchConfig { + op_submission_config: OpSubmissionConfig { batch_contract_address: None, max_batch_size: 32, bypass_batch_simulation: false, + ..Default::default() }, }), metrics_conf: Default::default(), diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index 79969c36632..7ae0d9f1364 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -2,7 +2,7 @@ #![allow(clippy::doc_lazy_continuation)] // TODO: `rustc` 1.80.1 clippy issue use std::fmt::Debug; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use std::time::Duration; use futures::future::join_all; @@ -37,19 +37,6 @@ use super::op_queue::OperationPriorityQueue; /// update the number of queues an OpSubmitter has. pub const SUBMITTER_QUEUE_COUNT: usize = 3; -/// Target max length of the submit queue. The prepare queue will -/// hold off on pushing new messages to the submit queue until -/// the submit queue is below this length. -pub static MAX_SUBMIT_QUEUE_LEN: OnceLock> = OnceLock::new(); - -fn max_submit_queue_len() -> Option { - *MAX_SUBMIT_QUEUE_LEN.get_or_init(|| { - std::env::var("HYP_MAXSUBMITQUEUELENGTH") - .ok() - .and_then(|s| s.parse::().ok()) - }) -} - /// SerialSubmitter accepts operations over a channel. It is responsible for /// executing the right strategy to deliver those messages to the destination /// chain. It is designed to be used in a scenario allowing only one @@ -106,6 +93,7 @@ pub struct SerialSubmitter { metrics: SerialSubmitterMetrics, /// Max batch size for submitting messages max_batch_size: u32, + max_submit_queue_len: Option, /// tokio task monitor task_monitor: TaskMonitor, prepare_queue: OpQueue, @@ -123,6 +111,7 @@ impl SerialSubmitter { retry_op_transmitter: &Sender, metrics: SerialSubmitterMetrics, max_batch_size: u32, + max_submit_queue_len: Option, task_monitor: TaskMonitor, payload_dispatcher_entrypoint: Option, db: HyperlaneRocksDB, @@ -149,6 +138,7 @@ impl SerialSubmitter { rx: Some(rx), metrics, max_batch_size, + max_submit_queue_len, task_monitor, prepare_queue, submit_queue, @@ -232,6 +222,7 @@ impl SerialSubmitter { self.submit_queue.clone(), self.confirm_queue.clone(), self.max_batch_size, + self.max_submit_queue_len, self.metrics.clone(), ), )) @@ -347,14 +338,15 @@ async fn prepare_task( submit_queue: OpQueue, confirm_queue: OpQueue, max_batch_size: u32, + max_submit_queue_len: Option, metrics: SerialSubmitterMetrics, ) { // Prepare at most `max_batch_size` ops at a time to avoid getting rate-limited let ops_to_prepare = max_batch_size as usize; loop { // Apply backpressure to the prepare queue if the submit queue is too long. - if let Some(max_len) = max_submit_queue_len() { - let submit_queue_len = submit_queue.len().await; + if let Some(max_len) = max_submit_queue_len { + let submit_queue_len = submit_queue.len().await as u32; if submit_queue_len >= max_len { debug!( %submit_queue_len, diff --git a/rust/main/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs index 27e27f192fd..9b6943d0836 100644 --- a/rust/main/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -525,7 +525,7 @@ pub mod test { url: "http://example.com".parse().unwrap(), }, transaction_overrides: Default::default(), - operation_batch: Default::default(), + op_submission_config: Default::default(), }), metrics_conf: Default::default(), index: Default::default(), diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index 227fc4320e7..623d43648f5 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -462,9 +462,13 @@ impl BaseAgent for Relayer { // Default to submitting one message at a time if there is no batch config self.core.settings.chains[dest_domain.name()] .connection - .operation_batch_config() + .operation_submission_config() .map(|c| c.max_batch_size) .unwrap_or(1), + self.core.settings.chains[dest_domain.name()] + .connection + .operation_submission_config() + .and_then(|c| c.max_submit_queue_length), task_monitor.clone(), payload_dispatcher_entrypoint, db, @@ -1038,8 +1042,8 @@ mod test { CRITICAL_ERROR_LABELS, }; use hyperlane_core::{ - config::OperationBatchConfig, HyperlaneDomain, IndexMode, KnownHyperlaneDomain, - ReorgPeriod, H256, + config::OpSubmissionConfig, HyperlaneDomain, IndexMode, KnownHyperlaneDomain, ReorgPeriod, + H256, }; use hyperlane_ethereum as h_eth; @@ -1098,7 +1102,7 @@ mod test { max_priority_fee_per_gas: None, ..Default::default() }, - operation_batch: OperationBatchConfig { + op_submission_config: OpSubmissionConfig { batch_contract_address: None, max_batch_size: 1, ..Default::default() diff --git a/rust/main/agents/scraper/src/agent.rs b/rust/main/agents/scraper/src/agent.rs index 9d3d067b1dd..67847d343e9 100644 --- a/rust/main/agents/scraper/src/agent.rs +++ b/rust/main/agents/scraper/src/agent.rs @@ -390,7 +390,7 @@ mod test { BLOCK_HEIGHT_HELP, BLOCK_HEIGHT_LABELS, CRITICAL_ERROR_HELP, CRITICAL_ERROR_LABELS, }; use hyperlane_core::{ - config::OperationBatchConfig, IndexMode, KnownHyperlaneDomain, ReorgPeriod, H256, + config::OpSubmissionConfig, IndexMode, KnownHyperlaneDomain, ReorgPeriod, H256, }; use hyperlane_ethereum as h_eth; @@ -446,7 +446,7 @@ mod test { max_priority_fee_per_gas: None, ..Default::default() }, - operation_batch: OperationBatchConfig { + op_submission_config: OpSubmissionConfig { batch_contract_address: None, max_batch_size: 1, ..Default::default() diff --git a/rust/main/chains/hyperlane-cosmos-native/src/trait_builder.rs b/rust/main/chains/hyperlane-cosmos-native/src/trait_builder.rs index 046207085f0..c529f63e505 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/trait_builder.rs @@ -4,7 +4,7 @@ use derive_new::new; use url::Url; use hyperlane_core::{ - config::OperationBatchConfig, ChainCommunicationError, FixedPointNumber, NativeToken, + config::OpSubmissionConfig, ChainCommunicationError, FixedPointNumber, NativeToken, }; /// Cosmos connection configuration @@ -31,7 +31,7 @@ pub struct ConnectionConf { /// bech32 with the appropriate length. contract_address_bytes: usize, /// Operation batching configuration - pub operation_batch: OperationBatchConfig, + pub operation_batch: OpSubmissionConfig, /// Native Token native_token: NativeToken, } @@ -141,7 +141,7 @@ impl ConnectionConf { minimum_gas_price: RawCosmosAmount, gas_multiplier: f64, contract_address_bytes: usize, - operation_batch: OperationBatchConfig, + operation_batch: OpSubmissionConfig, native_token: NativeToken, ) -> Self { Self { diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs b/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs index 72568df4fe0..5c7926ee687 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use hyperlane_metric::prometheus_metric::PrometheusClientMetrics; use url::Url; -use hyperlane_core::config::OperationBatchConfig; +use hyperlane_core::config::OpSubmissionConfig; use hyperlane_core::{ContractLocator, HyperlaneDomain, KnownHyperlaneDomain, NativeToken}; use crate::grpc::{WasmGrpcProvider, WasmProvider}; @@ -61,7 +61,7 @@ fn provider(address: &str) -> WasmGrpcProvider { "untrn".to_owned(), RawCosmosAmount::new("untrn".to_owned(), "0".to_owned()), 32, - OperationBatchConfig { + OpSubmissionConfig { batch_contract_address: None, max_batch_size: 1, ..Default::default() diff --git a/rust/main/chains/hyperlane-cosmos/src/trait_builder.rs b/rust/main/chains/hyperlane-cosmos/src/trait_builder.rs index 139fcd91eb7..dc0cd5528f0 100644 --- a/rust/main/chains/hyperlane-cosmos/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-cosmos/src/trait_builder.rs @@ -4,7 +4,7 @@ use derive_new::new; use url::Url; use hyperlane_core::{ - config::OperationBatchConfig, ChainCommunicationError, FixedPointNumber, NativeToken, + config::OpSubmissionConfig, ChainCommunicationError, FixedPointNumber, NativeToken, }; /// Cosmos connection configuration @@ -29,7 +29,7 @@ pub struct ConnectionConf { /// bech32 with the appropriate length. contract_address_bytes: usize, /// Operation batching configuration - pub operation_batch: OperationBatchConfig, + pub op_submission_config: OpSubmissionConfig, /// Native Token native_token: NativeToken, } @@ -133,7 +133,7 @@ impl ConnectionConf { canonical_asset: String, minimum_gas_price: RawCosmosAmount, contract_address_bytes: usize, - operation_batch: OperationBatchConfig, + op_submission_config: OpSubmissionConfig, native_token: NativeToken, ) -> Self { Self { @@ -144,7 +144,7 @@ impl ConnectionConf { canonical_asset, gas_price: minimum_gas_price, contract_address_bytes, - operation_batch, + op_submission_config, native_token, } } diff --git a/rust/main/chains/hyperlane-ethereum/src/config.rs b/rust/main/chains/hyperlane-ethereum/src/config.rs index a2d0267639d..5623896edb7 100644 --- a/rust/main/chains/hyperlane-ethereum/src/config.rs +++ b/rust/main/chains/hyperlane-ethereum/src/config.rs @@ -1,7 +1,7 @@ use ethers::providers::Middleware; use ethers_core::types::{BlockId, BlockNumber}; use hyperlane_core::{ - config::OperationBatchConfig, ChainCommunicationError, ChainResult, ReorgPeriod, U256, + config::OpSubmissionConfig, ChainCommunicationError, ChainResult, ReorgPeriod, U256, }; use url::Url; @@ -38,7 +38,7 @@ pub struct ConnectionConf { /// Transaction overrides to use when sending transactions. pub transaction_overrides: TransactionOverrides, /// Operation batching configuration - pub operation_batch: OperationBatchConfig, + pub op_submission_config: OpSubmissionConfig, } /// Ethereum transaction overrides. diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index b0b6605b77c..d2ae2e2300e 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -735,7 +735,7 @@ mod test { url: "http://127.0.0.1:8545".parse().unwrap(), }, transaction_overrides: Default::default(), - operation_batch: Default::default(), + op_submission_config: Default::default(), }; let mailbox = EthereumMailbox::new( diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs index f42a5368f12..92ef7fda4d7 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs @@ -31,7 +31,7 @@ pub async fn build_multicall( cache: Arc>, ) -> eyre::Result> { let address = conn - .operation_batch + .op_submission_config .batch_contract_address .unwrap_or(hex_or_base58_to_h256("0xcA11bde05977b3631167028862bE2a173976CA11").unwrap()); let is_contract_cache = { diff --git a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs index dd518e438e4..15d945de54e 100644 --- a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs @@ -1,4 +1,4 @@ -use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, NativeToken}; +use hyperlane_core::{config::OpSubmissionConfig, ChainCommunicationError, NativeToken}; use serde::Serialize; use url::Url; @@ -13,7 +13,7 @@ pub struct ConnectionConf { /// A list of urls to connect to pub urls: Vec, /// Operation batching configuration - pub operation_batch: OperationBatchConfig, + pub op_submission_config: OpSubmissionConfig, /// Native token and its denomination pub native_token: NativeToken, /// Priority fee oracle configuration diff --git a/rust/main/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs index 6a7241a3112..e4c93d458b0 100644 --- a/rust/main/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -9,7 +9,7 @@ use serde_json::Value; use ethers_prometheus::middleware::{ContractInfo, PrometheusMiddlewareConf}; use hyperlane_core::{ - config::OperationBatchConfig, AggregationIsm, CcipReadIsm, ChainResult, ContractLocator, + config::OpSubmissionConfig, AggregationIsm, CcipReadIsm, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, IndexMode, InterchainGasPaymaster, InterchainGasPayment, InterchainSecurityModule, Mailbox, MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, ReorgPeriod, RoutingIsm, @@ -184,11 +184,11 @@ impl ChainConnectionConf { } /// Get the message batch configuration for this chain. - pub fn operation_batch_config(&self) -> Option<&OperationBatchConfig> { + pub fn operation_submission_config(&self) -> Option<&OpSubmissionConfig> { match self { - Self::Ethereum(conf) => Some(&conf.operation_batch), - Self::Cosmos(conf) => Some(&conf.operation_batch), - Self::Sealevel(conf) => Some(&conf.operation_batch), + Self::Ethereum(conf) => Some(&conf.op_submission_config), + Self::Cosmos(conf) => Some(&conf.op_submission_config), + Self::Sealevel(conf) => Some(&conf.op_submission_config), _ => None, } } diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 39597233296..dbb5839fceb 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -6,7 +6,7 @@ use url::Url; use h_eth::TransactionOverrides; -use hyperlane_core::config::{ConfigErrResultExt, OperationBatchConfig}; +use hyperlane_core::config::{ConfigErrResultExt, OpSubmissionConfig}; use hyperlane_core::{config::ConfigParsingError, HyperlaneDomainProtocol, NativeToken}; use crate::settings::envs::*; @@ -20,7 +20,7 @@ pub fn build_ethereum_connection_conf( chain: &ValueParser, err: &mut ConfigParsingError, default_rpc_consensus_type: &str, - operation_batch: OperationBatchConfig, + operation_batch: OpSubmissionConfig, ) -> Option { let Some(first_url) = rpcs.to_owned().clone().into_iter().next() else { return None; @@ -118,7 +118,7 @@ pub fn build_ethereum_connection_conf( Some(ChainConnectionConf::Ethereum(h_eth::ConnectionConf { rpc_connection: rpc_connection_conf?, transaction_overrides, - operation_batch, + op_submission_config: operation_batch, })) } @@ -126,7 +126,7 @@ pub fn build_cosmos_connection_conf( rpcs: &[Url], chain: &ValueParser, err: &mut ConfigParsingError, - operation_batch: OperationBatchConfig, + operation_batch: OpSubmissionConfig, ) -> Option { let mut local_err = ConfigParsingError::default(); let grpcs = @@ -208,7 +208,7 @@ pub fn build_cosmos_native_connection_conf( rpcs: &[Url], chain: &ValueParser, err: &mut ConfigParsingError, - operation_batch: OperationBatchConfig, + operation_batch: OpSubmissionConfig, ) -> Option { let mut local_err = ConfigParsingError::default(); let grpcs = @@ -306,7 +306,7 @@ fn build_sealevel_connection_conf( urls: &[Url], chain: &ValueParser, err: &mut ConfigParsingError, - operation_batch: OperationBatchConfig, + operation_batch: OpSubmissionConfig, ) -> Option { let mut local_err = ConfigParsingError::default(); @@ -320,7 +320,7 @@ fn build_sealevel_connection_conf( } else { Some(ChainConnectionConf::Sealevel(h_sealevel::ConnectionConf { urls: urls.to_owned(), - operation_batch, + op_submission_config: operation_batch, native_token, priority_fee_oracle: priority_fee_oracle.unwrap(), transaction_submitter: transaction_submitter.unwrap(), @@ -502,7 +502,7 @@ pub fn build_connection_conf( chain: &ValueParser, err: &mut ConfigParsingError, default_rpc_consensus_type: &str, - operation_batch: OperationBatchConfig, + operation_batch: OpSubmissionConfig, ) -> Option { match domain_protocol { HyperlaneDomainProtocol::Ethereum => build_ethereum_connection_conf( diff --git a/rust/main/hyperlane-base/src/settings/parser/mod.rs b/rust/main/hyperlane-base/src/settings/parser/mod.rs index 0069aa8f088..c678238fb1b 100644 --- a/rust/main/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/main/hyperlane-base/src/settings/parser/mod.rs @@ -226,6 +226,12 @@ fn parse_chain( .parse_bool() .unwrap_or(false); + let max_submit_queue_length = chain + .chain(&mut err) + .get_opt_key("maxSubmitQueueLength") + .parse_u32() + .end(); + cfg_unwrap_all!(&chain.cwp, err: [domain]); let connection = build_connection_conf( domain.domain_protocol(), @@ -233,10 +239,11 @@ fn parse_chain( &chain, &mut err, default_rpc_consensus_type, - OperationBatchConfig { + OpSubmissionConfig { batch_contract_address, max_batch_size, bypass_batch_simulation, + max_submit_queue_length, }, ); diff --git a/rust/main/hyperlane-core/src/config/mod.rs b/rust/main/hyperlane-core/src/config/mod.rs index 6b8b09462e2..ea7c66504ab 100644 --- a/rust/main/hyperlane-core/src/config/mod.rs +++ b/rust/main/hyperlane-core/src/config/mod.rs @@ -24,7 +24,7 @@ pub type NoFilter = (); /// Config for batching messages #[derive(Debug, Clone, Default)] -pub struct OperationBatchConfig { +pub struct OpSubmissionConfig { /// Optional batch contract address (e.g. Multicall3 on EVM chains) pub batch_contract_address: Option, @@ -33,6 +33,9 @@ pub struct OperationBatchConfig { /// bypass batch simulation pub bypass_batch_simulation: bool, + + /// max submit queue length + pub max_submit_queue_length: Option, } /// A trait that allows for constructing `Self` from a raw config type. diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index 100ed9fd7ea..ae61e572ead 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -150,7 +150,7 @@ impl SealevelTxAdapter { fn batch_size(conf: &ChainConf) -> Result { Ok(conf .connection - .operation_batch_config() + .operation_submission_config() .ok_or_else(|| eyre!("no operation batch config"))? .max_batch_size) } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs index 035510cbc71..56ba4305a58 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs @@ -2,7 +2,7 @@ use std::num::NonZeroU32; use std::time::Duration; use hyperlane_base::settings::{ChainConf, ChainConnectionConf, SignerConf}; -use hyperlane_core::config::OperationBatchConfig; +use hyperlane_core::config::OpSubmissionConfig; use hyperlane_core::{HyperlaneDomain, KnownHyperlaneDomain, ReorgPeriod, SubmitterType}; use crate::chain_tx_adapter::chains::sealevel::adapter::tests::common::adapter_config; @@ -26,7 +26,7 @@ fn test_configuration_fields() { addresses: Default::default(), connection: ChainConnectionConf::Sealevel(hyperlane_sealevel::ConnectionConf { urls: vec![], - operation_batch: OperationBatchConfig { + op_submission_config: OpSubmissionConfig { batch_contract_address: None, max_batch_size: expected_max_batch_size, ..Default::default() From 6b4f5e4751bc518d41a92aeeb0cc91fca9ba8163 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Sat, 19 Apr 2025 18:54:04 +0100 Subject: [PATCH 036/223] feat: mainnet vanguard contexts (#5942) ### Description - Introduces new "vanguard" contexts. 5 are created to begin with. I've only actually deployed 1 so far. - Also introduces a `dbBootstrap` mechanism in the relayer. If the HYP_DB is empty and this is enabled, it'll first pull a tar from GCS and untar it as the starting point for indexing. - This means that a GCP service account is used. Logic is added for creating this, putting the service account secret in GCP secrets, loading this via external secrets, and putting it in the statefulset as an initContainer. - The bucket is `relayer-db-backups` and we will pull the manually created `mainnet3-latest.tar.gz` from here - This is manually created by: ``` $ kubectl cp omniscient-relayer-rc-hyperlane-agent-relayer-0:/usr/share/hyperlane ~/mainnet3-omniscient-relayer-rc-apr-14-2025 $ cd ~/mainnet3-omniscient-relayer-rc-apr-14-2025 $ tar -czf ../mainnet3-omniscient-relayer-rc-apr-14-2025.tar.gz . ``` ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: pbio <10051819+paulbalaji@users.noreply.github.com> Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> --- rust/main/config/testnet_config.json | 23 +++- .../templates/relayer-external-secret.yaml | 8 ++ .../templates/relayer-statefulset.yaml | 51 ++++++++ rust/main/helm/hyperlane-agent/values.yaml | 4 + typescript/infra/config/contexts.ts | 17 +++ .../config/environments/mainnet3/agent.ts | 63 +++++++++- .../environments/mainnet3/infrastructure.ts | 4 + .../config/environments/testnet4/agent.ts | 119 ++++++++++++++++-- .../config/environments/testnet4/chains.ts | 22 ++++ .../environments/testnet4/helloworld.ts | 6 +- .../environments/testnet4/infrastructure.ts | 2 + typescript/infra/config/relayer.json | 16 ++- typescript/infra/src/agents/index.ts | 99 ++++++++++++++- typescript/infra/src/config/agent/relayer.ts | 56 +++++++-- .../external-secrets/external-secrets.ts | 38 ++++-- typescript/infra/src/utils/gcloud.ts | 27 ++++ typescript/sdk/src/metadata/agentConfig.ts | 10 ++ 17 files changed, 521 insertions(+), 44 deletions(-) diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index 60759034e01..ce7fa096344 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -131,7 +131,10 @@ "gasCurrencyCoinGeckoId": "ethereum", "interchainAccountIsm": "0xaec6382e1e16Ee12DBEf0e7EA5ADa51217813Fc3", "interchainAccountRouter": "0x20cC3a33C49fa13627303669edf2DcA7F1E76a50", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "transactionOverrides": { + "gasPriceCap": 100000000000 + } }, "basesepolia": { "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", @@ -198,7 +201,10 @@ "gasCurrencyCoinGeckoId": "ethereum", "interchainAccountIsm": "0xDa5177080f7fC5d9255eB32cC64B9b4e5136A716", "interchainAccountRouter": "0xd876C01aB40e8cE42Db417fBC79c726d45504dE4", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "transactionOverrides": { + "gasPriceCap": 100000000000 + } }, "bsctestnet": { "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", @@ -269,7 +275,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1", "gasCurrencyCoinGeckoId": "binancecoin", "transactionOverrides": { - "gasPrice": 1000000000 + "gasPrice": 1000000000, + "gasPriceCap": 100000000000 } }, "connextsepolia": { @@ -631,7 +638,10 @@ "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", "interchainAccountIsm": "0xA7FA26ef3Ea88CD696779735AC9591E01146DA38", "interchainAccountRouter": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "transactionOverrides": { + "gasPriceCap": 100000000000 + } }, "plumetestnet": { "aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", @@ -918,7 +928,10 @@ "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", "staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f", "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94", - "gasCurrencyCoinGeckoId": "ethereum" + "gasCurrencyCoinGeckoId": "ethereum", + "transactionOverrides": { + "gasPriceCap": 100000000000 + } }, "solanatestnet": { "blockExplorers": [ diff --git a/rust/main/helm/hyperlane-agent/templates/relayer-external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-external-secret.yaml index c9bcd9a27d6..a8103a0d5b1 100644 --- a/rust/main/helm/hyperlane-agent/templates/relayer-external-secret.yaml +++ b/rust/main/helm/hyperlane-agent/templates/relayer-external-secret.yaml @@ -36,6 +36,9 @@ spec: AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }} AWS_SECRET_ACCESS_KEY: {{ print "'{{ .aws_secret_access_key | toString }}'" }} {{- end }} + {{- if .Values.hyperlane.relayer.dbBootstrap.enabled }} + DB_BOOTSTRAP_SERVICE_ACCOUNT_KEY: {{ print "'{{ .db_bootstrap_gcp_sa_json | toString }}'" }} + {{- end }} data: {{- range .Values.hyperlane.relayerChains }} {{- if or (eq .signer.type "hexKey") (eq .signer.type "cosmosKey") }} @@ -57,4 +60,9 @@ spec: remoteRef: key: {{ printf "%s-%s-relayer-aws-secret-access-key" .Values.hyperlane.context .Values.hyperlane.runEnv }} {{- end }} + {{- if .Values.hyperlane.relayer.dbBootstrap.enabled }} + - secretKey: db_bootstrap_gcp_sa_json + remoteRef: + key: {{ printf "%s-relayer-db-bootstrap-viewer-key" $.Values.hyperlane.runEnv }} + {{- end }} {{- end }} diff --git a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml index 60a51e7847c..4dcf1a258c8 100644 --- a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml @@ -43,6 +43,37 @@ spec: terminationGracePeriodSeconds: 10 securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + {{- if .Values.hyperlane.relayer.dbBootstrap.enabled }} + initContainers: + - name: db-bootstrap + image: google/cloud-sdk:alpine + command: + - sh + - -c + - | + if find {{ .Values.hyperlane.dbPath }} -type f | grep -q .; then + echo "Files already exist in {{ .Values.hyperlane.dbPath }} — skipping bootstrap" + else + echo "No data found in {{ .Values.hyperlane.dbPath }} — bootstrapping from GCS" + gsutil cp gs://{{ .Values.hyperlane.relayer.dbBootstrap.bucket }}/{{ .Values.hyperlane.relayer.dbBootstrap.object_targz }} /tmp/seed.tar.gz + # GNU tar has more options than busybox tar + echo "Installing GNU tar..." + apk add tar + echo "Extracting data to {{ .Values.hyperlane.dbPath }}" + tar --no-overwrite-dir --no-same-owner -xzf /tmp/seed.tar.gz -C {{ .Values.hyperlane.dbPath }} + chown -R 1000:2000 {{ .Values.hyperlane.dbPath }} + fi + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + valueFrom: + secretKeyRef: + name: {{ include "agent-common.fullname" . }}-relayer-secret + key: DB_BOOTSTRAP_SERVICE_ACCOUNT_KEY + volumeMounts: + - name: {{ .Values.hyperlane.relayer.storage.name | default "state" }} + mountPath: {{ .Values.hyperlane.dbPath }} + {{- end }} containers: - name: agent securityContext: @@ -61,6 +92,26 @@ spec: {{- include "agent-common.config-env-vars" (dict "config" .Values.hyperlane.relayer.envConfig) | nindent 10 }} - name: CONFIG_FILES value: "/relayer-configmap/relayer-config.json" + {{- with .Values.hyperlane.relayer.cacheDefaultExpirationSeconds }} + - name: HYP_CACHEDEFAULTEXPIRATIONSECONDS + value: {{ . | quote }} + {{- end }} + {{- with .Values.hyperlane.relayer.mixing }} + {{- if .enabled }} + - name: HYPERLANE_RELAYER_MIXING_ENABLED + value: "true" + - name: HYPERLANE_RELAYER_MIXING_SALT + value: {{ .salt | quote }} + {{- end }} + {{- end }} + {{- with .Values.hyperlane.relayer.environmentVariableEndpointEnabled }} + - name: HYPERLANE_RELAYER_ENVIRONMENT_VARIABLE_ENDPOINT_ENABLED + value: {{ . | quote }} + {{- end }} + {{- with .Values.hyperlane.relayer.maxSubmitQueueLength }} + - name: HYP_MAXSUBMITQUEUELENGTH + value: {{ . | quote }} + {{- end }} resources: {{- toYaml .Values.hyperlane.relayer.resources | nindent 10 }} volumeMounts: diff --git a/rust/main/helm/hyperlane-agent/values.yaml b/rust/main/helm/hyperlane-agent/values.yaml index 8e12fe7448d..3462b145059 100644 --- a/rust/main/helm/hyperlane-agent/values.yaml +++ b/rust/main/helm/hyperlane-agent/values.yaml @@ -122,6 +122,10 @@ hyperlane: # -- Specify whether a default signer key is used for all chains in Values.hyperlane.relayerChains list. # It affects chains whose signer type is hexKey. usingDefaultSignerKey: true + dbBootstrap: + enabled: false + bucket: '' + object_targz: '' relayerChains: - name: 'alfajores' diff --git a/typescript/infra/config/contexts.ts b/typescript/infra/config/contexts.ts index 6c9e5517004..51d6c45d8b3 100644 --- a/typescript/infra/config/contexts.ts +++ b/typescript/infra/config/contexts.ts @@ -3,4 +3,21 @@ export enum Contexts { Hyperlane = 'hyperlane', ReleaseCandidate = 'rc', Neutron = 'neutron', + Vanguard0 = 'vanguard0', + Vanguard1 = 'vanguard1', + Vanguard2 = 'vanguard2', + Vanguard3 = 'vanguard3', + Vanguard4 = 'vanguard4', + Vanguard5 = 'vanguard5', +} + +function isValidContext(context: string): context is Contexts { + return Object.values(Contexts).includes(context as Contexts); +} + +export function mustBeValidContext(context: string): Contexts { + if (!isValidContext(context)) { + throw new Error(`Invalid context: ${context}`); + } + return context as Contexts; } diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 93980e0ee55..08e33fc9ee8 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -30,7 +30,7 @@ import { } from '../../../src/config/agent/relayer.js'; import { BaseScraperConfig } from '../../../src/config/agent/scraper.js'; import { ALL_KEY_ROLES, Role } from '../../../src/roles.js'; -import { Contexts } from '../../contexts.js'; +import { Contexts, mustBeValidContext } from '../../contexts.js'; import { getDomainId } from '../../registry.js'; import { environment, ethereumChainNames } from './chains.js'; @@ -777,7 +777,9 @@ const hyperlane: RootAgentConfig = { gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, - allowContractCallCaching: true, + cache: { + enabled: true, + }, resources: relayerResources, }, validators: { @@ -818,7 +820,9 @@ const releaseCandidate: RootAgentConfig = { gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, - allowContractCallCaching: true, + cache: { + enabled: true, + }, resources: relayerResources, }, validators: { @@ -851,13 +855,64 @@ const neutron: RootAgentConfig = { gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, - allowContractCallCaching: true, + cache: { + enabled: true, + }, resources: relayerResources, }, }; +const getVanguardRootAgentConfig = (index: number): RootAgentConfig => ({ + ...contextBase, + context: mustBeValidContext(`vanguard${index}`), + contextChainNames: { + validator: [], + relayer: ['bsc', 'arbitrum', 'optimism', 'ethereum', 'base'], + scraper: [], + }, + rolesWithKeys: [Role.Relayer], + relayer: { + rpcConsensusType: RpcConsensusType.Fallback, + docker: { + repo, + tag: '385b307-20250418-150728', + }, + whitelist: [ + { + originDomain: getDomainId('base'), + senderAddress: '0x000000000000000000000000000000000000dead', + destinationDomain: getDomainId('arbitrum'), + recipientAddress: '0x000000000000000000000000000000000000dead', + }, + ], + blacklist, + gasPaymentEnforcement, + metricAppContextsGetter, + ismCacheConfigs, + cache: { + enabled: true, + }, + resources: relayerResources, + dbBootstrap: true, + batch: { + defaultBatchSize: 32, + batchSizeOverrides: { + // Slightly lower to ideally fit within 5M + ethereum: 23, + }, + bypassBatchSimulation: true, + }, + }, +}); + export const agents = { [Contexts.Hyperlane]: hyperlane, [Contexts.ReleaseCandidate]: releaseCandidate, [Contexts.Neutron]: neutron, + [Contexts.Vanguard0]: getVanguardRootAgentConfig(0), + [Contexts.Vanguard1]: getVanguardRootAgentConfig(1), + [Contexts.Vanguard2]: getVanguardRootAgentConfig(2), + [Contexts.Vanguard3]: getVanguardRootAgentConfig(3), + [Contexts.Vanguard4]: getVanguardRootAgentConfig(4), + [Contexts.Vanguard5]: getVanguardRootAgentConfig(5), }; diff --git a/typescript/infra/config/environments/mainnet3/infrastructure.ts b/typescript/infra/config/environments/mainnet3/infrastructure.ts index 36c15751c76..2d1e81f48ed 100644 --- a/typescript/infra/config/environments/mainnet3/infrastructure.ts +++ b/typescript/infra/config/environments/mainnet3/infrastructure.ts @@ -41,6 +41,10 @@ export const infrastructure: InfrastructureConfig = { 'hyperlane-mainnet3-', 'rc-mainnet3-', 'neutron-mainnet3-', + // All vanguard context secrets. There's a cap on the number of + // prefixes you can specify in a single IAM policy, so for convenience + // we just use a single prefix for all vanguard contexts. + 'vanguard', 'mainnet3-', ], }, diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 66a8892ed0e..9b2dbd0693a 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -4,6 +4,7 @@ import { IsmCacheConfig, IsmCachePolicy, IsmCacheSelectorType, + MatchingList, ModuleType, RpcConsensusType, } from '@hyperlane-xyz/sdk'; @@ -19,7 +20,7 @@ import { routerMatchingList, } from '../../../src/config/agent/relayer.js'; import { ALL_KEY_ROLES, Role } from '../../../src/roles.js'; -import { Contexts } from '../../contexts.js'; +import { Contexts, mustBeValidContext } from '../../contexts.js'; import { getDomainId } from '../../registry.js'; import { environment, ethereumChainNames } from './chains.js'; @@ -232,10 +233,32 @@ const scraperResources = { // Kessel is a load test, these are contracts involved in the load // test that we want to have certain relayers focus on or ignore. -const kesselMatchingList = [ +const kesselMatchingList: MatchingList = [ + // classic kessel test recipient { recipientAddress: '0x492b3653A38e229482Bab2f7De4A094B18017246', }, + // kessel run spice route + { + destinationDomain: getDomainId('basesepolia'), + recipientAddress: '0x4Cd2d5deD9D1ef5013fddCDceBeaCB32DFb5ad47', + }, + { + destinationDomain: getDomainId('bsctestnet'), + recipientAddress: '0x975B8Cf9501cBaD717812fcdE3b51a390AD77540', + }, + { + destinationDomain: getDomainId('optimismsepolia'), + recipientAddress: '0x554B0724432Ef42CB4a2C12E756F6F022e37aD8F', + }, + { + destinationDomain: getDomainId('arbitrumsepolia'), + recipientAddress: '0xdED2d823A5e4E82AfbBB68A3e9D947eE03EFbA9d', + }, + { + destinationDomain: getDomainId('sepolia'), + recipientAddress: '0x51BB50884Ec21063DEC3DCA0B2d4aCeF2559E65a', + }, ]; const kesselAppContext = 'kessel'; @@ -323,20 +346,22 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'cecb0d8-20250411-150743', + tag: '385b307-20250418-150728', }, blacklist: [...releaseCandidateHelloworldMatchingList, ...relayBlacklist], gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, - allowContractCallCaching: true, + cache: { + enabled: true, + }, resources: relayerResources, }, validators: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'f6ac77a-20250418-001005', + tag: '385b307-20250418-150728', }, chains: validatorChainConfig(Contexts.Hyperlane), resources: validatorResources, @@ -345,7 +370,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '45739bd-20250401-014114', + tag: 'da3978b-20250414-155929', }, resources: scraperResources, }, @@ -360,20 +385,22 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'cecb0d8-20250411-150743', + tag: '385b307-20250418-150728', }, blacklist: relayBlacklist, gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, - allowContractCallCaching: true, + cache: { + enabled: true, + }, resources: relayerResources, }, validators: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '73c232b-20240912-124300', + tag: '385b307-20250418-150728', }, chains: validatorChainConfig(Contexts.ReleaseCandidate), resources: validatorResources, @@ -400,13 +427,17 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '8e87bb6-20250416-174849', + tag: '385b307-20250418-150728', }, whitelist: kesselMatchingList, gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, - allowContractCallCaching: true, + cache: { + enabled: true, + // Cache for 10 minutes + defaultExpirationSeconds: 10 * 60, + }, resources: { requests: { cpu: '20000m', @@ -416,8 +447,74 @@ const neutron: RootAgentConfig = { }, }; +const getVanguardRootAgentConfig = (index: number): RootAgentConfig => ({ + ...contextBase, + context: mustBeValidContext(`vanguard${index}`), + contextChainNames: { + validator: [], + relayer: kesselRunnerNetworks, + scraper: [], + }, + rolesWithKeys: [Role.Relayer], + relayer: { + rpcConsensusType: RpcConsensusType.Fallback, + docker: { + repo, + // includes gasPriceCap overrides + per-chain maxSubmitQueueLength + tag: '9d20c65-20250418-220918', + }, + whitelist: kesselMatchingList, + gasPaymentEnforcement: [ + { + type: GasPaymentEnforcementPolicyType.None, + matchingList: kesselMatchingList, + }, + ], + metricAppContextsGetter, + ismCacheConfigs, + cache: { + enabled: true, + }, + resources: { + requests: { + cpu: '30000m', + memory: '100Gi', + }, + }, + dbBootstrap: true, + mixing: { + enabled: true, + // Arbitrary salt to ensure different agents have different sorting behavior for pending messages + salt: 69690 + index, + }, + batch: { + defaultBatchSize: 32, + batchSizeOverrides: { + // Slightly lower to ideally fit within 5M + sepolia: 26, + }, + bypassBatchSimulation: true, + maxSubmitQueueLength: { + arbitrumsepolia: 350, + basesepolia: 350, + bsctestnet: 350, + optimismsepolia: 350, + sepolia: 75, + }, + }, + txIdIndexingEnabled: false, + igpIndexingEnabled: false, + }, +}); + export const agents = { [Contexts.Hyperlane]: hyperlane, [Contexts.ReleaseCandidate]: releaseCandidate, [Contexts.Neutron]: neutron, + [Contexts.Vanguard0]: getVanguardRootAgentConfig(0), + [Contexts.Vanguard1]: getVanguardRootAgentConfig(1), + [Contexts.Vanguard2]: getVanguardRootAgentConfig(2), + [Contexts.Vanguard3]: getVanguardRootAgentConfig(3), + [Contexts.Vanguard4]: getVanguardRootAgentConfig(4), + [Contexts.Vanguard5]: getVanguardRootAgentConfig(5), }; diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 20e17ef9949..7a98bd984b8 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -16,8 +16,30 @@ export const chainMetadataOverrides: ChainMap> = { bsctestnet: { transactionOverrides: { gasPrice: 1 * 10 ** 9, // 1 gwei + gasPriceCap: 100 * 10 ** 9, // 100 gwei cap }, }, + arbitrumsepolia: { + transactionOverrides: { + gasPriceCap: 100 * 10 ** 9, // 100 gwei cap + }, + }, + basesepolia: { + transactionOverrides: { + gasPriceCap: 100 * 10 ** 9, // 100 gwei cap + }, + }, + optimismsepolia: { + transactionOverrides: { + gasPriceCap: 100 * 10 ** 9, // 100 gwei cap + }, + }, + sepolia: { + transactionOverrides: { + gasPriceCap: 100 * 10 ** 9, // 100 gwei cap + }, + }, + // deploy-only overrides // scrollsepolia: { // transactionOverrides: { diff --git a/typescript/infra/config/environments/testnet4/helloworld.ts b/typescript/infra/config/environments/testnet4/helloworld.ts index 059947b8788..b803840611a 100644 --- a/typescript/infra/config/environments/testnet4/helloworld.ts +++ b/typescript/infra/config/environments/testnet4/helloworld.ts @@ -4,7 +4,7 @@ import { } from '../../../src/config/helloworld/types.js'; import { Contexts } from '../../contexts.js'; -import { environment, ethereumChainNames } from './chains.js'; +import { environment } from './chains.js'; import hyperlaneAddresses from './helloworld/hyperlane/addresses.json'; import rcAddresses from './helloworld/rc/addresses.json'; @@ -13,7 +13,7 @@ export const hyperlaneHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '857338e-20240716-165320', + tag: '3e27f07-20250415-081849', }, chainsToSkip: [], runEnv: environment, @@ -32,7 +32,7 @@ export const releaseCandidateHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '857338e-20240716-165320', + tag: '3e27f07-20250415-081849', }, chainsToSkip: [], runEnv: environment, diff --git a/typescript/infra/config/environments/testnet4/infrastructure.ts b/typescript/infra/config/environments/testnet4/infrastructure.ts index eb747804c48..0449da0dbb5 100644 --- a/typescript/infra/config/environments/testnet4/infrastructure.ts +++ b/typescript/infra/config/environments/testnet4/infrastructure.ts @@ -41,6 +41,8 @@ export const infrastructure: InfrastructureConfig = { 'hyperlane-testnet4-', 'rc-testnet4-', 'testnet4-', + // All vanguard secrets + 'vanguard', ], }, }; diff --git a/typescript/infra/config/relayer.json b/typescript/infra/config/relayer.json index 4bb86d3bda3..4e7996f05e1 100644 --- a/typescript/infra/config/relayer.json +++ b/typescript/infra/config/relayer.json @@ -2,7 +2,13 @@ "mainnet3": { "hyperlane": "0x74cae0ecc47b02ed9b9d32e000fd70b9417970c5", "neutron": "0x03787bc64a4f352b4ad172947473342028513ef3", - "rc": "0x09b96417602ed6ac76651f7a8c4860e60e3aa6d0" + "rc": "0x09b96417602ed6ac76651f7a8c4860e60e3aa6d0", + "vanguard0": "0xbe2e6b1ce045422a08a3662fffa3fc5f114efc3d", + "vanguard1": "0xdbcd22e5223f5d0040398e66dbb525308f27c655", + "vanguard2": "0x226b721316ea44aad50a10f4cc67fc30658ab4a9", + "vanguard3": "0xcdd728647ecd9d75413c9b780de303b1d1eb12a5", + "vanguard4": "0x5401627b69f317da9adf3d6e1e1214724ce49032", + "vanguard5": "0x6fd953d1cbdf3a79663b4238898147a6cf36d459" }, "test": { "hyperlane": "", @@ -12,6 +18,12 @@ "testnet4": { "hyperlane": "0x16626cd24fd1f228a031e48b77602ae25f8930db", "neutron": "0xf2c72c0befa494d62949a1699a99e2c605a0b636", - "rc": "0x7fe8c60ead4ab10be736f4de2b3090db5a851f16" + "rc": "0x7fe8c60ead4ab10be736f4de2b3090db5a851f16", + "vanguard0": "0x2c9209efcaff2778d945e18fb24174e16845dc62", + "vanguard1": "0x939043d9db00f6ada1b742239beb7ddd5bf82096", + "vanguard2": "0x45b58e4d46a89c003cc7126bd971eb3794a66aeb", + "vanguard3": "0x1f4fdb150e8c9fda70687a2fd481e305af1e7f8e", + "vanguard4": "0xe41b227e7aaaf7bbd1d60258de0dd76a11a0c3fc", + "vanguard5": "0xb1d77c39166972c0873b6ae016d1a54ec3ce289b" } } diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 4a0d921fa9c..ffc70349fc4 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -22,6 +22,7 @@ import { import { RelayerConfigHelper, RelayerConfigMapConfig, + RelayerDbBootstrapConfig, RelayerEnvConfig, } from '../config/agent/relayer.js'; import { ScraperConfigHelper } from '../config/agent/scraper.js'; @@ -29,9 +30,12 @@ import { ValidatorConfigHelper } from '../config/agent/validator.js'; import { DeployEnvironment } from '../config/environment.js'; import { AgentRole, Role } from '../roles.js'; import { + createServiceAccountIfNotExists, + createServiceAccountKey, fetchGCPSecret, gcpSecretExistsUsingClient, getGcpSecretLatestVersionName, + grantServiceAccountStorageRoleIfNotExists, setGCPSecretUsingClient, } from '../utils/gcloud.js'; import { HelmManager } from '../utils/helm.js'; @@ -48,6 +52,12 @@ const HELM_CHART_PATH = join( '/../../rust/main/helm/hyperlane-agent/', ); +export interface BatchConfig { + maxBatchSize: number; + bypassBatchSimulation: boolean; + maxSubmitQueueLength?: number; +} + export abstract class AgentHelmManager extends HelmManager { abstract readonly role: AgentRole; readonly helmChartPath: string = HELM_CHART_PATH; @@ -106,13 +116,18 @@ export abstract class AgentHelmManager extends HelmManager ); } + const batchConfig = this.batchConfig(chain); + return { name: chain, rpcConsensusType: this.rpcConsensusType(chain), protocol: metadata.protocol, blocks: { reorgPeriod }, - maxBatchSize: 32, - bypassBatchSimulation: false, + maxBatchSize: batchConfig.maxBatchSize, + bypassBatchSimulation: batchConfig.bypassBatchSimulation, + ...(batchConfig.maxSubmitQueueLength + ? { maxSubmitQueueLength: batchConfig.maxSubmitQueueLength } + : {}), priorityFeeOracle, transactionSubmitter, }; @@ -137,6 +152,13 @@ export abstract class AgentHelmManager extends HelmManager kubernetesResources(): KubernetesResources | undefined { return this.config.agentRoleConfig.resources; } + + batchConfig(_: ChainName): BatchConfig { + return { + maxBatchSize: 32, + bypassBatchSimulation: false, + }; + } } abstract class OmniscientAgentHelmManager extends AgentHelmManager { @@ -199,6 +221,15 @@ export class RelayerHelmManager extends OmniscientAgentHelmManager { envConfig, configMapConfig, resources: this.kubernetesResources(), + dbBootstrap: await this.dbBootstrapConfig( + this.config.relayerConfig.dbBootstrap, + ), + mixing: this.config.relayerConfig.mixing ?? { enabled: false }, + // Enable by default in our infra + environmentVariableEndpointEnabled: + this.config.relayerConfig.environmentVariableEndpointEnabled ?? true, + cacheDefaultExpirationSeconds: + this.config.relayerConfig.cache?.defaultExpirationSeconds, }; const signers = await this.config.signers(); @@ -222,6 +253,70 @@ export class RelayerHelmManager extends OmniscientAgentHelmManager { return values; } + + batchConfig(chain: ChainName): BatchConfig { + const defaultBatchConfig = super.batchConfig(chain); + + let maxBatchSize = + this.config.relayerConfig.batch?.defaultBatchSize ?? + defaultBatchConfig.maxBatchSize; + const chainBatchSizeOverride = + this.config.relayerConfig.batch?.batchSizeOverrides?.[chain]; + if (chainBatchSizeOverride) { + maxBatchSize = chainBatchSizeOverride; + } + + return { + maxBatchSize, + bypassBatchSimulation: + this.config.relayerConfig.batch?.bypassBatchSimulation ?? + defaultBatchConfig.bypassBatchSimulation, + maxSubmitQueueLength: + this.config.relayerConfig.batch?.maxSubmitQueueLength?.[chain], + }; + } + + async dbBootstrapConfig( + enabled: boolean = false, + ): Promise { + if (!enabled) { + return undefined; + } + + await this.ensureDbBootstrapGcpServiceAccount('relayer-db-backups'); + + return { + enabled: true, + bucket: 'relayer-db-backups', + object_targz: `${this.environment}-latest.tar.gz`, + }; + } + + async ensureDbBootstrapGcpServiceAccount(bucket: string) { + const secretName = this.dbBootstrapServiceAccountKeySecretName(); + + if (await gcpSecretExistsUsingClient(secretName)) { + // The secret already exists, no need to create it again + return; + } + + const STORAGE_OBJECT_VIEWER_ROLE = 'roles/storage.objectViewer'; + + const serviceAccountEmail = await createServiceAccountIfNotExists( + `${this.environment}-db-bootstrap-reader`, + ); + await grantServiceAccountStorageRoleIfNotExists( + serviceAccountEmail, + bucket, + STORAGE_OBJECT_VIEWER_ROLE, + ); + const key = await createServiceAccountKey(serviceAccountEmail); + await setGCPSecretUsingClient(secretName, JSON.stringify(key)); + } + + dbBootstrapServiceAccountKeySecretName(): string { + return `${this.environment}-relayer-db-bootstrap-viewer-key`; + } } export class ScraperHelmManager extends OmniscientAgentHelmManager { diff --git a/typescript/infra/src/config/agent/relayer.ts b/typescript/infra/src/config/agent/relayer.ts index 56fc06f513b..bec81ca8bfb 100644 --- a/typescript/infra/src/config/agent/relayer.ts +++ b/typescript/infra/src/config/agent/relayer.ts @@ -5,15 +5,12 @@ import { z } from 'zod'; import { AgentConfig, ChainMap, - ChainName, GasPaymentEnforcement, HyperlaneAddresses, HyperlaneAddressesMap, HyperlaneFactories, IsmCacheConfig, - IsmCachePolicy, MatchingList, - ModuleType, RelayerConfig as RelayerAgentConfig, } from '@hyperlane-xyz/sdk'; import { @@ -45,6 +42,23 @@ export interface MetricAppContext { matchingList: MatchingList; } +export interface RelayerMixingConfig { + enabled: boolean; + salt?: number; +} + +export interface RelayerCacheConfig { + enabled: boolean; + defaultExpirationSeconds?: number; +} + +export interface RelayerBatchConfig { + bypassBatchSimulation?: boolean; + defaultBatchSize?: number; + batchSizeOverrides?: ChainMap; + maxSubmitQueueLength?: ChainMap; +} + // Incomplete basic relayer agent config export interface BaseRelayerConfig { gasPaymentEnforcement: GasPaymentEnforcement[]; @@ -55,7 +69,13 @@ export interface BaseRelayerConfig { skipTransactionGasLimitFor?: string[]; metricAppContextsGetter?: () => MetricAppContext[]; ismCacheConfigs?: Array; - allowContractCallCaching?: boolean; + dbBootstrap?: boolean; + mixing?: RelayerMixingConfig; + environmentVariableEndpointEnabled?: boolean; + cache?: RelayerCacheConfig; + batch?: RelayerBatchConfig; + txIdIndexingEnabled?: boolean; + igpIndexingEnabled?: boolean; } // Full relayer-specific agent config for a single chain @@ -83,6 +103,20 @@ export interface HelmRelayerValues extends HelmStatefulSetValues { envConfig?: RelayerEnvConfig; // Config intended to be set as configMap values configMapConfig?: RelayerConfigMapConfig; + // Config for setting up the database + dbBootstrap?: RelayerDbBootstrapConfig; + // Config for setting up the mixing service + mixing?: RelayerMixingConfig; + // Config for the environment variable endpoint + environmentVariableEndpointEnabled?: boolean; + // Config for the cache + cacheDefaultExpirationSeconds?: number; +} + +export interface RelayerDbBootstrapConfig { + enabled: boolean; + bucket: string; + object_targz: string; } // See rust/main/helm/values.yaml for the full list of options and their defaults. @@ -93,7 +127,7 @@ export interface HelmRelayerChainValues { } export class RelayerConfigHelper extends AgentConfigHelper { - readonly #relayerConfig: BaseRelayerConfig; + readonly relayerConfig: BaseRelayerConfig; readonly logger: Logger; constructor(agentConfig: RootAgentConfig) { @@ -101,12 +135,12 @@ export class RelayerConfigHelper extends AgentConfigHelper { throw Error('Relayer is not defined for this context'); super(agentConfig, agentConfig.relayer); - this.#relayerConfig = agentConfig.relayer; + this.relayerConfig = agentConfig.relayer; this.logger = rootLogger.child({ module: 'RelayerConfigHelper' }); } async buildConfig(): Promise { - const baseConfig = this.#relayerConfig!; + const baseConfig = this.relayerConfig!; const relayerConfig: RelayerConfig = { relayChains: this.relayChains.join(','), @@ -140,8 +174,10 @@ export class RelayerConfigHelper extends AgentConfigHelper { if (baseConfig.ismCacheConfigs) { relayerConfig.ismCacheConfigs = baseConfig.ismCacheConfigs; } - relayerConfig.allowContractCallCaching = - baseConfig.allowContractCallCaching; + relayerConfig.allowContractCallCaching = baseConfig.cache?.enabled ?? false; + relayerConfig.txIdIndexingEnabled = baseConfig.txIdIndexingEnabled ?? true; + relayerConfig.igpIndexingEnabled = baseConfig.igpIndexingEnabled ?? true; + return relayerConfig; } @@ -226,7 +262,7 @@ export class RelayerConfigHelper extends AgentConfigHelper { get requiresAwsCredentials(): boolean { // If AWS is present on the agentConfig, we are using AWS keys and need credentials regardless. if (!this.aws) { - console.warn( + this.logger.warn( `Relayer does not have AWS credentials. Be sure this is a non-k8s-based environment!`, ); return false; diff --git a/typescript/infra/src/infrastructure/external-secrets/external-secrets.ts b/typescript/infra/src/infrastructure/external-secrets/external-secrets.ts index 24569a6e9b7..7f691de5ef2 100644 --- a/typescript/infra/src/infrastructure/external-secrets/external-secrets.ts +++ b/typescript/infra/src/infrastructure/external-secrets/external-secrets.ts @@ -85,18 +85,42 @@ async function getGcpExternalSecretsConfig( }; } +async function isExternalSecretsReleaseInstalled( + infraConfig: InfrastructureConfig, +): Promise { + try { + // Gives a non-zero exit code if the release is not installed + await execCmd( + `helm status external-secrets --namespace ${infraConfig.externalSecrets.namespace}`, + ); + return true; + } catch (e) { + return false; + } +} + // Ensures the core `external-secrets` release (with all the CRDs etc) is up to date. async function ensureExternalSecretsRelease(infraConfig: InfrastructureConfig) { // Prometheus's helm chart requires a repository to be added await addHelmRepoIfRequired(infraConfig.externalSecrets.helmChart); - // The name passed in must be in the form `repo/chartName` - const chartName = getDeployableHelmChartName( - infraConfig.externalSecrets.helmChart, - ); - await execCmd( - `helm upgrade external-secrets ${chartName} --namespace ${infraConfig.externalSecrets.namespace} --create-namespace --version ${infraConfig.externalSecrets.helmChart.version} --install --set installCRDs=true `, - ); + // Only install the release if it doesn't already exist. We've observed + // some issues attempting an upgrade when the external-secrets release + // already exists. Doing so could result in the CRD being deleted and + // recreated, which would cause all existing external-secrets CRDs to be + // deleted! + if (!(await isExternalSecretsReleaseInstalled(infraConfig))) { + // The name passed in must be in the form `repo/chartName` + const chartName = getDeployableHelmChartName( + infraConfig.externalSecrets.helmChart, + ); + + await execCmd( + `helm upgrade external-secrets ${chartName} --namespace ${infraConfig.externalSecrets.namespace} --create-namespace --version ${infraConfig.externalSecrets.helmChart.version} --install --set installCRDs=true `, + ); + } else { + console.log('External-secrets release already installed.'); + } // Wait for the external-secrets-webhook deployment to have a ready replica. // The webhook deployment is required in order for subsequent deployments diff --git a/typescript/infra/src/utils/gcloud.ts b/typescript/infra/src/utils/gcloud.ts index 3fc157b4ce4..960a0f9e91e 100644 --- a/typescript/infra/src/utils/gcloud.ts +++ b/typescript/infra/src/utils/gcloud.ts @@ -273,6 +273,33 @@ export async function grantServiceAccountRoleIfNotExists( debugLog(`Granted role ${role} to service account ${serviceAccountEmail}`); } +export async function grantServiceAccountStorageRoleIfNotExists( + serviceAccountEmail: string, + bucketName: string, + role: string, +) { + const bucketUri = `gs://${bucketName}`; + const existingPolicies = await execCmdAndParseJson( + `gcloud storage buckets get-iam-policy ${bucketUri} --format="json"`, + ); + const existingBindings = existingPolicies.bindings || []; + const hasRole = existingBindings.some( + (binding: any) => + binding.role === role && + binding.members && + binding.members.includes(`serviceAccount:${serviceAccountEmail}`), + ); + if (hasRole) { + debugLog( + `Service account ${serviceAccountEmail} already has role ${role} on bucket ${bucketName}`, + ); + return; + } + await execCmd( + `gcloud storage buckets add-iam-policy-binding ${bucketUri} --member="serviceAccount:${serviceAccountEmail}" --role="${role}"`, + ); +} + export async function createServiceAccountKey(serviceAccountEmail: string) { const localKeyFile = '/tmp/tmp_key.json'; await execCmd( diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 9eb673408e2..6ca87dda348 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -450,6 +450,16 @@ export const RelayerAgentConfigSchema = AgentConfigSchema.extend({ .describe( 'If true, allows caching of certain contract calls that can be appropriately cached.', ), + txIdIndexingEnabled: z + .boolean() + .optional() + .describe( + 'Whether to enable TX ID based indexing for hook events given indexed messages', + ), + igpIndexingEnabled: z + .boolean() + .optional() + .describe('Whether to enable IGP indexing'), }); export type RelayerConfig = z.infer; From 81177395b28f18f10e181863768a039bbe5e62d8 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:04:32 +0100 Subject: [PATCH 037/223] feat: metadata building fast path (#5983) ### Description In most aggregation ISM setups, the messageIdMultisigIsm is one of the submodules. This is the fastest one to build, and if the aggregation threshold is 1, there's no need to make network calls to build metadata for the other modules. This PR adds this "fast path" to the aggregation ISM. If it fails, we fall back to the previous behavior where all submodules are built concurrently. ### Drive-by changes Fixes the s3 sdk log silencing by matching against the log targets that show up in gcp logs ### Backward compatibility Yes ### Testing e2e with a 1/2 threshold, looking up the log `Built metadata using fast path` (28 occurrences), but it did fail 64 times --- .../relayer/src/msg/metadata/aggregation.rs | 85 ++++++++++++++++++- .../agents/relayer/src/msg/metadata/base.rs | 2 + .../src/msg/metadata/message_builder.rs | 60 ++++++++----- .../agents/relayer/src/msg/pending_message.rs | 2 +- .../hyperlane-base/src/settings/trace/mod.rs | 4 +- .../environments/test/aggregationIsm.ts | 2 +- 6 files changed, 128 insertions(+), 27 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/aggregation.rs b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs index ddd5c54a19b..e5df0ccffba 100644 --- a/rust/main/agents/relayer/src/msg/metadata/aggregation.rs +++ b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs @@ -184,6 +184,72 @@ impl AggregationIsmMetadataBuilder { } } } + + async fn try_build_fast_path( + &self, + message: &HyperlaneMessage, + params: MessageMetadataBuildParams, + threshold: usize, + ism_addresses: Vec, + ) -> Result { + if threshold > 1 { + return Err(MetadataBuildError::FastPathError( + "Aggregation ISM threshold > 1".to_string(), + )); + } + let sub_isms = join_all(ism_addresses.iter().map(|sub_ism_address| { + message_builder::ism_and_module_type(self.base.clone(), *sub_ism_address) + })) + .await; + let (message_id_multisig_ism_index, message_id_multisig_ism) = sub_isms + .into_iter() + .enumerate() + .find_map(|(index, ism)| { + if let Ok((ism, ModuleType::MessageIdMultisig)) = ism { + Some((index, ism)) + } else { + None + } + }) + .ok_or(MetadataBuildError::FastPathError( + "No MessageIdMultisigIsm submodule in aggregation ISM".to_string(), + ))?; + let message_id_multisig_ism_address = ism_addresses + .get(message_id_multisig_ism_index) + .ok_or(MetadataBuildError::FastPathError(format!( + "No ism address found for messageIdMultisig index {}", + message_id_multisig_ism_index + )))?; + let sub_module_and_meta = message_builder::build_message_metadata( + self.base.clone(), + *message_id_multisig_ism_address, + message, + params.clone(), + Some((message_id_multisig_ism, ModuleType::MessageIdMultisig)), + ) + .await?; + + let metadata = sub_module_and_meta.metadata.to_vec(); + + // return an error if delivering with this metadata fails + if sub_module_and_meta + .ism + .dry_run_verify(message, &metadata) + .await + .map_err(|err| MetadataBuildError::FastPathError(err.to_string()))? + .is_none() + { + return Err(MetadataBuildError::FastPathError( + "Fast path metadata failed dry run (returned None)".to_string(), + )); + } + let sub_module_metadata = SubModuleMetadata::new(message_id_multisig_ism_index, metadata); + let metadata = Metadata::new(Self::format_metadata( + &mut [sub_module_metadata], + ism_addresses.len(), + )); + Ok(metadata) + } } #[async_trait] @@ -206,12 +272,29 @@ impl MetadataBuilder for AggregationIsmMetadataBuilder { let threshold = threshold as usize; + match self + .try_build_fast_path(message, params.clone(), threshold, ism_addresses.clone()) + .await + { + Ok(metadata) => { + info!("Built metadata using fast path"); + return Ok(metadata); + } + Err(err) => { + warn!( + ?err, + "Fast path failed, falling back to the other submodules in the aggregation ISM" + ); + } + } + let sub_modules_and_metas = join_all(ism_addresses.iter().map(|sub_ism_address| { message_builder::build_message_metadata( self.base.clone(), *sub_ism_address, message, params.clone(), + None, ) })) .await; @@ -224,7 +307,7 @@ impl MetadataBuilder for AggregationIsmMetadataBuilder { } // Partitions things into - // 1. ok_sub_modules: ISMs with metadata with valid metadata + // 1. ok_sub_modules: ISMs with valid metadata // 2. err_sub_modules: ISMs with invalid metadata let (ok_sub_modules, err_sub_modules): (Vec<_>, Vec<_>) = sub_modules_and_metas .into_iter() diff --git a/rust/main/agents/relayer/src/msg/metadata/base.rs b/rust/main/agents/relayer/src/msg/metadata/base.rs index 8c180568d1b..3fe4284a1db 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base.rs @@ -42,6 +42,8 @@ pub enum MetadataBuildError { MaxValidatorCountReached(u32), #[error("Aggregation threshold not met ({0})")] AggregationThresholdNotMet(u32), + #[error("Fast path error ({0})")] + FastPathError(String), } #[derive(Clone, Debug, new)] diff --git a/rust/main/agents/relayer/src/msg/metadata/message_builder.rs b/rust/main/agents/relayer/src/msg/metadata/message_builder.rs index 69ffc4d6ef7..b1ca182d9ef 100644 --- a/rust/main/agents/relayer/src/msg/metadata/message_builder.rs +++ b/rust/main/agents/relayer/src/msg/metadata/message_builder.rs @@ -61,7 +61,7 @@ impl MetadataBuilder for MessageMetadataBuilder { message: &HyperlaneMessage, params: MessageMetadataBuildParams, ) -> Result { - build_message_metadata(self.clone(), ism_address, message, params) + build_message_metadata(self.clone(), ism_address, message, params, None) .await .map(|res| res.metadata) } @@ -136,21 +136,31 @@ impl MessageMetadataBuilder { } } -/// Builds metadata for a message. -pub async fn build_message_metadata( +pub async fn ism_and_module_type( message_builder: MessageMetadataBuilder, ism_address: H256, - message: &HyperlaneMessage, - mut params: MessageMetadataBuildParams, -) -> Result { - let ism: Box = message_builder +) -> Result<(Box, ModuleType), MetadataBuildError> { + let ism = message_builder .base_builder() .build_ism(ism_address) .await .map_err(|err| MetadataBuildError::FailedToBuild(err.to_string()))?; - let module_type = message_builder.call_module_type(&ism).await?; + Ok((ism, module_type)) +} +/// Builds metadata for a message. +pub async fn build_message_metadata( + message_builder: MessageMetadataBuilder, + ism_address: H256, + message: &HyperlaneMessage, + mut params: MessageMetadataBuildParams, + maybe_ism_and_module_type: Option<(Box, ModuleType)>, +) -> Result { + let (ism, module_type) = match maybe_ism_and_module_type { + Some((ism, module_type)) => (ism, module_type), + None => ism_and_module_type(message_builder.clone(), ism_address).await?, + }; // check if max depth is reached if params.ism_depth >= message_builder.max_ism_depth { tracing::error!( @@ -214,8 +224,10 @@ mod test { use crate::{ msg::metadata::{ - base::MetadataBuildError, message_builder::build_message_metadata, DefaultIsmCache, - IsmAwareAppContextClassifier, IsmCachePolicyClassifier, MessageMetadataBuildParams, + base::MetadataBuildError, + message_builder::{build_message_metadata, ism_and_module_type}, + DefaultIsmCache, IsmAwareAppContextClassifier, IsmCachePolicyClassifier, + MessageMetadataBuildParams, }, settings::matching_list::{Filter, ListElement, MatchingList}, test_utils::{ @@ -460,9 +472,10 @@ mod test { builder }; let params = MessageMetadataBuildParams::default(); - let err = build_message_metadata(message_builder, ism_address, &message, params.clone()) - .await - .expect_err("Metadata found when it should have failed"); + let err = + build_message_metadata(message_builder, ism_address, &message, params.clone(), None) + .await + .expect_err("Metadata found when it should have failed"); assert_eq!(err, MetadataBuildError::MaxIsmDepthExceeded(0)); assert_eq!(*(params.ism_count.lock().await), 0); @@ -488,9 +501,10 @@ mod test { }; let params = MessageMetadataBuildParams::default(); - let err = build_message_metadata(message_builder, ism_address, &message, params.clone()) - .await - .expect_err("Metadata found when it should have failed"); + let err = + build_message_metadata(message_builder, ism_address, &message, params.clone(), None) + .await + .expect_err("Metadata found when it should have failed"); assert_eq!(err, MetadataBuildError::MaxIsmCountReached(0)); assert_eq!(*(params.ism_count.lock().await), 0); @@ -516,9 +530,10 @@ mod test { }; let params = MessageMetadataBuildParams::default(); - let err = build_message_metadata(message_builder, ism_address, &message, params.clone()) - .await - .expect_err("Metadata found when it should have failed"); + let err = + build_message_metadata(message_builder, ism_address, &message, params.clone(), None) + .await + .expect_err("Metadata found when it should have failed"); assert_eq!(err, MetadataBuildError::AggregationThresholdNotMet(2)); assert!(*(params.ism_count.lock().await) <= 4); assert!(logs_contain("Max ISM depth reached ism_depth=2")); @@ -543,9 +558,10 @@ mod test { }; let params = MessageMetadataBuildParams::default(); - let err = build_message_metadata(message_builder, ism_address, &message, params.clone()) - .await - .expect_err("Metadata found when it should have failed"); + let err = + build_message_metadata(message_builder, ism_address, &message, params.clone(), None) + .await + .expect_err("Metadata found when it should have failed"); assert_eq!(err, MetadataBuildError::AggregationThresholdNotMet(2)); assert_eq!(*(params.ism_count.lock().await), 5); assert!(logs_contain("Max ISM count reached ism_count=5")); diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index 13713535060..5f7c09b024b 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -987,7 +987,7 @@ impl PendingMessage { .build(ism_address, &self.message, params) .await .map_err(|err| match &err { - MetadataBuildError::FailedToBuild(_) => { + MetadataBuildError::FailedToBuild(_) | MetadataBuildError::FastPathError(_) => { self.on_reprepare(Some(err), ReprepareReason::ErrorBuildingMetadata) } MetadataBuildError::CouldNotFetch => { diff --git a/rust/main/hyperlane-base/src/settings/trace/mod.rs b/rust/main/hyperlane-base/src/settings/trace/mod.rs index ff10f5743d0..bae93d9697b 100644 --- a/rust/main/hyperlane-base/src/settings/trace/mod.rs +++ b/rust/main/hyperlane-base/src/settings/trace/mod.rs @@ -76,8 +76,8 @@ impl TracingConfig { .with_target("tendermint", Level::Info) .with_target("tokio", Level::Debug) .with_target("tokio_util", Level::Debug) - .with_target("aws_smithy", Level::Info) - .with_target("aws_sdk", Level::Info) + .with_target("aws_sdk_s3", Level::Info) + .with_target("aws_smithy_runtime_api", Level::Info) // Enable Trace level for Tokio if you want to use tokio-console // .with_target("tokio", Level::Trace) // .with_target("tokio_util", Level::Trace) diff --git a/typescript/infra/config/environments/test/aggregationIsm.ts b/typescript/infra/config/environments/test/aggregationIsm.ts index 3324e90bd64..d69f9220e31 100644 --- a/typescript/infra/config/environments/test/aggregationIsm.ts +++ b/typescript/infra/config/environments/test/aggregationIsm.ts @@ -9,6 +9,6 @@ export const aggregationIsm = (validatorKey: string): AggregationIsmConfig => { merkleRootMultisig(validatorKey), messageIdMultisig(validatorKey), ], - threshold: 2, + threshold: 1, }; }; From ecbacbdf293943535ead5a502f92c0aaed0eef39 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Mon, 21 Apr 2025 14:37:24 +0100 Subject: [PATCH 038/223] feat: Add EvmHypRebaseCollateralAdapter and EvmHypSyntheticRebaseAdaper for monitoring (#5917) ### Description - Add `EvmHypRebaseCollateralAdapter` and `EvmHypSyntheticRebaseAdapter` adapters with overriden implementations of `getBridgedSupply` - `getBridgedSupply` is called by our monitoring code to get the either the balance of the collateral on the collateral chain or the total supply on the synthetic side - When comparing balances, we can either compare the shares on both sides or the token balance. The shares should remain constant assuming not transfers happen but the token balances can increase after rebasing. Either options is valid, we have just chosen to compare shares - For HypRebaseCollateral type routers, the collateral is actual held in the 4626 vault not the router contract, and calling `getBalance` on the vault returns the number of shares - For the HypSyntheticRebase we are reading the number of shares ### Backward compatibility ### Testing Manual --- .changeset/stupid-trainers-poke.md | 5 ++ typescript/sdk/src/token/Token.ts | 18 ++++--- .../sdk/src/token/adapters/EvmTokenAdapter.ts | 53 +++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 .changeset/stupid-trainers-poke.md diff --git a/.changeset/stupid-trainers-poke.md b/.changeset/stupid-trainers-poke.md new file mode 100644 index 00000000000..02a55435ac0 --- /dev/null +++ b/.changeset/stupid-trainers-poke.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Add EvmHypRebaseCollateralAdapter and EvmHypSyntheticRebaseAdapter diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts index b4119201d61..3c5f3a55363 100644 --- a/typescript/sdk/src/token/Token.ts +++ b/typescript/sdk/src/token/Token.ts @@ -42,7 +42,9 @@ import { EvmHypCollateralAdapter, EvmHypCollateralFiatAdapter, EvmHypNativeAdapter, + EvmHypRebaseCollateralAdapter, EvmHypSyntheticAdapter, + EvmHypSyntheticRebaseAdapter, EvmHypXERC20Adapter, EvmHypXERC20LockboxAdapter, EvmNativeTokenAdapter, @@ -190,23 +192,27 @@ export class Token implements IToken { }); } else if ( standard === TokenStandard.EvmHypCollateral || - standard === TokenStandard.EvmHypOwnerCollateral || - standard === TokenStandard.EvmHypRebaseCollateral + standard === TokenStandard.EvmHypOwnerCollateral ) { return new EvmHypCollateralAdapter(chainName, multiProvider, { token: addressOrDenom, }); + } else if (standard === TokenStandard.EvmHypRebaseCollateral) { + return new EvmHypRebaseCollateralAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); } else if (standard === TokenStandard.EvmHypCollateralFiat) { return new EvmHypCollateralFiatAdapter(chainName, multiProvider, { token: addressOrDenom, }); - } else if ( - standard === TokenStandard.EvmHypSynthetic || - standard === TokenStandard.EvmHypSyntheticRebase - ) { + } else if (standard === TokenStandard.EvmHypSynthetic) { return new EvmHypSyntheticAdapter(chainName, multiProvider, { token: addressOrDenom, }); + } else if (standard === TokenStandard.EvmHypSyntheticRebase) { + return new EvmHypSyntheticRebaseAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); } else if ( standard === TokenStandard.EvmHypXERC20 || standard === TokenStandard.EvmHypVSXERC20 diff --git a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts index e971d35dbfe..60c3879eb54 100644 --- a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts @@ -3,10 +3,15 @@ import { BigNumber, PopulatedTransaction } from 'ethers'; import { ERC20, ERC20__factory, + ERC4626__factory, HypERC20, HypERC20Collateral, HypERC20Collateral__factory, HypERC20__factory, + HypERC4626, + HypERC4626Collateral, + HypERC4626Collateral__factory, + HypERC4626__factory, HypXERC20, HypXERC20Lockbox, HypXERC20Lockbox__factory, @@ -325,6 +330,54 @@ export class EvmHypCollateralFiatAdapter } } +export class EvmHypRebaseCollateralAdapter + extends EvmHypCollateralAdapter + implements IHypTokenAdapter +{ + public override collateralContract: HypERC4626Collateral; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address }, + ) { + super(chainName, multiProvider, addresses); + this.collateralContract = HypERC4626Collateral__factory.connect( + addresses.token, + this.getProvider(), + ); + } + + override async getBridgedSupply(): Promise { + const vault = ERC4626__factory.connect( + await this.collateralContract.vault(), + this.getProvider(), + ); + const balance = await vault.balanceOf(this.addresses.token); + return balance.toBigInt(); + } +} + +export class EvmHypSyntheticRebaseAdapter + extends EvmHypSyntheticAdapter + implements IHypTokenAdapter +{ + public declare contract: HypERC4626; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address }, + ) { + super(chainName, multiProvider, addresses, HypERC4626__factory); + } + + override async getBridgedSupply(): Promise { + const totalShares = await this.contract.totalShares(); + return totalShares.toBigInt(); + } +} + abstract class BaseEvmHypXERC20Adapter extends EvmHypCollateralAdapter implements IHypXERC20Adapter From 8b985a492ddf88e48997927126c2a8c66f30e0b1 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Mon, 21 Apr 2025 16:18:09 +0100 Subject: [PATCH 039/223] fix: Restore serial submission for Sealevel and Cosmos chains (#5987) ### Description Restore serial submission for Sealevel and Cosmos chains. It was removed by mistake during implementation of batch processing for Ethereum chains. ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/agents/relayer/src/msg/op_batch.rs | 30 ++++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index 32f4f2b6c24..c32084d33ce 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -8,7 +8,7 @@ use hyperlane_core::{ }; use itertools::{Either, Itertools}; use tokio::time::sleep; -use tracing::{info, instrument, warn}; +use tracing::{error, info, instrument, warn}; use super::{ op_queue::OpQueue, @@ -43,13 +43,25 @@ impl OperationBatch { } }; - if !excluded_ops.is_empty() { - warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Sending them back to prepare queue."); - let reason = ReprepareReason::ErrorSubmitting; - let status = Some(PendingOperationStatus::Retry(reason.clone())); - for mut op in excluded_ops.into_iter() { - op.on_reprepare(None, reason.clone()); - prepare_queue.push(op, status.clone()).await; + if let Some(first_item) = excluded_ops.first() { + let Some(mailbox) = first_item.try_get_mailbox() else { + error!(excluded_ops=?excluded_ops, "Excluded ops don't have mailbox while they should have"); + return; // we expect that excluded ops have mailbox + }; + + if mailbox.supports_batching() { + warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Sending them back to prepare queue."); + let reason = ReprepareReason::ErrorSubmitting; + let status = Some(PendingOperationStatus::Retry(reason.clone())); + for mut op in excluded_ops.into_iter() { + op.on_reprepare(None, reason.clone()); + prepare_queue.push(op, status.clone()).await; + } + } else { + info!(excluded_ops=?excluded_ops, "Chain does not support batching. Submitting serially."); + OperationBatch::new(excluded_ops, self.domain) + .submit_serially(prepare_queue, confirm_queue, metrics) + .await; } } } @@ -145,7 +157,7 @@ impl OperationBatch { } } - async fn _submit_serially( + async fn submit_serially( self, prepare_queue: &mut OpQueue, confirm_queue: &mut OpQueue, From 1a558c610cead673f1c2db237d6bb3e48adb5a1c Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Mon, 21 Apr 2025 17:20:32 +0100 Subject: [PATCH 040/223] chore: mainnet vanguards (#5988) ### Description Deployed all the relayers Deploys all 6 vanguard relayers to c3-standard-44 machines Updated the other relayers to an image that's after the move to the faster s3 querying, but not anything else - just being conservative to not impact things there ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Trevor Porter Co-authored-by: pbio <10051819+paulbalaji@users.noreply.github.com> --- .../config/environments/mainnet3/agent.ts | 113 +++++++++++++++--- typescript/infra/src/agents/index.ts | 9 ++ typescript/infra/src/config/agent/relayer.ts | 11 ++ 3 files changed, 115 insertions(+), 18 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 08e33fc9ee8..1391d008e1e 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -23,6 +23,7 @@ import { } from '../../../src/config/agent/agent.js'; import { MetricAppContext, + chainMapMatchingList, consistentSenderRecipientMatchingList, routerMatchingList, senderMatchingList, @@ -591,6 +592,43 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ }, ]; +// HYPER - https://github.com/hyperlane-xyz/hyperlane-registry-private/blob/6f9ef6ca2805480312b75894cf030acde37c5527/deployments/warp_routes/HYPER/arbitrum-base-bsc-ethereum-optimism-config.yaml +const hyperMatchingList = chainMapMatchingList({ + arbitrum: '0xC9d23ED2ADB0f551369946BD377f8644cE1ca5c4', + base: '0xC9d23ED2ADB0f551369946BD377f8644cE1ca5c4', + bsc: '0xC9d23ED2ADB0f551369946BD377f8644cE1ca5c4', + ethereum: '0x93A2Db22B7c736B341C32Ff666307F4a9ED910F5', + optimism: '0x9923DB8d7FBAcC2E69E87fAd19b886C81cd74979', +}); + +// stHYPER - https://github.com/hyperlane-xyz/hyperlane-registry-private/blob/6f9ef6ca2805480312b75894cf030acde37c5527/deployments/warp_routes/stHYPER/bsc-ethereum-config.yaml#L1 +const stHyperMatchingList = chainMapMatchingList({ + bsc: '0x6E9804a08092D8ba4E69DaCF422Df12459F2599E', + ethereum: '0x9F6E6d150977dabc82d5D4EaaBDB1F1Ab0D25F92', +}); + +// Staging HYPER - https://github.com/hyperlane-xyz/hyperlane-registry-private/blob/38b91443b960a7887653445ef094c730bf708717/deployments/warp_routes/HYPER/arbitrum-base-bsc-ethereum-optimism-config.yaml +const stagingHyperMatchingList = chainMapMatchingList({ + arbitrum: '0xF80dcED2488Add147E60561F8137338F7f3976e1', + base: '0x830B15a1986C75EaF8e048442a13715693CBD8bD', + bsc: '0x9537c772c6092DB4B93cFBA93659bB5a8c0E133D', + ethereum: '0xC10c27afcb915439C27cAe54F5F46Da48cd71190', + optimism: '0x31cD131F5F6e1Cc0d6743F695Fc023B70D0aeAd8', +}); + +// Staging stHYPER - https://github.com/hyperlane-xyz/hyperlane-registry-private/blob/38b91443b960a7887653445ef094c730bf708717/deployments/warp_routes/stHYPER/bsc-ethereum-config.yaml +const stagingStHyperMatchingList = chainMapMatchingList({ + bsc: '0xf0c8c5fc69fCC3fA49C319Fdf422D8279756afE2', + ethereum: '0x0C919509663cb273E156B706f065b9F7e6331891', +}); + +const vanguardMatchingList = [ + ...hyperMatchingList, + ...stHyperMatchingList, + ...stagingHyperMatchingList, + ...stagingStHyperMatchingList, +]; + // Gets metric app contexts, including: // - helloworld // - all warp routes defined in WarpRouteIds, using addresses from the registry @@ -654,6 +692,23 @@ const metricAppContextsGetter = (): MetricAppContext[] => { name: 'everclear_gateway', matchingList: senderMatchingList(everclearSenderAddresses), }, + // Manually specified for now until things are public + { + name: 'HYPER/arbitrum-base-bsc-ethereum-optimism', + matchingList: hyperMatchingList, + }, + { + name: 'stHYPER/bsc-ethereum', + matchingList: stHyperMatchingList, + }, + { + name: 'HYPER-STAGING/arbitrum-base-bsc-ethereum-optimism', + matchingList: stagingHyperMatchingList, + }, + { + name: 'stHYPER-STAGING/bsc-ethereum', + matchingList: stagingStHyperMatchingList, + }, ]; }; @@ -748,12 +803,12 @@ const ismCacheConfigs: Array = [ selector: { type: IsmCacheSelectorType.DefaultIsm, }, - // Default ISM Routing ISMs change configs based off message content, - // so they are not specified here. moduleTypes: [ ModuleType.AGGREGATION, ModuleType.MERKLE_ROOT_MULTISIG, ModuleType.MESSAGE_ID_MULTISIG, + // The relayer will cache these per-origin to accommodate DomainRoutingIsms + ModuleType.ROUTING, ], // SVM is explicitly not cached as the default ISM is a multisig ISM // that routes internally. @@ -771,9 +826,9 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'da3978b-20250414-155929', + tag: '8b985a4-20250421-152948', }, - blacklist, + blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, @@ -811,9 +866,9 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'da3978b-20250414-155929', + tag: '8b985a4-20250421-152948', }, - blacklist, + blacklist: [...blacklist, ...vanguardMatchingList], // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. // whitelist: releaseCandidateHelloworldMatchingList, @@ -849,9 +904,9 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'cecb0d8-20250411-150743', + tag: '8b985a4-20250421-152948', }, - blacklist, + blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, @@ -875,33 +930,55 @@ const getVanguardRootAgentConfig = (index: number): RootAgentConfig => ({ rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '385b307-20250418-150728', + // includes gasPriceCap overrides + per-chain maxSubmitQueueLength + tag: '8117739-20250420-201559', }, - whitelist: [ + whitelist: vanguardMatchingList, + // Not specifying a blacklist for optimization purposes -- all the message IDs + // in there are not vanguard-specific. + gasPaymentEnforcement: [ { - originDomain: getDomainId('base'), - senderAddress: '0x000000000000000000000000000000000000dead', - destinationDomain: getDomainId('arbitrum'), - recipientAddress: '0x000000000000000000000000000000000000dead', + type: GasPaymentEnforcementPolicyType.None, + matchingList: vanguardMatchingList, }, ], - blacklist, - gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, cache: { enabled: true, + // Cache for 10 minutes + defaultExpirationSeconds: 10 * 60, + }, + resources: { + requests: { + // Big enough to claim a c3-standard-44 each + cpu: '35000m', + memory: '100Gi', + }, }, - resources: relayerResources, dbBootstrap: true, + mixing: { + enabled: true, + // Arbitrary salt to ensure different agents have different sorting behavior for pending messages + salt: 69690 + index, + }, batch: { defaultBatchSize: 32, batchSizeOverrides: { // Slightly lower to ideally fit within 5M - ethereum: 23, + ethereum: 26, }, bypassBatchSimulation: true, + maxSubmitQueueLength: { + arbitrum: 350, + base: 350, + bsc: 350, + optimism: 350, + ethereum: 75, + }, }, + txIdIndexingEnabled: false, + igpIndexingEnabled: false, }, }); diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index ffc70349fc4..fc0510cd066 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -251,6 +251,15 @@ export class RelayerHelmManager extends OmniscientAgentHelmManager { effect: 'NoSchedule', }); + if (this.context.includes('vanguard')) { + values.tolerations.push({ + key: 'context-family', + operator: 'Equal', + value: 'vanguard', + effect: 'NoSchedule', + }); + } + return values; } diff --git a/typescript/infra/src/config/agent/relayer.ts b/typescript/infra/src/config/agent/relayer.ts index bec81ca8bfb..961712321c6 100644 --- a/typescript/infra/src/config/agent/relayer.ts +++ b/typescript/infra/src/config/agent/relayer.ts @@ -18,6 +18,7 @@ import { ProtocolType, addressToBytes32, isValidAddressEvm, + objMap, rootLogger, } from '@hyperlane-xyz/utils'; @@ -324,6 +325,16 @@ export function consistentSenderRecipientMatchingList( ]; } +export function chainMapMatchingList( + chainMap: ChainMap
, +): MatchingList { + // Convert to a router matching list + const routers = objMap(chainMap, (chain, address) => ({ + router: address, + })); + return routerMatchingList(routers); +} + // Create a matching list for the given contract addresses export function matchingList( addressesMap: HyperlaneAddressesMap, From 4569591089d7e839bc1c49e30c45e1fe7c1171ea Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:30:02 +0100 Subject: [PATCH 041/223] fix: label for reverted batch simulations (#5990) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/agents/relayer/src/msg/op_batch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index c32084d33ce..75a09ec0c77 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -51,7 +51,7 @@ impl OperationBatch { if mailbox.supports_batching() { warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Sending them back to prepare queue."); - let reason = ReprepareReason::ErrorSubmitting; + let reason = ReprepareReason::ErrorEstimatingGas; let status = Some(PendingOperationStatus::Retry(reason.clone())); for mut op in excluded_ops.into_iter() { op.on_reprepare(None, reason.clone()); From b62dc6a72dd523aa04791c8eba39595335e90748 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 22 Apr 2025 10:33:29 +0100 Subject: [PATCH 042/223] feat: deploy vanguard relayers with new image, other relayers with older image (#5991) ### Description Some chat here https://hyperlaneworkspace.slack.com/archives/C08GR6PBPGT/p1745272027027899?thread_ts=1745262374.703859&cid=C08GR6PBPGT - Vanguards from latest main with #5990 - Other relayers before #5968 which refuses to send txs that aren't batches, which we don't want for low throughput chains ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 1391d008e1e..764fea69805 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -826,7 +826,9 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '8b985a4-20250421-152948', + // Using an older image to ensure low-volume chains don't hit an edge case, + // see https://hyperlaneworkspace.slack.com/archives/C08GR6PBPGT/p1745272027027899?thread_ts=1745262374.703859&cid=C08GR6PBPGT + tag: '7b0f5a0-20250418-120540', }, blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement: gasPaymentEnforcement, @@ -866,7 +868,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '8b985a4-20250421-152948', + tag: '7b0f5a0-20250418-120540', }, blacklist: [...blacklist, ...vanguardMatchingList], // We're temporarily (ab)using the RC relayer as a way to increase @@ -904,7 +906,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '8b985a4-20250421-152948', + tag: '7b0f5a0-20250418-120540', }, blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement, @@ -931,7 +933,7 @@ const getVanguardRootAgentConfig = (index: number): RootAgentConfig => ({ docker: { repo, // includes gasPriceCap overrides + per-chain maxSubmitQueueLength - tag: '8117739-20250420-201559', + tag: '4569591-20250421-224434', }, whitelist: vanguardMatchingList, // Not specifying a blacklist for optimization purposes -- all the message IDs From 4c867b80a3915f5ec678bfc1d64562dae747b38d Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:37:54 +0100 Subject: [PATCH 043/223] feat: apr19 multisig batch (#5979) ### Description feat: apr19 multisig batch - add new validators to default ethereum set - migrate ownership to new safes note: - infinityvm rpc was not playing nice at all. will defer this as it's been disabled from agents at the moment anyway. ### Drive-by changes - update gas prices - update token prices - update kf thresholds ### Related issues registry PR https://github.com/hyperlane-xyz/hyperlane-registry/pull/767 ### Backward compatibility ### Testing --------- Co-authored-by: Trevor Porter --- .../mainnet3/balances/dailyRelayerBurn.json | 138 ++++----- .../desiredRelayerBalanceOverrides.json | 3 +- .../balances/desiredRelayerBalances.json | 96 +++---- .../balances/highUrgencyRelayerBalance.json | 112 ++++---- .../lowUrgencyEngKeyFunderBalance.json | 112 ++++---- .../balances/lowUrgencyKeyFunderBalance.json | 86 +++--- .../environments/mainnet3/gasPrices.json | 90 +++--- .../mainnet3/governance/ica/regular.ts | 211 +++++++------- .../mainnet3/governance/safe/regular.ts | 44 +-- .../config/environments/mainnet3/owners.ts | 2 +- .../environments/mainnet3/tokenPrices.json | 264 +++++++++--------- .../scripts/check/check-validator-announce.ts | 84 +++--- typescript/infra/scripts/safes/parse-txs.ts | 9 +- typescript/infra/src/config/chain.ts | 3 + typescript/infra/src/config/gas-oracle.ts | 7 +- typescript/infra/src/governance.ts | 2 +- .../infra/src/tx/govern-transaction-reader.ts | 78 +++--- typescript/sdk/src/consts/multisigIsm.ts | 7 +- 18 files changed, 686 insertions(+), 662 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json index 4ec7a5ad674..0ae62de58ae 100644 --- a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json +++ b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json @@ -1,113 +1,113 @@ { "abstract": 0.00428, - "alephzeroevmmainnet": 29.2, - "ancient8": 0.00204, - "apechain": 5.46, + "alephzeroevmmainnet": 33.9, + "ancient8": 0.00238, + "apechain": 7.04, "appchain": 0.0155, "arbitrum": 0.0344, - "arbitrumnova": 0.00152, - "arcadia": 0.00152, - "artela": 671, - "arthera": 369, - "astar": 90.6, - "aurora": 0.0015, - "avalanche": 0.136, + "arbitrumnova": 0.00195, + "arcadia": 0.00195, + "artela": 3350, + "arthera": 511, + "astar": 116, + "aurora": 0.00195, + "avalanche": 0.16, "b3": 0.00284, "base": 0.0931, - "berachain": 0.534, + "berachain": 0.916, "bitlayer": 0.00114, "blast": 0.00305, "bob": 0.00197, - "boba": 0.00183, - "bouncebit": 21, + "boba": 0.00195, + "bouncebit": 30.4, "bsc": 0.264, "bsquared": 0.00192, - "celo": 7.63, - "cheesechain": 6810, - "chilizmainnet": 63.3, - "conflux": 32.8, - "conwai": 1310, + "celo": 10.1, + "cheesechain": 10600, + "chilizmainnet": 84, + "conflux": 44.5, + "conwai": 1760, "coredao": 6.82, "corn": 0.0000356, - "coti": 34.8, + "coti": 47.3, "cyber": 0.00321, - "deepbrainchain": 1850, - "degenchain": 875, - "dogechain": 15.5, - "duckchain": 0.863, + "deepbrainchain": 3640, + "degenchain": 1230, + "dogechain": 19.7, + "duckchain": 1.05, "eclipsemainnet": 0.0232, - "endurance": 4.15, + "endurance": 6.29, "ethereum": 0.135, "everclear": 0.0081, - "evmos": 624, + "evmos": 994, "fantom": 7.39, - "flame": 1.03, + "flame": 1.3, "flare": 208, - "flowmainnet": 7.19, - "form": 0.00176, + "flowmainnet": 8.44, + "form": 0.00195, "fraxtal": 0.0217, "fusemainnet": 254, "glue": 28.8, "gnosis": 3.12, - "gravity": 174, - "guru": 758, - "harmony": 243, + "gravity": 219, + "guru": 1390, + "harmony": 285, "hemi": 0.00207, "hyperevm": 0.294, - "immutablezkevmmainnet": 4.69, - "inevm": 0.288, + "immutablezkevmmainnet": 6.74, + "inevm": 0.379, "infinityvm": 3.13, - "injective": 0.288, + "injective": 0.379, "ink": 0.0233, - "kaia": 27.8, - "kroma": 0.00177, + "kaia": 31.1, + "kroma": 0.00195, "linea": 0.0765, "lisk": 0.0104, "lukso": 3.66, - "lumia": 5.94, - "lumiaprism": 6.71, + "lumia": 9.31, + "lumiaprism": 11, "mantapacific": 0.00336, "mantle": 7.51, "matchain": 0.00537, "merlin": 0.000172, "metal": 0.00791, - "metis": 0.164, - "mint": 0.00253, + "metis": 0.229, + "mint": 0.00272, "mode": 0.0229, - "molten": 13.8, - "moonbeam": 34.1, + "molten": 24.4, + "moonbeam": 46.5, "morph": 0.178, "nero": 3.13, - "neutron": 20.2, - "nibiru": 148, - "oortmainnet": 52.2, - "opbnb": 0.00496, + "neutron": 25.5, + "nibiru": 183, + "oortmainnet": 73.6, + "opbnb": 0.00528, "optimism": 0.0383, - "orderly": 0.00249, - "osmosis": 11.2, + "orderly": 0.00281, + "osmosis": 14.4, "plume": 23, - "polygon": 13.1, + "polygon": 16.4, "polygonzkevm": 0.0244, "polynomialfi": 0.00275, "prom": 1.55, - "proofofplay": 0.00152, + "proofofplay": 0.00195, "rarichain": 0.00329, - "reactive": 35.5, - "real": 0.00152, + "reactive": 38.8, + "real": 0.00195, "redstone": 0.00217, - "rivalz": 0.00152, - "ronin": 3.89, + "rivalz": 0.00195, + "ronin": 6.23, "rootstockmainnet": 0.00165, - "sanko": 0.309, + "sanko": 0.483, "scroll": 0.00951, - "sei": 14.4, + "sei": 18.4, "shibarium": 11.3, - "snaxchain": 0.00222, + "snaxchain": 0.00283, "solanamainnet": 3.27, "soneium": 0.0394, "sonic": 7.39, "sonicsvm": 0.165, - "soon": 0.00152, + "soon": 0.00195, "sophon": 46.4, "story": 1.67, "stride": 15.5, @@ -119,20 +119,20 @@ "tangle": 3.13, "telos": 35.1, "torus": 22.7, - "treasure": 37.4, - "trumpchain": 0.264, + "treasure": 39.3, + "trumpchain": 0.376, "unichain": 0.00897, - "unitzero": 10.8, - "vana": 0.576, - "viction": 12.6, + "unitzero": 13.3, + "vana": 0.608, + "viction": 15.6, "worldchain": 0.0027, - "xai": 44.3, + "xai": 71.2, "xlayer": 0.114, - "xpla": 79.4, - "zeronetwork": 0.00152, - "zetachain": 10.7, + "xpla": 114, + "zeronetwork": 0.00195, + "zetachain": 13.3, "zircuit": 0.00424, - "zklink": 0.00152, - "zksync": 0.00159, + "zklink": 0.00195, + "zksync": 0.00195, "zoramainnet": 0.00876 } diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json index 72f3e3e174c..3861ccf7bd8 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json @@ -1,4 +1,5 @@ { + "artela": 2200, "arthera": 0.5, "coti": 0.1, "deepbrainchain": 100, @@ -7,7 +8,7 @@ "osmosis": 0, "plume": 0.05, "reactive": 0.1, - "sophon": 20, + "sophon": 50, "tangle": 6, "vana": 0.001 } diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index e525d193b4f..7b83a2b9a42 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -1,71 +1,71 @@ { "abstract": 0.0342, - "alephzeroevmmainnet": 234, - "ancient8": 0.0163, - "apechain": 50, + "alephzeroevmmainnet": 271, + "ancient8": 0.019, + "apechain": 56.3, "appchain": 0.124, "arbitrum": 0.275, - "arbitrumnova": 0.0122, - "arcadia": 0.0122, - "artela": 5370, + "arbitrumnova": 0.0156, + "arcadia": 0.0156, + "artela": 2200, "arthera": 0.5, - "astar": 725, - "aurora": 0.012, - "avalanche": 1.09, + "astar": 928, + "aurora": 0.0156, + "avalanche": 1.28, "b3": 0.0227, "base": 0.745, - "berachain": 4.27, + "berachain": 7.33, "bitlayer": 0.00912, "blast": 0.0244, "bob": 0.0158, - "boba": 0.0146, - "bouncebit": 168, + "boba": 0.0156, + "bouncebit": 243, "bsc": 5, "bsquared": 0.0154, - "celo": 61, - "cheesechain": 54500, - "chilizmainnet": 506, - "conflux": 262, - "conwai": 10500, + "celo": 80.8, + "cheesechain": 84800, + "chilizmainnet": 672, + "conflux": 356, + "conwai": 14100, "coredao": 54.6, "corn": 0.000285, "coti": 0.1, "cyber": 0.0257, "deepbrainchain": 100, - "degenchain": 7000, - "dogechain": 124, - "duckchain": 6.9, + "degenchain": 9840, + "dogechain": 158, + "duckchain": 8.4, "eclipsemainnet": 0.2, - "endurance": 33.2, + "endurance": 50.3, "ethereum": 1.08, "everclear": 0.0648, - "evmos": 4990, + "evmos": 7950, "fantom": 59.1, - "flame": 8.24, + "flame": 10.4, "flare": 1660, - "flowmainnet": 57.5, - "form": 0.0141, + "flowmainnet": 67.5, + "form": 0.0156, "fraxtal": 0.2, "fusemainnet": 2030, "glue": 230, "gnosis": 25, - "gravity": 1390, - "guru": 6060, - "harmony": 1940, + "gravity": 1750, + "guru": 11100, + "harmony": 2280, "hemi": 0.0166, "hyperevm": 2.35, - "immutablezkevmmainnet": 37.5, - "inevm": 3, + "immutablezkevmmainnet": 53.9, + "inevm": 3.03, "infinityvm": 0, - "injective": 2.3, + "injective": 3.03, "ink": 0.186, "kaia": 250, "kroma": 0.05, "linea": 1, "lisk": 0.0832, "lukso": 29.3, - "lumia": 47.5, - "lumiaprism": 53.7, + "lumia": 74.5, + "lumiaprism": 88, "mantapacific": 0.2, "mantle": 60.1, "matchain": 0.05, @@ -74,19 +74,19 @@ "metis": 3, "mint": 0.05, "mode": 0.2, - "molten": 110, - "moonbeam": 273, + "molten": 195, + "moonbeam": 372, "morph": 1.42, "nero": 25, - "neutron": 162, + "neutron": 204, "nibiru": 10, "oortmainnet": 2000, - "opbnb": 0.0397, + "opbnb": 0.0422, "optimism": 0.5, "orderly": 0.05, "osmosis": 0, "plume": 0.05, - "polygon": 105, + "polygon": 131, "polygonzkevm": 0.5, "polynomialfi": 0.05, "prom": 18, @@ -96,11 +96,11 @@ "real": 0.1, "redstone": 0.2, "rivalz": 0.05, - "ronin": 31.1, + "ronin": 49.8, "rootstockmainnet": 0.0132, - "sanko": 2.47, + "sanko": 3.86, "scroll": 0.5, - "sei": 115, + "sei": 147, "shibarium": 90.4, "snaxchain": 0.05, "solanamainnet": 40, @@ -108,7 +108,7 @@ "sonic": 59.1, "sonicsvm": 1.32, "soon": 0.08, - "sophon": 20, + "sophon": 50, "story": 13.4, "stride": 124, "subtensor": 1.82, @@ -120,17 +120,17 @@ "telos": 281, "torus": 182, "treasure": 900, - "trumpchain": 2.11, + "trumpchain": 3.01, "unichain": 0.0718, - "unitzero": 86.4, + "unitzero": 106, "vana": 0.001, - "viction": 101, + "viction": 125, "worldchain": 0.2, - "xai": 354, + "xai": 570, "xlayer": 0.912, - "xpla": 635, + "xpla": 912, "zeronetwork": 0.05, - "zetachain": 85.6, + "zetachain": 106, "zircuit": 0.0339, "zklink": 0.05, "zksync": 0.05, diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index 26336c18b26..a57e55ab69b 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -1,112 +1,112 @@ { "abstract": 0.00856, - "alephzeroevmmainnet": 58.4, - "ancient8": 0.00408, - "apechain": 10.9, + "alephzeroevmmainnet": 67.8, + "ancient8": 0.00476, + "apechain": 14.1, "appchain": 0.031, "arbitrum": 0.0688, - "arbitrumnova": 0.00304, - "arcadia": 0.00304, - "artela": 1340, + "arbitrumnova": 0.0039, + "arcadia": 0.0039, + "artela": 550, "arthera": 0.125, - "astar": 181, - "aurora": 0.003, - "avalanche": 0.272, + "astar": 232, + "aurora": 0.0039, + "avalanche": 0.32, "b3": 0.00568, "base": 0.186, - "berachain": 1.07, + "berachain": 1.83, "bitlayer": 0.00228, "blast": 0.0061, "bob": 0.00394, - "boba": 0.00366, - "bouncebit": 42, + "boba": 0.0039, + "bouncebit": 60.8, "bsc": 0.528, "bsquared": 0.00384, - "celo": 15.3, - "cheesechain": 13600, - "chilizmainnet": 127, - "conflux": 65.6, - "conwai": 2620, + "celo": 20.2, + "cheesechain": 21200, + "chilizmainnet": 168, + "conflux": 89, + "conwai": 3520, "coredao": 13.6, "corn": 0.0000712, "coti": 0.025, "cyber": 0.00642, "deepbrainchain": 25, - "degenchain": 1750, - "dogechain": 31, - "duckchain": 1.73, + "degenchain": 2460, + "dogechain": 39.4, + "duckchain": 2.1, "eclipsemainnet": 0.05, - "endurance": 8.3, + "endurance": 12.6, "ethereum": 0.27, "everclear": 0.0162, - "evmos": 1250, + "evmos": 1990, "fantom": 14.8, - "flame": 2.06, + "flame": 2.6, "flare": 416, - "flowmainnet": 14.4, - "form": 0.00352, + "flowmainnet": 16.9, + "form": 0.0039, "fraxtal": 0.0434, "fusemainnet": 508, "glue": 57.6, "gnosis": 6.24, - "gravity": 348, - "guru": 1520, - "harmony": 486, + "gravity": 438, + "guru": 2780, + "harmony": 570, "hemi": 0.00414, "hyperevm": 1, - "immutablezkevmmainnet": 9.38, - "inevm": 0.576, - "injective": 0.576, + "immutablezkevmmainnet": 13.5, + "inevm": 0.758, + "injective": 0.758, "ink": 0.0466, - "kaia": 55.6, - "kroma": 0.00354, + "kaia": 62.2, + "kroma": 0.0039, "linea": 0.22, "lisk": 0.0208, "lukso": 7.32, - "lumia": 11.9, - "lumiaprism": 13.4, + "lumia": 18.6, + "lumiaprism": 22, "mantapacific": 0.02, "mantle": 15, "matchain": 0.0107, "merlin": 0.000344, "metal": 0.0158, "metis": 1, - "mint": 0.00506, + "mint": 0.00544, "mode": 0.0458, - "molten": 27.6, - "moonbeam": 68.2, + "molten": 48.8, + "moonbeam": 93, "morph": 0.356, "nero": 6.26, - "neutron": 40.4, + "neutron": 51, "nibiru": 2.5, "oortmainnet": 400, - "opbnb": 0.00992, + "opbnb": 0.0106, "optimism": 0.083, - "orderly": 0.00498, + "orderly": 0.00562, "plume": 0.0125, - "polygon": 26.2, + "polygon": 32.8, "polygonzkevm": 0.0488, "polynomialfi": 0.0055, "prom": 3.1, - "proofofplay": 0.00304, + "proofofplay": 0.0039, "rarichain": 0.00658, "reactive": 0.025, - "real": 0.00304, + "real": 0.0039, "redstone": 0.00434, - "rivalz": 0.00304, - "ronin": 7.78, + "rivalz": 0.0039, + "ronin": 12.5, "rootstockmainnet": 0.0033, - "sanko": 0.618, + "sanko": 0.966, "scroll": 0.022, - "sei": 28.8, + "sei": 36.8, "shibarium": 22.6, - "snaxchain": 0.00444, + "snaxchain": 0.00566, "solanamainnet": 10, "soneium": 0.0788, "sonic": 14.8, "sonicsvm": 0.5, "soon": 0.02, - "sophon": 5, + "sophon": 12.5, "story": 3.34, "stride": 31, "subtensor": 0.456, @@ -118,17 +118,17 @@ "telos": 70.2, "torus": 45.4, "treasure": 250, - "trumpchain": 0.528, + "trumpchain": 0.752, "unichain": 0.0179, - "unitzero": 21.6, + "unitzero": 26.6, "vana": 0.00025, - "viction": 25.2, + "viction": 31.2, "worldchain": 0.05, - "xai": 88.6, + "xai": 142, "xlayer": 0.228, - "xpla": 159, + "xpla": 228, "zeronetwork": 0.0125, - "zetachain": 21.4, + "zetachain": 26.6, "zircuit": 0.0094, "zklink": 0.0125, "zksync": 0.0125, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index f91f00b37bf..d19b9f5f158 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -1,112 +1,112 @@ { "abstract": 0.0257, - "alephzeroevmmainnet": 175, - "ancient8": 0.0122, - "apechain": 32.8, + "alephzeroevmmainnet": 203, + "ancient8": 0.0143, + "apechain": 42.2, "appchain": 0.093, "arbitrum": 0.206, - "arbitrumnova": 0.00912, - "arcadia": 0.00912, - "artela": 4030, + "arbitrumnova": 0.0117, + "arcadia": 0.0117, + "artela": 1650, "arthera": 0.375, - "astar": 544, - "aurora": 0.009, - "avalanche": 0.816, + "astar": 696, + "aurora": 0.0117, + "avalanche": 0.96, "b3": 0.017, "base": 0.559, - "berachain": 3.2, + "berachain": 5.5, "bitlayer": 0.00684, "blast": 0.0183, "bob": 0.0118, - "boba": 0.011, - "bouncebit": 126, + "boba": 0.0117, + "bouncebit": 182, "bsc": 1.58, "bsquared": 0.0115, - "celo": 45.8, - "cheesechain": 40900, - "chilizmainnet": 380, - "conflux": 197, - "conwai": 7860, + "celo": 60.6, + "cheesechain": 63600, + "chilizmainnet": 504, + "conflux": 267, + "conwai": 10600, "coredao": 40.9, "corn": 0.000214, "coti": 0.075, "cyber": 0.0193, "deepbrainchain": 75, - "degenchain": 5250, - "dogechain": 93, - "duckchain": 5.18, + "degenchain": 7380, + "dogechain": 118, + "duckchain": 6.3, "eclipsemainnet": 0.15, - "endurance": 24.9, + "endurance": 37.7, "ethereum": 0.81, "everclear": 0.0486, - "evmos": 3740, + "evmos": 5960, "fantom": 44.3, - "flame": 6.18, + "flame": 7.8, "flare": 1250, - "flowmainnet": 43.1, - "form": 0.0106, + "flowmainnet": 50.6, + "form": 0.0117, "fraxtal": 0.13, "fusemainnet": 1520, "glue": 173, "gnosis": 18.7, - "gravity": 1040, - "guru": 4550, - "harmony": 1460, + "gravity": 1310, + "guru": 8340, + "harmony": 1710, "hemi": 0.0124, "hyperevm": 1.76, - "immutablezkevmmainnet": 28.1, - "inevm": 1.73, - "injective": 1.73, + "immutablezkevmmainnet": 40.4, + "inevm": 2.27, + "injective": 2.27, "ink": 0.14, - "kaia": 167, - "kroma": 0.0106, + "kaia": 187, + "kroma": 0.0117, "linea": 0.459, "lisk": 0.0624, "lukso": 22, - "lumia": 35.6, - "lumiaprism": 40.3, + "lumia": 55.9, + "lumiaprism": 66, "mantapacific": 0.06, "mantle": 45.1, "matchain": 0.0322, "merlin": 0.00103, "metal": 0.0475, "metis": 2.5, - "mint": 0.0152, + "mint": 0.0163, "mode": 0.137, - "molten": 82.8, - "moonbeam": 205, + "molten": 146, + "moonbeam": 279, "morph": 1.07, "nero": 18.8, - "neutron": 121, + "neutron": 153, "nibiru": 7.5, "oortmainnet": 1800, - "opbnb": 0.0298, + "opbnb": 0.0317, "optimism": 0.23, - "orderly": 0.0149, + "orderly": 0.0169, "plume": 0.0375, - "polygon": 78.6, + "polygon": 98.4, "polygonzkevm": 0.146, "polynomialfi": 0.0165, "prom": 9.3, - "proofofplay": 0.00912, + "proofofplay": 0.0117, "rarichain": 0.0197, "reactive": 0.075, - "real": 0.00912, + "real": 0.0117, "redstone": 0.013, - "rivalz": 0.00912, - "ronin": 23.3, + "rivalz": 0.0117, + "ronin": 37.4, "rootstockmainnet": 0.0099, - "sanko": 1.85, + "sanko": 2.9, "scroll": 0.0571, - "sei": 86.4, + "sei": 110, "shibarium": 67.8, - "snaxchain": 0.0133, + "snaxchain": 0.017, "solanamainnet": 30, "soneium": 0.236, "sonic": 44.3, "sonicsvm": 0.99, "soon": 0.05, - "sophon": 15, + "sophon": 37.5, "story": 10, "stride": 93, "subtensor": 1.37, @@ -118,17 +118,17 @@ "telos": 211, "torus": 136, "treasure": 618, - "trumpchain": 1.58, + "trumpchain": 2.26, "unichain": 0.0538, - "unitzero": 64.8, + "unitzero": 79.8, "vana": 0.00075, - "viction": 75.6, + "viction": 93.6, "worldchain": 0.15, - "xai": 266, + "xai": 427, "xlayer": 0.684, - "xpla": 476, + "xpla": 684, "zeronetwork": 0.0375, - "zetachain": 64.2, + "zetachain": 79.8, "zircuit": 0.0254, "zklink": 0.0375, "zksync": 0.0375, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index 1806373875b..007b0aa1ca5 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -1,70 +1,70 @@ { "abstract": 0.0514, - "alephzeroevmmainnet": 350, - "ancient8": 0.0245, + "alephzeroevmmainnet": 407, + "ancient8": 0.0286, "apechain": 100, "appchain": 0.186, "arbitrum": 0.413, - "arbitrumnova": 0.0182, - "arcadia": 0.0182, - "artela": 8050, + "arbitrumnova": 0.0234, + "arcadia": 0.0234, + "artela": 3300, "arthera": 0.75, - "astar": 1090, - "aurora": 0.018, - "avalanche": 1.63, + "astar": 1390, + "aurora": 0.0234, + "avalanche": 1.92, "b3": 0.0341, "base": 1.12, - "berachain": 6.41, + "berachain": 11, "bitlayer": 0.0137, "blast": 0.0366, "bob": 0.0236, - "boba": 0.022, - "bouncebit": 252, + "boba": 0.0234, + "bouncebit": 365, "bsc": 8, "bsquared": 0.023, "celo": 308, - "cheesechain": 81700, - "chilizmainnet": 760, - "conflux": 394, - "conwai": 15700, + "cheesechain": 127000, + "chilizmainnet": 1010, + "conflux": 534, + "conwai": 21100, "coredao": 81.8, "corn": 0.000427, "coti": 0.15, "cyber": 0.0385, "deepbrainchain": 150, - "degenchain": 10500, - "dogechain": 200, - "duckchain": 10.4, + "degenchain": 14800, + "dogechain": 236, + "duckchain": 12.6, "eclipsemainnet": 0.3, - "endurance": 49.8, + "endurance": 75.5, "ethereum": 1.8, "everclear": 0.1, - "evmos": 7490, + "evmos": 11900, "fantom": 88.7, - "flame": 12.4, + "flame": 15.6, "flare": 2500, - "flowmainnet": 86.3, - "form": 0.0211, + "flowmainnet": 101, + "form": 0.0234, "fraxtal": 0.4, "fusemainnet": 3050, "glue": 346, "gnosis": 37.4, - "gravity": 2090, - "guru": 9100, - "harmony": 2920, + "gravity": 2630, + "guru": 16700, + "harmony": 3420, "hemi": 0.0248, "hyperevm": 10, - "immutablezkevmmainnet": 56.3, + "immutablezkevmmainnet": 80.9, "inevm": 6.1, - "injective": 3.46, + "injective": 4.55, "ink": 0.28, "kaia": 500, "kroma": 0.1, "linea": 2, "lisk": 0.2, "lukso": 43.9, - "lumia": 71.3, - "lumiaprism": 80.5, + "lumia": 112, + "lumiaprism": 132, "mantapacific": 0.4, "mantle": 90.1, "matchain": 0.0644, @@ -73,14 +73,14 @@ "metis": 6, "mint": 0.1, "mode": 0.4, - "molten": 166, + "molten": 293, "moonbeam": 700, "morph": 2.14, "nero": 37.6, - "neutron": 242, + "neutron": 306, "nibiru": 15, "oortmainnet": 4000, - "opbnb": 0.0595, + "opbnb": 0.0634, "optimism": 1.2, "orderly": 0.1, "plume": 0.075, @@ -94,11 +94,11 @@ "real": 0.2, "redstone": 0.4, "rivalz": 0.1, - "ronin": 46.7, + "ronin": 74.8, "rootstockmainnet": 0.0198, - "sanko": 3.71, + "sanko": 5.8, "scroll": 1.1, - "sei": 173, + "sei": 221, "shibarium": 136, "snaxchain": 0.075, "solanamainnet": 60, @@ -106,7 +106,7 @@ "sonic": 88.7, "sonicsvm": 1.98, "soon": 0.1, - "sophon": 30, + "sophon": 75, "story": 20, "stride": 186, "subtensor": 2.74, @@ -118,17 +118,17 @@ "telos": 421, "torus": 272, "treasure": 1800, - "trumpchain": 3.17, + "trumpchain": 4.51, "unichain": 0.2, - "unitzero": 130, + "unitzero": 160, "vana": 0.0015, - "viction": 151, + "viction": 187, "worldchain": 0.4, - "xai": 532, + "xai": 854, "xlayer": 1.37, - "xpla": 953, + "xpla": 1370, "zeronetwork": 0.1, - "zetachain": 128, + "zetachain": 160, "zircuit": 0.0509, "zklink": 0.075, "zksync": 0.1, diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index 144e04b5701..bc265a7db57 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -20,7 +20,7 @@ "decimals": 9 }, "arbitrum": { - "amount": "0.068903", + "amount": "0.01", "decimals": 9 }, "arbitrumnova": { @@ -36,7 +36,7 @@ "decimals": 9 }, "arthera": { - "amount": "1.025041", + "amount": "1.02503", "decimals": 9 }, "astar": { @@ -52,11 +52,11 @@ "decimals": 9 }, "flame": { - "amount": "20.0", + "amount": "12.1", "decimals": 9 }, "avalanche": { - "amount": "1.46913265", + "amount": "0.097870287", "decimals": 9 }, "b3": { @@ -64,11 +64,11 @@ "decimals": 9 }, "base": { - "amount": "0.002901959", + "amount": "0.003076709", "decimals": 9 }, "berachain": { - "amount": "0.002338637", + "amount": "0.000992963", "decimals": 9 }, "bitlayer": { @@ -76,7 +76,7 @@ "decimals": 9 }, "blast": { - "amount": "0.002047803", + "amount": "0.001450792", "decimals": 9 }, "bob": { @@ -84,7 +84,7 @@ "decimals": 9 }, "boba": { - "amount": "0.001000082", + "amount": "0.001000096", "decimals": 9 }, "bsc": { @@ -96,7 +96,7 @@ "decimals": 9 }, "celo": { - "amount": "50.0", + "amount": "25.001", "decimals": 9 }, "cheesechain": { @@ -104,7 +104,7 @@ "decimals": 9 }, "chilizmainnet": { - "amount": "5000.0", + "amount": "2501.0", "decimals": 9 }, "conflux": { @@ -124,7 +124,7 @@ "decimals": 9 }, "coti": { - "amount": "0.005000007", + "amount": "1.500000007", "decimals": 9 }, "cyber": { @@ -156,7 +156,7 @@ "decimals": 9 }, "ethereum": { - "amount": "1.718727762", + "amount": "7.5", "decimals": 9 }, "everclear": { @@ -164,11 +164,11 @@ "decimals": 9 }, "evmos": { - "amount": "27.5", + "amount": "33.375931531", "decimals": 9 }, "fantom": { - "amount": "1.023229", + "amount": "1.026999", "decimals": 9 }, "flare": { @@ -184,7 +184,7 @@ "decimals": 9 }, "fraxtal": { - "amount": "0.00100101", + "amount": "0.001000253", "decimals": 9 }, "fusemainnet": { @@ -196,7 +196,7 @@ "decimals": 9 }, "gnosis": { - "amount": "1.000000007", + "amount": "1.000000008", "decimals": 9 }, "gravity": { @@ -220,7 +220,7 @@ "decimals": 9 }, "immutablezkevmmainnet": { - "amount": "11.00000005", + "amount": "11.000000049", "decimals": 9 }, "inevm": { @@ -232,7 +232,7 @@ "decimals": 9 }, "ink": { - "amount": "0.001000253", + "amount": "0.001000257", "decimals": 9 }, "injective": { @@ -248,7 +248,7 @@ "decimals": 9 }, "linea": { - "amount": "0.1074276", + "amount": "0.055646648", "decimals": 9 }, "lisk": { @@ -256,19 +256,19 @@ "decimals": 9 }, "lukso": { - "amount": "0.243957052", + "amount": "0.100026017", "decimals": 9 }, "lumia": { - "amount": "1.71", + "amount": "1.0", "decimals": 9 }, "lumiaprism": { - "amount": "1.71", + "amount": "1.0", "decimals": 9 }, "mantapacific": { - "amount": "0.003026419", + "amount": "0.003000488", "decimals": 9 }, "mantle": { @@ -276,7 +276,7 @@ "decimals": 9 }, "matchain": { - "amount": "0.001000008", + "amount": "0.001", "decimals": 9 }, "merlin": { @@ -288,7 +288,7 @@ "decimals": 9 }, "metis": { - "amount": "3.003118323", + "amount": "3.76552424", "decimals": 9 }, "mint": { @@ -296,7 +296,7 @@ "decimals": 9 }, "mode": { - "amount": "0.00100032", + "amount": "0.001000314", "decimals": 9 }, "molten": { @@ -308,7 +308,7 @@ "decimals": 9 }, "morph": { - "amount": "0.901", + "amount": "0.063", "decimals": 9 }, "nero": { @@ -332,7 +332,7 @@ "decimals": 9 }, "optimism": { - "amount": "0.001345842", + "amount": "0.002404968", "decimals": 9 }, "orderly": { @@ -344,15 +344,15 @@ "decimals": 1 }, "plume": { - "amount": "0.01", + "amount": "100.0", "decimals": 9 }, "polygon": { - "amount": "91.760798759", + "amount": "30.000000034", "decimals": 9 }, "polygonzkevm": { - "amount": "0.0257", + "amount": "0.01", "decimals": 9 }, "polynomialfi": { @@ -400,15 +400,15 @@ "decimals": 9 }, "scroll": { - "amount": "0.039318533", + "amount": "0.046868564", "decimals": 9 }, "sei": { - "amount": "1.1", + "amount": "1.100326643", "decimals": 9 }, "shibarium": { - "amount": "3.417583731", + "amount": "2.523983809", "decimals": 9 }, "snaxchain": { @@ -420,7 +420,7 @@ "decimals": 1 }, "soneium": { - "amount": "0.069907155", + "amount": "0.001134841", "decimals": 9 }, "sonic": { @@ -436,11 +436,11 @@ "decimals": 1 }, "sophon": { - "amount": "2214.613224136", + "amount": "2022.206337798", "decimals": 9 }, "story": { - "amount": "0.001298711", + "amount": "0.001000186", "decimals": 9 }, "stride": { @@ -448,7 +448,7 @@ "decimals": 1 }, "subtensor": { - "amount": "10.0", + "amount": "10.14637", "decimals": 9 }, "superseed": { @@ -464,7 +464,7 @@ "decimals": 9 }, "taiko": { - "amount": "0.010517821", + "amount": "0.008988459", "decimals": 9 }, "tangle": { @@ -472,7 +472,7 @@ "decimals": 9 }, "telos": { - "amount": "525.424236562", + "amount": "527.745263691", "decimals": 9 }, "torus": { @@ -480,7 +480,7 @@ "decimals": 9 }, "treasure": { - "amount": "767.364249527", + "amount": "1023.72101162", "decimals": 9 }, "trumpchain": { @@ -488,15 +488,15 @@ "decimals": 9 }, "unichain": { - "amount": "0.001000254", + "amount": "0.001000253", "decimals": 9 }, "unitzero": { - "amount": "11.000000042", + "amount": "2.000000007", "decimals": 9 }, "vana": { - "amount": "0.312673957", + "amount": "0.001006296", "decimals": 9 }, "viction": { @@ -504,7 +504,7 @@ "decimals": 9 }, "worldchain": { - "amount": "0.001000292", + "amount": "0.001000319", "decimals": 9 }, "xai": { diff --git a/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts b/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts index cecfe179e26..57e8add8029 100644 --- a/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts +++ b/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts @@ -2,110 +2,109 @@ import { ChainMap } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; export const regularIcas: ChainMap
= { - // PREDEPLOY ALL THE ICAS - // ancient8: '0xf789D8a609247c448E28d3af5b8EFC6Cb786C4ee', - // alephzeroevmmainnet: '0xA3dfBa2447F0A50706F22e38f2785e0Bf30BC069', - // apechain: '0x9422838f6fA763354756a3Aba18f34015cB4bF74', - // appchain: '0x8A4E9b5B445504727D7a9377eA329Bb38700F8FA', - // arbitrumnova: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // arcadia: '0x32879753603c140E34D46375F43Db8BdC9a8c545', - // artela: '0x2754282484EBC6103042BE6c2f17acFA96B2546a', - // arthera: '0x33fab5e0290b7b7a0f19Ba9Db8b97C6e7f7a8848', - // astar: '0x87433Bf4d46fB11C6c39c6F1876b95c9001D06E1', - // aurora: '0x793C99f45CF63dd101D7D6819e02333Fb6cFd57f', - // bouncebit: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', - // flame: '0x355e54B6D423db49735Cb701452bd98434A90BAd', - // avalanche: '0x3a1014df0202477a1222999c72bD36395904e8AB', - // b3: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // bitlayer: '0x39EBb0D2b62D623BBEB874505079a21d05A7Ab9d', - // bob: '0x4FB145Da6407F4F485A209332a38A5327B61f83e', - // boba: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', - // bsquared: '0x7C475877fc180f5aAB628Ae1B506316a0F3ADE5A', - // celo: '0x20D701Ac137BB131e735B403e0471b101423dDeC', - // cheesechain: '0x5e4277969e2EEEbe04091dc270c2363b6e694F8d', - // chilizmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', - // conflux: '0x04b7cBD37eeFe304655c7e8638BbE4ddEff576E8', - // conwai: '0x4BAe702C0D1cC7d6d343594735812b5723041528', - // coredao: '0x100D940B953b20378242Fe0FbC45792805d50556', - // corn: '0x4BAe702C0D1cC7d6d343594735812b5723041528', - // coti: '0x54b625734673868027c70F656B807588910f3a6f', - // cyber: '0x162F8F6B70f9EE57fED16C4337B90e939851ECA1', - // deepbrainchain: '0x1fbE174fc6B1d3123B890f34aBd982577377Bec8', - // degenchain: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', - // dogechain: '0x100D940B953b20378242Fe0FbC45792805d50556', - // duckchain: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', - // endurance: '0xd8b01D2fA7F50889eE0f51114D00ab4c8581A5F4', - // everclear: '0x975866f773aC8f6d816089B0A5Ab9DC8A01c9462', - // evmos: '0x4BAe702C0D1cC7d6d343594735812b5723041528', - // fantom: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // flare: '0x68be5bC1b1E373f3c6a278D290F39461DDD21968', - // flowmainnet: '0x8e9558D9bA4d3FB52ea75D18B337f778589AB1aF', - // form: '0x4BAe702C0D1cC7d6d343594735812b5723041528', - // fusemainnet: '0x4601d6260C532E73187248D0608cB88D446149AD', - // glue: '0x0Fb0635eAbB9332eDc2544A57e7C15fBc5204C0B', - // gnosis: '0x2984A89A1A0331ae200FE2Cb46cCa190e795672E', - // gravity: '0x4Ce5ecE7349E467EBd77F39Eb4232Dd8C6a7314D', - // guru: '0x562FF51582Ec53466c4B8506d3a2FBD3B2F675f4', - // harmony: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // hemi: '0xa16C2860414e52945Bae3eb957cC0897A21f04a6', - // immutablezkevmmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', - // inevm: '0x8427F6021a84a992008DA6a949cad986CEB0d0b3', - // infinityvm: '0xf0c8600D83dC8B517C347cb1D45e02B1F95057bb', - // ink: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', - // kaia: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // kroma: '0xBd887119d776f0c990e9a03B1A157A107CD45033', - // lisk: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', - // lukso: '0xBd887119d776f0c990e9a03B1A157A107CD45033', - // lumiaprism: '0x60a51cB66CF6012A2adF27090fb7D51aE17369CF', - // mantle: '0x9652721a385AF5B410AA0865Df959255a798F761', - // matchain: '0x262E9A3eDA180fCb84846BaBa2aEB9EDa88e9BeF', - // merlin: '0xe4a82d029b3aB2D8Ed12b6800e86452E979D8c5c', - // metal: '0x9E359Bd54B59D28218a47E4C9a1e84568f09aFb4', - // metis: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', - // mint: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', - // molten: '0x100D940B953b20378242Fe0FbC45792805d50556', - // moonbeam: '0xb3178E470E959340206AD9d9eE4E53d0911b2ba9', - // morph: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // nero: '0x4601d6260C532E73187248D0608cB88D446149AD', - // nibiru: '0x966A57078204CA6cA13f091eE66eE61Ada5D2318', - // oortmainnet: '0xBc2cc7372C018C2a0b1F78E29D8B49d21bc5f3bA', - // opbnb: '0x1fbE174fc6B1d3123B890f34aBd982577377Bec8', - // orderly: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // plume: '0xB9d20ea5f3bB574C923F0af67b06b8D87F111819', - // polygon: '0xff5b2F0Ae1FFBf6b92Ea9B5851D7643C81102064', - // polygonzkevm: '0x258B14038C610f7889C74162760A3A70Ce526CD5', - // polynomialfi: '0x45EFBEAD9009C2662a633ee6F307C90B647B7793', - // prom: '0x9123625687807F02FC92A9817cFb8db13A9a8B4d', - // proofofplay: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', - // rarichain: '0x80c7c5fF8F84BEeDEd18382eb45aA3246063fbc5', - // reactive: '0x97B3BE8f063E98Da6ac254716DA194da4634aC80', - // real: '0xdc13fF36978e860d2FC98EaFBA4fDeB64E1D864A', - // redstone: '0x27C06C13E512Cdd80A2E49fc9c803cFd0de7Ba9e', - // rivalz: '0xBd887119d776f0c990e9a03B1A157A107CD45033', - // ronin: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', - // rootstockmainnet: '0x77d190423A18e78C95269f4D1349141D15E433f3', - // sanko: '0x27C06C13E512Cdd80A2E49fc9c803cFd0de7Ba9e', - // scroll: '0xDD2e306EE337952b4f7C0c4Eb44A8B5b9925Bc76', - // shibarium: '0x778f0e4CD1434258A811ab493217f3DC8d501C1b', - // snaxchain: '0x83be90021870A93ff794E00E8CFe454547877E3E', - // soneium: '0x4BAe702C0D1cC7d6d343594735812b5723041528', - // sonic: '0x4BAe702C0D1cC7d6d343594735812b5723041528', - // story: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', - // subtensor: '0xB9d20ea5f3bB574C923F0af67b06b8D87F111819', - // superseed: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', - // superpositionmainnet: '0x9f921e87309829ecc61F6df0F2016fD08B80CDD1', - // tangle: '0x60109e724a30E89fEB1877fAd006317f8b81768a', - // telos: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', - // torus: '0xBd887119d776f0c990e9a03B1A157A107CD45033', - // trumpchain: '0x3FAA6Ff31e4FC67957792303bA35092Ee0EBd367', - // unichain: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', - // unitzero: '0x262E9A3eDA180fCb84846BaBa2aEB9EDa88e9BeF', - // vana: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', - // viction: '0x426FC4C5CC60E5e47101fe30d4f8B94F1b7C1C70', - // worldchain: '0x51E4E4b7317043A1D6e2fe6ccd9dA51844C385f0', - // xai: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', - // xlayer: '0x73581C1c03E64BDE5775b7236B57d78155B75fA7', - // xpla: '0x0Fb0635eAbB9332eDc2544A57e7C15fBc5204C0B', - // zetachain: '0x33d8605D4dF6661259640268DD32BC99A2F55a83', - // zoramainnet: '0xdbB6E8FDe96dE3a1604b93884d8fF9Cb521059fC', + ancient8: '0xf789D8a609247c448E28d3af5b8EFC6Cb786C4ee', + alephzeroevmmainnet: '0xA3dfBa2447F0A50706F22e38f2785e0Bf30BC069', + apechain: '0x9422838f6fA763354756a3Aba18f34015cB4bF74', + appchain: '0x8A4E9b5B445504727D7a9377eA329Bb38700F8FA', + arbitrumnova: '0x83be90021870A93ff794E00E8CFe454547877E3E', + arcadia: '0x32879753603c140E34D46375F43Db8BdC9a8c545', + artela: '0x2754282484EBC6103042BE6c2f17acFA96B2546a', + arthera: '0x33fab5e0290b7b7a0f19Ba9Db8b97C6e7f7a8848', + astar: '0x87433Bf4d46fB11C6c39c6F1876b95c9001D06E1', + aurora: '0x793C99f45CF63dd101D7D6819e02333Fb6cFd57f', + bouncebit: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', + flame: '0x355e54B6D423db49735Cb701452bd98434A90BAd', + avalanche: '0x3a1014df0202477a1222999c72bD36395904e8AB', + b3: '0x83be90021870A93ff794E00E8CFe454547877E3E', + bitlayer: '0x39EBb0D2b62D623BBEB874505079a21d05A7Ab9d', + bob: '0x4FB145Da6407F4F485A209332a38A5327B61f83e', + boba: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + bsquared: '0x7C475877fc180f5aAB628Ae1B506316a0F3ADE5A', + celo: '0x20D701Ac137BB131e735B403e0471b101423dDeC', + cheesechain: '0x5e4277969e2EEEbe04091dc270c2363b6e694F8d', + chilizmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', + conflux: '0x04b7cBD37eeFe304655c7e8638BbE4ddEff576E8', + conwai: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + coredao: '0x100D940B953b20378242Fe0FbC45792805d50556', + corn: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + coti: '0x54b625734673868027c70F656B807588910f3a6f', + cyber: '0x162F8F6B70f9EE57fED16C4337B90e939851ECA1', + deepbrainchain: '0x1fbE174fc6B1d3123B890f34aBd982577377Bec8', + degenchain: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', + dogechain: '0x100D940B953b20378242Fe0FbC45792805d50556', + duckchain: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + endurance: '0xd8b01D2fA7F50889eE0f51114D00ab4c8581A5F4', + everclear: '0x975866f773aC8f6d816089B0A5Ab9DC8A01c9462', + evmos: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + fantom: '0x83be90021870A93ff794E00E8CFe454547877E3E', + flare: '0x68be5bC1b1E373f3c6a278D290F39461DDD21968', + flowmainnet: '0x8e9558D9bA4d3FB52ea75D18B337f778589AB1aF', + form: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + fusemainnet: '0x4601d6260C532E73187248D0608cB88D446149AD', + glue: '0x0Fb0635eAbB9332eDc2544A57e7C15fBc5204C0B', + gnosis: '0x2984A89A1A0331ae200FE2Cb46cCa190e795672E', + gravity: '0x4Ce5ecE7349E467EBd77F39Eb4232Dd8C6a7314D', + guru: '0x562FF51582Ec53466c4B8506d3a2FBD3B2F675f4', + harmony: '0x83be90021870A93ff794E00E8CFe454547877E3E', + hemi: '0xa16C2860414e52945Bae3eb957cC0897A21f04a6', + immutablezkevmmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', + inevm: '0x8427F6021a84a992008DA6a949cad986CEB0d0b3', + infinityvm: '0xf0c8600D83dC8B517C347cb1D45e02B1F95057bb', + ink: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', + kaia: '0x83be90021870A93ff794E00E8CFe454547877E3E', + kroma: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + lisk: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', + lukso: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + lumiaprism: '0x60a51cB66CF6012A2adF27090fb7D51aE17369CF', + mantle: '0x9652721a385AF5B410AA0865Df959255a798F761', + matchain: '0x262E9A3eDA180fCb84846BaBa2aEB9EDa88e9BeF', + merlin: '0xe4a82d029b3aB2D8Ed12b6800e86452E979D8c5c', + metal: '0x9E359Bd54B59D28218a47E4C9a1e84568f09aFb4', + metis: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', + mint: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', + molten: '0x100D940B953b20378242Fe0FbC45792805d50556', + moonbeam: '0xb3178E470E959340206AD9d9eE4E53d0911b2ba9', + morph: '0x83be90021870A93ff794E00E8CFe454547877E3E', + nero: '0x4601d6260C532E73187248D0608cB88D446149AD', + // nibiru: '0x966A57078204CA6cA13f091eE66eE61Ada5D2318', // temporary while looking into decimals + oortmainnet: '0xBc2cc7372C018C2a0b1F78E29D8B49d21bc5f3bA', + opbnb: '0x1fbE174fc6B1d3123B890f34aBd982577377Bec8', + orderly: '0x83be90021870A93ff794E00E8CFe454547877E3E', + plume: '0xB9d20ea5f3bB574C923F0af67b06b8D87F111819', + polygon: '0xff5b2F0Ae1FFBf6b92Ea9B5851D7643C81102064', + polygonzkevm: '0x258B14038C610f7889C74162760A3A70Ce526CD5', + polynomialfi: '0x45EFBEAD9009C2662a633ee6F307C90B647B7793', + prom: '0x9123625687807F02FC92A9817cFb8db13A9a8B4d', + proofofplay: '0x319aB070390C8f2A914a61f8DE366f3295bb44fF', + rarichain: '0x80c7c5fF8F84BEeDEd18382eb45aA3246063fbc5', + reactive: '0x97B3BE8f063E98Da6ac254716DA194da4634aC80', + real: '0xdc13fF36978e860d2FC98EaFBA4fDeB64E1D864A', + redstone: '0x27C06C13E512Cdd80A2E49fc9c803cFd0de7Ba9e', + rivalz: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + ronin: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', + rootstockmainnet: '0x77d190423A18e78C95269f4D1349141D15E433f3', + sanko: '0x27C06C13E512Cdd80A2E49fc9c803cFd0de7Ba9e', + scroll: '0xDD2e306EE337952b4f7C0c4Eb44A8B5b9925Bc76', + shibarium: '0x778f0e4CD1434258A811ab493217f3DC8d501C1b', + snaxchain: '0x83be90021870A93ff794E00E8CFe454547877E3E', + soneium: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + sonic: '0x4BAe702C0D1cC7d6d343594735812b5723041528', + story: '0xa3EC6913675e9686bfC458F02a0F737EdD0362c8', + subtensor: '0xB9d20ea5f3bB574C923F0af67b06b8D87F111819', + superseed: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + superpositionmainnet: '0x9f921e87309829ecc61F6df0F2016fD08B80CDD1', + tangle: '0x60109e724a30E89fEB1877fAd006317f8b81768a', + telos: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', + torus: '0xBd887119d776f0c990e9a03B1A157A107CD45033', + trumpchain: '0x3FAA6Ff31e4FC67957792303bA35092Ee0EBd367', + unichain: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + unitzero: '0x262E9A3eDA180fCb84846BaBa2aEB9EDa88e9BeF', + vana: '0xEc65c06c606006Db682197230bF8E7740C2BcFF9', + viction: '0x426FC4C5CC60E5e47101fe30d4f8B94F1b7C1C70', + worldchain: '0x51E4E4b7317043A1D6e2fe6ccd9dA51844C385f0', + xai: '0xF01B503E5cC884f0EB6eF062F704A610FD69842D', + xlayer: '0x73581C1c03E64BDE5775b7236B57d78155B75fA7', + xpla: '0x0Fb0635eAbB9332eDc2544A57e7C15fBc5204C0B', + zetachain: '0x33d8605D4dF6661259640268DD32BC99A2F55a83', + zoramainnet: '0xdbB6E8FDe96dE3a1604b93884d8fF9Cb521059fC', }; diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts b/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts index 2250f7d771f..acb47bbc64f 100644 --- a/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts +++ b/typescript/infra/config/environments/mainnet3/governance/safe/regular.ts @@ -2,26 +2,26 @@ import { ChainMap } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; export const regularSafes: ChainMap
= { - // abstract: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - // arbitrum: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // base: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - // berachain: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // blast: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // bsc: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // ethereum: '0x562Dfaac27A84be6C96273F5c9594DA1681C0DA7', - // fraxtal: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - // hyperevm: '0x290Eb7bbf939A36B2c350a668c04815E49757eDC', - // linea: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // mantapacific: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // mode: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // optimism: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - // sei: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // sophon: '0x113d3a19031Fe5DB58884D6aa54545dD4De499c0', - // swell: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', - // taiko: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', - // treasure: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - // zeronetwork: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - // zksync: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - // zklink: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', - // zircuit: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + abstract: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + arbitrum: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + base: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + berachain: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + blast: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + bsc: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + ethereum: '0x562Dfaac27A84be6C96273F5c9594DA1681C0DA7', + fraxtal: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + hyperevm: '0x290Eb7bbf939A36B2c350a668c04815E49757eDC', + linea: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + mantapacific: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + mode: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + optimism: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + sei: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + sophon: '0x113d3a19031Fe5DB58884D6aa54545dD4De499c0', + swell: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', + taiko: '0x890ac177Fe3052B8676A65f32C1589Bc329f3d50', + treasure: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zeronetwork: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zksync: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zklink: '0xcd81ccFe7D9306849136Fa96397113345a32ECf3', + zircuit: '0x7379D7bB2ccA68982E467632B6554fD4e72e9431', }; diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index cbceba86074..b10d98a4c55 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -48,7 +48,7 @@ export const ethereumChainOwners: ChainMap = Object.fromEntries( { owner, ownerOverrides: { - proxyAdmin: owner, + proxyAdmin: upgradeTimelocks[local] ?? owner, validatorAnnounce: DEPLOYER, // unused testRecipient: DEPLOYER, fallbackRoutingHook: DEPLOYER, diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index 8dfb056f5e4..b5a3096bdd9 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -1,138 +1,138 @@ { - "abstract": "2054.11", - "ancient8": "2054.11", - "alephzeroevmmainnet": "0.106945", - "apechain": "0.57248", - "appchain": "2054.11", - "arbitrum": "2054.11", - "arbitrumnova": "2054.11", - "arcadia": "2054.11", - "artela": "0.00465492", - "arthera": "0.00845931", - "astar": "0.03447512", - "aurora": "2054.11", - "bouncebit": "0.156235", - "flame": "3.65", - "avalanche": "22.58", - "b3": "2054.11", - "base": "2054.11", - "berachain": "7.87", - "bitlayer": "87747", - "blast": "2054.11", - "bob": "2054.11", - "boba": "2054.11", - "bsc": "629.83", - "bsquared": "87747", - "celo": "0.397064", - "cheesechain": "0.00051627", - "chilizmainnet": "0.04934281", - "conflux": "0.095348", - "conwai": "0.00239384", - "coredao": "0.458289", - "corn": "87747", - "coti": "0.089803", - "cyber": "2054.11", - "deepbrainchain": "0.00168509", - "degenchain": "0.00357109", - "dogechain": "0.201434", - "duckchain": "3.59", - "eclipsemainnet": "2054.11", - "endurance": "0.753059", - "ethereum": "2054.11", - "everclear": "2054.11", - "evmos": "0.00501066", - "fantom": "0.634786", - "flare": "0.01502485", - "flowmainnet": "0.434717", - "form": "2054.11", - "fraxtal": "2049.92", - "fusemainnet": "0.01229848", - "glue": "0.108488", - "gnosis": "1.003", - "gravity": "0.01714157", - "guru": "0.00412234", - "harmony": "0.01288063", - "hemi": "2054.11", - "hyperevm": "15.06", - "immutablezkevmmainnet": "0.665922", - "inevm": "10.84", + "abstract": "1599.53", + "ancient8": "1599.53", + "alephzeroevmmainnet": "0.092064", + "apechain": "0.443681", + "appchain": "1599.53", + "arbitrum": "1599.53", + "arbitrumnova": "1599.53", + "arcadia": "1599.53", + "artela": "0.00093234", + "arthera": "0.00611031", + "astar": "0.0268412", + "aurora": "1599.53", + "bouncebit": "0.102952", + "flame": "2.41", + "avalanche": "19.48", + "b3": "1599.53", + "base": "1599.53", + "berachain": "3.41", + "bitlayer": "85131", + "blast": "1599.53", + "bob": "1599.53", + "boba": "1599.53", + "bsc": "592.22", + "bsquared": "85131", + "celo": "0.308083", + "cheesechain": "0.00029572", + "chilizmainnet": "0.03718311", + "conflux": "0.070167", + "conwai": "0.00177459", + "coredao": "0.68447", + "corn": "85131", + "coti": "0.066086", + "cyber": "1599.53", + "deepbrainchain": "0.00085848", + "degenchain": "0.00254968", + "dogechain": "0.15876", + "duckchain": "2.99", + "eclipsemainnet": "1599.53", + "endurance": "0.496753", + "ethereum": "1599.53", + "everclear": "1599.53", + "evmos": "0.00314416", + "fantom": "0.464854", + "flare": "0.01602446", + "flowmainnet": "0.370122", + "form": "1599.53", + "fraxtal": "1595.81", + "fusemainnet": "0.01187583", + "glue": "0.123497", + "gnosis": "1", + "gravity": "0.0142485", + "guru": "0.00224974", + "harmony": "0.01097306", + "hemi": "1599.53", + "hyperevm": "18.16", + "immutablezkevmmainnet": "0.463869", + "inevm": "8.25", "infinityvm": "1", - "ink": "2054.11", - "injective": "10.84", - "kaia": "0.112228", - "kroma": "2054.11", - "linea": "2054.11", - "lisk": "2054.11", - "lukso": "0.8531", - "lumia": "0.52645", - "lumiaprism": "0.499733", - "mantapacific": "2054.11", - "mantle": "0.844786", - "matchain": "629.83", - "merlin": "87769", - "metal": "2054.11", - "metis": "19.03", - "mint": "2054.11", - "mode": "2054.11", - "molten": "0.2257", - "moonbeam": "0.091772", - "morph": "2054.11", + "ink": "1599.53", + "injective": "8.25", + "kaia": "0.100334", + "kroma": "1599.53", + "linea": "1599.53", + "lisk": "1599.53", + "lukso": "1.015", + "lumia": "0.335583", + "lumiaprism": "0.285301", + "mantapacific": "1599.53", + "mantle": "0.655372", + "matchain": "592.22", + "merlin": "85091", + "metal": "1599.53", + "metis": "13.62", + "mint": "1599.53", + "mode": "1599.53", + "molten": "0.127992", + "moonbeam": "0.067257", + "morph": "1599.53", "nero": "1", - "neutron": "0.154792", - "nibiru": "0.02117295", - "oortmainnet": "0.05927", - "opbnb": "629.83", - "optimism": "2054.11", - "orderly": "2054.11", - "osmosis": "0.3037", - "plume": "0.188632", - "polygon": "0.237997", - "polygonzkevm": "2054.11", - "polynomialfi": "2054.11", - "prom": "6.2", - "proofofplay": "2054.11", - "rarichain": "2054.11", - "reactive": "0.087976", - "real": "2054.11", - "redstone": "2054.11", - "rivalz": "2054.11", - "ronin": "0.802351", - "rootstockmainnet": "87076", - "sanko": "10.1", - "scroll": "2054.11", - "sei": "0.215432", - "shibarium": "0.399826", - "snaxchain": "2054.11", - "solanamainnet": "143.92", - "soneium": "2054.11", - "sonic": "0.634786", - "sonicsvm": "143.92", - "soon": "2054.11", + "neutron": "0.122576", + "nibiru": "0.01709542", + "oortmainnet": "0.0424777", + "opbnb": "592.22", + "optimism": "1599.53", + "orderly": "1599.53", + "osmosis": "0.216516", + "plume": "0.163739", + "polygon": "0.190008", + "polygonzkevm": "1599.53", + "polynomialfi": "1599.53", + "prom": "5.71", + "proofofplay": "1599.53", + "rarichain": "1599.53", + "reactive": "0.080498", + "real": "1599.53", + "redstone": "1599.53", + "rivalz": "1599.53", + "ronin": "0.501632", + "rootstockmainnet": "84707", + "sanko": "6.47", + "scroll": "1599.53", + "sei": "0.170216", + "shibarium": "0.27927", + "snaxchain": "1599.53", + "solanamainnet": "138.62", + "soneium": "1599.53", + "sonic": "0.464854", + "sonicsvm": "138.62", + "soon": "1599.53", "sophon": "1", - "story": "6.22", - "stride": "0.336439", - "subtensor": "273", - "superseed": "2054.11", - "superpositionmainnet": "2054.11", - "swell": "2054.11", - "taiko": "2054.11", + "story": "3.87", + "stride": "0.193807", + "subtensor": "280.37", + "superseed": "1599.53", + "superpositionmainnet": "1599.53", + "swell": "1599.53", + "taiko": "1599.53", "tangle": "1", - "telos": "0.105677", - "torus": "0.137549", - "treasure": "0.145629", - "trumpchain": "11.83", - "unichain": "2054.11", - "unitzero": "0.428111", - "vana": "7.18", - "viction": "0.248455", - "worldchain": "2054.11", - "xai": "0.07055", - "xlayer": "50.37", - "xpla": "0.03934581", - "zeronetwork": "2054.11", - "zetachain": "0.328577", - "zircuit": "2054.11", - "zklink": "2054.11", - "zksync": "2054.11", - "zoramainnet": "2054.11" + "telos": "0.086451", + "torus": "0.140374", + "treasure": "0.079584", + "trumpchain": "8.31", + "unichain": "1599.53", + "unitzero": "0.235709", + "vana": "5.14", + "viction": "0.200071", + "worldchain": "1599.53", + "xai": "0.04386052", + "xlayer": "50.69", + "xpla": "0.02738309", + "zeronetwork": "1599.53", + "zetachain": "0.234442", + "zircuit": "1599.53", + "zklink": "1599.53", + "zksync": "1599.53", + "zoramainnet": "1599.53" } diff --git a/typescript/infra/scripts/check/check-validator-announce.ts b/typescript/infra/scripts/check/check-validator-announce.ts index a57e89e8f7c..fb5f606b1b5 100644 --- a/typescript/infra/scripts/check/check-validator-announce.ts +++ b/typescript/infra/scripts/check/check-validator-announce.ts @@ -32,43 +32,55 @@ async function main() { const results = await Promise.all( targetNetworks.map(async (chain) => { - const validatorAnnounce = core.getContracts(chain).validatorAnnounce; - const announcedValidators = - await validatorAnnounce.getAnnouncedValidators(); - - const defaultValidatorConfigs = - defaultMultisigConfigs[chain].validators || []; - const validators = defaultValidatorConfigs.map((v) => v.address); - const unannouncedValidators = validators.filter( - (validator) => - !announcedValidators.some((x) => eqAddress(x, validator)), - ); - - if (unannouncedValidators.length > 0) { - chainsWithUnannouncedValidators[chain] = unannouncedValidators; + try { + const validatorAnnounce = core.getContracts(chain).validatorAnnounce; + const announcedValidators = + await validatorAnnounce.getAnnouncedValidators(); + + const defaultValidatorConfigs = + defaultMultisigConfigs[chain].validators || []; + const validators = defaultValidatorConfigs.map((v) => v.address); + const unannouncedValidators = validators.filter( + (validator) => + !announcedValidators.some((x) => eqAddress(x, validator)), + ); + + if (unannouncedValidators.length > 0) { + chainsWithUnannouncedValidators[chain] = unannouncedValidators; + } + + const validatorCount = validators.length; + const unannouncedValidatorCount = unannouncedValidators.length; + + const threshold = defaultMultisigConfigs[chain].threshold; + const minimumThreshold = getMinimumThreshold(validatorCount); + + return { + chain, + threshold, + [thresholdOK]: + threshold < minimumThreshold || threshold > validatorCount + ? CheckResult.WARNING + : CheckResult.OK, + total: validatorCount, + [totalOK]: + validatorCount < minimumValidatorCount + ? CheckResult.WARNING + : CheckResult.OK, + unannounced: + unannouncedValidatorCount > 0 ? unannouncedValidatorCount : '', + }; + } catch (error) { + console.error(`Error processing chain ${chain}:`, error); + return { + chain, + threshold: 'ERROR', + [thresholdOK]: CheckResult.WARNING, + total: 0, + [totalOK]: CheckResult.WARNING, + unannounced: 'ERROR', + }; } - - const validatorCount = validators.length; - const unannouncedValidatorCount = unannouncedValidators.length; - - const threshold = defaultMultisigConfigs[chain].threshold; - const minimumThreshold = getMinimumThreshold(validatorCount); - - return { - chain, - threshold, - [thresholdOK]: - threshold < minimumThreshold || threshold > validatorCount - ? CheckResult.WARNING - : CheckResult.OK, - total: validatorCount, - [totalOK]: - validatorCount < minimumValidatorCount - ? CheckResult.WARNING - : CheckResult.OK, - unannounced: - unannouncedValidatorCount > 0 ? unannouncedValidatorCount : '', - }; }), ); diff --git a/typescript/infra/scripts/safes/parse-txs.ts b/typescript/infra/scripts/safes/parse-txs.ts index f9bdbc40f66..02c64c349eb 100644 --- a/typescript/infra/scripts/safes/parse-txs.ts +++ b/typescript/infra/scripts/safes/parse-txs.ts @@ -102,11 +102,12 @@ async function main() { process.exit(1); } else { rootLogger.info('✅✅✅✅✅ No fatal errors ✅✅✅✅✅'); - const chainResults = Object.fromEntries(chainResultEntries); - const resultsPath = `safe-tx-results-${Date.now()}.yaml`; - writeYamlAtPath(resultsPath, chainResults); - rootLogger.info(`Results written to ${resultsPath}`); } + + const chainResults = Object.fromEntries(chainResultEntries); + const resultsPath = `safe-tx-results-${Date.now()}.yaml`; + writeYamlAtPath(resultsPath, chainResults); + rootLogger.info(`Results written to ${resultsPath}`); } main().catch((err) => { diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index 65a4bda00d9..766102eba3d 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -20,6 +20,9 @@ import { DeployEnvironment } from './environment.js'; // Used by scripts like check-owner-ica.ts to exclude chains that are temporarily // unsupported (e.g. zksync, zeronetwork) or have known issues (e.g. lumia). export const chainsToSkip: ChainName[] = [ + // TODO: complete work when RPC is available again + 'infinityvm', + // TODO: remove once zksync PR is merged into main // mainnets 'zksync', diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index 279ccaa482e..ddebc4ce768 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -167,6 +167,8 @@ function getMinUsdCost(local: ChainName, remote: ChainName): number { } const remoteMinCostOverrides: ChainMap = { + ethereum: 0.5, + // For Ethereum L2s, we need to account for the L1 DA costs that // aren't accounted for directly in the gas price. arbitrum: 0.5, @@ -179,11 +181,11 @@ function getMinUsdCost(local: ChainName, remote: ChainName): number { polygonzkevm: 0.5, // op stack chains - base: 0.2, + base: 0.5, fraxtal: 0.2, lisk: 0.2, mode: 0.2, - optimism: 0.2, + optimism: 0.5, soneium: 0.2, superseed: 0.2, unichain: 0.2, @@ -195,6 +197,7 @@ function getMinUsdCost(local: ChainName, remote: ChainName): number { neutron: 0.5, // For Solana, special min cost solanamainnet: 1.2, + bsc: 0.5, }; const override = remoteMinCostOverrides[remote]; if (override !== undefined) { diff --git a/typescript/infra/src/governance.ts b/typescript/infra/src/governance.ts index 19a7424e9db..ce95e00d7bb 100644 --- a/typescript/infra/src/governance.ts +++ b/typescript/infra/src/governance.ts @@ -34,7 +34,7 @@ export function withGovernanceType(args: Argv) { type: 'string', description: 'Type of governance to use', choices: Object.values(GovernanceType), - default: GovernanceType.AbacusWorks, + default: GovernanceType.Regular, }); } diff --git a/typescript/infra/src/tx/govern-transaction-reader.ts b/typescript/infra/src/tx/govern-transaction-reader.ts index c5892d5f050..89c04a2254c 100644 --- a/typescript/infra/src/tx/govern-transaction-reader.ts +++ b/typescript/infra/src/tx/govern-transaction-reader.ts @@ -4,6 +4,10 @@ import { MetaTransactionData, OperationType, } from '@safe-global/safe-core-sdk-types'; +import { + getMultiSendCallOnlyDeployments, + getMultiSendDeployments, +} from '@safe-global/safe-deployments'; import assert from 'assert'; import chalk from 'chalk'; import { BigNumber, ethers } from 'ethers'; @@ -29,6 +33,7 @@ import { normalizeConfig, } from '@hyperlane-xyz/sdk'; import { + Address, addressToBytes32, bytes32ToAddress, deepEquals, @@ -41,7 +46,6 @@ import { timelocks, } from '../../config/environments/mainnet3/owners.js'; import { DeployEnvironment } from '../config/environment.js'; -import { getSafeAndService } from '../utils/safe.js'; interface GovernTransaction extends Record { chain: ChainName; @@ -94,6 +98,9 @@ export class GovernTransactionReader { Record > = {}; + readonly multiSendCallOnlyDeployments: Address[] = []; + readonly multiSendDeployments: Address[] = []; + constructor( readonly environment: DeployEnvironment, readonly multiProvider: MultiProvider, @@ -113,6 +120,28 @@ export class GovernTransactionReader { this.warpRouteIndex[token.chainName][address] = token; } } + + // Get deployments for each version + const versions = ['1.3.0', '1.4.1']; + for (const version of versions) { + const multiSendCallOnlyDeployments = getMultiSendCallOnlyDeployments({ + version, + }); + const multiSendDeployments = getMultiSendDeployments({ + version, + }); + assert( + multiSendCallOnlyDeployments && multiSendDeployments, + `MultiSend and MultiSendCallOnly deployments not found for version ${version}`, + ); + + Object.values(multiSendCallOnlyDeployments.deployments).forEach((d) => { + this.multiSendCallOnlyDeployments.push(d.address); + }); + Object.values(multiSendDeployments.deployments).forEach((d) => { + this.multiSendDeployments.push(d.address); + }); + } } async read( @@ -139,8 +168,8 @@ export class GovernTransactionReader { return this.readTimelockControllerTransaction(chain, tx); } - // If it's a Multisend - if (await this.isMultisendTransaction(chain, tx)) { + // If it's a Multisend or MultisendCallOnly transaction + if (await this.isMultisendTransaction(tx)) { return this.readMultisendTransaction(chain, tx); } @@ -794,21 +823,17 @@ export class GovernTransactionReader { ); } - async isMultisendTransaction( - chain: ChainName, - tx: AnnotatedEV5Transaction, - ): Promise { + async isMultisendTransaction(tx: AnnotatedEV5Transaction): Promise { if (tx.to === undefined) { return false; } - const multiSendCallOnlyAddress = await this.getMultiSendCallOnlyAddress( - chain, - ); - if (!multiSendCallOnlyAddress) { - return false; - } - return eqAddress(multiSendCallOnlyAddress, tx.to); + // Check if the transaction is to a MultiSend or MultiSendCallOnly deployment + return ( + this.multiSendCallOnlyDeployments.some((addr) => + eqAddress(addr, tx.to!), + ) || this.multiSendDeployments.some((addr) => eqAddress(addr, tx.to!)) + ); } async isOwnableTransaction( @@ -827,31 +852,6 @@ export class GovernTransactionReader { return false; } } - - private multiSendCallOnlyAddressCache: ChainMap = {}; - - async getMultiSendCallOnlyAddress( - chain: ChainName, - ): Promise { - if (this.multiSendCallOnlyAddressCache[chain]) { - return this.multiSendCallOnlyAddressCache[chain]; - } - - const safe = this.safes[chain]; - if (!safe) { - return undefined; - } - - const { safeSdk } = await getSafeAndService( - chain, - this.multiProvider, - safe, - ); - - this.multiSendCallOnlyAddressCache[chain] = - safeSdk.getMultiSendCallOnlyAddress(); - return this.multiSendCallOnlyAddressCache[chain]; - } } function metaTransactionDataToEV5Transaction( diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index 764a0beaa31..65e75d5ab9e 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -758,7 +758,7 @@ export const defaultMultisigConfigs: ChainMap = { }, ethereum: { - threshold: 4, + threshold: 6, validators: [ { address: '0x03c842db86a6a3e524d4a6615390c1ea8e2b9541', @@ -776,6 +776,11 @@ export const defaultMultisigConfigs: ChainMap = { address: '0xbf1023eff3dba21263bf2db2add67a0d6bcda2de', alias: 'AVS: Pier Two', }, + { + address: '0x5d7442439959af11172bf92d9a8d21cf88d136e3', + alias: 'P2P', + }, + DEFAULT_ZKV_VALIDATOR, ], }, From f267f85e1168854ca3c1790e879f67a8ee605888 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 22 Apr 2025 18:13:36 +0100 Subject: [PATCH 044/223] feat: vanguard funding (#5986) ### Description feat: vanguard funding - script to manually fund vanguards with a special config - add vanguards to keyfunder ### Drive-by changes ### Related issues ### Backward compatibility ### Testing ![image](https://github.com/user-attachments/assets/ce44b06e-4583-4125-bc99-89224abeecdb) --------- Co-authored-by: Trevor Porter --- .../config/environments/mainnet3/funding.ts | 3 +- .../funding/fund-keys-from-deployer.ts | 16 ++ typescript/infra/scripts/funding/vanguard.ts | 244 ++++++++++++++++++ 3 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 typescript/infra/scripts/funding/vanguard.ts diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index f47f18b4ea0..d89c7e3c8d5 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -19,7 +19,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '4fd2990-20250414-150005', + tag: '2d0234b-20250422-165314', }, // We're currently using the same deployer/key funder key as mainnet2. // To minimize nonce clobbering we offset the key funder cron @@ -32,6 +32,7 @@ export const keyFunderConfig: KeyFunderConfig< contextsAndRolesToFund: { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], + [Contexts.Vanguard0]: [Role.Relayer], }, chainsToSkip: [], // desired balance config, must be set for each chain diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index b36934564e2..03537689af6 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -91,6 +91,10 @@ const RC_FUNDING_DISCOUNT_DENOMINATOR = ethers.BigNumber.from(10); const CONTEXT_FUNDING_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes const CHAIN_FUNDING_TIMEOUT_MS = 1 * 60 * 1000; // 1 minute +// Need to ensure we don't fund non-vanguard chains in the vanguard contexts +const VANGUARD_CHAINS = ['base', 'arbitrum', 'optimism', 'ethereum', 'bsc']; +const VANGUARD_CONTEXTS: Contexts[] = [Contexts.Vanguard0]; + // Funds key addresses for multiple contexts from the deployer key of the context // specified via the `--context` flag. // The --contexts-and-roles flag is used to specify the contexts and the key roles @@ -259,6 +263,18 @@ class ContextFunder { roleKeysPerChain = objFilter( roleKeysPerChain, (chain, _roleKeys): _roleKeys is Record => { + // Skip funding for vanguard contexts on non-vanguard chains + if ( + VANGUARD_CONTEXTS.includes(this.context) && + !VANGUARD_CHAINS.includes(chain) + ) { + logger.warn( + { chain, context: this.context }, + 'Skipping funding for vanguard context on non-vanguard chain', + ); + return false; + } + const valid = isEthereumProtocolChain(chain) && multiProvider.tryGetChainName(chain) !== null; diff --git a/typescript/infra/scripts/funding/vanguard.ts b/typescript/infra/scripts/funding/vanguard.ts new file mode 100644 index 00000000000..0f8a65484c9 --- /dev/null +++ b/typescript/infra/scripts/funding/vanguard.ts @@ -0,0 +1,244 @@ +import chalk from 'chalk'; +import { formatUnits, parseUnits } from 'ethers/lib/utils.js'; +import yargs from 'yargs'; + +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address, rootLogger } from '@hyperlane-xyz/utils'; + +import { Contexts } from '../../config/contexts.js'; +import { DEPLOYER } from '../../config/environments/mainnet3/owners.js'; +import { Role } from '../../src/roles.js'; +import { getEnvironmentConfig } from '../core-utils.js'; + +const VANGUARDS = [ + 'vanguard0', // regular + 'vanguard1', // regular + 'vanguard2', // regular + 'vanguard3', // 5x gas cap + 'vanguard4', // 5x gas cap + 'vanguard5', // 10x gas cap +] as const; + +type VanguardName = (typeof VANGUARDS)[number]; +type VanguardBalance = { + chain: string; + vanguard: VanguardName; + balance: string; +}; + +const TOKEN_DECIMALS = 18; +const MIN_FUNDING_AMOUNT = parseUnits('0.05', TOKEN_DECIMALS); + +const VANGUARD_ENVIRONMENT = 'mainnet3'; +const VANGUARD_ADDRESSES: Record = { + vanguard0: '0xbe2e6b1ce045422a08a3662fffa3fc5f114efc3d', + vanguard1: '0xdbcd22e5223f5d0040398e66dbb525308f27c655', + vanguard2: '0x226b721316ea44aad50a10f4cc67fc30658ab4a9', + vanguard3: '0xcdd728647ecd9d75413c9b780de303b1d1eb12a5', + vanguard4: '0x5401627b69f317da9adf3d6e1e1214724ce49032', + vanguard5: '0x6fd953d1cbdf3a79663b4238898147a6cf36d459', +}; + +const VANGUARD_NETWORKS = [ + 'base', + 'arbitrum', + 'optimism', + 'ethereum', + 'bsc', +] as const; + +const VANGUARD_FUNDING_CONFIGS: Record< + (typeof VANGUARD_NETWORKS)[number], + Record +> = { + base: { + vanguard0: '1', + vanguard1: '1', + vanguard2: '1', + vanguard3: '1', + vanguard4: '1', + vanguard5: '1', + }, + arbitrum: { + vanguard0: '1', + vanguard1: '1', + vanguard2: '1', + vanguard3: '1', + vanguard4: '1', + vanguard5: '1', + }, + optimism: { + vanguard0: '1', + vanguard1: '1', + vanguard2: '1', + vanguard3: '1', + vanguard4: '1', + vanguard5: '1', + }, + ethereum: { + vanguard0: '5', + vanguard1: '5', + vanguard2: '5', + vanguard3: '5', + vanguard4: '5', + vanguard5: '5', + }, + bsc: { + vanguard0: '10', + vanguard1: '10', + vanguard2: '5', + vanguard3: '5', + vanguard4: '5', + vanguard5: '5', + }, +} as const; + +const ACTIVE_VANGUARDS: VanguardName[] = ['vanguard0']; + +async function fundVanguards() { + const { fund } = await yargs(process.argv.slice(2)) + .describe('fund', 'Fund vanguards') + .boolean('fund') + .default('fund', false).argv; + + const envConfig = getEnvironmentConfig(VANGUARD_ENVIRONMENT); + const multiProvider = await envConfig.getMultiProvider( + Contexts.Hyperlane, + Role.Deployer, + true, + ); + + // Print balances before funding + const currentBalances: ChainMap> = {}; + for (const chain of VANGUARD_NETWORKS) { + currentBalances[chain] = {}; + const provider = multiProvider.getProvider(chain); + const deployerBalance = await provider.getBalance(DEPLOYER); + currentBalances[chain]['deployer'] = Number( + formatUnits(deployerBalance, TOKEN_DECIMALS), + ).toFixed(3); // Round to 3 decimal places + + for (const vanguard of VANGUARDS) { + const address = VANGUARD_ADDRESSES[vanguard]; + const currentBalance = await provider.getBalance(address); + currentBalances[chain][vanguard] = Number( + formatUnits(currentBalance, TOKEN_DECIMALS), + ).toFixed(3); // Round to 3 decimal places + } + } + + rootLogger.info('\nCurrent balances:'); + // eslint-disable-next-line no-console + console.table(currentBalances); + + // Track which vanguards were topped up + const topUpsNeeded: ChainMap = {}; + + await Promise.all( + VANGUARD_NETWORKS.map(async (chain) => { + for (const vanguard of ACTIVE_VANGUARDS) { + const address = VANGUARD_ADDRESSES[vanguard]; + try { + const provider = multiProvider.getProvider(chain); + const currentBalance = await provider.getBalance(address); + const desiredBalance = parseUnits( + VANGUARD_FUNDING_CONFIGS[chain][vanguard], + TOKEN_DECIMALS, + ); + const delta = desiredBalance.sub(currentBalance); + + if (delta.gt(MIN_FUNDING_AMOUNT)) { + topUpsNeeded[chain] = topUpsNeeded[chain] || []; + topUpsNeeded[chain].push({ + chain, + vanguard, + balance: Number(formatUnits(delta, TOKEN_DECIMALS)).toFixed(3), + }); + } + } catch (error) { + rootLogger.error( + chalk.bold.red( + `Error topping up ${vanguard} on chain ${chain}:`, + error, + ), + ); + } + } + }), + ); + + // Print summary of topped up vanguards + if (Object.keys(topUpsNeeded).length > 0) { + rootLogger.info('\nTop ups needed for the following:'); + // eslint-disable-next-line no-console + console.table( + Object.entries(topUpsNeeded).reduce((acc, [chain, topUps]) => { + const chainEntries: Record = {} as Record< + VanguardName, + string + >; + VANGUARDS.forEach((vanguard) => { + const match = topUps.find((t) => t.vanguard === vanguard); + chainEntries[vanguard] = match ? match.balance : '-'; + }); + acc[chain] = chainEntries; + return acc; + }, {} as Record>), + ); + + if (fund) { + rootLogger.info(chalk.italic.blue('\nFunding vanguards...')); + } else { + rootLogger.info(chalk.italic.yellow('\nDry run - not funding vanguards')); + process.exit(0); + } + + await Promise.all( + Object.entries(topUpsNeeded).map(async ([chain, topUps]) => { + for (const { vanguard, balance: topUpAmount } of topUps) { + try { + const signer = multiProvider.getSigner(chain); + const signerBalance = await signer.getBalance(); + + // Convert balance to a BigNumber by using parseUnits + const amount = parseUnits(topUpAmount, TOKEN_DECIMALS); + + if (signerBalance.lt(amount)) { + rootLogger.warn( + chalk.bold.yellow( + `Insufficient balance for ${vanguard} on ${chain}. Required: ${formatUnits( + amount, + TOKEN_DECIMALS, + )}, Available: ${formatUnits(signerBalance, TOKEN_DECIMALS)}`, + ), + ); + continue; + } + + await multiProvider.sendTransaction(chain, { + to: VANGUARD_ADDRESSES[vanguard], + value: amount, + }); + } catch (error) { + rootLogger.error( + chalk.bold.red( + `Error checking balance for ${vanguard} on ${chain}:`, + error, + ), + ); + continue; + } + } + }), + ); + } else { + rootLogger.info(chalk.bold.green('\nNo vanguards needed topping up')); + } + + process.exit(0); +} + +fundVanguards().catch((error) => { + rootLogger.error(chalk.bold.red('Error funding agents:', error)); + process.exit(1); +}); From d904a845eddd7940bb6bf1555fb519bfd97ba571 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Tue, 22 Apr 2025 21:32:36 +0100 Subject: [PATCH 045/223] fix: ingore mailbox in warp-configs.test.ts (#5993) ### Description - Change `warp-configs.test.ts` to ignore the `mailbox` field of warp route configurations - Switch to using `.deep.equal()` from `chai` to get better mismatch errors than we got from `diffObjMerge` - Make the config comparison logic more detailed, to further improve the test errors readability ### Drive-by changes Use the spread operator to avoid `undefined` fields from being generated in warp configs, in two places: - `getRenzoWarpConfigGenerator` - `getWarpConfig` ### Related issues - https://github.com/hyperlane-xyz/hyperlane-registry/pull/679 - #5840 - Related to ENG-1057 ### Backward compatibility Yes, as the test logic only changes for the `mailbox` field that is being removed. ### Testing Manually ran `warp-configs.test.ts` on the registry from https://github.com/hyperlane-xyz/hyperlane-registry/pull/679 Co-authored-by: Le --- .../configGetters/getRenzoEZETHWarpConfig.ts | 4 +- typescript/infra/config/warp.ts | 2 +- typescript/infra/test/warp-configs.test.ts | 40 ++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts index 9bd81ef5da3..9820048dc7e 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts @@ -443,7 +443,9 @@ export function getRenzoWarpConfigGenerator(params: { ], }, hook: getRenzoHook(defaultHook, chain, safes[chain]), - proxyAdmin: existingProxyAdmins?.[chain] ?? undefined, // when 'undefined' yaml will not include the field + ...(existingProxyAdmins?.[chain] + ? { proxyAdmin: existingProxyAdmins?.[chain] } + : {}), }, ]; diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index 7dc05c485cb..995a65f86c7 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -237,7 +237,7 @@ export async function getWarpConfig( const { owner, ownerOverrides } = config; return { owner, - ownerOverrides, + ...(ownerOverrides ? { ownerOverrides } : {}), }; }); diff --git a/typescript/infra/test/warp-configs.test.ts b/typescript/infra/test/warp-configs.test.ts index 36fd448d1f9..bb66ac3aea4 100644 --- a/typescript/infra/test/warp-configs.test.ts +++ b/typescript/infra/test/warp-configs.test.ts @@ -3,8 +3,8 @@ import chaiAsPromised from 'chai-as-promised'; import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry'; import { getRegistry } from '@hyperlane-xyz/registry/fs'; -import { MultiProvider } from '@hyperlane-xyz/sdk'; -import { diffObjMerge, rootLogger } from '@hyperlane-xyz/utils'; +import { HypTokenRouterConfig, MultiProvider } from '@hyperlane-xyz/sdk'; +import { rootLogger } from '@hyperlane-xyz/utils'; import { getWarpConfig, warpConfigGetterMap } from '../config/warp.js'; import { @@ -47,24 +47,28 @@ describe('Warp Configs', async function () { for (const warpRouteId of warpIdsToCheck) { it(`should match Github Registry configs for ${warpRouteId}`, async function () { - const warpConfig = await getWarpConfig( - multiProvider, - envConfig, - warpRouteId, - ); - const { mergedObject, isInvalid } = diffObjMerge( - warpConfig, - configsFromGithub![warpRouteId], - ); - - if (isInvalid) { - console.log('Differences', JSON.stringify(mergedObject, null, 2)); + const warpConfig: Record< + string, + Partial + > = await getWarpConfig(multiProvider, envConfig, warpRouteId); + for (const key in warpConfig) { + if (warpConfig[key].mailbox) { + delete warpConfig[key].mailbox; + } + } + const expectedConfig = configsFromGithub![warpRouteId]; + for (const key in expectedConfig) { + if (expectedConfig[key].mailbox) { + delete expectedConfig[key].mailbox; + } } - expect( - isInvalid, - `Registry config does not match Getter for ${warpRouteId}`, - ).to.be.false; + expect(warpConfig).to.have.keys(Object.keys(expectedConfig)); + for (const key in warpConfig) { + if (warpConfig[key]) { + expect(warpConfig[key]).to.deep.equal(expectedConfig[key]); + } + } }); } From f540e6558b20cc695206d84987f72d85577744fb Mon Sep 17 00:00:00 2001 From: Christopher Brumm <97845034+christopherbrumm@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:29:29 +0200 Subject: [PATCH 046/223] chore: extend `LUMIA` (#5842) ### Description This PR extends LUMIA/bsc-ethereum-lumiaprism route with Arbitrum, Avalanche, Optimism and Polygon (collateral) and adds the deployment artifacts. ### Backward compatibility Yes ### Testing N/A --------- Co-authored-by: Le Yu <6251863+ltyu@users.noreply.github.com> Co-authored-by: Le --- ...umiaprismOptimismPolygonLUMIAWarpConfig.ts | 126 ++++++++++++++++++ .../environments/mainnet3/warp/warpIds.ts | 1 + typescript/infra/config/warp.ts | 8 ++ 3 files changed, 135 insertions(+) create mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig.ts diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig.ts new file mode 100644 index 00000000000..c180bc9f166 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig.ts @@ -0,0 +1,126 @@ +import { + ChainMap, + ChainSubmissionStrategy, + HypTokenRouterConfig, + TokenType, + TxSubmitterType, +} from '@hyperlane-xyz/sdk'; + +import { RouterConfigWithoutOwner } from '../../../../../src/config/warp.js'; + +const safeOwners: Record = { + arbitrum: '0xc8A9Dea7359Bd6FDCAD3B8EDE108416C25cF4CE9', + avalanche: '0x6d5Cd9e6EB9a2E74bF9857c53aA44F659f0Cc332', + base: '0xcEC53d6fF9B4C7b8E77f0C0D3f8828Bb872f2377', + bsc: '0xa86C4AF592ddAa676f53De278dE9cfCD52Ae6B39', + ethereum: '0xa86C4AF592ddAa676f53De278dE9cfCD52Ae6B39', + lumiaprism: '0xa86C4AF592ddAa676f53De278dE9cfCD52Ae6B39', + optimism: '0x914931eBb5638108651455F50C1F784d3E5fd3EC', + polygon: '0x7a412dD3812369226cd42023FC9301A66788122e', +}; + +export const getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig = + async ( + routerConfig: ChainMap, + ): Promise> => { + const arbitrum: HypTokenRouterConfig = { + ...routerConfig.arbitrum, + owner: safeOwners.arbitrum, + type: TokenType.synthetic, + symbol: 'LUMIA', + }; + + const avalanche: HypTokenRouterConfig = { + ...routerConfig.avalanche, + owner: safeOwners.avalanche, + type: TokenType.synthetic, + symbol: 'LUMIA', + }; + + const base: HypTokenRouterConfig = { + ...routerConfig.base, + owner: safeOwners.base, + type: TokenType.synthetic, + symbol: 'LUMIA', + }; + + const bsc: HypTokenRouterConfig = { + ...routerConfig.bsc, + owner: safeOwners.bsc, + type: TokenType.synthetic, + proxyAdmin: { + address: '0x54bB5b12c67095eB3dabCD11FA74AAcfE46E7767', + owner: '0x8bBA07Ddc72455b55530C17e6f6223EF6E156863', + }, + }; + + const ethereum: HypTokenRouterConfig = { + ...routerConfig.ethereum, + owner: safeOwners.ethereum, + type: TokenType.collateral, + token: '0xD9343a049D5DBd89CD19DC6BcA8c48fB3a0a42a7', + proxyAdmin: { + address: '0xdaB976ae358D4D2d25050b5A25f71520983C46c9', + owner: '0x8bBA07Ddc72455b55530C17e6f6223EF6E156863', + }, + }; + + const lumiaprism: HypTokenRouterConfig = { + ...routerConfig.lumiaprism, + owner: safeOwners.lumiaprism, + type: TokenType.native, + proxyAdmin: { + address: '0xBC53dACd8c0ac0d2bAC461479EAaf5519eCC8853', + owner: '0x8bBA07Ddc72455b55530C17e6f6223EF6E156863', + }, + }; + + const optimism: HypTokenRouterConfig = { + ...routerConfig.optimism, + owner: safeOwners.optimism, + type: TokenType.synthetic, + symbol: 'LUMIA', + }; + + const polygon: HypTokenRouterConfig = { + ...routerConfig.polygon, + owner: safeOwners.polygon, + type: TokenType.synthetic, + symbol: 'LUMIA', + }; + + return { + arbitrum, + avalanche, + base, + bsc, + ethereum, + lumiaprism, + optimism, + polygon, + }; + }; + +// Create a GnosisSafeBuilder Strategy for each safe address +export function getLUMIAGnosisSafeBuilderStrategyConfigGenerator( + lumiaSafes: Record, +) { + return (): ChainSubmissionStrategy => { + return Object.fromEntries( + Object.entries(lumiaSafes).map(([chain, safeAddress]) => [ + chain, + { + submitter: { + type: TxSubmitterType.GNOSIS_TX_BUILDER, + version: '1.0', + chain, + safeAddress, + }, + }, + ]), + ); + }; +} + +export const getLUMIAGnosisSafeBuilderStrategyConfig = + getLUMIAGnosisSafeBuilderStrategyConfigGenerator(safeOwners); diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index ece9954f48a..66c6eed9abb 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -44,6 +44,7 @@ export enum WarpRouteIds { EthereumBscLumiaprismLUMIA = 'LUMIA/bsc-ethereum-lumiaprism', EthereumZircuitRe7LRT = 'Re7LRT/ethereum-zircuit', InevmInjectiveINJ = 'INJ/inevm-injective', + ArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIA = 'LUMIA/arbitrum-avalanche-base-bsc-ethereum-lumiaprism-optimism-polygon', MantapacificNeutronTIA = 'TIA/mantapacific-neutron', BaseZeroNetworkCBBTC = 'CBBTC/base-zeronetwork', BaseEthereumREZ = 'REZ/base-ethereum', diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index 995a65f86c7..f0e31631530 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -18,6 +18,10 @@ import { RouterConfigWithoutOwner } from '../src/config/warp.js'; import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.js'; import { getAppChainBaseUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAppchainBaseUSDCWarpConfig.js'; +import { + getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig, + getLUMIAGnosisSafeBuilderStrategyConfig, +} from './environments/mainnet3/warp/configGetters/getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig.js'; import { getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig.js'; import { getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC } from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.js'; import { getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.js'; @@ -129,10 +133,14 @@ export const warpConfigGetterMap: Record = { [WarpRouteIds.BscHyperevmEnzoBTC]: getBscHyperevmEnzoBTCWarpConfig, [WarpRouteIds.BscHyperevmSTBTC]: getBscHyperevmSTBTCWarpConfig, [WarpRouteIds.EthereumLineaTURTLE]: getEthereumLineaTurtleWarpConfig, + [WarpRouteIds.ArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIA]: + getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig, }; type StrategyConfigGetter = () => ChainSubmissionStrategy; export const strategyConfigGetterMap: Record = { + [WarpRouteIds.ArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIA]: + getLUMIAGnosisSafeBuilderStrategyConfig, [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETH]: getEZETHGnosisSafeBuilderStrategyConfig, [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETHSTAGE]: From 99cd61fa988f17e9732cebea0d05e3509da14640 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 23 Apr 2025 11:07:55 +0100 Subject: [PATCH 047/223] feat: add hyper warp ids and update monitor image (#5992) ### Description - add Hyper/stHYPER warp route ids for monitoring, update monitor image ### Testing Manual --- typescript/infra/config/environments/mainnet3/warp/warpIds.ts | 4 ++++ typescript/infra/src/warp/helm.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index 66c6eed9abb..c44fb2a904d 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -99,4 +99,8 @@ export enum WarpRouteIds { SolanaSoonGIGA = 'GIGA/solanamainnet-soon', SolanaSoonGOAT = 'GOAT/solanamainnet-soon', SolanaSoonSPORE = 'SPORE/solanamainnet-soon', + + // HYPER routes + HYPER = 'HYPER/arbitrum-base-bsc-ethereum-optimism', + stHYPER = 'stHYPER/bsc-ethereum', } diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 29226211898..792aeaec3da 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -73,7 +73,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'e6aa40b-20250402-213202', + tag: '8b985a4-20250421-152953', }, warpRouteId: this.warpRouteId, fullnameOverride: this.helmReleaseName, From ef2783703ae1e4dcf2450d99b8ca034432111d6f Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 23 Apr 2025 12:17:23 +0100 Subject: [PATCH 048/223] fix: Report all operations as failed when we are failing a batch (#5996) ### Description When there is a single successful operation in a batch and decide to fail the batch as a whole, currently, we will reports only failed operations. We should include the successful operation into the failed list. ### Backward compatibility Yes ### Testing None Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index d2ae2e2300e..bd0bfd8625c 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -366,6 +366,7 @@ where let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()); let call_results = batch.call().await?; + let call_count = contract_calls.len(); let (successful, failed) = multicall::filter_failed(contract_calls, call_results); if successful.is_empty() { @@ -383,7 +384,7 @@ where failed, )) } else { - Ok(BatchSimulation::failed(failed.len())) + Ok(BatchSimulation::failed(call_count)) } } From 6f50d7fbcfc6be2bfac6656f882b24dfbed9b192 Mon Sep 17 00:00:00 2001 From: Christopher Brumm <97845034+christopherbrumm@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:26:27 +0200 Subject: [PATCH 049/223] chore: extend to `ETH/arbitrum-base-ethereum-lumiaprism-optimism-polygon` (#5828) ### Description This PR extends `ETH/base-ethereum-lumiaprism` route with Arbitrum, Optimism (both native) and Polygon (collateral). ### Backward compatibility Yes ### Testing Followed the runbook tests & executed `check-deploy` script successfully. Registry-PR: https://github.com/hyperlane-xyz/hyperlane-registry/pull/737 --------- Co-authored-by: Trevor Porter Co-authored-by: Le Co-authored-by: Le --- ...mLumiaprismOptimismPolygonETHWarpConfig.ts | 74 +++++++++++++++++++ .../getBaseEthereumLumiaprismETHWarpConfig.ts | 46 ------------ .../getRenzoEZETHSTAGEWarpConfig.ts | 5 +- .../configGetters/getRenzoEZETHWarpConfig.ts | 24 +----- .../getRenzoPZETHSTAGEWarpConfig.ts | 5 +- .../environments/mainnet3/warp/warpIds.ts | 2 +- typescript/infra/config/environments/utils.ts | 31 +++++++- typescript/infra/config/warp.ts | 11 ++- 8 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig.ts delete mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseEthereumLumiaprismETHWarpConfig.ts diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig.ts new file mode 100644 index 00000000000..11b60dc7cea --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig.ts @@ -0,0 +1,74 @@ +import { ethers } from 'ethers'; + +import { ChainMap, HypTokenRouterConfig, TokenType } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { + RouterConfigWithoutOwner, + tokens, +} from '../../../../../src/config/warp.js'; +import { getGnosisSafeBuilderStrategyConfigGenerator } from '../../../utils.js'; + +const safeOwners: ChainMap
= { + arbitrum: '0xc8A9Dea7359Bd6FDCAD3B8EDE108416C25cF4CE9', + ethereum: '0xb10B260fBf5F33CC5Ff81761e090aeCDffcb1fd5', + base: '0xC92aB408512defCf1938858E726dc5C0ada9175a', + lumiaprism: '0x1b06089dA471355F8F05C7A6d8DE1D9dAC397629', + optimism: '0x914931eBb5638108651455F50C1F784d3E5fd3EC', + polygon: '0x7a412dD3812369226cd42023FC9301A66788122e', +}; + +export const getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig = + async ( + routerConfig: ChainMap, + ): Promise> => { + const arbitrum: HypTokenRouterConfig = { + ...routerConfig.arbitrum, + owner: safeOwners.arbitrum, + type: TokenType.native, + }; + + const base: HypTokenRouterConfig = { + ...routerConfig.base, + owner: safeOwners.base, + type: TokenType.native, + }; + + const ethereum: HypTokenRouterConfig = { + ...routerConfig.ethereum, + owner: safeOwners.ethereum, + type: TokenType.native, + }; + + const lumiaprism: HypTokenRouterConfig = { + ...routerConfig.lumiaprism, + owner: safeOwners.lumiaprism, + type: TokenType.synthetic, + symbol: 'WETH', + }; + + const optimism: HypTokenRouterConfig = { + ...routerConfig.optimism, + owner: safeOwners.optimism, + type: TokenType.native, + }; + + const polygon: HypTokenRouterConfig = { + ...routerConfig.polygon, + owner: safeOwners.polygon, + token: tokens.polygon.WETH, + type: TokenType.collateral, + }; + + return { + arbitrum, + base, + ethereum, + lumiaprism, + optimism, + polygon, + }; + }; + +export const getArbitrumBaseEthereumLumiaprismOptimismPolygonETHGnosisSafeBuilderStrategyConfig = + getGnosisSafeBuilderStrategyConfigGenerator(safeOwners); diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseEthereumLumiaprismETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseEthereumLumiaprismETHWarpConfig.ts deleted file mode 100644 index e768c6f762a..00000000000 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseEthereumLumiaprismETHWarpConfig.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ethers } from 'ethers'; - -import { ChainMap, HypTokenRouterConfig, TokenType } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; - -import { RouterConfigWithoutOwner } from '../../../../../src/config/warp.js'; - -const safeOwners: ChainMap
= { - ethereum: '0xb10B260fBf5F33CC5Ff81761e090aeCDffcb1fd5', - base: '0xC92aB408512defCf1938858E726dc5C0ada9175a', - lumiaprism: '0x1b06089dA471355F8F05C7A6d8DE1D9dAC397629', -}; - -const ISM_CONFIG = ethers.constants.AddressZero; // Default ISM - -export const getBaseEthereumLumiaprismETHWarpConfig = async ( - routerConfig: ChainMap, -): Promise> => { - const base: HypTokenRouterConfig = { - ...routerConfig.base, - owner: safeOwners.base, - type: TokenType.native, - interchainSecurityModule: ISM_CONFIG, - }; - - const ethereum: HypTokenRouterConfig = { - ...routerConfig.ethereum, - owner: safeOwners.ethereum, - type: TokenType.native, - interchainSecurityModule: ISM_CONFIG, - }; - - const lumiaprism: HypTokenRouterConfig = { - ...routerConfig.lumiaprism, - owner: safeOwners.lumiaprism, - type: TokenType.synthetic, - interchainSecurityModule: ISM_CONFIG, - symbol: 'WETH', - }; - - return { - base, - ethereum, - lumiaprism, - }; -}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts index 7658e8a6c74..f7998b25550 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts @@ -1,7 +1,8 @@ +import { getGnosisSafeBuilderStrategyConfigGenerator } from '../../../utils.js'; + import { ezEthChainsToDeploy, ezEthValidators, - getRenzoGnosisSafeBuilderStrategyConfigGenerator, getRenzoWarpConfigGenerator, renzoTokenPrices, } from './getRenzoEZETHWarpConfig.js'; @@ -59,4 +60,4 @@ export const getRenzoEZETHSTAGEWarpConfig = getRenzoWarpConfigGenerator({ }); export const getEZETHSTAGEGnosisSafeBuilderStrategyConfig = - getRenzoGnosisSafeBuilderStrategyConfigGenerator(ezEthStagingSafes); + getGnosisSafeBuilderStrategyConfigGenerator(ezEthStagingSafes); diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts index 9820048dc7e..c7eac5e935b 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts @@ -17,6 +17,7 @@ import { import { Address, assert, symmetricDifference } from '@hyperlane-xyz/utils'; import { getEnvironmentConfig } from '../../../../../scripts/core-utils.js'; +import { getGnosisSafeBuilderStrategyConfigGenerator } from '../../../utils.js'; import { getRegistry as getMainnet3Registry } from '../../chains.js'; export const ezEthChainsToDeploy = [ @@ -469,26 +470,5 @@ export const getRenzoEZETHWarpConfig = getRenzoWarpConfigGenerator({ existingProxyAdmins: existingProxyAdmins, }); -// Create a GnosisSafeBuilder Strategy for each safe address -export function getRenzoGnosisSafeBuilderStrategyConfigGenerator( - ezEthSafes: Record, -) { - return (): ChainSubmissionStrategy => { - return Object.fromEntries( - Object.entries(ezEthSafes).map(([chain, safeAddress]) => [ - chain, - { - submitter: { - type: TxSubmitterType.GNOSIS_TX_BUILDER, - version: '1.0', - chain, - safeAddress, - }, - }, - ]), - ); - }; -} - export const getEZETHGnosisSafeBuilderStrategyConfig = - getRenzoGnosisSafeBuilderStrategyConfigGenerator(ezEthSafes); + getGnosisSafeBuilderStrategyConfigGenerator(ezEthSafes); diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHSTAGEWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHSTAGEWarpConfig.ts index 328f9b568a7..99a4ddde45c 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHSTAGEWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHSTAGEWarpConfig.ts @@ -1,9 +1,10 @@ import { pick } from '@hyperlane-xyz/utils'; +import { getGnosisSafeBuilderStrategyConfigGenerator } from '../../../utils.js'; + import { ezEthStagingSafes } from './getRenzoEZETHSTAGEWarpConfig.js'; import { ezEthValidators, - getRenzoGnosisSafeBuilderStrategyConfigGenerator, getRenzoWarpConfigGenerator, renzoTokenPrices, } from './getRenzoEZETHWarpConfig.js'; @@ -35,4 +36,4 @@ export const getRenzoPZETHStagingWarpConfig = getRenzoWarpConfigGenerator({ }); export const getPZETHSTAGEGnosisSafeBuilderStrategyConfig = - getRenzoGnosisSafeBuilderStrategyConfigGenerator(pzEthStagingSafes); + getGnosisSafeBuilderStrategyConfigGenerator(pzEthStagingSafes); diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index c44fb2a904d..44eb32b710a 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -86,7 +86,7 @@ export enum WarpRouteIds { HyperevmSolanaSOL = 'SOL/hyperevm-solanamainnet', MintSolanaMINT = 'MINT/mint-solanamainnet', EthereumUnichainPumpBTC = 'pumpBTCuni/ethereum-unichain', - BaseEthereumLumiaprismETH = 'ETH/base-ethereum-lumiaprism', + ArbitrumBaseEthereumLumiaprismOptimismPolygonETH = 'ETH/arbitrum-base-ethereum-lumiaprism-optimism-polygon', BscHyperevmEnzoBTC = 'enzoBTC/bsc-hyperevm', BscHyperevmSTBTC = 'stBTC/bsc-hyperevm', // Soon Routes diff --git a/typescript/infra/config/environments/utils.ts b/typescript/infra/config/environments/utils.ts index 48830ad6112..3c23fc11c87 100644 --- a/typescript/infra/config/environments/utils.ts +++ b/typescript/infra/config/environments/utils.ts @@ -1,4 +1,8 @@ -import { ChainName } from '@hyperlane-xyz/sdk'; +import { + ChainName, + ChainSubmissionStrategy, + TxSubmitterType, +} from '@hyperlane-xyz/sdk'; import { CheckpointSyncerType, @@ -66,3 +70,28 @@ export const validatorBaseConfigsFn = chain, addresses[context], ); + +/** + * Create a GnosisSafeBuilder Strategy for each safe address + * @param safes Safe addresses for strategy + * @returns GnosisSafeBuilder Strategy for each safe address + */ +export function getGnosisSafeBuilderStrategyConfigGenerator( + safes: Record, +) { + return (): ChainSubmissionStrategy => { + return Object.fromEntries( + Object.entries(safes).map(([chain, safeAddress]) => [ + chain, + { + submitter: { + type: TxSubmitterType.GNOSIS_TX_BUILDER, + version: '1.0', + chain, + safeAddress, + }, + }, + ]), + ); + }; +} diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index f0e31631530..d74e16e90b8 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -23,11 +23,14 @@ import { getLUMIAGnosisSafeBuilderStrategyConfig, } from './environments/mainnet3/warp/configGetters/getArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIAWarpConfig.js'; import { getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig.js'; +import { + getArbitrumBaseEthereumLumiaprismOptimismPolygonETHGnosisSafeBuilderStrategyConfig, + getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig, +} from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig.js'; import { getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC } from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.js'; import { getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.js'; import { getArbitrumEthereumSolanaTreasureSMOLWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.js'; import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js'; -import { getBaseEthereumLumiaprismETHWarpConfig } from './environments/mainnet3/warp/configGetters/getBaseEthereumLumiaprismETHWarpConfig.js'; import { getBaseEthereumSuperseedCBBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBaseEthereumSuperseedCBBTCWarpConfig.js'; import { getTRUMPWarpConfig, @@ -128,8 +131,8 @@ export const warpConfigGetterMap: Record = { // [WarpRouteIds.SuperTokenStaging]: getSuperTokenStagingWarpConfig, [WarpRouteIds.SuperUSDT]: getSuperTokenProductionWarpConfig, [WarpRouteIds.MintSolanaMINT]: getMintSolanaMintWarpConfig, - [WarpRouteIds.BaseEthereumLumiaprismETH]: - getBaseEthereumLumiaprismETHWarpConfig, + [WarpRouteIds.ArbitrumBaseEthereumLumiaprismOptimismPolygonETH]: + getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig, [WarpRouteIds.BscHyperevmEnzoBTC]: getBscHyperevmEnzoBTCWarpConfig, [WarpRouteIds.BscHyperevmSTBTC]: getBscHyperevmSTBTCWarpConfig, [WarpRouteIds.EthereumLineaTURTLE]: getEthereumLineaTurtleWarpConfig, @@ -145,6 +148,8 @@ export const strategyConfigGetterMap: Record = { getEZETHGnosisSafeBuilderStrategyConfig, [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETHSTAGE]: getEZETHSTAGEGnosisSafeBuilderStrategyConfig, + [WarpRouteIds.ArbitrumBaseEthereumLumiaprismOptimismPolygonETH]: + getArbitrumBaseEthereumLumiaprismOptimismPolygonETHGnosisSafeBuilderStrategyConfig, [WarpRouteIds.BerachainEthereumSwellUnichainZircuitPZETH]: getEZETHGnosisSafeBuilderStrategyConfig, [WarpRouteIds.BerachainEthereumSwellUnichainZircuitPZETHSTAGE]: From 5314d026b52359c45964a777791a8e13b15f4019 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 23 Apr 2025 17:39:05 +0100 Subject: [PATCH 050/223] fix: Submit failed operations from a batch in a serial manner (#6000) ### Description * Submit failed operations from a batch in a serial manner * Allow batches to contains a single successful operation ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> --- rust/main/agents/relayer/src/msg/op_batch.rs | 29 +++++-------------- .../src/contracts/mailbox.rs | 17 ++++------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index 75a09ec0c77..aeec6050c7d 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -4,11 +4,11 @@ use derive_new::new; use hyperlane_core::{ rpc_clients::DEFAULT_MAX_RPC_RETRIES, total_estimated_cost, BatchResult, ChainCommunicationError, ChainResult, ConfirmReason, HyperlaneDomain, Mailbox, - PendingOperation, PendingOperationStatus, QueueOperation, ReprepareReason, TxOutcome, + PendingOperation, PendingOperationStatus, QueueOperation, TxOutcome, }; use itertools::{Either, Itertools}; use tokio::time::sleep; -use tracing::{error, info, instrument, warn}; +use tracing::{info, instrument, warn}; use super::{ op_queue::OpQueue, @@ -43,26 +43,11 @@ impl OperationBatch { } }; - if let Some(first_item) = excluded_ops.first() { - let Some(mailbox) = first_item.try_get_mailbox() else { - error!(excluded_ops=?excluded_ops, "Excluded ops don't have mailbox while they should have"); - return; // we expect that excluded ops have mailbox - }; - - if mailbox.supports_batching() { - warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Sending them back to prepare queue."); - let reason = ReprepareReason::ErrorEstimatingGas; - let status = Some(PendingOperationStatus::Retry(reason.clone())); - for mut op in excluded_ops.into_iter() { - op.on_reprepare(None, reason.clone()); - prepare_queue.push(op, status.clone()).await; - } - } else { - info!(excluded_ops=?excluded_ops, "Chain does not support batching. Submitting serially."); - OperationBatch::new(excluded_ops, self.domain) - .submit_serially(prepare_queue, confirm_queue, metrics) - .await; - } + if !excluded_ops.is_empty() { + warn!(excluded_ops=?excluded_ops, "Either operations reverted in the batch or the txid wasn't included. Falling back to serial submission."); + OperationBatch::new(excluded_ops, self.domain) + .submit_serially(prepare_queue, confirm_queue, metrics) + .await; } } diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index bd0bfd8625c..6d896ad0c20 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -366,26 +366,19 @@ where let batch = multicall::batch::<_, ()>(multicall, contract_calls.clone()); let call_results = batch.call().await?; - let call_count = contract_calls.len(); let (successful, failed) = multicall::filter_failed(contract_calls, call_results); if successful.is_empty() { return Ok(BatchSimulation::failed(failed.len())); } - let successful_calls_len = successful.len(); let successful_batch = multicall::batch::<_, ()>(multicall, successful.clone()); - // only send a batch if there are at least two successful calls - if successful_calls_len >= 2 { - Ok(BatchSimulation::new( - Some(self.submittable_batch(successful_batch)), - successful, - failed, - )) - } else { - Ok(BatchSimulation::failed(call_count)) - } + Ok(BatchSimulation::new( + Some(self.submittable_batch(successful_batch)), + successful, + failed, + )) } fn submittable_batch( From a8043c03d84e71ba89d76b3c7724ed3d32fe4619 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Wed, 23 Apr 2025 14:27:43 -0400 Subject: [PATCH 051/223] ci: Pack and Install CLI in ci (#5613) ### Description This PR adds a CI step where the CLI is locally packaged and installed ### Drive-by changes - Update e2e tests to use locally built cli instead of relying on `yarn run` ### Issue Fixes #5616 ### Backward compatibility - Yes ### Testing - Manual --------- Co-authored-by: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> --- .github/actions/install-cli/action.yml | 28 ++++++++ .github/workflows/test.yml | 22 ++++++ eslint.config.mjs | 1 + package.json | 4 +- typescript/cli/.gitignore | 1 + typescript/cli/package.json | 54 +++++++------- typescript/cli/scripts/ncc.post-bundle.mjs | 37 ++++++++++ typescript/cli/src/tests/commands/core.ts | 71 ++++--------------- typescript/cli/src/tests/commands/helpers.ts | 12 +++- typescript/cli/src/tests/commands/warp.ts | 19 +++-- typescript/infra/scripts/agent-utils.ts | 2 +- typescript/infra/scripts/deploy.ts | 4 +- typescript/infra/src/config/chain.ts | 8 ++- .../src/infrastructure/monitoring/grafana.ts | 3 +- typescript/infra/src/utils/utils.ts | 4 -- typescript/utils/src/env.ts | 4 ++ typescript/utils/src/index.ts | 2 +- yarn.lock | 14 +++- 18 files changed, 181 insertions(+), 109 deletions(-) create mode 100644 .github/actions/install-cli/action.yml create mode 100644 typescript/cli/scripts/ncc.post-bundle.mjs diff --git a/.github/actions/install-cli/action.yml b/.github/actions/install-cli/action.yml new file mode 100644 index 00000000000..81539a61a3e --- /dev/null +++ b/.github/actions/install-cli/action.yml @@ -0,0 +1,28 @@ +name: "Install Hyperlane CLI" +description: "Install the Hyperlane CLI by packaging from a local build" + +inputs: + ref: + description: "The Git ref to checkout" + required: true + +runs: + using: "composite" + steps: + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache + with: + ref: ${{ inputs.ref }} + + - name: Pack the CLI + shell: bash + id: pack-cli + run: | + cd typescript/cli + yarn pack + + - name: Install the packaged CLI + shell: bash + run: | + cd typescript/cli + npm i -g package.tgz diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e1a2d553764..6cd22da9035 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -113,6 +113,22 @@ jobs: - name: Unit Tests run: yarn test:ci + cli-install-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: install-hyperlane-cli + id: install-hyperlane-cli + uses: ./.github/actions/install-cli + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Test run the CLI + run: hyperlane --version + cli-e2e-matrix: runs-on: ubuntu-latest strategy: @@ -153,6 +169,12 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: install-hyperlane-cli + id: install-hyperlane-cli + uses: ./.github/actions/install-cli + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Checkout registry uses: ./.github/actions/checkout-registry diff --git a/eslint.config.mjs b/eslint.config.mjs index ec37ff4eda8..6bd3017d75b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -21,6 +21,7 @@ export default [ ignores: [ '**/node_modules', '**/dist', + '**/cli-bundle', '**/coverage', '**/*.cjs', '**/*.cts', diff --git a/package.json b/package.json index 92d5b4b5d2b..7605c93cf91 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,12 @@ "private": true, "scripts": { "agent-configs": "yarn --cwd typescript/infra/ update-agent-config:mainnet3 && yarn --cwd typescript/infra/ update-agent-config:testnet4 && yarn prettier", - "build": "yarn workspaces foreach --all --parallel --topological run build", + "build": "yarn workspaces foreach --all --parallel --topological-dev run build", "clean": "yarn workspaces foreach --all --parallel run clean", "prettier": "yarn workspaces foreach --since --parallel run prettier", "lint": "yarn workspaces foreach --all --parallel run lint", "test": "yarn workspaces foreach --all --parallel run test", - "test:ci": "yarn workspaces foreach --all --topological run test:ci", + "test:ci": "yarn workspaces foreach --all --topological-dev run test:ci", "coverage": "yarn workspaces foreach --all --parallel run coverage", "version:prepare": "yarn changeset version && yarn workspaces foreach --all --parallel run version:update && yarn install --no-immutable", "version:check": "yarn changeset status", diff --git a/typescript/cli/.gitignore b/typescript/cli/.gitignore index 4a0b6f51bf2..37175be29a5 100644 --- a/typescript/cli/.gitignore +++ b/typescript/cli/.gitignore @@ -1,5 +1,6 @@ .env* /dist +/cli-bundle /cache # Deployment artifacts and local registry configs diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 1191e5ec4c5..bb3fbeec1ff 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -2,9 +2,12 @@ "name": "@hyperlane-xyz/cli", "version": "12.1.0", "description": "A command-line utility for common Hyperlane operations", - "dependencies": { + "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", + "@eslint/js": "^9.15.0", + "@ethersproject/abi": "*", + "@ethersproject/providers": "*", "@hyperlane-xyz/registry": "11.1.0", "@hyperlane-xyz/sdk": "12.1.0", "@hyperlane-xyz/utils": "12.1.0", @@ -12,68 +15,63 @@ "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", "@inquirer/search": "^3.0.1", - "ansi-escapes": "^7.0.0", - "asn1.js": "^5.4.1", - "bignumber.js": "^9.1.1", - "chalk": "^5.3.0", - "ethers": "^5.7.2", - "latest-version": "^8.0.0", - "terminal-link": "^3.0.0", - "tsx": "^4.19.1", - "yaml": "2.4.5", - "yargs": "^17.7.2", - "zksync-ethers": "^5.10.0", - "zod": "^3.21.2", - "zod-validation-error": "^3.3.0", - "zx": "^8.1.4" - }, - "devDependencies": { - "@eslint/js": "^9.15.0", - "@ethersproject/abi": "*", - "@ethersproject/providers": "*", "@types/chai-as-promised": "^8", "@types/mocha": "^10.0.1", "@types/node": "^18.14.5", "@types/yargs": "^17.0.24", "@typescript-eslint/eslint-plugin": "^8.1.6", "@typescript-eslint/parser": "^8.1.6", + "@vercel/ncc": "^0.38.3", + "ansi-escapes": "^7.0.0", + "asn1.js": "^5.4.1", + "bignumber.js": "^9.1.1", "chai": "^4.5.0", "chai-as-promised": "^8.0.0", + "chalk": "^5.3.0", "eslint": "^9.15.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", + "ethers": "^5.7.2", + "latest-version": "^8.0.0", "mocha": "^10.2.0", "prettier": "^2.8.8", - "typescript": "5.3.3" + "terminal-link": "^3.0.0", + "tsx": "^4.19.1", + "typescript": "5.3.3", + "yaml": "2.4.5", + "yargs": "^17.7.2", + "zksync-ethers": "^5.10.0", + "zod": "^3.21.2", + "zod-validation-error": "^3.3.0", + "zx": "^8.1.4" }, "scripts": { "hyperlane": "node ./dist/cli.js", "build": "yarn version:update && tsc", + "bundle": "rm -rf ./cli-bundle && ncc build ./dist/cli.js -o cli-bundle && node ./scripts/ncc.post-bundle.mjs", "dev": "yarn version:update && tsc --watch", - "clean": "rm -rf ./dist", + "clean": "rm -rf ./dist && rm -rf ./cli-bundle", "lint": "eslint -c ./eslint.config.mjs", "prettier": "prettier --write ./src ./examples", "test:ci": "yarn mocha --config .mocharc.json", "test:e2e": "./scripts/run-e2e-test.sh", - "version:update": "echo \"export const VERSION = '$npm_package_version';\" > src/version.ts" + "version:update": "echo \"export const VERSION = '$npm_package_version';\" > src/version.ts", + "prepack": "yarn bundle" }, "files": [ - "./dist", + "./cli-bundle", "./examples", "package.json" ], "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "type": "module", - "exports": { - ".": "./dist/src/index.js" - }, "engines": { "node": ">=16" }, "bin": { - "hyperlane": "./dist/cli.js" + "hyperlane": "./cli-bundle/index.js" }, "repository": { "type": "git", diff --git a/typescript/cli/scripts/ncc.post-bundle.mjs b/typescript/cli/scripts/ncc.post-bundle.mjs new file mode 100644 index 00000000000..337fe95835f --- /dev/null +++ b/typescript/cli/scripts/ncc.post-bundle.mjs @@ -0,0 +1,37 @@ +import path from 'path'; +import { fileURLToPath } from 'url'; + +import { readFile, writeFile } from 'fs/promises'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const outputFile = path.join(__dirname, '..', 'cli-bundle', 'index.js'); + +const shebang = '#! /usr/bin/env node'; +const dirnameDef = `import { fileURLToPath } from 'url'; +import path from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename);`; + +async function prepend() { + try { + const content = await readFile(outputFile, 'utf8'); + + // Assume the 'cli.ts' file entry point already has the shebang + if (!content.startsWith(shebang)) { + throw new Error('Missing shebang from cli entry point'); + } + + if (!content.includes(dirnameDef)) { + const [, executable] = content.split(shebang); + const newContent = `${shebang}\n${dirnameDef}\n${executable}`; + await writeFile(outputFile, newContent, 'utf8'); + console.log('Adding missing __dirname definition to cli executable'); + } + } catch (err) { + console.error('Error processing output file:', err); + } +} + +await prepend(); diff --git a/typescript/cli/src/tests/commands/core.ts b/typescript/cli/src/tests/commands/core.ts index e1cbe0cb59f..ee68e25c6a9 100644 --- a/typescript/cli/src/tests/commands/core.ts +++ b/typescript/cli/src/tests/commands/core.ts @@ -5,7 +5,7 @@ import { Address } from '@hyperlane-xyz/utils'; import { readYamlOrJson } from '../../utils/files.js'; -import { ANVIL_KEY, REGISTRY_PATH } from './helpers.js'; +import { ANVIL_KEY, REGISTRY_PATH, localTestRunCmdPrefix } from './helpers.js'; /** * Deploys the Hyperlane core contracts to the specified chain using the provided config. @@ -16,28 +16,14 @@ export function hyperlaneCoreDeployRaw( skipConfirmationPrompts?: boolean, hypKey?: string, ): ProcessPromise { - if (hypKey) { - return $`HYP_KEY=${hypKey} yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ + return $`${ + hypKey ? ['HYP_KEY=' + hypKey] : '' + } ${localTestRunCmdPrefix()} hyperlane core deploy \ --registry ${REGISTRY_PATH} \ --config ${coreInputPath} \ + ${privateKey ? ['--key', privateKey] : ''} \ --verbosity debug \ - ${skipConfirmationPrompts ? '--yes' : ''}`; - } - - if (privateKey) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ - --registry ${REGISTRY_PATH} \ - --config ${coreInputPath} \ - --key ${privateKey} \ - --verbosity debug \ - ${skipConfirmationPrompts ? '--yes' : ''}`; - } - - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ - --registry ${REGISTRY_PATH} \ - --config ${coreInputPath} \ - --verbosity debug \ - ${skipConfirmationPrompts ? '--yes' : ''}`; + ${skipConfirmationPrompts ? ['--yes'] : ''}`; } /** @@ -47,7 +33,7 @@ export async function hyperlaneCoreDeploy( chain: string, coreInputPath: string, ) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ + return $`${localTestRunCmdPrefix()} hyperlane core deploy \ --registry ${REGISTRY_PATH} \ --config ${coreInputPath} \ --chain ${chain} \ @@ -60,7 +46,7 @@ export async function hyperlaneCoreDeploy( * Reads a Hyperlane core deployment on the specified chain using the provided config. */ export async function hyperlaneCoreRead(chain: string, coreOutputPath: string) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core read \ + return $`${localTestRunCmdPrefix()} hyperlane core read \ --registry ${REGISTRY_PATH} \ --config ${coreOutputPath} \ --chain ${chain} \ @@ -76,20 +62,11 @@ export function hyperlaneCoreCheck( coreOutputPath: string, mailbox?: Address, ): ProcessPromise { - if (mailbox) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core check \ - --registry ${REGISTRY_PATH} \ - --config ${coreOutputPath} \ - --chain ${chain} \ - --mailbox ${mailbox} \ - --verbosity debug \ - --yes`; - } - - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core check \ + return $`${localTestRunCmdPrefix()} hyperlane core check \ --registry ${REGISTRY_PATH} \ --config ${coreOutputPath} \ --chain ${chain} \ + ${mailbox ? ['--mailbox', mailbox] : ''} \ --verbosity debug \ --yes`; } @@ -102,30 +79,12 @@ export function hyperlaneCoreInit( privateKey?: string, hyp_key?: string, ): ProcessPromise { - if (hyp_key) { - return $`${ - hyp_key ? `HYP_KEY=${hyp_key}` : '' - } yarn workspace @hyperlane-xyz/cli run hyperlane core init \ - --registry ${REGISTRY_PATH} \ - --config ${coreOutputPath} \ - --verbosity debug \ - --yes`; - } - - if (privateKey) { - return $`${ - hyp_key ? 'HYP_KEY=${hyp_key}' : '' - } yarn workspace @hyperlane-xyz/cli run hyperlane core init \ - --registry ${REGISTRY_PATH} \ - --config ${coreOutputPath} \ - --verbosity debug \ - --key ${privateKey} \ - --yes`; - } - - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core init \ + return $`${ + hyp_key ? ['HYP_KEY=' + hyp_key] : '' + } ${localTestRunCmdPrefix()} hyperlane core init \ --registry ${REGISTRY_PATH} \ --config ${coreOutputPath} \ + ${privateKey ? ['--key', privateKey] : ''} \ --verbosity debug \ --yes`; } @@ -137,7 +96,7 @@ export async function hyperlaneCoreApply( chain: string, coreOutputPath: string, ) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane core apply \ + return $`${localTestRunCmdPrefix()} hyperlane core apply \ --registry ${REGISTRY_PATH} \ --config ${coreOutputPath} \ --chain ${chain} \ diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 3467fe1f651..ba4c2475b05 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -20,7 +20,7 @@ import { WarpCoreConfig, WarpCoreConfigSchema, } from '@hyperlane-xyz/sdk'; -import { Address, sleep } from '@hyperlane-xyz/utils'; +import { Address, inCIMode, sleep } from '@hyperlane-xyz/utils'; import { getContext } from '../../context/context.js'; import { CommandContext } from '../../context/types.js'; @@ -493,11 +493,17 @@ export async function sendWarpRouteMessageRoundTrip( return hyperlaneWarpSendRelay(chain2, chain1, warpCoreConfigPath); } +// Verifies if the IS_CI var is set and generates the correct prefix for running the command +// in the current env +export function localTestRunCmdPrefix() { + return inCIMode() ? [] : ['yarn', 'workspace', '@hyperlane-xyz/cli', 'run']; +} + export async function hyperlaneSendMessage( origin: string, destination: string, ) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane send message \ + return $`${localTestRunCmdPrefix()} hyperlane send message \ --registry ${REGISTRY_PATH} \ --origin ${origin} \ --destination ${destination} \ @@ -507,7 +513,7 @@ export async function hyperlaneSendMessage( } export function hyperlaneRelayer(chains: string[], warp?: string) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane relayer \ + return $`${localTestRunCmdPrefix()} hyperlane relayer \ --registry ${REGISTRY_PATH} \ --chains ${chains.join(',')} \ --warp ${warp ?? ''} \ diff --git a/typescript/cli/src/tests/commands/warp.ts b/typescript/cli/src/tests/commands/warp.ts index 278ed6d4344..ca59297128e 100644 --- a/typescript/cli/src/tests/commands/warp.ts +++ b/typescript/cli/src/tests/commands/warp.ts @@ -12,7 +12,12 @@ import { Address } from '@hyperlane-xyz/utils'; import { readYamlOrJson } from '../../utils/files.js'; -import { ANVIL_KEY, REGISTRY_PATH, getDeployedWarpAddress } from './helpers.js'; +import { + ANVIL_KEY, + REGISTRY_PATH, + getDeployedWarpAddress, + localTestRunCmdPrefix, +} from './helpers.js'; $.verbose = true; @@ -20,7 +25,7 @@ $.verbose = true; * Deploys the Warp route to the specified chain using the provided config. */ export function hyperlaneWarpInit(warpCorePath: string): ProcessPromise { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp init \ + return $`${localTestRunCmdPrefix()} hyperlane warp init \ --registry ${REGISTRY_PATH} \ --out ${warpCorePath} \ --key ${ANVIL_KEY} \ @@ -44,7 +49,7 @@ export function hyperlaneWarpDeployRaw({ }): ProcessPromise { return $`${ hypKey ? ['HYP_KEY=' + hypKey] : '' - } yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \ + } ${localTestRunCmdPrefix()} hyperlane warp deploy \ --registry ${REGISTRY_PATH} \ ${warpCorePath ? ['--config', warpCorePath] : ''} \ ${privateKey ? ['--key', privateKey] : ''} \ @@ -71,7 +76,7 @@ export async function hyperlaneWarpApply( warpCorePath: string, strategyUrl = '', ) { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp apply \ + return $`${localTestRunCmdPrefix()} hyperlane warp apply \ --registry ${REGISTRY_PATH} \ --config ${warpDeployPath} \ --warp ${warpCorePath} \ @@ -92,7 +97,7 @@ export function hyperlaneWarpReadRaw({ warpAddress?: string; outputPath?: string; }): ProcessPromise { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp read \ + return $`${localTestRunCmdPrefix()} hyperlane warp read \ --registry ${REGISTRY_PATH} \ ${warpAddress ? ['--address', warpAddress] : ''} \ ${chain ? ['--chain', chain] : ''} \ @@ -120,7 +125,7 @@ export function hyperlaneWarpCheckRaw({ symbol?: string; warpDeployPath?: string; }): ProcessPromise { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp check \ + return $`${localTestRunCmdPrefix()} hyperlane warp check \ --registry ${REGISTRY_PATH} \ ${symbol ? ['--symbol', symbol] : ''} \ --verbosity debug \ @@ -144,7 +149,7 @@ export function hyperlaneWarpSendRelay( relay = true, value = 1, ): ProcessPromise { - return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp send \ + return $`${localTestRunCmdPrefix()} hyperlane warp send \ ${relay ? '--relay' : ''} \ --registry ${REGISTRY_PATH} \ --origin ${origin} \ diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index 53483f0090a..149bef1e51e 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -15,6 +15,7 @@ import { import { Address, ProtocolType, + inCIMode, objFilter, objMap, promiseObjAll, @@ -51,7 +52,6 @@ import { assertRole, filterRemoteDomainMetadata, getInfraPath, - inCIMode, readJSONAtPath, writeMergedJSONAtPath, } from '../src/utils/utils.js'; diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index 994bc6ebd9d..99ad6aefb49 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -22,7 +22,7 @@ import { LiquidityLayerDeployer, TestRecipientDeployer, } from '@hyperlane-xyz/sdk'; -import { objFilter, objMap } from '@hyperlane-xyz/utils'; +import { inCIMode, objFilter, objMap } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts.js'; import { core as coreConfig } from '../config/environments/mainnet3/core.js'; @@ -37,7 +37,7 @@ import { } from '../src/deployment/verify.js'; import { Role } from '../src/roles.js'; import { impersonateAccount, useLocalProvider } from '../src/utils/fork.js'; -import { inCIMode, writeYamlAtPath } from '../src/utils/utils.js'; +import { writeYamlAtPath } from '../src/utils/utils.js'; import { Modules, diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index 766102eba3d..a2b2dc213ac 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -8,11 +8,15 @@ import { HyperlaneSmartProvider, ProviderRetryOptions, } from '@hyperlane-xyz/sdk'; -import { ProtocolType, objFilter, objMerge } from '@hyperlane-xyz/utils'; +import { + ProtocolType, + inCIMode, + objFilter, + objMerge, +} from '@hyperlane-xyz/utils'; import { getChain, getRegistryWithOverrides } from '../../config/registry.js'; import { getSecretRpcEndpoints } from '../agents/index.js'; -import { inCIMode } from '../utils/utils.js'; import { DeployEnvironment } from './environment.js'; diff --git a/typescript/infra/src/infrastructure/monitoring/grafana.ts b/typescript/infra/src/infrastructure/monitoring/grafana.ts index fb243cf5a13..bdd57301bf9 100644 --- a/typescript/infra/src/infrastructure/monitoring/grafana.ts +++ b/typescript/infra/src/infrastructure/monitoring/grafana.ts @@ -1,5 +1,5 @@ import { ChainMap } from '@hyperlane-xyz/sdk'; -import { rootLogger } from '@hyperlane-xyz/utils'; +import { inCIMode, rootLogger } from '@hyperlane-xyz/utils'; import { AlertType, @@ -10,7 +10,6 @@ import { walletNameQueryFormat, } from '../../config/funding/grafanaAlerts.js'; import { fetchGCPSecret } from '../../utils/gcloud.js'; -import { inCIMode } from '../../utils/utils.js'; export const logger = rootLogger.child({ module: 'grafana' }); diff --git a/typescript/infra/src/utils/utils.ts b/typescript/infra/src/utils/utils.ts index db1830f4448..ee498581f41 100644 --- a/typescript/infra/src/utils/utils.ts +++ b/typescript/infra/src/utils/utils.ts @@ -290,10 +290,6 @@ export function getInfraPath() { return join(dirname(fileURLToPath(import.meta.url)), '../../'); } -export function inCIMode() { - return process.env.CI === 'true'; -} - // Filter out chains that are not supported by the multiProvider // Filter out any value that is not a string e.g. remote domain metadata export function filterRemoteDomainMetadata( diff --git a/typescript/utils/src/env.ts b/typescript/utils/src/env.ts index 8841f56a805..bfa7ecfae7b 100644 --- a/typescript/utils/src/env.ts +++ b/typescript/utils/src/env.ts @@ -7,3 +7,7 @@ export function safelyAccessEnvVar(name: string, toLowerCase = false) { return undefined; } } + +export function inCIMode() { + return process.env.CI === 'true'; +} diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index f1612b6c834..8e4880d6831 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -80,7 +80,7 @@ export { isS3CheckpointWithId, } from './checkpoints.js'; export { domainHash } from './domains.js'; -export { safelyAccessEnvVar } from './env.js'; +export { safelyAccessEnvVar, inCIMode } from './env.js'; export { canonizeId, evmId } from './ids.js'; export { LogFormat, diff --git a/yarn.lock b/yarn.lock index 2bc8f335da3..0bbd6a14944 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7663,6 +7663,7 @@ __metadata: "@types/yargs": "npm:^17.0.24" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" + "@vercel/ncc": "npm:^0.38.3" ansi-escapes: "npm:^7.0.0" asn1.js: "npm:^5.4.1" bignumber.js: "npm:^9.1.1" @@ -7687,7 +7688,7 @@ __metadata: zod-validation-error: "npm:^3.3.0" zx: "npm:^8.1.4" bin: - hyperlane: ./dist/cli.js + hyperlane: ./cli-bundle/index.js languageName: unknown linkType: soft @@ -17330,6 +17331,17 @@ __metadata: languageName: node linkType: hard +"@vercel/ncc@npm:^0.38.3": + version: 0.38.3 + resolution: "@vercel/ncc@npm:0.38.3" + dependencies: + node-gyp: "npm:latest" + bin: + ncc: dist/ncc/cli.js + checksum: 10/f1a05a58e9c90d6940027b628590715a62bf1611c47bca546ad51bd6a0e8d25ce64c1c39eb27ba0b6747017182cb59ec42088da8d6530a6d561e9e1a4e8c9941 + languageName: node + linkType: hard + "@vitejs/plugin-react@npm:^3.0.1": version: 3.1.0 resolution: "@vitejs/plugin-react@npm:3.1.0" From 93e88a32beda26249690e6720cf32844b617928c Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 23 Apr 2025 14:52:36 -0400 Subject: [PATCH 052/223] feat: add debug message for unknown domains for chain metrics (#5963) ### Description Add debug message so we can better debug missing block height metrics for certains chains: https://linear.app/hyperlane-xyz/issue/BACK-94/hyperlane-block-height-metrics-are-missing-for-some-chains ### Related issues - related: https://linear.app/hyperlane-xyz/issue/BACK-94/hyperlane-block-height-metrics-are-missing-for-some-chains ### Backward compatibility Yes --- .../hyperlane-base/src/metrics/agent_metrics.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rust/main/hyperlane-base/src/metrics/agent_metrics.rs b/rust/main/hyperlane-base/src/metrics/agent_metrics.rs index 140116c1a55..b5fe6ce8288 100644 --- a/rust/main/hyperlane-base/src/metrics/agent_metrics.rs +++ b/rust/main/hyperlane-base/src/metrics/agent_metrics.rs @@ -206,7 +206,16 @@ impl ChainSpecificMetricsUpdater { } async fn update_block_details(&self) { - if let HyperlaneDomain::Unknown { .. } = self.conf.domain { + if let HyperlaneDomain::Unknown { + domain_id, + domain_name, + .. + } = &self.conf.domain + { + debug!( + domain_id, + domain_name, "Unknown domain, skipping chain metrics" + ); return; }; let chain = self.conf.domain.name(); @@ -218,7 +227,7 @@ impl ChainSpecificMetricsUpdater { return; } _ => { - trace!(chain, "No chain metrics available"); + debug!(chain, "No chain metrics available"); return; } }; @@ -226,6 +235,7 @@ impl ChainSpecificMetricsUpdater { let height = chain_metrics.latest_block.number as i64; trace!(chain, height, "Fetched block height for metrics"); self.chain_metrics.set_block_height(chain, height); + if self.chain_metrics.gas_price.is_some() { let protocol = self.conf.domain.domain_protocol(); let decimals_scale = 10f64.powf(decimals_by_protocol(protocol).into()); From 2d4353a9b9ace219eceac62422e1f880bc9130c9 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Wed, 23 Apr 2025 17:03:06 -0400 Subject: [PATCH 053/223] chore: Update getters to fix violations (#5967) ### Description Updates the Getters that update the owners to fix violations ### Drive-by changes - Remove extraneous ownerOverrides ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/765 ### Backward compatibility Yes ### Testing - run check-deploy script --------- Co-authored-by: Le --- ...ptimismPolygonZeroNetworkUSDCWarpConfig.ts | 27 ++++++++--------- ...ePolygonScrollZeronetworkUSDTWarpConfig.ts | 29 ++++++++++--------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts index 9dae492537a..49fc6289494 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts @@ -11,6 +11,7 @@ import { RouterConfigWithoutOwner, tokens, } from '../../../../../src/config/warp.js'; +import { timelocks } from '../../owners.js'; export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( routerConfig: ChainMap, @@ -20,10 +21,10 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const arbitrum: HypTokenRouterConfig = { ...routerConfig.arbitrum, - ...abacusWorksEnvOwnerConfig.arbitrum, + owner: abacusWorksEnvOwnerConfig.arbitrum.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.arbitrum, address: '0x02317D525FA7ceb5ea388244b4618f0c8Ac1CeC2', + owner: timelocks.arbitrum, }, type: TokenType.collateral, token: tokens.arbitrum.USDC, @@ -32,9 +33,9 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const base: HypTokenRouterConfig = { ...routerConfig.base, - ...abacusWorksEnvOwnerConfig.base, + owner: abacusWorksEnvOwnerConfig.base.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.base, + owner: abacusWorksEnvOwnerConfig.base.owner, address: '0xB6E9331576C5aBF69376AF6989eA61b7C7ea67F1', }, type: TokenType.collateral, @@ -44,9 +45,9 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const optimism: HypTokenRouterConfig = { ...routerConfig.optimism, - ...abacusWorksEnvOwnerConfig.optimism, + owner: abacusWorksEnvOwnerConfig.optimism.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.optimism, + owner: abacusWorksEnvOwnerConfig.optimism.owner, address: '0xca9e64761C97b049901dF4E7a5926464969528b1', }, type: TokenType.collateral, @@ -56,9 +57,9 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const polygon: HypTokenRouterConfig = { ...routerConfig.polygon, - ...abacusWorksEnvOwnerConfig.polygon, + owner: abacusWorksEnvOwnerConfig.polygon.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.polygon, + owner: abacusWorksEnvOwnerConfig.polygon.owner, address: '0x7fd5be37d560626625f395A2e6E30eA89150cc98', }, type: TokenType.collateral, @@ -68,9 +69,9 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const zeronetwork: HypTokenRouterConfig = { ...routerConfig.zeronetwork, - ...abacusWorksEnvOwnerConfig.zeronetwork, + owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.zeronetwork, + owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, address: '0x6E906d8AeEBE9025a410887EAafc58C2561705e0', }, type: TokenType.collateral, @@ -80,9 +81,9 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const ethereum: HypTokenRouterConfig = { ...routerConfig.ethereum, - ...abacusWorksEnvOwnerConfig.ethereum, + owner: abacusWorksEnvOwnerConfig.ethereum.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.ethereum, + owner: abacusWorksEnvOwnerConfig.ethereum.owner, address: '0x81063D413Ed6Eac3FCf0521eea14906fD27fEb1A', }, type: TokenType.collateral, @@ -92,7 +93,7 @@ export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( const lisk: HypTokenRouterConfig = { ...routerConfig.lisk, - ...abacusWorksEnvOwnerConfig.lisk, + owner: abacusWorksEnvOwnerConfig.lisk.owner, proxyAdmin: { ...abacusWorksEnvOwnerConfig.lisk, address: '0x81Db8B4Bc6F2e95781eeA2a21D0A453Ac046eFc0', diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts index 8de2710ee2a..bb25b14036b 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts @@ -12,6 +12,7 @@ import { RouterConfigWithoutOwner, tokens, } from '../../../../../src/config/warp.js'; +import { timelocks } from '../../owners.js'; export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig = async ( @@ -22,10 +23,10 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const arbitrum: HypTokenRouterConfig = { ...routerConfig.arbitrum, - ...abacusWorksEnvOwnerConfig.arbitrum, + owner: abacusWorksEnvOwnerConfig.arbitrum.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.arbitrum, address: '0x6701d503369cf6aA9e5EdFfEBFA40A2ffdf3dB21', + owner: timelocks.arbitrum, }, type: TokenType.collateral, token: tokens.arbitrum.USDT, @@ -34,9 +35,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const ethereum: HypTokenRouterConfig = { ...routerConfig.ethereum, - ...abacusWorksEnvOwnerConfig.ethereum, + owner: abacusWorksEnvOwnerConfig.ethereum.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.ethereum, + owner: abacusWorksEnvOwnerConfig.ethereum.owner, address: '0xA92D6084709469A2B2339919FfC568b7C5D7888D', }, type: TokenType.collateral, @@ -46,9 +47,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const mantle: HypTokenRouterConfig = { ...routerConfig.mantle, - ...abacusWorksEnvOwnerConfig.mantle, + owner: abacusWorksEnvOwnerConfig.mantle.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.mantle, + owner: abacusWorksEnvOwnerConfig.mantle.owner, address: '0x633268639892C73Fa7340Ec1da4e397cf3913c8C', }, type: TokenType.collateral, @@ -58,9 +59,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const mode: HypTokenRouterConfig = { ...routerConfig.mode, - ...abacusWorksEnvOwnerConfig.mode, + owner: abacusWorksEnvOwnerConfig.mode.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.mode, + owner: abacusWorksEnvOwnerConfig.mode.owner, address: '0x633268639892C73Fa7340Ec1da4e397cf3913c8C', }, type: TokenType.collateral, @@ -70,9 +71,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const polygon: HypTokenRouterConfig = { ...routerConfig.polygon, - ...abacusWorksEnvOwnerConfig.polygon, + owner: abacusWorksEnvOwnerConfig.polygon.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.polygon, + owner: abacusWorksEnvOwnerConfig.polygon.owner, address: '0x5DBeAEC137d1ef9a240599656073Ae3E717fae3c', }, type: TokenType.collateral, @@ -82,9 +83,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const scroll: HypTokenRouterConfig = { ...routerConfig.scroll, - ...abacusWorksEnvOwnerConfig.scroll, + owner: abacusWorksEnvOwnerConfig.scroll.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.scroll, + owner: abacusWorksEnvOwnerConfig.scroll.owner, address: '0x81Db8B4Bc6F2e95781eeA2a21D0A453Ac046eFc0', }, type: TokenType.collateral, @@ -94,9 +95,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig const zeronetwork: HypTokenRouterConfig = { ...routerConfig.zeronetwork, - ...abacusWorksEnvOwnerConfig.zeronetwork, + owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, proxyAdmin: { - ...abacusWorksEnvOwnerConfig.zeronetwork, + owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, address: '0xa3F188BDd6e3894b393e12396347545bC47E7B0e', }, type: TokenType.synthetic, From c7934f7112b1b5d61efd0051458368c88dc7b78f Mon Sep 17 00:00:00 2001 From: xeno097 Date: Wed, 23 Apr 2025 21:33:42 -0400 Subject: [PATCH 054/223] feat: revoke approvals in warp core (#5973) ### Description This PR modifies the `ITokenAdapter` interface to add a new `isRevokeApprovalRequired` function used in WarpCore to check if allowance should be reset before setting a new allowance amount. This is useful for tokens like USDT, where they require that the current allowance is 0 when setting a new amount. ref https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code#L205 ### Drive-by changes ### Related issues ### Backward compatibility - Yes ### Testing - Manual testing locally with the UI --- .changeset/shiny-turtles-hide.md | 5 +++ .../token/adapters/CosmWasmTokenAdapter.ts | 21 ++++++++++ .../src/token/adapters/CosmosTokenAdapter.ts | 7 ++++ .../sdk/src/token/adapters/EvmTokenAdapter.ts | 39 +++++++++++++++++++ .../sdk/src/token/adapters/ITokenAdapter.ts | 1 + .../token/adapters/SealevelTokenAdapter.ts | 14 +++++++ typescript/sdk/src/warp/WarpCore.test.ts | 4 ++ typescript/sdk/src/warp/WarpCore.ts | 37 +++++++++++++++--- typescript/sdk/src/warp/types.ts | 1 + 9 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 .changeset/shiny-turtles-hide.md diff --git a/.changeset/shiny-turtles-hide.md b/.changeset/shiny-turtles-hide.md new file mode 100644 index 00000000000..f8238bcea29 --- /dev/null +++ b/.changeset/shiny-turtles-hide.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Adds the isRevokeApprovalRequired method on the token adapters to check if the user should revoke any previously set allowances on the token to transfer to avoid approvals failing like in the case of USDT diff --git a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts index c1a58487ec1..bbd6a78c386 100644 --- a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts @@ -72,6 +72,13 @@ export class CwNativeTokenAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + async populateApproveTx( _params: TransferParams, ): Promise { @@ -154,6 +161,13 @@ export class CwTokenAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + async populateApproveTx({ weiAmountOrId, recipient, @@ -460,4 +474,11 @@ export class CwHypCollateralAdapter ) { super(chainName, multiProvider, addresses); } + + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } } diff --git a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts index 0983e2b45db..4cd8def5c06 100644 --- a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts @@ -54,6 +54,13 @@ export class CosmNativeTokenAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + populateApproveTx( _transferParams: TransferParams, ): Promise { diff --git a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts index 60c3879eb54..34bf24801b7 100644 --- a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts @@ -79,6 +79,13 @@ export class EvmNativeTokenAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + async populateApproveTx( _params: TransferParams, ): Promise { @@ -142,6 +149,15 @@ export class EvmTokenAdapter return allowance.lt(weiAmountOrId); } + async isRevokeApprovalRequired( + owner: Address, + spender: Address, + ): Promise { + const allowance = await this.contract.allowance(owner, spender); + + return !allowance.isZero(); + } + override populateApproveTx({ weiAmountOrId, recipient, @@ -190,6 +206,13 @@ export class EvmHypSyntheticAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + getDomains(): Promise { return this.contract.domains(); } @@ -298,6 +321,15 @@ export class EvmHypCollateralAdapter ); } + override async isRevokeApprovalRequired( + owner: Address, + spender: Address, + ): Promise { + const collateral = await this.getWrappedTokenAdapter(); + + return collateral.isRevokeApprovalRequired(owner, spender); + } + override populateApproveTx( params: TransferParams, ): Promise { @@ -627,6 +659,13 @@ export class EvmHypNativeAdapter return false; } + override async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + override async populateTransferRemoteTx({ weiAmountOrId, destination, diff --git a/typescript/sdk/src/token/adapters/ITokenAdapter.ts b/typescript/sdk/src/token/adapters/ITokenAdapter.ts index c892ab9f04a..95986b6922e 100644 --- a/typescript/sdk/src/token/adapters/ITokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/ITokenAdapter.ts @@ -39,6 +39,7 @@ export interface ITokenAdapter { spender: Address, weiAmountOrId: Numberish, ): Promise; + isRevokeApprovalRequired(owner: Address, spender: Address): Promise; populateApproveTx(params: TransferParams): Promise; populateTransferTx(params: TransferParams): Promise; } diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index eb7757054d1..d0f11bef727 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -118,6 +118,13 @@ export class SealevelNativeTokenAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + async populateApproveTx(): Promise { throw new Error('Approve not required for native tokens'); } @@ -188,6 +195,13 @@ export class SealevelTokenAdapter return false; } + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + populateApproveTx(_params: TransferParams): Promise { throw new Error('Approve not required for sealevel tokens'); } diff --git a/typescript/sdk/src/warp/WarpCore.test.ts b/typescript/sdk/src/warp/WarpCore.test.ts index a6f3cf06895..33b947f9623 100644 --- a/typescript/sdk/src/warp/WarpCore.test.ts +++ b/typescript/sdk/src/warp/WarpCore.test.ts @@ -98,6 +98,7 @@ describe('WarpCore', () => { quoteTransferRemoteGas: () => Promise.resolve(MOCK_INTERCHAIN_QUOTE), isApproveRequired: () => Promise.resolve(false), populateTransferRemoteTx: () => Promise.resolve({}), + isRevokeApprovalRequired: () => Promise.resolve(false), } as any), ); @@ -162,6 +163,7 @@ describe('WarpCore', () => { sinon.stub(t, 'getHypAdapter').returns({ getBalance: () => Promise.resolve(MOCK_BALANCE), getBridgedSupply: () => Promise.resolve(MOCK_BALANCE), + isRevokeApprovalRequired: () => Promise.resolve(false), } as any), ); @@ -215,6 +217,7 @@ describe('WarpCore', () => { getBridgedSupply: () => Promise.resolve(MOCK_BALANCE), getMintLimit: () => Promise.resolve(MEDIUM_MOCK_BALANCE), getMintMaxLimit: () => Promise.resolve(MEDIUM_MOCK_BALANCE), + isRevokeApprovalRequired: () => Promise.resolve(false), } as any), ); @@ -319,6 +322,7 @@ describe('WarpCore', () => { sinon.stub(t, 'getHypAdapter').returns({ quoteTransferRemoteGas: () => Promise.resolve(MOCK_INTERCHAIN_QUOTE), populateTransferRemoteTx: () => Promise.resolve({}), + isRevokeApprovalRequired: () => Promise.resolve(false), } as any), ); diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index ddccadeb43d..a3d06f84e3c 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -3,6 +3,7 @@ import { Logger } from 'pino'; import { Address, HexString, + Numberish, ProtocolType, assert, convertDecimalsToIntegerString, @@ -332,16 +333,42 @@ export class WarpCore { const providerType = TOKEN_STANDARD_TO_PROVIDER_TYPE[token.standard]; const hypAdapter = token.getHypAdapter(this.multiProvider, destinationName); - if (await this.isApproveRequired({ originTokenAmount, owner: sender })) { - this.logger.info(`Approval required for transfer of ${token.symbol}`); + const [isApproveRequired, isRevokeApprovalRequired] = await Promise.all([ + this.isApproveRequired({ + originTokenAmount, + owner: sender, + }), + hypAdapter.isRevokeApprovalRequired( + sender, + originTokenAmount.token.addressOrDenom, + ), + ]); + + const preTransferRemoteTxs: [Numberish, WarpTxCategory][] = []; + // if the approval is required and the current allowance is not 0 we reset + // the allowance before setting the right approval as some tokens don't allow + // to override an already existing allowance. USDT is one of these tokens + // see: https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code#L205 + if (isApproveRequired && isRevokeApprovalRequired) { + preTransferRemoteTxs.push([0, WarpTxCategory.Revoke]); + } + + if (isApproveRequired) { + preTransferRemoteTxs.push([amount.toString(), WarpTxCategory.Approval]); + } + + for (const [approveAmount, txCategory] of preTransferRemoteTxs) { + this.logger.info( + `${txCategory} required for transfer of ${token.symbol}`, + ); const approveTxReq = await hypAdapter.populateApproveTx({ - weiAmountOrId: amount.toString(), + weiAmountOrId: approveAmount, recipient: token.addressOrDenom, }); - this.logger.debug(`Approval tx for ${token.symbol} populated`); + this.logger.debug(`${txCategory} tx for ${token.symbol} populated`); const approveTx = { - category: WarpTxCategory.Approval, + category: txCategory, type: providerType, transaction: approveTxReq, } as WarpTypedTransaction; diff --git a/typescript/sdk/src/warp/types.ts b/typescript/sdk/src/warp/types.ts index e5db231af78..3da7999873c 100644 --- a/typescript/sdk/src/warp/types.ts +++ b/typescript/sdk/src/warp/types.ts @@ -49,6 +49,7 @@ export type RouteBlacklist = Array<{ // Transaction types for warp core remote transfers export enum WarpTxCategory { Approval = 'approval', + Revoke = 'revoke', Transfer = 'transfer', } From 22681c9974493665c9d2316b94e12ca4451ffec0 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:42:09 +0100 Subject: [PATCH 055/223] revert: "chore: update cosmos package versions only when there are changes" (#6005) Reverts hyperlane-xyz/hyperlane-monorepo#5827 --- .changeset/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/config.json b/.changeset/config.json index fca79a0560f..d72044ed2d3 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -2,7 +2,7 @@ "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", "changelog": "@changesets/cli/changelog", "commit": true, - "fixed": [["@hyperlane-xyz/!(core|cosmos-types|cosmos-sdk)|*"]], + "fixed": [["@hyperlane-xyz/!(core)|*"]], "linked": [], "access": "public", "baseBranch": "main", From f5da762e0c32e2a8b5dfae79c7a3d8668a0aefad Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:33:46 +0100 Subject: [PATCH 056/223] feat: support CosmosNative agents in infra (#5815) ### Description feat: support CosmosNative agents in infra - includes kyvetestnet support, but agents are disabled ### Drive-by changes - update registryrc + agent configs - remove RC relayer whitelisting - remove gasPriceCap overrides set from load testing ### Related issues ### Backward compatibility ### Testing manual by deploying and spinning down kyvestestnet agents --------- Co-authored-by: Trevor Porter --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 306 ++++++++++-------- rust/main/config/testnet_config.json | 25 +- .../testnet4/aw-validators/hyperlane.json | 3 + .../config/environments/testnet4/chains.ts | 20 +- .../environments/testnet4/gasPrices.json | 2 +- .../scripts/agents/update-agent-config.ts | 6 +- typescript/infra/scripts/print-gas-prices.ts | 3 +- typescript/infra/src/agents/gcp.ts | 3 +- typescript/infra/src/config/agent/agent.ts | 1 + 10 files changed, 194 insertions(+), 177 deletions(-) diff --git a/.registryrc b/.registryrc index 5f704a58125..c736e2e3f9d 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -26f9cf910f7433645e6abb4f4b4ceb0116399759 +0bb72738327f4abf3af81963ede5f0da79ae2dc1 diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 9a5141f41b7..d575ea258a1 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -34,7 +34,7 @@ "interchainAccountIsm": "0xd766e7C7517f2d0D92754b2fe4aE7AdEf7bDEC3e", "interchainAccountRouter": "0x25C87e735021F72d8728438C2130b02E3141f2cb", "interchainGasPaymaster": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", - "interchainSecurityModule": "0xf036EFd98d4d4f559f39B7281360A4956484305f", + "interchainSecurityModule": "0x8C2BcFCb7AFf811388180f21366Ae89f07D58aE8", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162", @@ -100,7 +100,7 @@ "interchainAccountIsm": "0x2A7574358Ec53522CE2452887661AB4c86F7d400", "interchainAccountRouter": "0x91874Dbed74925dFe6059B90385EEb90DdE0B2E6", "interchainGasPaymaster": "0x3b6044acd6767f017e99318AA6Ef93b7B06A5a22", - "interchainSecurityModule": "0x3486c63bc98d46Bf49Edc2e4d51822EBaC9893D2", + "interchainSecurityModule": "0x58F3e93505E05Eeceb740a7298b609f9F9c0bCDc", "mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9", "merkleTreeHook": "0x748040afB89B8FdBb992799808215419d36A0930", "name": "arbitrum", @@ -116,13 +116,13 @@ "proxyAdmin": "0x80Cebd56A65e46c474a1A101e89E76C4c51D179c", "rpcUrls": [ { - "http": "https://arbitrum.llamarpc.com" + "http": "https://arb1.arbitrum.io/rpc" }, { - "http": "https://rpc.ankr.com/arbitrum" + "http": "https://arbitrum.rpc.subquery.network/public" }, { - "http": "https://arb1.arbitrum.io/rpc" + "http": "https://arbitrum.drpc.org" } ], "staticAggregationHookFactory": "0x9B5f440bBb64Fee337F37e03362b628711Ea09C7", @@ -172,7 +172,7 @@ "interchainAccountIsm": "0x27a3233c05C1Df7c163123301D14bE9349E3Cb48", "interchainAccountRouter": "0xa82a0227e6d6db53AF4B264A852bfF91C6504a51", "interchainGasPaymaster": "0x95519ba800BBd0d34eeAE026fEc620AD978176C0", - "interchainSecurityModule": "0x430e9C6f19aa66a0d699a6547288dd4c5C173EeB", + "interchainSecurityModule": "0xFc3b95B5f48d093769Eea2a312b83145Eb2132FE", "mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6", "merkleTreeHook": "0x84eea61D679F42D92145fA052C89900CBAccE95A", "name": "avalanche", @@ -245,7 +245,7 @@ "interchainAccountIsm": "0x223F7D3f27E6272266AE4B5B91Fd5C7A2d798cD8", "interchainAccountRouter": "0x4767D22117bBeeb295413000B620B93FD8522d53", "interchainGasPaymaster": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", - "interchainSecurityModule": "0x3236Ad296df3389E1916DD6D8410dAF36361F869", + "interchainSecurityModule": "0x1f6d21eE25AF46C5D21d97F0CFCE3042b8e662fB", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x19dc38aeae620380430C200a6E990D5Af5480117", "name": "base", @@ -268,6 +268,18 @@ }, { "http": "https://base.blockpi.network/v1/rpc/public" + }, + { + "http": "https://base.drpc.org" + }, + { + "http": "https://base.llamarpc.com" + }, + { + "http": "https://1rpc.io/base" + }, + { + "http": "https://base-pokt.nodies.app" } ], "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", @@ -320,7 +332,7 @@ "interchainAccountIsm": "0xe93f2f409ad8B5000431D234472973fe848dcBEC", "interchainAccountRouter": "0x2f4Eb04189e11Af642237Da62d163Ab714614498", "interchainGasPaymaster": "0xB3fCcD379ad66CED0c91028520C64226611A48c9", - "interchainSecurityModule": "0xE6DCad3Cb356b9278c2597b27f8005818EcDe35e", + "interchainSecurityModule": "0xa8bb4345Fc8277Ad510523Ba4702973Fcb80F871", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "name": "blast", @@ -388,7 +400,7 @@ "interchainAccountIsm": "0x451dF8AB0936D85526D816f0b4dCaDD934A034A4", "interchainAccountRouter": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "interchainGasPaymaster": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05", - "interchainSecurityModule": "0x722e40abcbCa22b026D2B7CaAE3f045cD74BC36F", + "interchainSecurityModule": "0x395F0F52195B24E123b4DBC494B068bA09FD4c55", "mailbox": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "merkleTreeHook": "0x781bE492F1232E66990d83a9D3AC3Ec26f56DAfB", "name": "bob", @@ -454,7 +466,7 @@ "interchainAccountIsm": "0x9e22945bE593946618383B108CC5bce09eBA4C26", "interchainAccountRouter": "0x32A07c1B7a7fe8D4A0e44B0181873aB9d64C16c1", "interchainGasPaymaster": "0x78E25e7f84416e69b9339B0A6336EB6EFfF6b451", - "interchainSecurityModule": "0xd53e71905018cC84118F71aD01e6e7D04ce54331", + "interchainSecurityModule": "0x006654622C311436f41C15cB0f236e7e8f2852d0", "mailbox": "0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4", "merkleTreeHook": "0xFDb9Cd5f9daAA2E4474019405A328a88E7484f26", "name": "bsc", @@ -470,13 +482,19 @@ "proxyAdmin": "0x65993Af9D0D3a64ec77590db7ba362D6eB78eF70", "rpcUrls": [ { - "http": "https://rpc.ankr.com/bsc" + "http": "https://bsc.drpc.org" }, { - "http": "https://bsc.drpc.org" + "http": "https://bnb.rpc.subquery.network/public" + }, + { + "http": "https://binance.llamarpc.com" + }, + { + "http": "https://bsc.blockrazor.xyz" }, { - "http": "https://bscrpc.com" + "http": "https://bsc-pokt.nodies.app" } ], "staticAggregationHookFactory": "0xe70E86a7D1e001D419D71F960Cb6CaD59b6A3dB6", @@ -535,7 +553,7 @@ "interchainAccountIsm": "0xB732c83aeE29596E3163Da2260710eAB67Bc0B29", "interchainAccountRouter": "0x27a6cAe33378bB6A6663b382070427A01fc9cB37", "interchainGasPaymaster": "0x571f1435613381208477ac5d6974310d88AC7cB7", - "interchainSecurityModule": "0x5698f94618093Bb122e946E2804d91017def3Ed4", + "interchainSecurityModule": "0x81f094745657CaAcCe6B826caC2255269E1A0Eff", "mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb", "merkleTreeHook": "0x04dB778f05854f26E67e0a66b740BBbE9070D366", "name": "celo", @@ -600,7 +618,7 @@ "interchainAccountIsm": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "interchainAccountRouter": "0xEF9A332Ec1fD233Bf9344A58be56ff9E104B4f60", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0x2bBB4d8337eaeFB6dA75b1647403722cf2Fe8801", + "interchainSecurityModule": "0x136084E87579E03caC815e975B9cE19178f99C5b", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "cheesechain", @@ -663,7 +681,7 @@ "from": 4842212 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xA3A8aF70997949B138aBEd936D270Fd95bE60cF5", + "interchainSecurityModule": "0x1E643A23c074aB3d28b55B4e410CE49734E1e54e", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "cyber", @@ -730,7 +748,7 @@ "from": 23783929 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x7F14B16C446884E06aF21AF2340C6B91D9cD4AF6", + "interchainSecurityModule": "0xA19c9f9E0781c301C080fb0b95b8C62fc5f43b61", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "degenchain", @@ -843,7 +861,7 @@ "interchainAccountIsm": "0xCeafc098e5c3c7768b9229Be2FEC275862A81Abd", "interchainAccountRouter": "0xed9a722c543883FB7e07E78F3879762DE09eA7D5", "interchainGasPaymaster": "0xB30EAB08aa87138D57168D0e236850A530f49921", - "interchainSecurityModule": "0x438e8B0d9fDdB16E29Fa22AfE3a63cD7Bf0D4909", + "interchainSecurityModule": "0x76F54FC582Bd93B808FaC289847AEe18F5BF115d", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xC831271c1fB212012811a91Dd43e5926C1020563", "name": "endurance", @@ -914,7 +932,7 @@ "interchainAccountIsm": "0x292C614ED53DaaDBf971521bc2C652d1ca51cB47", "interchainAccountRouter": "0x5E532F7B610618eE73C2B462978e94CB1F7995Ce", "interchainGasPaymaster": "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611", - "interchainSecurityModule": "0x59f2ba715A3d4bA4beC4e7ea7988eC3c579ecE77", + "interchainSecurityModule": "0xD56421450D656c0e9bDea3EEdb29Cee3D9c24751", "mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239", "merkleTreeHook": "0x48e6c30B97748d1e2e03bf3e9FbE3890ca5f8CCA", "name": "ethereum", @@ -936,10 +954,16 @@ "http": "https://ethereum.publicnode.com" }, { - "http": "https://cloudflare-eth.com" + "http": "https://eth.drpc.org" }, { - "http": "https://eth.drpc.org" + "http": "https://rpc.flashbots.net" + }, + { + "http": "https://eth.blockrazor.xyz" + }, + { + "http": "https://eth-pokt.nodies.app" } ], "staticAggregationHookFactory": "0x6D2555A8ba483CcF4409C39013F5e9a3285D3C9E", @@ -989,7 +1013,7 @@ "interchainAccountIsm": "0x7C012DCA02C42cfA3Fd7Da3B0ED7234B52AE68eF", "interchainAccountRouter": "0xbed53B5C5BCE9433f25A2A702e6df13E22d84Ae9", "interchainGasPaymaster": "0x2Fca7f6eC3d4A0408900f2BB30004d4616eE985E", - "interchainSecurityModule": "0xDa87175A8D1d6C5102228fB02b0D74AA13f496fb", + "interchainSecurityModule": "0xd3807eA5b1722c3021a5e0C72de8ac7aC7Cfff07", "mailbox": "0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3", "merkleTreeHook": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "name": "fraxtal", @@ -1057,7 +1081,7 @@ "interchainAccountIsm": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD", "interchainAccountRouter": "0x2351FBe24C1212F253b7a300ff0cBCFd97952a19", "interchainGasPaymaster": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", - "interchainSecurityModule": "0xcBE8A3d5A9714A7ae5acFA3185e27b8859149AF7", + "interchainSecurityModule": "0x5aF4854dA8efae889fC0A12B3b1b2668b77A6a4F", "mailbox": "0x3071D4DA6020C956Fe15Bfd0a9Ca8D4574f16696", "merkleTreeHook": "0xfBc08389224d23b79cb21cDc16c5d42F0ad0F57f", "name": "fusemainnet", @@ -1131,7 +1155,7 @@ "interchainAccountIsm": "0x07E2062A1bC66a2C1d05cb5C3870a4AF86e0056E", "interchainAccountRouter": "0xBE70Ab882D1F7E37e04a70CDd9Ec23b37a234064", "interchainGasPaymaster": "0xDd260B99d302f0A3fF885728c086f729c06f227f", - "interchainSecurityModule": "0x9B35A8A81Af682178c501ca2A03759582E663cc8", + "interchainSecurityModule": "0xC5d836F4037eb4a25c6153400497FFD5838C79Ff", "mailbox": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "name": "gnosis", @@ -1202,7 +1226,7 @@ "interchainAccountIsm": "0x708E002637792FDC031E6B62f23DD60014AC976a", "interchainAccountRouter": "0xfB8cea1c7F45608Da30655b50bbF355D123A4358", "interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117", - "interchainSecurityModule": "0xB5A787404B071415CDba76Aa6823C4313D326e6e", + "interchainSecurityModule": "0x09126B1c560808679D2257F3b9c828f53b029352", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", "name": "inevm", @@ -1333,7 +1357,7 @@ "from": 14616307 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xD75EddFf11a00b86Ac6d7Af98C090FfA928FceEC", + "interchainSecurityModule": "0xB5Dff92028d2114281281C0bA4743Ba884f7f689", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "kroma", @@ -1406,7 +1430,7 @@ "interchainAccountIsm": "0xdcA646C56E7768DD11654956adE24bfFf9Ba4893", "interchainAccountRouter": "0xD59dA396F162Ed93a41252Cebb8d5DD4F093238C", "interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28", - "interchainSecurityModule": "0xe13288068F0D7b0c0FE12C4fDb0B3FD3F4F7E101", + "interchainSecurityModule": "0x176Ff5aa9cba29E5a0BDc6a694885067F24eC7B5", "mailbox": "0x02d16BC51af6BfD153d67CA61754cF912E82C4d9", "merkleTreeHook": "0xC077A0Cc408173349b1c9870C667B40FE3C01dd7", "name": "linea", @@ -1477,7 +1501,7 @@ "from": 4195553 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x1F444C08D2Dc01cE735fC11F8986008d7B611001", + "interchainSecurityModule": "0xfb44E6A12e1B34c5f801b2045Dd34434D0b0D65A", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "lisk", @@ -1542,7 +1566,7 @@ "from": 3088760 }, "interchainGasPaymaster": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", - "interchainSecurityModule": "0x2A609D036b0a30F87340726c0E29e67c0a424201", + "interchainSecurityModule": "0x7eeA9f24D2b70e3bC8470febEEF5a714c168F4F5", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x062200d92dF6bB7bA89Ce4D6800110450f94784e", "name": "lukso", @@ -1616,7 +1640,7 @@ "interchainAccountIsm": "0x8Ea50255C282F89d1A14ad3F159437EE5EF0507f", "interchainAccountRouter": "0x693A4cE39d99e46B04cb562329e3F0141cA17331", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x10d65CAF7B1c764b77015913d4bF69aEfdd57f06", + "interchainSecurityModule": "0xc9D68dD246872600D8a17EC0f1C689fc6c596d5A", "isTestnet": false, "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", @@ -1686,7 +1710,7 @@ "interchainAccountIsm": "0xe039DA3A0071BEd087A12660D7b03cf669c7776E", "interchainAccountRouter": "0x45285463352c53a481e882cD5E2AF2E25BBdAd0D", "interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28", - "interchainSecurityModule": "0x1104Fb7e5CA092aBB9154bB2B1E1905ff35332fe", + "interchainSecurityModule": "0x5fDE4c3004295bA37c6288142b85b927d4D77BB8", "mailbox": "0x398633D19f4371e1DB5a8EFE90468eB70B1176AA", "merkleTreeHook": "0x5332D1AC0A626D265298c14ff681c0A8D28dB86d", "name": "mantle", @@ -1748,7 +1772,7 @@ "from": 13523607 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x6828457a0727cE2DEe5c9f6f4a7498565844067f", + "interchainSecurityModule": "0xB84AabD9955080Aa940e6772523dB809441d4CD0", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "merlin", @@ -1815,7 +1839,7 @@ "from": 17966274 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x231B529b4f34FBfF2c609D4F721e9343fDA7B97f", + "interchainSecurityModule": "0xCAE42c772F2faCF17095B0b0275891dA45EFB276", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "metis", @@ -1880,7 +1904,7 @@ "from": 3752032 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x4761669387477aB606C9aF717accFe395F4b4699", + "interchainSecurityModule": "0x0195648ad26c8E3e14bE8251d122F6c7FF31c7d3", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "mint", @@ -1947,7 +1971,7 @@ "interchainAccountIsm": "0xa377b8269e0A47cdd2fD5AAeAe860b45623c6d82", "interchainAccountRouter": "0x6e1B9f776bd415d7cC3C7458A5f0d801016918f8", "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "interchainSecurityModule": "0xC0BA461D612361F374066F8Ffa850717d3eE003b", + "interchainSecurityModule": "0x2B6Bb944E5ce12A455Db41afF80755B9a66eC51c", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "name": "mode", @@ -2019,7 +2043,7 @@ "interchainAccountIsm": "0x79b3730CE3685f65802aF1771319992bA960EB9D", "interchainAccountRouter": "0xc4482f66191754a8629D35289043C4EB0285F10E", "interchainGasPaymaster": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "interchainSecurityModule": "0x981974103BC1C6D40D9E7b79F3dAaEA672913E3e", + "interchainSecurityModule": "0xC3d997D97D56a1c18E93325Ea47FA11222D4cFa9", "mailbox": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", "merkleTreeHook": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", "name": "moonbeam", @@ -2161,7 +2185,7 @@ "interchainAccountIsm": "0x2c46BF14641d00549ECa4779BF5CBf91602C1DEd", "interchainAccountRouter": "0x03D6cC17d45E9EA27ED757A8214d1F07F7D901aD", "interchainGasPaymaster": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "interchainSecurityModule": "0x66B72EA92140d727f9B831dA997a7bf2B4241CbA", + "interchainSecurityModule": "0xd2B05DD7e2a3cd8016E4B0E0Df31402F4A51F37F", "mailbox": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", "merkleTreeHook": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", "name": "optimism", @@ -2178,6 +2202,15 @@ "rpcUrls": [ { "http": "https://mainnet.optimism.io" + }, + { + "http": "https://optimism.drpc.org" + }, + { + "http": "https://optimism-rpc.publicnode.com" + }, + { + "http": "https://op-pokt.nodies.app" } ], "staticAggregationHookFactory": "0x15DEeAB8dECDe553bb0B1F9C00984cbcae1af3D7", @@ -2300,7 +2333,7 @@ "interchainAccountIsm": "0xBAC4529cdfE7CCe9E858BF706e41F8Ed096C1BAd", "interchainAccountRouter": "0xF163949AD9F88977ebF649D0461398Ca752E64B9", "interchainGasPaymaster": "0x0071740Bf129b05C4684abfbBeD248D80971cce2", - "interchainSecurityModule": "0x46640247fd58Ddd6FFd669842EAa8f1126653b47", + "interchainSecurityModule": "0xC80A77B3851709265D246553248FA9Ec5dE11ac1", "mailbox": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", "merkleTreeHook": "0x73FbD25c3e817DC4B4Cd9d00eff6D83dcde2DfF6", "name": "polygon", @@ -2322,7 +2355,10 @@ "http": "https://polygon-rpc.com" }, { - "http": "https://rpc.ankr.com/polygon" + "http": "https://polygon.drpc.org" + }, + { + "http": "https://polygon-pokt.nodies.app" } ], "staticAggregationHookFactory": "0xFeeB86e70e4a640cDd29636CCE19BD6fe8628135", @@ -2374,7 +2410,7 @@ "interchainAccountIsm": "0xc1198e241DAe48BF5AEDE5DCE49Fe4A6064cF7a7", "interchainAccountRouter": "0x20a0A32a110362920597F72974E1E0d7e25cA20a", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x8E3221dfeB876698AE86DD24fF8C9668AFa1a365", + "interchainSecurityModule": "0xF124d464534b3a17aa362fa3B613847dfF6c3B1F", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "polygonzkevm", @@ -2442,7 +2478,7 @@ "from": 32018468 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x7d86188dad2e53078E78CaFEdaAaec85df850a32", + "interchainSecurityModule": "0x958feE8928cDF05a3bBc99D9B3399eAE260aDe3f", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "proofofplay", @@ -2506,7 +2542,7 @@ "from": 363159 }, "interchainGasPaymaster": "0x3071D4DA6020C956Fe15Bfd0a9Ca8D4574f16696", - "interchainSecurityModule": "0x541c30815Ae4268EfD2c71356E0eF7b954BA81EC", + "interchainSecurityModule": "0x94e972d871b8475f3BC7Dc343F367c596b8a4BaF", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x55E4F0bc6b7Bb493D50839A8592e7ad8d5e93cf7", "name": "real", @@ -2573,7 +2609,7 @@ "interchainAccountIsm": "0x5DA60220C5dDe35b7aE91c042ff5979047FA0785", "interchainAccountRouter": "0x7a4d31a686A36285d68e14EDD53631417eB19603", "interchainGasPaymaster": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", - "interchainSecurityModule": "0x79461FE1BedB824630b7E3c896409cb41FAC2bAd", + "interchainSecurityModule": "0x75546E3b8493D8813e8B010F0ED1b4e844a3A63E", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", "name": "redstone", @@ -2635,7 +2671,7 @@ "from": 937117 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x3eC59Fcd81b2542aa8661A8e25011Fda4b342c69", + "interchainSecurityModule": "0x1f37EeE496aFfD1E37E1faef1C56d6d3e0d8cE07", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "sanko", @@ -2703,7 +2739,7 @@ "interchainAccountIsm": "0x32af5Df81fEd5E26119F6640FBB13f3d63a94CDe", "interchainAccountRouter": "0x0B48a744698ba8dFa514742dFEB6728f52fD66f7", "interchainGasPaymaster": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "interchainSecurityModule": "0x22bC113c8279393E5995c64e6Af78b0397437f43", + "interchainSecurityModule": "0x03dfbdB235e3CA8C2898af852151784637a5D366", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", "name": "scroll", @@ -2784,7 +2820,7 @@ "interchainAccountIsm": "0xf35dc7B9eE4Ebf0cd3546Bd6EE3b403dE2b9F5D6", "interchainAccountRouter": "0xBcaedE97a98573A88242B3b0CB0A255F3f90d4d5", "interchainGasPaymaster": "0xFC62DeF1f08793aBf0E67f69257c6be258194F72", - "interchainSecurityModule": "0x8bB29A4c9F4985b2b18d02da19994E1f8f49a456", + "interchainSecurityModule": "0xD68DC7cd1AFFC059BB7e2DDcA482F58F4bEC1738", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xca1b69fA4c4a7c7fD839bC50867c589592bcfe49", "name": "sei", @@ -2899,7 +2935,7 @@ "interchainAccountIsm": "0xAE557e108b3336130370aC74836f1356B4b30Cf2", "interchainAccountRouter": "0x1F8CF09F060A2AE962c0Bb1F92e209a1E7b0E10B", "interchainGasPaymaster": "0x273Bc6b01D9E88c064b6E5e409BdF998246AEF42", - "interchainSecurityModule": "0x52FA98bdbf1217f3670387d8587487Bb620aBdC5", + "interchainSecurityModule": "0x55eB3F2c6675230D8B726e3Ef5F2Aa0B214a5E24", "mailbox": "0x28EFBCadA00A7ed6772b3666F3898d276e88CAe3", "merkleTreeHook": "0x6A55822cf11f9fcBc4c75BC2638AfE8Eb942cAdd", "name": "taiko", @@ -2961,7 +2997,7 @@ "from": 1678063 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x5aac07D4DB9cC53Fc9dED7075E331F0CA374104a", + "interchainSecurityModule": "0x6CDfadC52B4484a8C832a693a15f1DDD5D15C27b", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", @@ -3029,7 +3065,7 @@ "interchainAccountIsm": "0x551BbEc45FD665a8C95ca8731CbC32b7653Bc59B", "interchainAccountRouter": "0xc11f8Cf2343d3788405582F65B8af6A4F7a6FfC8", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x04Df0F875eBF8b7E81Df0c8014C2c88c08d47375", + "interchainSecurityModule": "0x95245ADD3Dfd7513e098d787dcA6D0a48f28B36C", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "viction", @@ -3097,7 +3133,7 @@ "interchainAccountIsm": "0xCB9f90EE5d83Ea52ABd922BD70898f0155D54798", "interchainAccountRouter": "0x473884010F0C1742DA8Ad01E7E295624B931076b", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0x3bd1AbAe2c79c522Bb1724EcCDE64a5B3EFD7306", + "interchainSecurityModule": "0xc7a8d9D347ac7FdFD9E52965047F14176d981325", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "worldchain", @@ -3160,7 +3196,7 @@ "from": 24395308 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x8d6885498eaFe02248578d19143538F56CF9c0e1", + "interchainSecurityModule": "0xcb5C7fc7cD8eF739b54F41E63E10b175345ac62f", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "xai", @@ -3228,7 +3264,7 @@ "interchainAccountIsm": "0x29B37088724B745C0ABcE591449Cf042772160C2", "interchainAccountRouter": "0x03cF708E42C89623bd83B281A56935cB562b9258", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0x34881587eE748410d4adE581c88D19520b4405F5", + "interchainSecurityModule": "0x40cF5F0fBebba93A5Eb2d4206947EAC7e94528E0", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "xlayer", @@ -3296,7 +3332,7 @@ "interchainAccountIsm": "0x2b6d3F7d28B5EC8C3C028fBCAdcf774D9709Dd29", "interchainAccountRouter": "0x3AdCBc94ab8C48EC52D06dc65Bb787fD1981E3d5", "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "interchainSecurityModule": "0xdA297907c3700234d7deDE5d346D8c7376d77584", + "interchainSecurityModule": "0xc5deE2eE7087126Ef75D243960f098c02cD87948", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "name": "zetachain", @@ -3362,7 +3398,7 @@ "from": 1511458 }, "interchainGasPaymaster": "0x03cF708E42C89623bd83B281A56935cB562b9258", - "interchainSecurityModule": "0x48793Ed9b3fb48a759Fb2e0D4988d2BE71c4F141", + "interchainSecurityModule": "0x41FE6eb1b63e6328fB7f5bB050822C5FdDdD1Dd4", "mailbox": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", "merkleTreeHook": "0x4C97D35c668EE5194a13c8DE8Afc18cce40C9F28", "name": "zircuit", @@ -3435,7 +3471,7 @@ "interchainAccountIsm": "0xb2674E213019972f937CCFc5e23BF963D915809e", "interchainAccountRouter": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainGasPaymaster": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", - "interchainSecurityModule": "0xa752ADc2a1b2bcCBfB08216f567b8ad4a6B1c54D", + "interchainSecurityModule": "0x112D8fc80e79f6cEbc9F6c34cE3E6E0e390Be3b6", "mailbox": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "merkleTreeHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", "name": "zoramainnet", @@ -3506,7 +3542,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x79891a4B04cfcD56932102D69d6cbd57E77b51A4", + "interchainSecurityModule": "0x554Da726a393661d5b32915D5D2a03244951E1bE", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3576,7 +3612,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xcA0dEbdcd926cf06507a2e19ca7f5c3087577F09", + "interchainSecurityModule": "0x53a773b22C7e936290e1b5cD227E19eA39AEcfEF", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3652,7 +3688,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xf71E5802FeFDdd09CcF131362c8cfD195EeB3C4E", + "interchainSecurityModule": "0x01d7f73622EFbBF02805Cd7a5E7c7408Ec311044", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3716,7 +3752,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x239A0567841a1489DfD134089a5806bE7823A38C", + "interchainSecurityModule": "0x035C61A46A63E461b9e611D472e47E722DC66494", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3789,7 +3825,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xf813C66da07b10438CE199AAD100F14C00D050aF", + "interchainSecurityModule": "0xC41695645E9a28A8ac47c985f754ab3f256893Ca", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3858,7 +3894,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x97B619407F1EFEA56bA0da09cDfdf2B88B5dC360", + "interchainSecurityModule": "0xf6826c462DFE186ca52BECe88657573A96BD6F69", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3921,7 +3957,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xF730bB6EAA4c07aAA3C247d4276457cb14A7C6B6", + "interchainSecurityModule": "0x56A3De59b9a74a95f7e5b5c5FFCD14F8C34D32ff", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3991,7 +4027,7 @@ "interchainAccountIsm": "0xcd9D3744512F07AE844c40E27912092d7c503565", "interchainAccountRouter": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", "interchainGasPaymaster": "0xb58257cc81E47EC72fD38aE16297048de23163b4", - "interchainSecurityModule": "0xd03554d5e47B129F843Dad6fCB01335cF2bB956b", + "interchainSecurityModule": "0x5a7056ac73D3455A7cD24F3E6f73af87bca5F757", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0xCC3D1659D50461d27a2F025dDb2c9B06B584B7e1", "pausableHook": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", @@ -4051,7 +4087,7 @@ "interchainAccountIsm": "0xc23BaF5Eb5848D19701BbE7f139645e6bd58a319", "interchainAccountRouter": "0x7c58Cadcc2b60ACF794eE1843488d6f5703f76BE", "interchainGasPaymaster": "0xb4fc9B5fD57499Ef6FfF3995728a55F7A618ef86", - "interchainSecurityModule": "0xE3ce115F5a0561364e4576BE408AEB3d362e77ee", + "interchainSecurityModule": "0xc9C321AaB3AbbE4645aa7DC7dADb4D82b4DE7626", "mailbox": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", "merkleTreeHook": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", "pausableHook": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2", @@ -4182,7 +4218,7 @@ "interchainAccountIsm": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", "interchainAccountRouter": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "interchainGasPaymaster": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", - "interchainSecurityModule": "0xca63656D17e9a368c527edeAE562a653bc1700fb", + "interchainSecurityModule": "0xc657Fb2209363288476E0B7c24a7c0aa3fA6e639", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", "pausableHook": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", @@ -4246,7 +4282,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0xE124bDAc9235650406363165cBcE61bA32c1461C", + "interchainSecurityModule": "0xF761DcDB76C01053aee82A43D52108d241f8BA04", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4307,7 +4343,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x8c2306B2c5E8D6fBba256e5De1AF1bDae46c9772", + "interchainSecurityModule": "0x14ea517FEc3Ab71761cAF32eECa3964085C25Bc7", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4386,7 +4422,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x6a696868Bb1676FD6F621F47826b5B94EAaD2992", + "interchainSecurityModule": "0xD20E6abfec4FE45Ce5C4b5394f85a5282BDf17c6", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4453,7 +4489,7 @@ "interchainAccountIsm": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", "interchainAccountRouter": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", "interchainGasPaymaster": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", - "interchainSecurityModule": "0xc7F5fFadDb69a9BB79e7Bdd9707F8a964DA12ED2", + "interchainSecurityModule": "0xb3d730e1039967EE81550E5065C6CAeD150D1D67", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", "pausableHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", @@ -4524,7 +4560,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x5A8b89A37500f5d30Aa6A8855714e48eEA513928", + "interchainSecurityModule": "0xe4DB36906F565925b79e5125690Da9dD46f91034", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4589,7 +4625,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x68d516c4eD1545b9530f909c5875332eac6435A7", + "interchainSecurityModule": "0x14ea517FEc3Ab71761cAF32eECa3964085C25Bc7", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4653,7 +4689,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x5cd17cc5F5802aa63645d3AF8A9071bE3F9B5E0c", + "interchainSecurityModule": "0x05e90D54Dd4f648bc5CA2D051D154C1594eddBCa", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4723,7 +4759,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x1f62894DDec2FA56581D483E0DFAC93cc0051dD5", + "interchainSecurityModule": "0x85b8a06DBfC28aef36F6802C63735bdd6c74CCad", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4787,7 +4823,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0xFf828bCb21b15A5c9aA172331dd48e317B728B64", + "interchainSecurityModule": "0x85b8a06DBfC28aef36F6802C63735bdd6c74CCad", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4849,7 +4885,7 @@ "fallbackDomainRoutingHook": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", "fallbackRoutingHook": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", "interchainGasPaymaster": "0x318FbdB17d4e743aBF3183658a4730777101B75C", - "interchainSecurityModule": "0x9C066e6c2BDF294c16587EE1e69a708022EB7c19", + "interchainSecurityModule": "0x17e89240A20eB14B902f91EB15b16303BEe6C3EE", "mailbox": "0xd7b351D2dE3495eA259DD10ab4b9300A378Afbf3", "merkleTreeHook": "0x55379421409961Ef129738c24261379ef8A547Df", "proxyAdmin": "0x72e2A678442Edc65f14476A0E4c94312C0469f4A", @@ -4907,7 +4943,7 @@ "fallbackDomainRoutingHook": "0xe4e98Cc5D0318aBFD2adA8A3C6817b727063F500", "fallbackRoutingHook": "0xe4e98Cc5D0318aBFD2adA8A3C6817b727063F500", "interchainGasPaymaster": "0xf44AdA86a1f765A938d404699B8070Dd47bD2431", - "interchainSecurityModule": "0xEc6D1906aD2Ab42377141C3eFA48cCa438e99022", + "interchainSecurityModule": "0x3f49308B3d4861cFA7c7f075524e7144d0Fbc9d2", "mailbox": "0x6bD0A2214797Bc81e0b006F7B74d6221BcD8cb6E", "merkleTreeHook": "0x823500D69D77A52212DC93f8836E9c08581487eE", "proxyAdmin": "0xD01274DC164D32F8595bE707F221375E68cE300C", @@ -4974,7 +5010,7 @@ "interchainAccountIsm": "0x4d264424905535E97396Db83bd553D0d73A4EF9d", "interchainAccountRouter": "0x26A29486480BD74f9B830a9B8dB33cb43C40f496", "interchainGasPaymaster": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", - "interchainSecurityModule": "0xAF6B4222dB762a21E5DB86e5a096A9f61ff8536B", + "interchainSecurityModule": "0xCb6bd8F1b8bDC81b69684b0Af8b8896d24D0252A", "mailbox": "0x5bdADEAD721Eb4C4038fF7c989E3C7BbBA302435", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "pausableHook": "0xC8E323036AAFB4B4201e7B640E79C4Db285A3FC8", @@ -5038,7 +5074,7 @@ "interchainAccountIsm": "0x545E289B88c6d97b74eC0B96e308cae46Bf5f832", "interchainAccountRouter": "0x4ef363Da5bb09CC6aeA16973786963d0C8820778", "interchainGasPaymaster": "0x561BcA8D862536CD9C88f332C1A1Da0fC8F96e40", - "interchainSecurityModule": "0x2ed1BED972266d0873734B9439A671A8aa84fEA2", + "interchainSecurityModule": "0x90eF233257c069dC96200a35EDF8B51FC29677A0", "mailbox": "0x248aDe14C0489E20C9a7Fea5F86DBfC3702208eF", "merkleTreeHook": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", "pausableHook": "0x2f536FB7a37bd817Af644072a904Ddc02Dae429f", @@ -5105,7 +5141,7 @@ "interchainAccountIsm": "0x60bB6D060393D3C206719A7bD61844cC82891cfB", "interchainAccountRouter": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", "interchainGasPaymaster": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", - "interchainSecurityModule": "0x436add4839EEFA86c68fC3411ACdFA30CF522Ba9", + "interchainSecurityModule": "0xEb6F2551205D99D787C77a33bb4c0a5D042D18B6", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", "pausableHook": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", @@ -5170,7 +5206,7 @@ "interchainAccountIsm": "0xcdc31BA959DE8C035A03167ebAE1961208CDf172", "interchainAccountRouter": "0x349831a180eE4265008C5FFB9465Ff97c1CF0028", "interchainGasPaymaster": "0x6AA10748a036a49Cb290C0e12B77319b76792D5E", - "interchainSecurityModule": "0x1f9bB28a9f79B9F1464DC894C39490acdB208bc8", + "interchainSecurityModule": "0x6bb244262684315924ea4f56E8F9B989C294945d", "mailbox": "0xd9Cc2e652A162bb93173d1c44d46cd2c0bbDA59D", "merkleTreeHook": "0x2783D98CC073dbcDa90241C804d16982D3d75821", "pausableHook": "0x3bb2D0a828f7dD91bA786091F421f6d7cF376445", @@ -5240,7 +5276,7 @@ "interchainAccountIsm": "0x545E289B88c6d97b74eC0B96e308cae46Bf5f832", "interchainAccountRouter": "0x4ef363Da5bb09CC6aeA16973786963d0C8820778", "interchainGasPaymaster": "0xc6835e52C1b976F1ebC71Bc8919738E02849FdA9", - "interchainSecurityModule": "0x5cA9D5bad6E75F5062bab703E2828E7D1D007FbD", + "interchainSecurityModule": "0xD71daD86F85de1f773e461617acC21FcFfDB7000", "mailbox": "0x1c6f404800bA49Ed581af734eA0d25c0c7d017B2", "merkleTreeHook": "0xdAa1B65547fB969c9ff5678956AB2FF9771B883D", "pausableHook": "0xA0e0829DA397CcF55d5B779C31728f21Cb8219DF", @@ -5353,7 +5389,7 @@ "interchainAccountIsm": "0x8c794a781327b819416E7b67908f1D22397f1E67", "interchainAccountRouter": "0x16625230dD6cFe1B2bec3eCaEc7d43bA3A902CD6", "interchainGasPaymaster": "0x2b79328DA089E89A9E9c08732b56dd31F01011Db", - "interchainSecurityModule": "0x294127e3D5Cf438430e7ce6e7822aF07EB528A6b", + "interchainSecurityModule": "0x5abf9e3D079A2cac613b6aE6D3f9de6E9C7a691e", "mailbox": "0x730f8a4128Fa8c53C777B62Baa1abeF94cAd34a9", "merkleTreeHook": "0x9c64f327F0140DeBd430aab3E2F1d6cbcA921227", "pausableHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", @@ -5417,7 +5453,7 @@ "interchainAccountIsm": "0xE67Dc24970B482579923551Ede52BD35a2858989", "interchainAccountRouter": "0xDDE46032Baf4da13fDD79BF9dfbaA2749615C409", "interchainGasPaymaster": "0x2f536FB7a37bd817Af644072a904Ddc02Dae429f", - "interchainSecurityModule": "0x3cB87AAa6CaA54cf14BB4D0063B79a7CDDeBCD1A", + "interchainSecurityModule": "0x5a72E5db6d8423ABe9be4A3E69D7982Eb0aa0fE6", "mailbox": "0x2f0E57527Bb37E5E064EF243fad56CCE6241906c", "merkleTreeHook": "0xC8E323036AAFB4B4201e7B640E79C4Db285A3FC8", "pausableHook": "0xdAa1B65547fB969c9ff5678956AB2FF9771B883D", @@ -5481,7 +5517,7 @@ "interchainAccountIsm": "0x20a0A32a110362920597F72974E1E0d7e25cA20a", "interchainAccountRouter": "0x5b3EeADcc0E2d4284eA6816e2E503c24d30a9E54", "interchainGasPaymaster": "0x282629Af1A2f9b8e2c5Cbc54C35C7989f21950c6", - "interchainSecurityModule": "0xBA5683c94dc66dCE0CB5489B15C2E9eed0C31924", + "interchainSecurityModule": "0xC7262Ce0625a9D51AEf403A42F5856ac2883F9E0", "mailbox": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "merkleTreeHook": "0xf147bBD944C610F86DaE6C7668497D22932C1E4A", "pausableHook": "0x872Bd98057931c8809927c6dE2ef39738a80Eb0C", @@ -5548,7 +5584,7 @@ "interchainAccountIsm": "0xf40eE9FF75Fa34910b7C4C8d68d4850B3bD184D3", "interchainAccountRouter": "0xf6fB78dc009C1A4286c0E7d90C10c9E8906a62Ea", "interchainGasPaymaster": "0xDDE46032Baf4da13fDD79BF9dfbaA2749615C409", - "interchainSecurityModule": "0x0Af112173cCd9375080DFd11c92Ccfb4E8eA0041", + "interchainSecurityModule": "0x073a33dBb4654E59a54FDD3024EAd142057aC7F5", "mailbox": "0x65dCf8F6b3f6a0ECEdf3d0bdCB036AEa47A1d615", "merkleTreeHook": "0x8c794a781327b819416E7b67908f1D22397f1E67", "pausableHook": "0x4d264424905535E97396Db83bd553D0d73A4EF9d", @@ -5615,7 +5651,7 @@ "interchainAccountIsm": "0xd9Cc2e652A162bb93173d1c44d46cd2c0bbDA59D", "interchainAccountRouter": "0x7279B1e11142078b8dC9e69620200f4C84FB8aaa", "interchainGasPaymaster": "0x5ae1ECA065aC8ee92Ce98E584fc3CE43070020e7", - "interchainSecurityModule": "0x4cD5E75A30d225c1E4Bf1838F6F8F27E480F4083", + "interchainSecurityModule": "0xf27f91975DdC41d65737d83A68FF9dFAf1167f47", "mailbox": "0x96D51cc3f7500d501bAeB1A2a62BB96fa03532F8", "merkleTreeHook": "0x086c3947F71BE98A0bDf4AB7239955e7542b0CbA", "pausableHook": "0x9C6e8d989ea7F212e679191BEb44139d83ac927a", @@ -5685,7 +5721,7 @@ "interchainAccountIsm": "0x8a733038eF4BbC314eE0F7595257D8d3799B6aA9", "interchainAccountRouter": "0xCE8260c1b5cF2fAD15bb4B6542716b050Fdf35c9", "interchainGasPaymaster": "0xa1c3884EbE24Cccb120B2E98a55f85140563aa4C", - "interchainSecurityModule": "0x6825B2925953128090597695694926077e1D42B0", + "interchainSecurityModule": "0xB451931b256A10a153e63eEDC1df551711d9111A", "mailbox": "0x5e8a0fCc0D1DF583322943e01F02cB243e5300f6", "merkleTreeHook": "0x2f536FB7a37bd817Af644072a904Ddc02Dae429f", "pausableHook": "0xc6835e52C1b976F1ebC71Bc8919738E02849FdA9", @@ -5746,7 +5782,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x394F2bF9E6e6CCB0b26f3970ee4C178860d2e961", + "interchainSecurityModule": "0xef02E6C475921CcF8F4E873Ab03D1e327dA5D886", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -5816,7 +5852,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0xB16b9E925C454cDbf8b63832F9402b6484E26787", + "interchainSecurityModule": "0xD8eB6f922eD5B4dF67B518A74dDaf8C8De6883d0", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -5877,7 +5913,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x07291a67F6EF475C0713495fd58339E39C7C85E3", + "interchainSecurityModule": "0x2450D9b0A2BC9E1fF4F143dbB7A308218FbC9135", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -5942,7 +5978,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x3B4B8A030D1f668e3ecBB4b5F6d2B5c1265D8951", + "interchainSecurityModule": "0x4d41Fd8d63E06fd197aB2E16fA13963526c03B9f", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -6007,7 +6043,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x343F3617495669bb5f01B3fF8c0EEeC14694Fc82", + "interchainSecurityModule": "0x41F71447c7d9765ca7be4f73ee3Da20E117fDF23", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -6080,7 +6116,7 @@ "interchainAccountIsm": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainAccountRouter": "0x4D50044335dc1d4D26c343AdeDf6E47808475Deb", "interchainGasPaymaster": "0x70EbA87Cd15616f32C736B3f3BdCfaeD0713a82B", - "interchainSecurityModule": "0xBa99F47802E9Bf7A1d91ca8E1Da5939ad9507Faf", + "interchainSecurityModule": "0x474E8b489afb87943767ea2F39a4De75889FC0a1", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xbb0AE51BCa526cF313b6a95BfaB020794af6C394", "pausableHook": "0x83475ca5bEB2Eaa59A2FF48a0544ebaa4a32c2de", @@ -6144,7 +6180,7 @@ "interchainAccountIsm": "0x31Bb27f6007C33acD1be83ACEd3164C60f8F7b13", "interchainAccountRouter": "0xEeb5a99a75585fe137c83E7b62b74f87264A5481", "interchainGasPaymaster": "0xb7C9307fE90B9AB093c6D3EdeE3259f5378D5f03", - "interchainSecurityModule": "0x6a8c4A6FE157fbeB0A216B023065951eaF3e797D", + "interchainSecurityModule": "0x258c98DfA4e3A51344FEafc01C119E1b0702FbF0", "mailbox": "0x0dF25A2d59F03F039b56E90EdC5B89679Ace28Bc", "merkleTreeHook": "0xC88636fFdFAc7cb87b7A76310B7a62AF0A000595", "pausableHook": "0x2AF32cF8e3Cf42d221eDa0c843818fA5ee129E27", @@ -6209,7 +6245,7 @@ "interchainAccountIsm": "0x28291a7062afA569104bEd52F7AcCA3dD2FafD11", "interchainAccountRouter": "0xe9E3444DDD80c50276c0Fcf316026f6d7fEc2c47", "interchainGasPaymaster": "0x25EAC2007b0D40E3f0AF112FD346412321038719", - "interchainSecurityModule": "0x6EcFFECAa23368CdcFcFb797a136333179c32e9e", + "interchainSecurityModule": "0x6681E5D10cf8A0B37428cEf81Fb20BF76b2a88D5", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "pausableHook": "0x99fEFc1119E86Ee0153eb887cF8E8ab2d92A16e8", @@ -6277,7 +6313,7 @@ "interchainAccountIsm": "0x027eFD1695941969435AA640542B690044dF7E06", "interchainAccountRouter": "0x65F1343AC23D4fF48bf6c7E0c55872d245397567", "interchainGasPaymaster": "0x28291a7062afA569104bEd52F7AcCA3dD2FafD11", - "interchainSecurityModule": "0xC2a8832Aa1a9cE1dbf3DF228Dc1919e6e42622EA", + "interchainSecurityModule": "0x3A7cBF6a4c53262a311150014DD107006dF09c79", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xcd90D49b046772F710250b9119117169CB2e4D8b", "pausableHook": "0x7CE76f5f0C469bBB4cd7Ea6EbabB54437A093127", @@ -6337,7 +6373,7 @@ "fallbackDomainRoutingHook": "0x697a90753B7dCf6512189c239E612fC12baaE500", "fallbackRoutingHook": "0x697a90753B7dCf6512189c239E612fC12baaE500", "interchainGasPaymaster": "0x388289cd5862e17AAfD6ffF7F46A9Ec48a969bCd", - "interchainSecurityModule": "0x93a89Fb2E22DAed1631DB6dbC39E173A26BD497D", + "interchainSecurityModule": "0x4C219E944168AD29831Fd311285ac2ab92d96f28", "mailbox": "0x9BbDf86b272d224323136E15594fdCe487F40ce7", "merkleTreeHook": "0xc1FC99Ea4D8B2b06b674A2e91b5A4617c1dcFd22", "proxyAdmin": "0x038F9F4e93e88Af2C688da265222FdE80e455aA4", @@ -6399,7 +6435,7 @@ "fallbackDomainRoutingHook": "0x388289cd5862e17AAfD6ffF7F46A9Ec48a969bCd", "fallbackRoutingHook": "0x388289cd5862e17AAfD6ffF7F46A9Ec48a969bCd", "interchainGasPaymaster": "0xB35eCb9714e8f48332Af22B48C18ca21E2607438", - "interchainSecurityModule": "0xce6e7d128E168412c7dc67FCd785933EBF9eAd54", + "interchainSecurityModule": "0x434f75095093D323263A195Cf0fe48eD642B9640", "mailbox": "0x9BbDf86b272d224323136E15594fdCe487F40ce7", "merkleTreeHook": "0xA1ADFCa9666Bcd68b7b5C8b55e3ecC465DcDfE65", "proxyAdmin": "0x038F9F4e93e88Af2C688da265222FdE80e455aA4", @@ -6462,7 +6498,7 @@ "interchainAccountIsm": "0xf9609bB22847e0DB5F6fB8f95b84D25A19b46ac5", "interchainAccountRouter": "0x2b6d3F7d28B5EC8C3C028fBCAdcf774D9709Dd29", "interchainGasPaymaster": "0xFb7D175d6F53800D68D32C3Fe1416807A394cC24", - "interchainSecurityModule": "0xb3edE81D4A2d5dEb1b604F5DA664DC47964757d9", + "interchainSecurityModule": "0x920c9787Ac72553E636724Ccdf0B216bAF9AA6D3", "mailbox": "0x473884010F0C1742DA8Ad01E7E295624B931076b", "merkleTreeHook": "0xdA629E1B79e3420ECd1e80571aDd6a4a3b13AE79", "pausableHook": "0xe93f2f409ad8B5000431D234472973fe848dcBEC", @@ -6529,7 +6565,7 @@ "interchainAccountIsm": "0xF457D831d9F55e87B2F0b35AD6D033fd6b4181Ed", "interchainAccountRouter": "0x021D2810a758c833080DEc2F1Fa8F571Aae97D45", "interchainGasPaymaster": "0xc0C2dB448fC2c84213394Fcb93a3C467e50ECa9E", - "interchainSecurityModule": "0xFD5B169AdBC9c5851ad1C69dB79C1A29e38d1f88", + "interchainSecurityModule": "0x0f10ccB87BB6a28D8aE2c90e171c6a7fcD95e425", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", "pausableHook": "0x48C427782Bc1e9ecE406b3e277481b28ABcBdf03", @@ -6599,7 +6635,7 @@ "interchainAccountIsm": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainAccountRouter": "0xc2466492C451E1AE49d8C874bB9f89293Aaad59b", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0x36b420b9091F010aB6571516B2DbF9E32BD8750e", + "interchainSecurityModule": "0x27436C77cdFB78557348ec9C9E06f7c0Cc642639", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6667,7 +6703,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xFA75e09D3eDD644F9B0Fb42f384B3A91A9D2C3D5", + "interchainSecurityModule": "0x8BA03bC7425841114e210657fdD7B22369aBcC1C", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6737,7 +6773,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0x95F67EBdcf5b7a3DA25124ACfB8B45e89cA13871", + "interchainSecurityModule": "0x84A1d7C72E35d876Ec191B9f070b065645c85b84", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6807,7 +6843,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xaC4b20f1C73bc1d4dA7f453C434F34d559a8eE55", + "interchainSecurityModule": "0x5b0Cb7Be7C4c8Fb9F9C324d476B4F08D8c2825A4", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6871,7 +6907,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0x01cD09F5c40286c1894EBFb7b3eC98f2aE4026FA", + "interchainSecurityModule": "0x2b8374D90FF47ad58074F61C3B7Cbc122275d55F", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6936,7 +6972,7 @@ "interchainAccountIsm": "0x60515f328B2c55Df63f456D9D839a0082892dEf8", "interchainAccountRouter": "0xF457D831d9F55e87B2F0b35AD6D033fd6b4181Ed", "interchainGasPaymaster": "0xc0C2dB448fC2c84213394Fcb93a3C467e50ECa9E", - "interchainSecurityModule": "0x123D1ba7ec942f13eE1F05FEe6cd6FFAE3CD3Dcf", + "interchainSecurityModule": "0xE236af1191a497aa7dC9cf24843980EaB08A3c6e", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", "pausableHook": "0x48C427782Bc1e9ecE406b3e277481b28ABcBdf03", @@ -7004,7 +7040,7 @@ "interchainAccountIsm": "0xd64d126941EaC2Cf53e0E4E8146cC70449b60D73", "interchainAccountRouter": "0x1A4F09A615aA4a35E5a146DC2fa19975bebF21A5", "interchainGasPaymaster": "0x3cECBa60A580dE20CC57D87528953a00f4ED99EA", - "interchainSecurityModule": "0x5e74434e07B64F6025Cbac2f423e29Fd92c12C6B", + "interchainSecurityModule": "0x94b6Cd86f9a4127959B345155cEbb80c6e518403", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x1c6f404800bA49Ed581af734eA0d25c0c7d017B2", "pausableHook": "0x9e8b689e83d929cb8c2d9166E55319a4e6aA83B7", @@ -7065,7 +7101,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xD329D039e51AFd7A6166E51243A56B482B150Afd", + "interchainSecurityModule": "0xaDc37A3433cD430986D431AD554d1BAE0a203aA1", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -7130,7 +7166,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0x73c4926837bD5Ce7D07Bd9Ad011469c05bE290F6", + "interchainSecurityModule": "0xa338ecB19615c32FeA77C1BA28628743C63BbAB3", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -7197,7 +7233,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0xF457D831d9F55e87B2F0b35AD6D033fd6b4181Ed", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xF25c3035eB5fb5207F64Ca4704eE40aab9720bCA", + "interchainSecurityModule": "0x229E28C62ab0C8469812bd4D4689c17EEed3083B", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -7310,7 +7346,7 @@ "interchainAccountIsm": "0xd64d126941EaC2Cf53e0E4E8146cC70449b60D73", "interchainAccountRouter": "0x1A4F09A615aA4a35E5a146DC2fa19975bebF21A5", "interchainGasPaymaster": "0x3cECBa60A580dE20CC57D87528953a00f4ED99EA", - "interchainSecurityModule": "0x9b4193BB8B62dD285DBabE6Da17856b29c4FF446", + "interchainSecurityModule": "0xeBF69992C068F6DEA226C28fb3B0DF7F41A7Dd87", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x1c6f404800bA49Ed581af734eA0d25c0c7d017B2", "pausableHook": "0x9e8b689e83d929cb8c2d9166E55319a4e6aA83B7", @@ -7377,7 +7413,7 @@ "interchainAccountIsm": "0x2351FBe24C1212F253b7a300ff0cBCFd97952a19", "interchainAccountRouter": "0xC5f2c60073DCAA9D157C45d5B017D639dF9C5CeB", "interchainGasPaymaster": "0xc2466492C451E1AE49d8C874bB9f89293Aaad59b", - "interchainSecurityModule": "0x18d58C1CAD950c7914E2E4395e1f1f9167B11DE1", + "interchainSecurityModule": "0xAF98857a21416B3D683eA9319FD9B2e496473f1b", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x7B032cBB00AD7438E802A66D8b64761A06E5df22", "pausableHook": "0x3881c3e945CBB89ae67c43E82f570baDF1c6EA94", @@ -7441,7 +7477,7 @@ "interchainAccountIsm": "0x8F23872dAb3B166cef411EeB6C391Ff6Ce419532", "interchainAccountRouter": "0xb201817dFdd822B75Fa9b595457E6Ee466a7C187", "interchainGasPaymaster": "0xA9D06082F4AA449D95b49D85F27fdC0cFb491d4b", - "interchainSecurityModule": "0xba29059bdfE0d7cd12017Fcb6E8eD133C472990b", + "interchainSecurityModule": "0xD57759BF5eA313bBeABa322d39279Ae566fce78C", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "pausableHook": "0x60515f328B2c55Df63f456D9D839a0082892dEf8", @@ -7505,7 +7541,7 @@ "interchainAccountIsm": "0x7937CB2886f01F38210506491A69B0D107Ea0ad9", "interchainAccountRouter": "0xc31B1E6c8E706cF40842C3d728985Cd2f85413eD", "interchainGasPaymaster": "0xc2466492C451E1AE49d8C874bB9f89293Aaad59b", - "interchainSecurityModule": "0xB1877578A1Ae5A109e0b596f9c395739ea954561", + "interchainSecurityModule": "0x2791A158067d7e3769e8290b741DdaB993c8D652", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x7B032cBB00AD7438E802A66D8b64761A06E5df22", "pausableHook": "0x3881c3e945CBB89ae67c43E82f570baDF1c6EA94", @@ -7570,7 +7606,7 @@ "interchainAccountIsm": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD", "interchainAccountRouter": "0x2351FBe24C1212F253b7a300ff0cBCFd97952a19", "interchainGasPaymaster": "0xc2466492C451E1AE49d8C874bB9f89293Aaad59b", - "interchainSecurityModule": "0x9201A273e99bdCAB57c44f58131268f4980F97f8", + "interchainSecurityModule": "0x2791A158067d7e3769e8290b741DdaB993c8D652", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x7B032cBB00AD7438E802A66D8b64761A06E5df22", "pausableHook": "0x3881c3e945CBB89ae67c43E82f570baDF1c6EA94", @@ -7638,7 +7674,7 @@ "interchainAccountIsm": "0xE350143242a2F7962F23D71ee9Dd98f6e86D1772", "interchainAccountRouter": "0x5B24EE24049582fF74c1d311d72c70bA5B76a554", "interchainGasPaymaster": "0x8F23872dAb3B166cef411EeB6C391Ff6Ce419532", - "interchainSecurityModule": "0x725f03eB3B2ee426d9e7e298485e6Ef9095ea22a", + "interchainSecurityModule": "0x2Ed1f376E178550D1FB8C6B54D4b81647C837969", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x7B032cBB00AD7438E802A66D8b64761A06E5df22", "pausableHook": "0x3881c3e945CBB89ae67c43E82f570baDF1c6EA94", @@ -7703,7 +7739,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xb2674E213019972f937CCFc5e23BF963D915809e", "interchainGasPaymaster": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", - "interchainSecurityModule": "0x876C9c47379A0184e00EB7FdECBC27a9042EbD6C", + "interchainSecurityModule": "0x09b4C4fFBCa62Cea4d43481AaFbA302F2279d798", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x67F36550b73B731e5b2FC44E4F8f250d89c87bD6", "pausableHook": "0xD8aF449f8fEFbA2064863DCE5aC248F8B232635F", @@ -7767,7 +7803,7 @@ "fallbackDomainRoutingHook": "0x72f747270ED9C92c7026b222c5767561Be281DaF", "fallbackRoutingHook": "0x72f747270ED9C92c7026b222c5767561Be281DaF", "interchainGasPaymaster": "0x874AaCa847B365592B2b9dB7235517c5F3a5c689", - "interchainSecurityModule": "0x21e03C43967c27C7909CA90A164dB4789d736b0B", + "interchainSecurityModule": "0xb82A1AC8516610FB139DBee6Cc92841E9f8518b2", "mailbox": "0x9BbDf86b272d224323136E15594fdCe487F40ce7", "merkleTreeHook": "0x11b69aB33AD8a550dcF9B4A041AA1121255F08A5", "proxyAdmin": "0x038F9F4e93e88Af2C688da265222FdE80e455aA4", @@ -7824,7 +7860,7 @@ "interchainAccountIsm": "0xE350143242a2F7962F23D71ee9Dd98f6e86D1772", "interchainAccountRouter": "0x5B24EE24049582fF74c1d311d72c70bA5B76a554", "interchainGasPaymaster": "0x8F23872dAb3B166cef411EeB6C391Ff6Ce419532", - "interchainSecurityModule": "0xE62144D2A6c85f52263595Ad11eF4265E7038be2", + "interchainSecurityModule": "0x4750dcCCa172963A15917310d3cF901048eF493a", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xf0F937943Cd6D2a5D02b7f96C9Dd9e04AB633813", "pausableHook": "0xff72A726Ce261846f2dF6F32113e514b5Ddb0E37", @@ -7888,7 +7924,7 @@ "interchainAccountIsm": "0xD84981Ecd4c411A86E1Ccda77F944c8f3D9737ab", "interchainAccountRouter": "0xF16E63B42Df7f2676B373979120BBf7e6298F473", "interchainGasPaymaster": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD", - "interchainSecurityModule": "0xc9258a12d5dfbE6CC74B8da9396961f338AbEbEE", + "interchainSecurityModule": "0x64d0e53FD3042C3d6E98A06e598b3b7DE5c4D3A2", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x021D2810a758c833080DEc2F1Fa8F571Aae97D45", "pausableHook": "0x46008F5971eFb16e6c354Ef993EA021B489bc055", @@ -7953,7 +7989,7 @@ "interchainAccountIsm": "0xD84981Ecd4c411A86E1Ccda77F944c8f3D9737ab", "interchainAccountRouter": "0xF16E63B42Df7f2676B373979120BBf7e6298F473", "interchainGasPaymaster": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD", - "interchainSecurityModule": "0x8737665978597195590cE01979309BbAF5243a1E", + "interchainSecurityModule": "0x3412bd6Aa48DE46c84cF2F0c241955CaD5026e91", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x021D2810a758c833080DEc2F1Fa8F571Aae97D45", "pausableHook": "0x46008F5971eFb16e6c354Ef993EA021B489bc055", @@ -8058,7 +8094,7 @@ "interchainAccountIsm": "0x30a539E2E2d09FB4e68661B1EDD70D266211602a", "interchainAccountRouter": "0xB2b0A80b2fa3fC9aB1564A4FaF013d4D6084B325", "interchainGasPaymaster": "0x7Ce3a48cd9FD80004d95b088760bD05bA86C1f7b", - "interchainSecurityModule": "0x297550Ec70C734ce9BC9d0194e349BCcf250E1F9", + "interchainSecurityModule": "0x1B15a82544DDac65D1647057e8d4a6E523Ea966b", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0x8F23872dAb3B166cef411EeB6C391Ff6Ce419532", "pausableHook": "0x2351FBe24C1212F253b7a300ff0cBCFd97952a19", @@ -8123,7 +8159,7 @@ "interchainAccountIsm": "0xc261Bd2BD995d3D0026e918cBFD44b0Cc5416a57", "interchainAccountRouter": "0xf4035357EB3e3B48E498FA6e1207892f615A2c2f", "interchainGasPaymaster": "0x30a539E2E2d09FB4e68661B1EDD70D266211602a", - "interchainSecurityModule": "0x9fb2C92266B20233CCC0f92EB358186696D5601f", + "interchainSecurityModule": "0x94ae2312C21369c1F7a2f566f41c571A8794B561", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x6e1B9f776bd415d7cC3C7458A5f0d801016918f8", "pausableHook": "0x63012EE26bda8E5D1b96218778Eaf2492E553469", @@ -8187,7 +8223,7 @@ "interchainAccountIsm": "0xcDD89f19b2d00DCB9510BB3fBd5eCeCa761fe5Ab", "interchainAccountRouter": "0x7947b7Fe737B4bd1D3387153f32148974066E591", "interchainGasPaymaster": "0x5cD695ADCB156589cde501822C314bFD74398cA1", - "interchainSecurityModule": "0xF243582e9a0F3b2D45f033CC8Bb9FBA209922309", + "interchainSecurityModule": "0x7FA2F4891fBEef75C68a3E0e28063cd27A15e970", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xa377b8269e0A47cdd2fD5AAeAe860b45623c6d82", "pausableHook": "0x3E57E4908FB4Ac05e9928feE2Ffd78f59692317C", @@ -8255,7 +8291,7 @@ "interchainAccountIsm": "0xcDD89f19b2d00DCB9510BB3fBd5eCeCa761fe5Ab", "interchainAccountRouter": "0x7947b7Fe737B4bd1D3387153f32148974066E591", "interchainGasPaymaster": "0x5cD695ADCB156589cde501822C314bFD74398cA1", - "interchainSecurityModule": "0x169DD2263dB5Cd3B9cE71c3B67c613edCd403967", + "interchainSecurityModule": "0x75A9d40E40D4415ab1056D000F78A1F406d8bA89", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xa377b8269e0A47cdd2fD5AAeAe860b45623c6d82", "pausableHook": "0x3E57E4908FB4Ac05e9928feE2Ffd78f59692317C", @@ -8320,7 +8356,7 @@ "fallbackDomainRoutingHook": "0xc364cfedefE854c1275B0f4088EaFA9695e1FC56", "fallbackRoutingHook": "0xc364cfedefE854c1275B0f4088EaFA9695e1FC56", "interchainGasPaymaster": "0x73a82061Cd258d02BEa145fe183120456e718c2A", - "interchainSecurityModule": "0xf7eFb59BB61A8F5A58EA9316F21423602a58E0ac", + "interchainSecurityModule": "0x7b50a8264aE3054a8Ed5d08Eb3E64311EaA391da", "mailbox": "0x9BbDf86b272d224323136E15594fdCe487F40ce7", "merkleTreeHook": "0x697a90753B7dCf6512189c239E612fC12baaE500", "proxyAdmin": "0x038F9F4e93e88Af2C688da265222FdE80e455aA4", @@ -8380,7 +8416,7 @@ "interchainAccountIsm": "0xcDD89f19b2d00DCB9510BB3fBd5eCeCa761fe5Ab", "interchainAccountRouter": "0x7947b7Fe737B4bd1D3387153f32148974066E591", "interchainGasPaymaster": "0x5cD695ADCB156589cde501822C314bFD74398cA1", - "interchainSecurityModule": "0x94ae2312C21369c1F7a2f566f41c571A8794B561", + "interchainSecurityModule": "0x2247F94513a15805E954CFF2a8BF6F81eC4E71E6", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xa377b8269e0A47cdd2fD5AAeAe860b45623c6d82", "pausableHook": "0x3E57E4908FB4Ac05e9928feE2Ffd78f59692317C", @@ -8444,7 +8480,7 @@ "interchainAccountIsm": "0x72246331d057741008751AB3976a8297Ce7267Bc", "interchainAccountRouter": "0xf303B04d9ad21dAe2658Cf302478A424e0B45368", "interchainGasPaymaster": "0x8d7E604460E1133ebB91513a6D1024f3A3ca17F9", - "interchainSecurityModule": "0x5eBf236C500972ee837eFfCBb491d58ecd6aF502", + "interchainSecurityModule": "0x4299c894286BcB78b795eB67143Bfb1B26BDf57B", "mailbox": "0xF767D698c510FE5E53b46BA6Fd1174F5271e390A", "merkleTreeHook": "0xB2b0A80b2fa3fC9aB1564A4FaF013d4D6084B325", "pausableHook": "0x1e4dE25C3b07c8DF66D4c193693d8B5f3b431d51", @@ -8508,7 +8544,7 @@ "interchainAccountIsm": "0xbE58F200ffca4e1cE4D2F4541E94Ae18370fC405", "interchainAccountRouter": "0xdf4aA3905e0391C7763e33CB6A08fFa97221D49B", "interchainGasPaymaster": "0xEa2Bcee14eA30bbBe3018E5E7829F963230F71C3", - "interchainSecurityModule": "0x36BdC1EE4c4ac1E8bDB6B4Da41bC21efF033A2F4", + "interchainSecurityModule": "0x729A719815B6e1bc55f0b647c0d110e94697D174", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x3862A9B1aCd89245a59002C2a08658EC1d5690E3", "pausableHook": "0x8da1aE5A1fA3883c1c12b46270989EAC0EE7BA78", @@ -8633,7 +8669,7 @@ "interchainAccountIsm": "0x72246331d057741008751AB3976a8297Ce7267Bc", "interchainAccountRouter": "0xf303B04d9ad21dAe2658Cf302478A424e0B45368", "interchainGasPaymaster": "0xc261Bd2BD995d3D0026e918cBFD44b0Cc5416a57", - "interchainSecurityModule": "0x2cB87354746a55F29bCAaE80736aED3f5087bf5e", + "interchainSecurityModule": "0x0aE55696Fe03dFf84776D265d22693341AC76855", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x25C87e735021F72d8728438C2130b02E3141f2cb", "pausableHook": "0x51545389E04c2Ac07d98A40b85d29B480a2AF6ce", @@ -8747,7 +8783,7 @@ "interchainAccountIsm": "0xA9Adb480F10547f10202173a49b7F52116304476", "interchainAccountRouter": "0xA697222b77cDe62A8C47E627d5A1c49A87018236", "interchainGasPaymaster": "0x8452363d5c78bf95538614441Dc8B465e03A89ca", - "interchainSecurityModule": "0x44979aBe91F80a33109f2904f5A767B8c99fd0B4", + "interchainSecurityModule": "0xBdAAa3d98930c349aeC80e8b228a2Fc3f74781b5", "mailbox": "0x398633D19f4371e1DB5a8EFE90468eB70B1176AA", "merkleTreeHook": "0x6D48135b7584E8Bf828B6e23110Bc0Da4252704f", "pausableHook": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", @@ -8811,7 +8847,7 @@ "interchainAccountIsm": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", "interchainAccountRouter": "0xBCD18636e5876DFd7AAb5F2B2a5Eb5ca168BA1d8", "interchainGasPaymaster": "0xE911e75CECe0eF01df3ceD96BCd362941AE474D4", - "interchainSecurityModule": "0x99a3482265C7f7309E7375846a18D226Ce83C045", + "interchainSecurityModule": "0xbCf7D7B684130A3a840bB9d60084fFD2cD66e068", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xcDD89f19b2d00DCB9510BB3fBd5eCeCa761fe5Ab", "pausableHook": "0x72246331d057741008751AB3976a8297Ce7267Bc", @@ -8875,7 +8911,7 @@ "interchainAccountIsm": "0x284226F651eb5cbd696365BC27d333028FCc5D54", "interchainAccountRouter": "0x64F077915B41479901a23Aa798B30701589C5063", "interchainGasPaymaster": "0x5887BDA66EC9e854b0da6BFFe423511e69d327DC", - "interchainSecurityModule": "0xadbEea48a2DE87834ffe1b0AC22CA800912C53F5", + "interchainSecurityModule": "0x1ca277fbf0dE70651b0059Ed6fA492b1cb8B625c", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x72246331d057741008751AB3976a8297Ce7267Bc", "pausableHook": "0x1A41a365A693b6A7aED1a46316097d290f569F22", @@ -8948,7 +8984,7 @@ "interchainAccountIsm": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", "interchainAccountRouter": "0xBCD18636e5876DFd7AAb5F2B2a5Eb5ca168BA1d8", "interchainGasPaymaster": "0xE911e75CECe0eF01df3ceD96BCd362941AE474D4", - "interchainSecurityModule": "0x99a3482265C7f7309E7375846a18D226Ce83C045", + "interchainSecurityModule": "0x1C241771Dbf491e57eE9bEf97b861B6f5cf3627d", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xcDD89f19b2d00DCB9510BB3fBd5eCeCa761fe5Ab", "pausableHook": "0x72246331d057741008751AB3976a8297Ce7267Bc", @@ -9012,7 +9048,7 @@ "interchainAccountIsm": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", "interchainAccountRouter": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143", "interchainGasPaymaster": "0xE911e75CECe0eF01df3ceD96BCd362941AE474D4", - "interchainSecurityModule": "0x0A4247f2837733d62bd1B2369eb2E25f5fECA681", + "interchainSecurityModule": "0x82AAF4809b72B7C075B3d4569619737fc9207e2a", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xcDD89f19b2d00DCB9510BB3fBd5eCeCa761fe5Ab", "pausableHook": "0x72246331d057741008751AB3976a8297Ce7267Bc", diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index ce7fa096344..7b5cb2c9d45 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -131,10 +131,7 @@ "gasCurrencyCoinGeckoId": "ethereum", "interchainAccountIsm": "0xaec6382e1e16Ee12DBEf0e7EA5ADa51217813Fc3", "interchainAccountRouter": "0x20cC3a33C49fa13627303669edf2DcA7F1E76a50", - "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPriceCap": 100000000000 - } + "timelockController": "0x0000000000000000000000000000000000000000" }, "basesepolia": { "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", @@ -201,10 +198,7 @@ "gasCurrencyCoinGeckoId": "ethereum", "interchainAccountIsm": "0xDa5177080f7fC5d9255eB32cC64B9b4e5136A716", "interchainAccountRouter": "0xd876C01aB40e8cE42Db417fBC79c726d45504dE4", - "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPriceCap": 100000000000 - } + "timelockController": "0x0000000000000000000000000000000000000000" }, "bsctestnet": { "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", @@ -275,8 +269,7 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1", "gasCurrencyCoinGeckoId": "binancecoin", "transactionOverrides": { - "gasPrice": 1000000000, - "gasPriceCap": 100000000000 + "gasPrice": 1000000000 } }, "connextsepolia": { @@ -638,10 +631,7 @@ "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", "interchainAccountIsm": "0xA7FA26ef3Ea88CD696779735AC9591E01146DA38", "interchainAccountRouter": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", - "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPriceCap": 100000000000 - } + "timelockController": "0x0000000000000000000000000000000000000000" }, "plumetestnet": { "aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", @@ -928,10 +918,7 @@ "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", "staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f", "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94", - "gasCurrencyCoinGeckoId": "ethereum", - "transactionOverrides": { - "gasPriceCap": 100000000000 - } + "gasCurrencyCoinGeckoId": "ethereum" }, "solanatestnet": { "blockExplorers": [ @@ -2922,7 +2909,7 @@ "domainId": 1262571342, "gasPrice": { "denom": "tkyve", - "amount": "2.0" + "amount": "0.001" }, "index": { "chunk": 10, diff --git a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json index 5657c996700..2866807361d 100644 --- a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json @@ -74,6 +74,9 @@ "inksepolia": { "validators": ["0xe61c846aee275070207fcbf43674eb254f06097a"] }, + "kyvetestnet": { + "validators": ["0x3c470ad2640bc0bcb6a790e8cf85e54d34ca92f5"] + }, "modetestnet": { "validators": ["0x9a9de3e406ab3e4ff12aa03ca9b868b48dc40402"] }, diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 7a98bd984b8..92e444f2ad9 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -16,27 +16,11 @@ export const chainMetadataOverrides: ChainMap> = { bsctestnet: { transactionOverrides: { gasPrice: 1 * 10 ** 9, // 1 gwei - gasPriceCap: 100 * 10 ** 9, // 100 gwei cap }, }, - arbitrumsepolia: { + kyvetestnet: { transactionOverrides: { - gasPriceCap: 100 * 10 ** 9, // 100 gwei cap - }, - }, - basesepolia: { - transactionOverrides: { - gasPriceCap: 100 * 10 ** 9, // 100 gwei cap - }, - }, - optimismsepolia: { - transactionOverrides: { - gasPriceCap: 100 * 10 ** 9, // 100 gwei cap - }, - }, - sepolia: { - transactionOverrides: { - gasPriceCap: 100 * 10 ** 9, // 100 gwei cap + gasPrice: '2.0', }, }, diff --git a/typescript/infra/config/environments/testnet4/gasPrices.json b/typescript/infra/config/environments/testnet4/gasPrices.json index 32ffd9e5c55..e3207126aca 100644 --- a/typescript/infra/config/environments/testnet4/gasPrices.json +++ b/typescript/infra/config/environments/testnet4/gasPrices.json @@ -88,7 +88,7 @@ "decimals": 9 }, "kyvetestnet": { - "amount": "0.03", + "amount": "0.001", "decimals": 1 }, "modetestnet": { diff --git a/typescript/infra/scripts/agents/update-agent-config.ts b/typescript/infra/scripts/agents/update-agent-config.ts index ca87040bc83..2d991881e7b 100644 --- a/typescript/infra/scripts/agents/update-agent-config.ts +++ b/typescript/infra/scripts/agents/update-agent-config.ts @@ -68,7 +68,11 @@ export async function writeAgentConfig( const additionalConfig = Object.fromEntries( await Promise.all( environmentChains - .filter((chain) => chainIsProtocol(chain, ProtocolType.Cosmos)) + .filter( + (chain) => + chainIsProtocol(chain, ProtocolType.Cosmos) || + chainIsProtocol(chain, ProtocolType.CosmosNative), + ) .map(async (chain) => { try { const gasPrice = await getCosmosChainGasPrice(chain, multiProvider); diff --git a/typescript/infra/scripts/print-gas-prices.ts b/typescript/infra/scripts/print-gas-prices.ts index 48070726fc0..0c63b3b1929 100644 --- a/typescript/infra/scripts/print-gas-prices.ts +++ b/typescript/infra/scripts/print-gas-prices.ts @@ -83,7 +83,8 @@ async function getGasPrice( decimals: 9, }; } - case ProtocolType.Cosmos: { + case ProtocolType.Cosmos: + case ProtocolType.CosmosNative: { try { const { amount } = await getCosmosChainGasPrice(chain, mpp); return { diff --git a/typescript/infra/src/agents/gcp.ts b/typescript/infra/src/agents/gcp.ts index 782c34631db..8a28f4b82bb 100644 --- a/typescript/infra/src/agents/gcp.ts +++ b/typescript/infra/src/agents/gcp.ts @@ -113,7 +113,8 @@ export class AgentGCPKey extends CloudAgentKey { return Keypair.fromSeed( Buffer.from(strip0x(this.privateKey), 'hex'), ).publicKey.toBase58(); - case ProtocolType.Cosmos: { + case ProtocolType.Cosmos: + case ProtocolType.CosmosNative: { const compressedPubkey = ethers.utils.computePublicKey( this.privateKey, true, diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index 7d2dbe54b6b..2b97e27c96b 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -228,6 +228,7 @@ export function defaultChainSignerKeyConfig(chainName: ChainName): KeyConfig { switch (metadata?.protocol) { case ProtocolType.Cosmos: + case ProtocolType.CosmosNative: if (metadata.bech32Prefix === undefined) { throw new Error( `Bech32 prefix for cosmos chain ${chainName} is undefined`, From 3d8df0a92ea01691262781d60245c0e9ba738333 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:21:58 +0100 Subject: [PATCH 057/223] chore: revert configs specific to high-throughput period (#6003) ### Description Reverts two of the things from https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/5962: - removes the gas price multiplier of `1.1` on eip1559 txs - sets the escalator's multiplier to `1.125` again, back from `1.25` - uses a new version of ethers, which also removed its internal `1.1` multiplier on gas price --- rust/main/Cargo.lock | 20 +++++----- rust/main/Cargo.toml | 10 ++--- .../src/rpc_clients/trait_builder.rs | 2 +- rust/main/chains/hyperlane-ethereum/src/tx.rs | 37 ++----------------- 4 files changed, 20 insertions(+), 49 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 18323263f60..56941fac057 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -3485,7 +3485,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3499,7 +3499,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "ethers-core", "once_cell", @@ -3510,7 +3510,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -3528,7 +3528,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "Inflector", "cfg-if", @@ -3552,7 +3552,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -3566,7 +3566,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "arrayvec", "bytes", @@ -3596,7 +3596,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3612,7 +3612,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3663,7 +3663,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3699,7 +3699,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-18#bae8199df9d2a02b0393bedfde1e5114d5badb80" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" dependencies = [ "async-trait", "coins-bip32 0.7.0", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 70428cc8fcd..397092ae260 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -218,27 +218,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-18" +tag = "2025-04-23" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-18" +tag = "2025-04-23" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-18" +tag = "2025-04-23" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-18" +tag = "2025-04-23" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-18" +tag = "2025-04-23" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs index b135b382249..e3156df4568 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs @@ -285,7 +285,7 @@ where M: Middleware + 'static, { // Increase the gas price by 25% every 90 seconds - const COEFFICIENT: f64 = 1.25; + const COEFFICIENT: f64 = 1.125; // escalating creates a new tx hash, and the submitter tracks each tx hash for at most // `PENDING_TX_TIMEOUT_SECS`. So the escalator will send a new tx when the initial diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index ce17acaf23f..aa33e692e3e 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -35,10 +35,6 @@ pub const GAS_LIMIT_BUFFER: u32 = 75_000; pub const DEFAULT_GAS_LIMIT_MULTIPLIER_NUMERATOR: u32 = 11; pub const DEFAULT_GAS_LIMIT_MULTIPLIER_DENOMINATOR: u32 = 10; -// A multiplier to apply to the estimated gas price, i.e. 10%. -pub const DEFAULT_GAS_PRICE_MULTIPLIER_NUMERATOR: u32 = 11; -pub const DEFAULT_GAS_PRICE_MULTIPLIER_DENOMINATOR: u32 = 10; - pub const PENDING_TX_TIMEOUT_SECS: u64 = 90; pub fn apply_gas_estimate_buffer(gas: U256, domain: &HyperlaneDomain) -> ChainResult { @@ -59,29 +55,6 @@ pub fn apply_gas_estimate_buffer(gas: U256, domain: &HyperlaneDomain) -> ChainRe Ok(gas.saturating_add(GAS_LIMIT_BUFFER.into())) } -pub fn apply_gas_price_multiplier( - gas_price: EthersU256, - transaction_overrides: &TransactionOverrides, -) -> EthersU256 { - let numerator: EthersU256 = transaction_overrides - .gas_price_multiplier_numerator - .map(Into::into) - .unwrap_or(DEFAULT_GAS_PRICE_MULTIPLIER_NUMERATOR.into()); - let denominator: EthersU256 = transaction_overrides - .gas_price_multiplier_denominator - .map(Into::into) - .unwrap_or(DEFAULT_GAS_PRICE_MULTIPLIER_DENOMINATOR.into()); - let multiplied = gas_price.saturating_mul(numerator).checked_div(denominator); - let Some(multiplied) = multiplied else { - warn!( - ?gas_price, - "Gas price multiplier divide by zero, using original gas price" - ); - return gas_price; - }; - multiplied -} - const PENDING_TRANSACTION_POLLING_INTERVAL: Duration = Duration::from_secs(2); const EVM_RELAYER_ADDRESS: &str = "0x74cae0ecc47b02ed9b9d32e000fd70b9417970c5"; @@ -217,7 +190,7 @@ where // Apply overrides for EIP 1559 tx params if they exist. let (max_fee, max_priority_fee) = - apply_1559_multipliers_and_overrides(max_fee, max_priority_fee, transaction_overrides); + apply_1559_overrides(max_fee, max_priority_fee, transaction_overrides); // Is EIP 1559 chain let mut request = Eip1559TransactionRequest::new(); @@ -263,25 +236,23 @@ where tx.gas_price(gas_price) } -fn apply_1559_multipliers_and_overrides( +fn apply_1559_overrides( max_fee: EthersU256, max_priority_fee: EthersU256, transaction_overrides: &TransactionOverrides, ) -> (EthersU256, EthersU256) { - let max_fee = transaction_overrides + let mut max_fee = transaction_overrides .max_fee_per_gas .map(Into::into) .unwrap_or(max_fee); - let mut max_fee = apply_gas_price_multiplier(max_fee, transaction_overrides); if let Some(min_fee) = transaction_overrides.min_fee_per_gas { max_fee = max_fee.max(min_fee.into()); } - let max_priority_fee = transaction_overrides + let mut max_priority_fee = transaction_overrides .max_priority_fee_per_gas .map(Into::into) .unwrap_or(max_priority_fee); - let mut max_priority_fee = apply_gas_price_multiplier(max_priority_fee, transaction_overrides); if let Some(min_priority_fee) = transaction_overrides.min_priority_fee_per_gas { max_priority_fee = max_priority_fee.max(min_priority_fee.into()); From 4b54f2aee147868bc1136e55239979c2c718813b Mon Sep 17 00:00:00 2001 From: Jamin <57451149+yjamin@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:44:09 +0200 Subject: [PATCH 058/223] feat(agents): cosmosnative routing ism (#5849) ### Description Added new routing ism to the agents. ### Drive-by changes Decrease provider count for `cosmosnative` by implementing the Drop trait. ### Related issues [Linear Issue](https://linear.app/hyperlane-xyz/issue/BACK-127/cosmosnative-relayer-routing-ism-support) ### Backward compatibility Full backwards compatibility. ### Testing Added new `RoutingIsm` to the E2E `cosmosnative` tests. --- rust/main/Cargo.lock | 4 +- .../chains/hyperlane-cosmos-native/Cargo.toml | 2 +- .../src/indexers/delivery.rs | 4 +- .../src/indexers/dispatch.rs | 4 +- .../src/indexers/interchain_gas.rs | 4 +- .../src/indexers/merkle_tree_hook.rs | 4 +- .../chains/hyperlane-cosmos-native/src/ism.rs | 38 +++++++++- .../src/prometheus/metrics_channel.rs | 20 +++++- .../src/providers/rpc.rs | 20 +++++- .../hyperlane-base/src/settings/chains.rs | 7 +- .../utils/run-locally/src/cosmosnative/cli.rs | 70 ++++++++++++------- .../utils/run-locally/src/cosmosnative/mod.rs | 2 +- 12 files changed, 136 insertions(+), 43 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 56941fac057..8251b777e9a 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -5294,9 +5294,9 @@ dependencies = [ [[package]] name = "hyperlane-cosmos-rs" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0354da8627e2110442d445e55df8cff1d73c67e6e62e2af401fb53122e92b65e" +checksum = "dc98aea32657307552b9e58d53d2af8de83cda7f99ce8ea1daa8884221feba46" dependencies = [ "cosmrs", "prost 0.13.4", diff --git a/rust/main/chains/hyperlane-cosmos-native/Cargo.toml b/rust/main/chains/hyperlane-cosmos-native/Cargo.toml index 8c59ad27639..cbdb3ed6bc4 100644 --- a/rust/main/chains/hyperlane-cosmos-native/Cargo.toml +++ b/rust/main/chains/hyperlane-cosmos-native/Cargo.toml @@ -43,7 +43,7 @@ pin-project.workspace = true tracing = { workspace = true } tracing-futures = { workspace = true } url = { workspace = true } -hyperlane-cosmos-rs = "0.1.2" +hyperlane-cosmos-rs = "0.1.4" hyperlane-metric = { path = "../../hyperlane-metric" } hyperlane-core = { path = "../../hyperlane-core", features = ["async"] } diff --git a/rust/main/chains/hyperlane-cosmos-native/src/indexers/delivery.rs b/rust/main/chains/hyperlane-cosmos-native/src/indexers/delivery.rs index d681b3a8b0b..214e66f94e0 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/indexers/delivery.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/indexers/delivery.rs @@ -1,6 +1,6 @@ use std::ops::RangeInclusive; -use hyperlane_cosmos_rs::{hyperlane::core::v1::Process, prost::Name}; +use hyperlane_cosmos_rs::{hyperlane::core::v1::EventProcess, prost::Name}; use tendermint::abci::EventAttribute; use tonic::async_trait; use tracing::instrument; @@ -33,7 +33,7 @@ impl CosmosNativeDeliveryIndexer { impl CosmosEventIndexer for CosmosNativeDeliveryIndexer { fn target_type() -> String { - Process::full_name() + EventProcess::full_name() } fn provider(&self) -> &RpcProvider { diff --git a/rust/main/chains/hyperlane-cosmos-native/src/indexers/dispatch.rs b/rust/main/chains/hyperlane-cosmos-native/src/indexers/dispatch.rs index d1e07e1a7d0..5297a5b9213 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/indexers/dispatch.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/indexers/dispatch.rs @@ -2,7 +2,7 @@ use std::io::Cursor; use std::ops::RangeInclusive; use hex::ToHex; -use hyperlane_cosmos_rs::hyperlane::core::v1::Dispatch; +use hyperlane_cosmos_rs::hyperlane::core::v1::EventDispatch; use hyperlane_cosmos_rs::prost::Name; use tendermint::abci::EventAttribute; use tonic::async_trait; @@ -36,7 +36,7 @@ impl CosmosNativeDispatchIndexer { impl CosmosEventIndexer for CosmosNativeDispatchIndexer { fn target_type() -> String { - Dispatch::full_name() + EventDispatch::full_name() } fn provider(&self) -> &RpcProvider { diff --git a/rust/main/chains/hyperlane-cosmos-native/src/indexers/interchain_gas.rs b/rust/main/chains/hyperlane-cosmos-native/src/indexers/interchain_gas.rs index 6df3bacb499..3fef5ab69f1 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/indexers/interchain_gas.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/indexers/interchain_gas.rs @@ -1,6 +1,6 @@ use std::ops::RangeInclusive; -use hyperlane_cosmos_rs::{hyperlane::core::post_dispatch::v1::GasPayment, prost::Name}; +use hyperlane_cosmos_rs::{hyperlane::core::post_dispatch::v1::EventGasPayment, prost::Name}; use tendermint::abci::EventAttribute; use tonic::async_trait; use tracing::instrument; @@ -69,7 +69,7 @@ impl CosmosNativeInterchainGas { impl CosmosEventIndexer for CosmosNativeInterchainGas { fn target_type() -> String { - GasPayment::full_name() + EventGasPayment::full_name() } fn provider(&self) -> &RpcProvider { diff --git a/rust/main/chains/hyperlane-cosmos-native/src/indexers/merkle_tree_hook.rs b/rust/main/chains/hyperlane-cosmos-native/src/indexers/merkle_tree_hook.rs index 9f7dc3b05c0..cef3d841eaf 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/indexers/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/indexers/merkle_tree_hook.rs @@ -3,7 +3,7 @@ use std::ops::RangeInclusive; use hex::ToHex; use hyperlane_cosmos_rs::{ hyperlane::core::post_dispatch::v1::{ - InsertedIntoTree, TreeResponse, WrappedMerkleTreeHookResponse, + EventInsertedIntoTree, TreeResponse, WrappedMerkleTreeHookResponse, }, prost::Name, }; @@ -131,7 +131,7 @@ impl MerkleTreeHook for CosmosNativeMerkleTreeHook { impl CosmosEventIndexer for CosmosNativeMerkleTreeHook { fn target_type() -> String { - InsertedIntoTree::full_name() + EventInsertedIntoTree::full_name() } fn provider(&self) -> &RpcProvider { diff --git a/rust/main/chains/hyperlane-cosmos-native/src/ism.rs b/rust/main/chains/hyperlane-cosmos-native/src/ism.rs index 6e8f6ae48cc..b892026c421 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/ism.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/ism.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use cosmrs::Any; use hex::ToHex; use hyperlane_cosmos_rs::{ - hyperlane::core::interchain_security::v1::{MerkleRootMultisigIsm, NoopIsm}, + hyperlane::core::interchain_security::v1::{ + MerkleRootMultisigIsm, NoopIsm, RoutingIsm as CosmosRoutingIsm, + }, prost::{Message, Name}, }; use tonic::async_trait; @@ -11,7 +13,7 @@ use tonic::async_trait; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, InterchainSecurityModule, ModuleType, - MultisigIsm, H160, H256, U256, + MultisigIsm, RoutingIsm, H160, H256, U256, }; use crate::{CosmosNativeProvider, HyperlaneCosmosError}; @@ -78,6 +80,7 @@ impl InterchainSecurityModule for CosmosNativeIsm { let ism = self.get_ism().await?; match ism.type_url.as_str() { t if t == MerkleRootMultisigIsm::type_url() => Ok(ModuleType::MerkleRootMultisig), + t if t == CosmosRoutingIsm::type_url() => Ok(ModuleType::Routing), t if t == NoopIsm::type_url() => Ok(ModuleType::Null), other => Err(ChainCommunicationError::from_other_str(&format!( "Unknown ISM type: {}", @@ -126,3 +129,34 @@ impl MultisigIsm for CosmosNativeIsm { } } } + +/// Interface for the RoutingIsm chain contract. Allows abstraction over +/// different chains +#[async_trait] +impl RoutingIsm for CosmosNativeIsm { + /// Returns the ISM needed to verify message + async fn route(&self, message: &HyperlaneMessage) -> ChainResult { + let ism = self.get_ism().await?; + match ism.type_url.as_str() { + t if t == CosmosRoutingIsm::type_url() => { + let ism = CosmosRoutingIsm::decode(ism.value.as_slice()) + .map_err(HyperlaneCosmosError::from)?; + let route = ism + .routes + .iter() + .find(|r| r.domain == message.origin) + .ok_or_else(|| { + ChainCommunicationError::from_other_str(&format!( + "Route not found for message with origin domain: {}", + message.origin + )) + })?; + Ok(H256::from_str(&route.ism)?) + } + _ => Err(ChainCommunicationError::from_other_str(&format!( + "ISM {:?} not a routing ism", + self.address + ))), + } + } +} diff --git a/rust/main/chains/hyperlane-cosmos-native/src/prometheus/metrics_channel.rs b/rust/main/chains/hyperlane-cosmos-native/src/prometheus/metrics_channel.rs index f4b18b6e64d..371d0dcb6df 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/prometheus/metrics_channel.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/prometheus/metrics_channel.rs @@ -10,7 +10,7 @@ use tower::Service; use super::metrics_future::MetricsChannelFuture; -#[derive(Clone, Debug)] +#[derive(Debug)] /// Wrapper for instrumenting a tonic client channel with gRPC metrics. pub struct MetricsChannel { metrics: PrometheusClientMetrics, @@ -38,6 +38,24 @@ impl MetricsChannel { } } +impl Drop for MetricsChannel { + fn drop(&mut self) { + // decrement provider metric count + let chain_name = PrometheusConfig::chain_name(&self.metrics_config.chain); + self.metrics.decrement_provider_instance(chain_name); + } +} + +impl Clone for MetricsChannel { + fn clone(&self) -> Self { + Self::new( + self.inner.clone(), + self.metrics.clone(), + self.metrics_config.clone(), + ) + } +} + impl Service> for MetricsChannel where T: Service, Response = Response>, diff --git a/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs b/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs index 9ce118d1e51..fb7269d54c4 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs @@ -37,7 +37,7 @@ use crate::{ConnectionConf, CosmosAmount, HyperlaneCosmosError, Signer}; use super::cosmos::CosmosFallbackProvider; -#[derive(Debug, Clone)] +#[derive(Debug)] struct CosmosHttpClient { client: HttpClient, metrics: PrometheusClientMetrics, @@ -110,6 +110,24 @@ impl CosmosHttpClient { } } +impl Drop for CosmosHttpClient { + fn drop(&mut self) { + // decrement provider metric count + let chain_name = PrometheusConfig::chain_name(&self.metrics_config.chain); + self.metrics.decrement_provider_instance(chain_name); + } +} + +impl Clone for CosmosHttpClient { + fn clone(&self) -> Self { + Self::new( + self.client.clone(), + self.metrics.clone(), + self.metrics_config.clone(), + ) + } +} + impl RpcProvider { /// Returns a new Rpc Provider pub fn new( diff --git a/rust/main/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs index e4c93d458b0..50594b88e34 100644 --- a/rust/main/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -885,8 +885,11 @@ impl ChainConf { let ism = Box::new(h_cosmos::CosmosRoutingIsm::new(provider, locator.clone())?); Ok(ism as Box) } - ChainConnectionConf::CosmosNative(_) => { - Err(eyre!("Cosmos Native does not support routing ISM yet")).context(ctx) + ChainConnectionConf::CosmosNative(conf) => { + let provider = build_cosmos_native_provider(self, conf, metrics, &locator, None)?; + let ism: Box = + Box::new(h_cosmos_native::CosmosNativeIsm::new(provider, locator)?); + Ok(ism as Box) } } .context(ctx) diff --git a/rust/main/utils/run-locally/src/cosmosnative/cli.rs b/rust/main/utils/run-locally/src/cosmosnative/cli.rs index eaa8b549198..81f719f7be3 100644 --- a/rust/main/utils/run-locally/src/cosmosnative/cli.rs +++ b/rust/main/utils/run-locally/src/cosmosnative/cli.rs @@ -12,6 +12,18 @@ use super::{ }; const GENESIS_FUND: u128 = 1000000000000; +const IGP_ADDRESS: &str = "0x726f757465725f706f73745f6469737061746368000000040000000000000000"; +const MERKLE_ISM_ADDRESS: &str = + "0x726f757465725f69736d00000000000000000000000000040000000000000000"; +const ROUTING_ISM_ADDRESS: &str = + "0x726f757465725f69736d00000000000000000000000000010000000000000001"; +const MAILBOX_ADDRESS: &str = "0x68797065726c616e650000000000000000000000000000000000000000000000"; +const MERKLE_TREE_HOOK_ADDRESS: &str = + "0x726f757465725f706f73745f6469737061746368000000030000000000000001"; +const COLLATERAL_TOKEN_ADDRESS: &str = + "0x726f757465725f61707000000000000000000000000000010000000000000000"; +const SYNTHETIC_TOKEN_ADDRESS: &str = + "0x726f757465725f61707000000000000000000000000000020000000000000001"; #[derive(Debug)] pub struct SimApp { @@ -159,7 +171,7 @@ impl SimApp { "hooks", "igp", "set-destination-gas-config", - "0x726f757465725f706f73745f6469737061746368000000040000000000000000", + IGP_ADDRESS, destination_domain, "10000000000", //1e10 "1", @@ -178,6 +190,22 @@ impl SimApp { "1", ]); + // create routing ism and configure it to use the merkle tree ism just created + // cmd is following: create-routing + // expected ism address: 0x726f757465725f69736d00000000000000000000000000010000000000000001 + self.tx(vec!["hyperlane", "ism", "create-routing"]); + + // configure the routing ism to use the merkle tree ism + // cmd is following: set-routing-ism-domain [routing-ism-id] [domain] [ism-id] + self.tx(vec![ + "hyperlane", + "ism", + "set-routing-ism-domain", + ROUTING_ISM_ADDRESS, + destination_domain, + MERKLE_ISM_ADDRESS, + ]); + // create mailbox // cmd is following: default-ism local-domain // expected mailbox address: 0x68797065726c616e650000000000000000000000000000000000000000000000 @@ -185,7 +213,7 @@ impl SimApp { "hyperlane", "mailbox", "create", - "0x726f757465725f69736d00000000000000000000000000040000000000000000", + ROUTING_ISM_ADDRESS, local_domain, ]); @@ -197,7 +225,7 @@ impl SimApp { "hooks", "merkle", "create", - "0x68797065726c616e650000000000000000000000000000000000000000000000", + MAILBOX_ADDRESS, ]); // set mailbox to use the hooks @@ -207,15 +235,9 @@ impl SimApp { .cmd("hyperlane") .cmd("mailbox") .cmd("set") - .cmd("0x68797065726c616e650000000000000000000000000000000000000000000000") - .arg( - "required-hook", - "0x726f757465725f706f73745f6469737061746368000000030000000000000001", - ) - .arg( - "default-hook", - "0x726f757465725f706f73745f6469737061746368000000040000000000000000", - ) + .cmd(MAILBOX_ADDRESS) + .arg("required-hook", MERKLE_TREE_HOOK_ADDRESS) + .arg("default-hook", IGP_ADDRESS) .arg("from", KEY_CHAIN_VALIDATOR.0) .arg("chain-id", CHAIN_ID) .arg("fees", format!("80000{}", DENOM)) @@ -233,7 +255,7 @@ impl SimApp { self.tx(vec![ "hyperlane-transfer", "create-collateral-token", - "0x68797065726c616e650000000000000000000000000000000000000000000000", + MAILBOX_ADDRESS, DENOM, ]); @@ -242,9 +264,9 @@ impl SimApp { self.tx(vec![ "hyperlane-transfer", "enroll-remote-router", - "0x726f757465725f61707000000000000000000000000000010000000000000000", + COLLATERAL_TOKEN_ADDRESS, destination_domain, - "0x726f757465725f61707000000000000000000000000000020000000000000001", + SYNTHETIC_TOKEN_ADDRESS, "50000", ]); @@ -253,7 +275,7 @@ impl SimApp { self.tx(vec![ "hyperlane-transfer", "create-synthetic-token", - "0x68797065726c616e650000000000000000000000000000000000000000000000", + MAILBOX_ADDRESS, ]); // enroll the remote domain to this token @@ -261,21 +283,19 @@ impl SimApp { self.tx(vec![ "hyperlane-transfer", "enroll-remote-router", - "0x726f757465725f61707000000000000000000000000000020000000000000001", + SYNTHETIC_TOKEN_ADDRESS, destination_domain, - "0x726f757465725f61707000000000000000000000000000010000000000000000", + COLLATERAL_TOKEN_ADDRESS, "50000", ]); Contracts { - mailbox: "0x68797065726c616e650000000000000000000000000000000000000000000000" - .to_owned(), - merkle_tree_hook: "0x726f757465725f706f73745f6469737061746368000000030000000000000001" - .to_owned(), - igp: "0x726f757465725f706f73745f6469737061746368000000040000000000000000".to_owned(), + mailbox: MAILBOX_ADDRESS.to_owned(), + merkle_tree_hook: MERKLE_TREE_HOOK_ADDRESS.to_owned(), + igp: IGP_ADDRESS.to_owned(), tokens: vec![ - "0x726f757465725f61707000000000000000000000000000010000000000000000".to_owned(), - "0x726f757465725f61707000000000000000000000000000020000000000000001".to_owned(), + COLLATERAL_TOKEN_ADDRESS.to_owned(), + SYNTHETIC_TOKEN_ADDRESS.to_owned(), ], } } diff --git a/rust/main/utils/run-locally/src/cosmosnative/mod.rs b/rust/main/utils/run-locally/src/cosmosnative/mod.rs index 0c7971508cf..09657e33357 100644 --- a/rust/main/utils/run-locally/src/cosmosnative/mod.rs +++ b/rust/main/utils/run-locally/src/cosmosnative/mod.rs @@ -196,7 +196,7 @@ fn install_sim_app() -> PathBuf { let release_name = format!("{BINARY_NAME}_{target}"); log!("Downloading Sim App {}", release_name); let uri = format!( - "https://github.com/bcp-innovations/hyperlane-cosmos/releases/download/v1.0.0-beta0/{}", + "https://github.com/bcp-innovations/hyperlane-cosmos/releases/download/v1.0.0/{}", release_name ); From 9c7776b1543f692a1584c9c4ee927f26dca04a59 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:59:29 +0100 Subject: [PATCH 059/223] feat(submitter): metrics and e2e invariants (#5904) ### Description Adds submitter metrics and e2e invariants. The `DispatcherMetrics` struct only needs the Prometheus `registry` to be instantiated, to avoid coupling with the rest of the codebase. I did notice that the adapter is instantiated with the full `CoreMetrics` unfortunately. If we ever look to fully decouple the submitter from the rest of the codebase we'll need to fix this, such as by moving provider metrics logic into their own crate. Metrics added: - **task_liveness**: timestamp marking liveness for the stages and db loaders - **building_stage_queue_length** - **inclusion_stage_pool_length** - **finality_stage_pool_length** - **dropped_payloads**: includes the reason for dropping - **dropped_transactions**: includes the reason for dropping - **finalized_transactions** - **call_retries**: includes the error message causing the retry. However, since eyre errors could take on any value, errors are first simplified as labels that only take on finite values. - **in_flight_transaction_time**: not maintained yet ### Drive-by changes - fixes the H256 string formatting bug that made it hard to track the message id lifecycle - adds more logs at various steps ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5819 ### Backward compatibility Yes ### Testing The new metrics are tested both via unit tests and e2e tests. Failures are much easier to mock in unit tests. --- rust/main/Cargo.lock | 1 + .../src/msg/metadata/message_builder.rs | 6 +- .../agents/relayer/src/msg/op_submitter.rs | 6 +- rust/main/agents/relayer/src/relayer.rs | 16 +- rust/main/hyperlane-base/src/metrics/core.rs | 5 + rust/main/submitter/Cargo.toml | 1 + rust/main/submitter/src/error.rs | 19 ++ rust/main/submitter/src/lib.rs | 2 +- rust/main/submitter/src/payload_dispatcher.rs | 2 + .../src/payload_dispatcher/db/loader.rs | 49 ++- .../payload_dispatcher/db/payload/loader.rs | 4 +- .../db/transaction/loader.rs | 4 +- .../src/payload_dispatcher/dispatcher.rs | 28 +- .../src/payload_dispatcher/entrypoint.rs | 28 +- .../src/payload_dispatcher/metrics.rs | 197 +++++++++++ .../src/payload_dispatcher/stages.rs | 6 +- .../stages/building_stage.rs | 28 +- .../stages/finality_stage.rs | 63 +++- .../stages/inclusion_stage.rs | 54 ++- .../src/payload_dispatcher/stages/state.rs | 41 ++- .../src/payload_dispatcher/stages/utils.rs | 28 ++ .../submitter/src/payload_dispatcher/tests.rs | 316 +++++++++++++++--- rust/main/utils/run-locally/src/config.rs | 2 +- .../src/invariants/termination_invariants.rs | 1 + .../utils/run-locally/src/sealevel/mod.rs | 7 +- .../src/sealevel/termination_invariants.rs | 157 ++++++++- 26 files changed, 954 insertions(+), 117 deletions(-) create mode 100644 rust/main/submitter/src/payload_dispatcher/metrics.rs diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 8251b777e9a..50d08130ccc 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -10296,6 +10296,7 @@ dependencies = [ "hyperlane-core", "hyperlane-sealevel", "mockall", + "prometheus", "serde", "serde_json", "solana-client", diff --git a/rust/main/agents/relayer/src/msg/metadata/message_builder.rs b/rust/main/agents/relayer/src/msg/metadata/message_builder.rs index b1ca182d9ef..9bee10789f0 100644 --- a/rust/main/agents/relayer/src/msg/metadata/message_builder.rs +++ b/rust/main/agents/relayer/src/msg/metadata/message_builder.rs @@ -224,10 +224,8 @@ mod test { use crate::{ msg::metadata::{ - base::MetadataBuildError, - message_builder::{build_message_metadata, ism_and_module_type}, - DefaultIsmCache, IsmAwareAppContextClassifier, IsmCachePolicyClassifier, - MessageMetadataBuildParams, + base::MetadataBuildError, message_builder::build_message_metadata, DefaultIsmCache, + IsmAwareAppContextClassifier, IsmCachePolicyClassifier, MessageMetadataBuildParams, }, settings::matching_list::{Filter, ListElement, MatchingList}, test_utils::{ diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index 7ae0d9f1364..a516356287b 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -13,7 +13,7 @@ use tokio::sync::{broadcast::Sender, mpsc, Mutex}; use tokio::task::JoinHandle; use tokio::time::sleep; use tokio_metrics::TaskMonitor; -use tracing::{debug, error, info_span, instrument, trace, warn, Instrument}; +use tracing::{debug, error, info, info_span, instrument, trace, warn, Instrument}; use hyperlane_base::db::{HyperlaneDb, HyperlaneRocksDB}; use hyperlane_base::CoreMetrics; @@ -501,7 +501,7 @@ async fn submit_via_lander( }; let message_id = op.id(); - let metadata = message_id.to_string(); + let metadata = format!("{message_id:?}"); let mailbox = op .try_get_mailbox() .expect("Operation should contain Mailbox address") @@ -708,6 +708,7 @@ async fn confirm_lander_task( .into_iter() .map(|(op, status_result)| async { let Ok(payload_status) = status_result else { + warn!(?op, "Error retrieving payload status",); send_back_on_failed_submisison( op, prepare_queue.clone(), @@ -731,6 +732,7 @@ async fn confirm_lander_task( ) .await; } else { + info!(?op, ?payload_status, "Operation not finalized yet"); process_confirm_result( op, prepare_queue.clone(), diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index 623d43648f5..e8a69eb74f5 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -36,7 +36,8 @@ use hyperlane_core::{ }; use hyperlane_operation_verifier::ApplicationOperationVerifier; use submitter::{ - DatabaseOrPath, PayloadDispatcher, PayloadDispatcherEntrypoint, PayloadDispatcherSettings, + DatabaseOrPath, DispatcherMetrics, PayloadDispatcher, PayloadDispatcherEntrypoint, + PayloadDispatcherSettings, }; use crate::{ @@ -189,10 +190,13 @@ impl BaseAgent for Relayer { debug!(elapsed = ?start_entity_init.elapsed(), event = "initialized validator announces", "Relayer startup duration measurement"); start_entity_init = Instant::now(); + let dispatcher_metrics = DispatcherMetrics::new(core_metrics.registry()) + .expect("Creating dispatcher metrics is infallible"); let dispatcher_entrypoints = Self::build_payload_dispatcher_entrypoints( &settings, core_metrics.clone(), &chain_metrics, + dispatcher_metrics.clone(), db.clone(), ) .await; @@ -203,6 +207,7 @@ impl BaseAgent for Relayer { &settings, core_metrics.clone(), &chain_metrics, + dispatcher_metrics, db.clone(), ) .await; @@ -900,6 +905,7 @@ impl Relayer { settings: &RelayerSettings, core_metrics: Arc, chain_metrics: &ChainMetrics, + dispatcher_metrics: DispatcherMetrics, db: DB, ) -> HashMap { settings.destination_chains.iter() @@ -911,7 +917,7 @@ impl Relayer { db: DatabaseOrPath::Database(db.clone()), metrics: core_metrics.clone(), })) - .map(|(chain, s)| (chain, PayloadDispatcherEntrypoint::try_from_settings(s))) + .map(|(chain, s)| (chain, PayloadDispatcherEntrypoint::try_from_settings(s, dispatcher_metrics.clone()))) .filter_map(|(chain, result)| match result { Ok(entrypoint) => Some((chain, entrypoint)), Err(err) => { @@ -931,6 +937,7 @@ impl Relayer { settings: &RelayerSettings, core_metrics: Arc, chain_metrics: &ChainMetrics, + dispatcher_metrics: DispatcherMetrics, db: DB, ) -> HashMap { settings @@ -951,7 +958,10 @@ impl Relayer { }) .map(|(chain, s)| { let chain_name = chain.to_string(); - (chain, PayloadDispatcher::try_from_settings(s, chain_name)) + ( + chain, + PayloadDispatcher::try_from_settings(s, chain_name, dispatcher_metrics.clone()), + ) }) .filter_map(|(chain, result)| match result { Ok(entrypoint) => Some((chain, entrypoint)), diff --git a/rust/main/hyperlane-base/src/metrics/core.rs b/rust/main/hyperlane-base/src/metrics/core.rs index ea95c37b086..cc78deed864 100644 --- a/rust/main/hyperlane-base/src/metrics/core.rs +++ b/rust/main/hyperlane-base/src/metrics/core.rs @@ -327,6 +327,11 @@ impl CoreMetrics { }) } + /// Get the prometheus registry for this core metrics instance. + pub fn registry(&self) -> Registry { + self.registry.clone() + } + /// Create the provider metrics attached to this core metrics instance. pub fn provider_metrics(&self) -> MiddlewareMetrics { self.provider_metrics diff --git a/rust/main/submitter/Cargo.toml b/rust/main/submitter/Cargo.toml index 8019db4ae01..5a96475d2c0 100644 --- a/rust/main/submitter/Cargo.toml +++ b/rust/main/submitter/Cargo.toml @@ -17,6 +17,7 @@ chrono.workspace = true derive-new.workspace = true eyre.workspace = true futures-util.workspace = true +prometheus.workspace = true serde.workspace = true serde_json.workspace = true solana-client.workspace = true diff --git a/rust/main/submitter/src/error.rs b/rust/main/submitter/src/error.rs index 376c04dcfc8..9ad20e95c9f 100644 --- a/rust/main/submitter/src/error.rs +++ b/rust/main/submitter/src/error.rs @@ -34,6 +34,25 @@ pub enum SubmitterError { ChainCommunicationError(#[from] hyperlane_core::ChainCommunicationError), } +impl SubmitterError { + pub fn to_metrics_label(&self) -> String { + match self { + SubmitterError::NetworkError(_) => "NetworkError".to_string(), + SubmitterError::TxSubmissionError(_) => "TxSubmissionError".to_string(), + SubmitterError::TxAlreadyExists => "TxAlreadyExists".to_string(), + SubmitterError::TxReverted => "TxReverted".to_string(), + SubmitterError::ChannelSendFailure(_) => "ChannelSendFailure".to_string(), + SubmitterError::ChannelClosed => "ChannelClosed".to_string(), + SubmitterError::EyreError(_) => "EyreError".to_string(), + SubmitterError::PayloadNotFound => "PayloadNotFound".to_string(), + SubmitterError::SimulationFailed => "SimulationFailed".to_string(), + SubmitterError::NonRetryableError(_) => "NonRetryableError".to_string(), + SubmitterError::DbError(_) => "DbError".to_string(), + SubmitterError::ChainCommunicationError(_) => "ChainCommunicationError".to_string(), + } + } +} + pub trait IsRetryable { fn is_retryable(&self) -> bool; } diff --git a/rust/main/submitter/src/lib.rs b/rust/main/submitter/src/lib.rs index 6935d118a26..8b95cef9e42 100644 --- a/rust/main/submitter/src/lib.rs +++ b/rust/main/submitter/src/lib.rs @@ -4,7 +4,7 @@ pub use payload::{ RetryReason as PayloadRetryReason, }; pub use payload_dispatcher::{ - DatabaseOrPath, Entrypoint, PayloadDispatcher, PayloadDispatcherEntrypoint, + DatabaseOrPath, DispatcherMetrics, Entrypoint, PayloadDispatcher, PayloadDispatcherEntrypoint, PayloadDispatcherSettings, }; pub use transaction::{DropReason as TransactionDropReason, TransactionStatus}; diff --git a/rust/main/submitter/src/payload_dispatcher.rs b/rust/main/submitter/src/payload_dispatcher.rs index bfb0ed68b9a..da3e82da678 100644 --- a/rust/main/submitter/src/payload_dispatcher.rs +++ b/rust/main/submitter/src/payload_dispatcher.rs @@ -4,6 +4,7 @@ mod db; mod dispatcher; mod entrypoint; +mod metrics; mod stages; #[cfg(test)] pub mod test_utils; @@ -13,4 +14,5 @@ mod tests; pub use db::*; pub use dispatcher::*; pub use entrypoint::*; +pub use metrics::*; pub use stages::*; diff --git a/rust/main/submitter/src/payload_dispatcher/db/loader.rs b/rust/main/submitter/src/payload_dispatcher/db/loader.rs index e74db0b9a77..deb33e1ae82 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/loader.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/loader.rs @@ -12,9 +12,10 @@ use hyperlane_base::db::HyperlaneDb; use async_trait::async_trait; use hyperlane_base::db::DbResult; use tokio::time::sleep; -use tracing::{debug, instrument}; +use tracing::{debug, info, instrument}; use crate::error::SubmitterError; +use crate::payload_dispatcher::metrics::DispatcherMetrics; #[async_trait] pub trait LoadableFromDb { @@ -34,20 +35,25 @@ pub enum LoadingOutcome { pub struct DbIterator { low_index_iter: DirectionalIndexIterator, high_index_iter: Option>, - // here for debugging purposes - _metadata: String, + iterated_item_name: String, + domain: String, } impl DbIterator { #[instrument(skip(loader), ret)] - pub async fn new(loader: Arc, metadata: String, only_load_backward: bool) -> Self { + pub async fn new( + loader: Arc, + iterated_item_name: String, + only_load_backward: bool, + domain: String, + ) -> Self { // the db returns 0 if uninitialized let high_index = max(loader.highest_index().await.unwrap_or_default(), 1); let mut low_index_iter = DirectionalIndexIterator::new( high_index, IndexDirection::Low, loader.clone(), - metadata.clone(), + iterated_item_name.clone(), ); let high_index_iter = if only_load_backward { None @@ -57,7 +63,7 @@ impl DbIterator { high_index, IndexDirection::High, loader, - metadata.clone(), + iterated_item_name.clone(), ); // Decrement the low index to avoid processing the same index twice low_index_iter.iterate(); @@ -67,13 +73,14 @@ impl DbIterator { debug!( ?low_index_iter, ?high_index_iter, - ?metadata, + ?iterated_item_name, "Initialized ForwardBackwardIterator" ); Self { low_index_iter, high_index_iter, - _metadata: metadata, + iterated_item_name, + domain, } } @@ -98,15 +105,22 @@ impl DbIterator { Ok(LoadingOutcome::Skipped) } - pub async fn load_from_db(&mut self) -> Result<(), SubmitterError> { + pub async fn load_from_db(&mut self, metrics: DispatcherMetrics) -> Result<(), SubmitterError> { loop { + metrics.update_liveness_metric( + format!("{}DbLoader", self.iterated_item_name,).as_str(), + self.domain.as_str(), + ); if let LoadingOutcome::Skipped = self.try_load_next_item().await? { if self.high_index_iter.is_none() { + debug!(?self, "No more items to process, stopping iterator",); // If we are only loading backward, we have processed all items return Ok(()); } // sleep to wait for new items to be added - sleep(Duration::from_secs(1)).await; + sleep(Duration::from_millis(100)).await; + } else { + debug!(?self, "Loaded item"); } } } @@ -235,7 +249,13 @@ mod tests { state.highest_index = num_items as u32; } ( - DbIterator::new(db.clone(), metadata.clone(), only_load_backward).await, + DbIterator::new( + db.clone(), + metadata.clone(), + only_load_backward, + "test_domain".to_string(), + ) + .await, db, ) } @@ -272,7 +292,10 @@ mod tests { let num_db_insertions = 5; let (mut iterator, _) = set_up_state(only_load_backward, num_db_insertions).await; - iterator.load_from_db().await.unwrap(); + iterator + .load_from_db(DispatcherMetrics::dummy_instance()) + .await + .unwrap(); assert_eq!(iterator.low_index_iter.index, 0); assert!(iterator.high_index_iter.is_none()); } @@ -301,7 +324,7 @@ mod tests { }; tokio::select! { - _ = iterator.load_from_db() => { + _ = iterator.load_from_db(DispatcherMetrics::dummy_instance()) => { panic!("Loading from db finished although the high iterator should've kept waiting for new items"); } _ = first_assertion_and_state_change => { diff --git a/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs b/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs index 898d02ed92d..2b1e297ada0 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs @@ -19,11 +19,13 @@ use super::PayloadDb; pub struct PayloadDbLoader { db: Arc, building_stage_queue: BuildingStageQueue, + domain: String, } impl PayloadDbLoader { pub async fn into_iterator(self) -> DbIterator { - DbIterator::new(Arc::new(self), "payload_db_loader".to_string(), false).await + let domain = self.domain.clone(); + DbIterator::new(Arc::new(self), "Payload".to_string(), false, domain).await } } diff --git a/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs b/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs index fec29e084d4..8bcf98fd7ff 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs @@ -21,10 +21,12 @@ pub struct TransactionDbLoader { db: Arc, inclusion_stage_sender: Sender, finality_stage_sender: Sender, + domain: String, } impl TransactionDbLoader { pub async fn into_iterator(self) -> DbIterator { - DbIterator::new(Arc::new(self), "transaction_db_loader".to_string(), true).await + let domain = self.domain.clone(); + DbIterator::new(Arc::new(self), "Transaction".to_string(), true, domain).await } } diff --git a/rust/main/submitter/src/payload_dispatcher/dispatcher.rs b/rust/main/submitter/src/payload_dispatcher/dispatcher.rs index 8d5014e7709..29523bf2aa3 100644 --- a/rust/main/submitter/src/payload_dispatcher/dispatcher.rs +++ b/rust/main/submitter/src/payload_dispatcher/dispatcher.rs @@ -24,7 +24,7 @@ use crate::{ transaction::Transaction, }; -use super::{PayloadDispatcherState, TransactionDbLoader}; +use super::{metrics::DispatcherMetrics, PayloadDispatcherState, TransactionDbLoader}; const SUBMITTER_CHANNEL_SIZE: usize = 1_000; @@ -54,9 +54,14 @@ pub struct PayloadDispatcher { } impl PayloadDispatcher { - pub fn try_from_settings(settings: PayloadDispatcherSettings, domain: String) -> Result { + pub fn try_from_settings( + settings: PayloadDispatcherSettings, + domain: String, + metrics: DispatcherMetrics, + ) -> Result { + let state = PayloadDispatcherState::try_from_settings(settings, metrics)?; Ok(Self { - inner: PayloadDispatcherState::try_from_settings(settings)?, + inner: state, domain, }) } @@ -76,6 +81,7 @@ impl PayloadDispatcher { building_stage_queue.clone(), inclusion_stage_sender.clone(), self.inner.clone(), + self.domain.clone(), ); let building_task = tokio::task::Builder::new() .name("building_stage") @@ -92,6 +98,7 @@ impl PayloadDispatcher { inclusion_stage_receiver, finality_stage_sender.clone(), self.inner.clone(), + self.domain.clone(), ); let inclusion_task = tokio::task::Builder::new() .name("inclusion_stage") @@ -108,6 +115,7 @@ impl PayloadDispatcher { finality_stage_receiver, building_stage_queue.clone(), self.inner.clone(), + self.domain.clone(), ); let finality_task = tokio::task::Builder::new() .name("finality_stage") @@ -120,15 +128,19 @@ impl PayloadDispatcher { .expect("spawning tokio task from Builder is infallible"); tasks.push(finality_task); - let payload_db_loader = - PayloadDbLoader::new(self.inner.payload_db.clone(), building_stage_queue.clone()); + let payload_db_loader = PayloadDbLoader::new( + self.inner.payload_db.clone(), + building_stage_queue.clone(), + self.domain.clone(), + ); let mut payload_iterator = payload_db_loader.into_iterator().await; + let metrics = self.inner.metrics.clone(); let payload_loader_task = tokio::task::Builder::new() .name("payload_loader") .spawn( async move { payload_iterator - .load_from_db() + .load_from_db(metrics) .await .expect("Payload loader crashed"); } @@ -141,14 +153,16 @@ impl PayloadDispatcher { self.inner.tx_db.clone(), inclusion_stage_sender.clone(), finality_stage_sender.clone(), + self.domain.clone(), ); let mut transaction_iterator = transaction_db_loader.into_iterator().await; + let metrics = self.inner.metrics.clone(); let transaction_loader_task = tokio::task::Builder::new() .name("transaction_loader") .spawn( async move { transaction_iterator - .load_from_db() + .load_from_db(metrics) .await .expect("Transaction loader crashed"); } diff --git a/rust/main/submitter/src/payload_dispatcher/entrypoint.rs b/rust/main/submitter/src/payload_dispatcher/entrypoint.rs index 07320afddc2..a558af0b5bf 100644 --- a/rust/main/submitter/src/payload_dispatcher/entrypoint.rs +++ b/rust/main/submitter/src/payload_dispatcher/entrypoint.rs @@ -11,7 +11,7 @@ use crate::{ payload::{FullPayload, PayloadId, PayloadStatus}, }; -use super::{PayloadDispatcherSettings, PayloadDispatcherState}; +use super::{metrics::DispatcherMetrics, PayloadDispatcherSettings, PayloadDispatcherState}; #[async_trait] pub trait Entrypoint { @@ -28,9 +28,12 @@ pub struct PayloadDispatcherEntrypoint { } impl PayloadDispatcherEntrypoint { - pub fn try_from_settings(settings: PayloadDispatcherSettings) -> Result { + pub fn try_from_settings( + settings: PayloadDispatcherSettings, + metrics: DispatcherMetrics, + ) -> Result { Ok(Self { - inner: PayloadDispatcherState::try_from_settings(settings)?, + inner: PayloadDispatcherState::try_from_settings(settings, metrics)?, }) } @@ -42,7 +45,7 @@ impl PayloadDispatcherEntrypoint { #[async_trait] impl Entrypoint for PayloadDispatcherEntrypoint { async fn send_payload(&self, payload: &FullPayload) -> Result<(), SubmitterError> { - info!(payload_id=?payload.id(), "Sending payload to dispatcher"); + info!(payload=?payload.details, "Sending payload to dispatcher"); self.inner.payload_db.store_payload_by_id(payload).await?; Ok(()) } @@ -80,6 +83,7 @@ mod tests { use super::*; use crate::chain_tx_adapter::*; use crate::payload::*; + use crate::payload_dispatcher::metrics::DispatcherMetrics; use crate::payload_dispatcher::test_utils::MockAdapter; use crate::payload_dispatcher::PayloadDb; use crate::payload_dispatcher::TransactionDb; @@ -220,7 +224,13 @@ mod tests { tx_db: Arc, ) -> Box { let adapter = Arc::new(MockAdapter::new()) as Arc; - let entrypoint_state = PayloadDispatcherState::new(payload_db, tx_db, adapter); + let entrypoint_state = PayloadDispatcherState::new( + payload_db, + tx_db, + adapter, + DispatcherMetrics::dummy_instance(), + "test".to_string(), + ); Box::new(PayloadDispatcherEntrypoint::from_inner(entrypoint_state)) } @@ -287,7 +297,13 @@ mod tests { .expect_estimate_gas_limit() .returning(move |_| Ok(Some(mock_gas_limit))); let adapter = Arc::new(mock_adapter) as Arc; - let entrypoint_state = PayloadDispatcherState::new(payload_db, tx_db, adapter); + let entrypoint_state = PayloadDispatcherState::new( + payload_db, + tx_db, + adapter, + DispatcherMetrics::dummy_instance(), + "test".to_string(), + ); let entrypoint = Box::new(PayloadDispatcherEntrypoint::from_inner(entrypoint_state)); let payload = FullPayload::default(); diff --git a/rust/main/submitter/src/payload_dispatcher/metrics.rs b/rust/main/submitter/src/payload_dispatcher/metrics.rs new file mode 100644 index 00000000000..a5f889381bd --- /dev/null +++ b/rust/main/submitter/src/payload_dispatcher/metrics.rs @@ -0,0 +1,197 @@ +// TODO: Re-enable clippy warnings +#![allow(dead_code)] + +use std::time::UNIX_EPOCH; + +use prometheus::{ + core::{AtomicU64, GenericGauge}, + labels, opts, register_int_counter_vec_with_registry, register_int_gauge_vec_with_registry, + Encoder, IntCounterVec, IntGaugeVec, Registry, +}; +use tracing::{debug, info, warn}; + +const METRICS_NAMESPACE: &str = "hyperlane_lander"; + +/// Macro to prefix a string with the namespace. +fn namespaced(name: &str) -> String { + format!("{}_{}", METRICS_NAMESPACE, name) +} + +/// Metrics for a particular domain +#[derive(Clone)] +pub struct DispatcherMetrics { + /// Metrics registry for adding new metrics and gathering reports + registry: Registry, + // with a label for the stage, e.g. "building", "inclusion", "finality", "payload_db_loader", "tx_db_loader" + pub task_liveness: IntGaugeVec, + + pub building_stage_queue_length: IntGaugeVec, + pub inclusion_stage_pool_length: IntGaugeVec, + pub finality_stage_pool_length: IntGaugeVec, + + pub dropped_payloads: IntCounterVec, + pub dropped_transactions: IntCounterVec, + + pub finalized_transactions: IntCounterVec, + + // includes a label for the error causing the retry, and a label for the type of call + pub call_retries: IntCounterVec, + + // total time spent submitting transactions + pub in_flight_transaction_time: IntGaugeVec, +} + +impl DispatcherMetrics { + pub fn new(registry: Registry) -> eyre::Result { + let task_liveness = register_int_gauge_vec_with_registry!( + opts!( + namespaced("task_liveness"), + "The liveness of the dispatcher tasks, expressed as a timestamp since the epoch", + ), + &["destination", "stage",], + registry.clone() + )?; + let building_stage_queue_length = register_int_gauge_vec_with_registry!( + opts!( + namespaced("building_stage_queue_length"), + "The number of payloads in the building stage queue", + ), + &["destination",], + registry.clone() + )?; + let inclusion_stage_pool_length = register_int_gauge_vec_with_registry!( + opts!( + namespaced("inclusion_stage_pool_length"), + "The number of transactions in the inclusion stage pool", + ), + &["destination",], + registry.clone() + )?; + let finality_stage_pool_length = register_int_gauge_vec_with_registry!( + opts!( + namespaced("finality_stage_pool_length"), + "The number of transactions in the finality stage pool", + ), + &["destination",], + registry.clone() + )?; + let dropped_payloads = register_int_counter_vec_with_registry!( + opts!( + namespaced("dropped_payloads"), + "The number of payloads dropped", + ), + &["destination", "reason",], + registry.clone() + )?; + let dropped_transactions = register_int_counter_vec_with_registry!( + opts!( + namespaced("dropped_transactions"), + "The number of transactions dropped", + ), + &["destination", "reason",], + registry.clone() + )?; + let finalized_transactions = register_int_counter_vec_with_registry!( + opts!( + namespaced("finalized_transactions"), + "The number of transactions finalized", + ), + &["destination",], + registry.clone() + )?; + let call_retries = register_int_counter_vec_with_registry!( + opts!( + namespaced("call_retries"), + "The number of times a call was retried", + ), + &["destination", "error_type", "call_type",], + registry.clone() + )?; + let in_flight_transaction_time = register_int_gauge_vec_with_registry!( + opts!( + namespaced("in_flight_transaction_time"), + "Total time spent in flight for transactions", + ), + &["destination",], + registry.clone() + )?; + Ok(Self { + registry: registry.clone(), + task_liveness, + building_stage_queue_length, + inclusion_stage_pool_length, + finality_stage_pool_length, + dropped_payloads, + dropped_transactions, + finalized_transactions, + call_retries, + in_flight_transaction_time, + }) + } + + pub fn update_liveness_metric(&self, stage: &str, domain: &str) { + self.task_liveness.with_label_values(&[domain, stage]).set( + UNIX_EPOCH + .elapsed() + .map(|d| d.as_secs() as i64) + .unwrap_or(0), + ); + } + + pub fn update_queue_length_metric(&self, stage: &str, length: u64, domain: &str) { + match stage { + crate::payload_dispatcher::building_stage::STAGE_NAME => self + .building_stage_queue_length + .with_label_values(&[domain]) + .set(length as i64), + crate::payload_dispatcher::inclusion_stage::STAGE_NAME => self + .inclusion_stage_pool_length + .with_label_values(&[domain]) + .set(length as i64), + crate::payload_dispatcher::finality_stage::STAGE_NAME => self + .finality_stage_pool_length + .with_label_values(&[domain]) + .set(length as i64), + _ => {} + } + } + + pub fn update_dropped_payloads_metric(&self, reason: &str, domain: &str) { + self.dropped_payloads + .with_label_values(&[domain, reason]) + .inc(); + } + + pub fn update_dropped_transactions_metric(&self, reason: &str, domain: &str) { + self.dropped_transactions + .with_label_values(&[domain, reason]) + .inc(); + } + + pub fn update_finalized_transactions_metric(&self, domain: &str) { + self.finalized_transactions + .with_label_values(&[domain]) + .inc(); + } + + pub fn update_call_retries_metric(&self, error_type: &str, call_type: &str, domain: &str) { + self.call_retries + .with_label_values(&[domain, error_type, call_type]) + .inc(); + } + + pub fn gather(&self) -> prometheus::Result> { + let collected_metrics = self.registry.gather(); + let mut out_buf = Vec::with_capacity(1024 * 64); + let encoder = prometheus::TextEncoder::new(); + encoder.encode(&collected_metrics, &mut out_buf)?; + Ok(out_buf) + } + + #[cfg(test)] + pub fn dummy_instance() -> Self { + let registry = Registry::new(); + let instance = Self::new(registry.clone()); + instance.unwrap() + } +} diff --git a/rust/main/submitter/src/payload_dispatcher/stages.rs b/rust/main/submitter/src/payload_dispatcher/stages.rs index 8a14919a52d..5d28bdf3a4d 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages.rs @@ -1,6 +1,6 @@ -mod building_stage; -mod finality_stage; -mod inclusion_stage; +pub(crate) mod building_stage; +pub(crate) mod finality_stage; +pub(crate) mod inclusion_stage; mod state; mod utils; diff --git a/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs index c9f7efb3557..7609dc560f1 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs @@ -19,6 +19,8 @@ use super::{state::PayloadDispatcherState, utils::call_until_success_or_nonretry pub type BuildingStageQueue = Arc>>; +pub const STAGE_NAME: &str = "BuildingStage"; + #[derive(new)] pub(crate) struct BuildingStage { /// This queue is the entrypoint and event driver of the Building Stage @@ -26,12 +28,14 @@ pub(crate) struct BuildingStage { /// This channel is the exitpoint of the Building Stage inclusion_stage_sender: mpsc::Sender, pub(crate) state: PayloadDispatcherState, + domain: String, } impl BuildingStage { #[instrument(skip(self), name = "BuildingStage::run")] pub async fn run(&self) { loop { + self.update_metrics().await; // event-driven by the Building queue let payload = match self.queue.lock().await.pop_front() { Some(payload) => payload, @@ -95,6 +99,7 @@ impl BuildingStage { let simulation_success = call_until_success_or_nonretryable_error( || self.state.adapter.simulate_tx(&tx), "Simulating transaction", + &self.state, ) .await .unwrap_or(false); @@ -110,6 +115,7 @@ impl BuildingStage { call_until_success_or_nonretryable_error( || self.send_tx_to_inclusion_stage(tx.clone()), "Sending transaction to inclusion stage", + &self.state, ) .await?; self.state.store_tx(&tx).await; @@ -136,6 +142,16 @@ impl BuildingStage { info!(?tx, "Transaction sent to Inclusion Stage"); Ok(()) } + + async fn update_metrics(&self) { + self.state + .metrics + .update_liveness_metric(STAGE_NAME, &self.domain); + let length = self.queue.lock().await.len(); + self.state + .metrics + .update_queue_length_metric(STAGE_NAME, length as u64, &self.domain); + } } fn get_full_payloads_from_details( @@ -158,6 +174,7 @@ mod tests { chain_tx_adapter::{AdaptsChain, TxBuildingResult}, payload::{self, DropReason, FullPayload, PayloadDetails, PayloadStatus}, payload_dispatcher::{ + metrics::DispatcherMetrics, test_utils::{dummy_tx, initialize_payload_db, tmp_dbs, MockAdapter}, PayloadDb, PayloadDispatcherState, TransactionDb, }, @@ -339,10 +356,17 @@ mod tests { BuildingStageQueue, ) { let adapter = Arc::new(mock_adapter) as Arc; - let state = PayloadDispatcherState::new(payload_db, tx_db, adapter); + let state = PayloadDispatcherState::new( + payload_db, + tx_db, + adapter, + DispatcherMetrics::dummy_instance(), + "dummy_domain".to_string(), + ); let (sender, receiver) = tokio::sync::mpsc::channel(100); let queue = Arc::new(tokio::sync::Mutex::new(VecDeque::new())); - let building_stage = BuildingStage::new(queue.clone(), sender, state); + let building_stage = + BuildingStage::new(queue.clone(), sender, state, "test_domain".to_string()); (building_stage, receiver, queue) } diff --git a/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs index a575f74221f..8bf8761d99a 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs @@ -31,11 +31,14 @@ use super::{ pub type FinalityStagePool = Arc>>; +pub const STAGE_NAME: &str = "FinalityStage"; + pub struct FinalityStage { pub(crate) pool: FinalityStagePool, tx_receiver: mpsc::Receiver, building_stage_queue: BuildingStageQueue, state: PayloadDispatcherState, + domain: String, } impl FinalityStage { @@ -43,27 +46,32 @@ impl FinalityStage { tx_receiver: mpsc::Receiver, building_stage_queue: BuildingStageQueue, state: PayloadDispatcherState, + domain: String, ) -> Self { Self { pool: Arc::new(Mutex::new(HashMap::new())), tx_receiver, building_stage_queue, state, + domain, } } + pub async fn run(self) { let FinalityStage { pool, tx_receiver, building_stage_queue, state, + domain, } = self; let futures = vec![ tokio::spawn( - Self::receive_txs(tx_receiver, pool.clone()).instrument(info_span!("receive_txs")), + Self::receive_txs(tx_receiver, pool.clone(), state.clone(), domain.clone()) + .instrument(info_span!("receive_txs")), ), tokio::spawn( - Self::process_txs(pool, building_stage_queue, state) + Self::process_txs(pool, building_stage_queue, state, domain) .instrument(info_span!("process_txs")), ), ]; @@ -78,8 +86,13 @@ impl FinalityStage { async fn receive_txs( mut tx_receiver: mpsc::Receiver, pool: FinalityStagePool, + state: PayloadDispatcherState, + domain: String, ) -> Result<(), SubmitterError> { loop { + state + .metrics + .update_liveness_metric(format!("{}::receive_txs", STAGE_NAME).as_str(), &domain); if let Some(tx) = tx_receiver.recv().await { pool.lock().await.insert(tx.id.clone(), tx.clone()); info!(?tx, "Received transaction"); @@ -94,13 +107,22 @@ impl FinalityStage { pool: FinalityStagePool, building_stage_queue: BuildingStageQueue, state: PayloadDispatcherState, + domain: String, ) -> Result<(), SubmitterError> { let estimated_block_time = state.adapter.estimated_block_time(); loop { + state + .metrics + .update_liveness_metric(format!("{}::process_txs", STAGE_NAME).as_str(), &domain); // evaluate the pool every block sleep(*estimated_block_time).await; let pool_snapshot = pool.lock().await.clone(); + state.metrics.update_queue_length_metric( + STAGE_NAME, + pool_snapshot.len() as u64, + &domain, + ); info!(pool_size=?pool_snapshot.len() , "Processing transactions in finality pool"); for (_, tx) in pool_snapshot { if let Err(err) = Self::try_process_tx( @@ -139,6 +161,7 @@ impl FinalityStage { let tx_status = call_until_success_or_nonretryable_error( || state.adapter.tx_status(&tx), "Querying transaction status", + state, ) .await?; @@ -149,6 +172,7 @@ impl FinalityStage { let reverted_payloads = call_until_success_or_nonretryable_error( || state.adapter.reverted_payloads(&tx), "Checking reverted payloads", + state, ) .await?; state @@ -234,6 +258,7 @@ mod tests { use crate::{ payload::{PayloadDetails, PayloadId}, payload_dispatcher::{ + metrics::DispatcherMetrics, stages::{building_stage, finality_stage}, test_utils::{ are_all_txs_in_pool, are_no_txs_in_pool, create_random_txs_and_store_them, @@ -313,10 +338,19 @@ mod tests { let building_queue = Arc::new(tokio::sync::Mutex::new(VecDeque::new())); - let state = - PayloadDispatcherState::new(payload_db.clone(), tx_db.clone(), Arc::new(mock_adapter)); - let finality_stage = - FinalityStage::new(inclusion_stage_receiver, building_queue.clone(), state); + let state = PayloadDispatcherState::new( + payload_db.clone(), + tx_db.clone(), + Arc::new(mock_adapter), + DispatcherMetrics::dummy_instance(), + "test".to_string(), + ); + let finality_stage = FinalityStage::new( + inclusion_stage_receiver, + building_queue.clone(), + state, + "test".to_string(), + ); let pool = finality_stage.pool.clone(); send_txs_to_channel(generated_txs.clone(), inclusion_stage_sender).await; @@ -454,10 +488,19 @@ mod tests { let building_queue = Arc::new(tokio::sync::Mutex::new(VecDeque::new())); - let state = - PayloadDispatcherState::new(payload_db.clone(), tx_db.clone(), Arc::new(mock_adapter)); - let finality_stage = - FinalityStage::new(inclusion_stage_receiver, building_queue.clone(), state); + let state = PayloadDispatcherState::new( + payload_db.clone(), + tx_db.clone(), + Arc::new(mock_adapter), + DispatcherMetrics::dummy_instance(), + "test".to_string(), + ); + let finality_stage = FinalityStage::new( + inclusion_stage_receiver, + building_queue.clone(), + state, + "test".to_string(), + ); let pool = finality_stage.pool.clone(); let test_txs = diff --git a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs index 9e9c940ce63..ecf1163ee52 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs @@ -28,11 +28,14 @@ use super::{utils::call_until_success_or_nonretryable_error, PayloadDispatcherSt pub type InclusionStagePool = Arc>>; +pub const STAGE_NAME: &str = "InclusionStage"; + pub struct InclusionStage { pub(crate) pool: InclusionStagePool, tx_receiver: mpsc::Receiver, finality_stage_sender: mpsc::Sender, state: PayloadDispatcherState, + domain: String, } impl InclusionStage { @@ -40,12 +43,14 @@ impl InclusionStage { tx_receiver: mpsc::Receiver, finality_stage_sender: mpsc::Sender, state: PayloadDispatcherState, + domain: String, ) -> Self { Self { pool: Arc::new(Mutex::new(HashMap::new())), tx_receiver, finality_stage_sender, state, + domain, } } @@ -55,13 +60,15 @@ impl InclusionStage { tx_receiver, finality_stage_sender, state, + domain, } = self; let futures = vec![ tokio::spawn( - Self::receive_txs(tx_receiver, pool.clone()).instrument(info_span!("receive_txs")), + Self::receive_txs(tx_receiver, pool.clone(), state.clone(), domain.clone()) + .instrument(info_span!("receive_txs")), ), tokio::spawn( - Self::process_txs(pool, finality_stage_sender, state) + Self::process_txs(pool, finality_stage_sender, state, domain) .instrument(info_span!("process_txs")), ), ]; @@ -76,8 +83,13 @@ impl InclusionStage { async fn receive_txs( mut building_stage_receiver: mpsc::Receiver, pool: InclusionStagePool, + state: PayloadDispatcherState, + domain: String, ) -> Result<(), SubmitterError> { loop { + state + .metrics + .update_liveness_metric(format!("{}::receive_txs", STAGE_NAME).as_str(), &domain); if let Some(tx) = building_stage_receiver.recv().await { pool.lock().await.insert(tx.id.clone(), tx.clone()); info!(?tx, "Received transaction"); @@ -92,13 +104,22 @@ impl InclusionStage { pool: InclusionStagePool, finality_stage_sender: mpsc::Sender, state: PayloadDispatcherState, + domain: String, ) -> Result<(), SubmitterError> { let estimated_block_time = state.adapter.estimated_block_time(); loop { + state + .metrics + .update_liveness_metric(format!("{}::process_txs", STAGE_NAME).as_str(), &domain); // evaluate the pool every block sleep(*estimated_block_time).await; let pool_snapshot = pool.lock().await.clone(); + state.metrics.update_queue_length_metric( + STAGE_NAME, + pool_snapshot.len() as u64, + &domain, + ); info!(pool_size=?pool_snapshot.len() , "Processing transactions in inclusion pool"); for (_, tx) in pool_snapshot { if let Err(err) = @@ -125,6 +146,7 @@ impl InclusionStage { let tx_status = call_until_success_or_nonretryable_error( || state.adapter.tx_status(&tx), "Querying transaction status", + state, ) .await?; info!(?tx, ?tx_status, "Transaction status"); @@ -164,6 +186,7 @@ impl InclusionStage { let simulation_success = call_until_success_or_nonretryable_error( || state.adapter.simulate_tx(&tx), "Simulating transaction", + state, ) .await?; if !simulation_success { @@ -187,6 +210,7 @@ impl InclusionStage { } }, "Submitting transaction", + state, ) .await?; info!(?tx, "Transaction submitted to node"); @@ -209,14 +233,8 @@ impl InclusionStage { ) -> Result<()> { warn!(?tx, "Dropping tx"); let new_tx_status = TransactionStatus::Dropped(reason); + // this will drop the payloads as well update_tx_status(state, tx, new_tx_status.clone()).await?; - // drop the payloads as well - state - .update_status_for_payloads( - &tx.payload_details, - PayloadStatus::InTransaction(new_tx_status), - ) - .await; pool.lock().await.remove(&tx.id); Ok(()) } @@ -227,6 +245,7 @@ mod tests { use super::*; use crate::{ payload_dispatcher::{ + metrics::DispatcherMetrics, test_utils::{ are_all_txs_in_pool, are_no_txs_in_pool, create_random_txs_and_store_them, dummy_tx, initialize_payload_db, tmp_dbs, MockAdapter, @@ -340,10 +359,19 @@ mod tests { let (building_stage_sender, building_stage_receiver) = mpsc::channel(txs_to_process); let (finality_stage_sender, mut finality_stage_receiver) = mpsc::channel(txs_to_process); - let state = - PayloadDispatcherState::new(payload_db.clone(), tx_db.clone(), Arc::new(mock_adapter)); - let inclusion_stage = - InclusionStage::new(building_stage_receiver, finality_stage_sender, state); + let state = PayloadDispatcherState::new( + payload_db.clone(), + tx_db.clone(), + Arc::new(mock_adapter), + DispatcherMetrics::dummy_instance(), + "test".to_string(), + ); + let inclusion_stage = InclusionStage::new( + building_stage_receiver, + finality_stage_sender, + state, + "test".to_string(), + ); let pool = inclusion_stage.pool.clone(); let txs_created = create_random_txs_and_store_them( diff --git a/rust/main/submitter/src/payload_dispatcher/stages/state.rs b/rust/main/submitter/src/payload_dispatcher/stages/state.rs index 14c7a25ab40..e914b881f7d 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/state.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/state.rs @@ -1,12 +1,14 @@ // TODO: re-enable clippy warnings #![allow(dead_code)] +use chrono::format; use derive_new::new; use eyre::Result; use std::{path::PathBuf, sync::Arc}; use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, + metrics, settings::{ChainConf, RawChainConf}, }; use hyperlane_core::HyperlaneDomain; @@ -16,16 +18,22 @@ use tracing::{error, info, instrument::Instrumented, warn}; use crate::{ chain_tx_adapter::{AdaptsChain, ChainTxAdapterFactory}, payload::{DropReason, PayloadDetails, PayloadStatus}, - payload_dispatcher::{DatabaseOrPath, PayloadDb, PayloadDispatcherSettings, TransactionDb}, + payload_dispatcher::{ + metrics::DispatcherMetrics, DatabaseOrPath, PayloadDb, PayloadDispatcherSettings, + TransactionDb, + }, transaction::Transaction, + TransactionStatus, }; -/// State that is common (but not shared) to all components of the `PayloadDispatcher` +/// State that is common to all components of the `PayloadDispatcher` #[derive(Clone)] pub struct PayloadDispatcherState { pub(crate) payload_db: Arc, pub(crate) tx_db: Arc, pub(crate) adapter: Arc, + pub(crate) metrics: DispatcherMetrics, + pub(crate) domain: String, } impl PayloadDispatcherState { @@ -33,15 +41,22 @@ impl PayloadDispatcherState { payload_db: Arc, tx_db: Arc, adapter: Arc, + metrics: DispatcherMetrics, + domain: String, ) -> Self { Self { payload_db, tx_db, adapter, + metrics, + domain, } } - pub fn try_from_settings(settings: PayloadDispatcherSettings) -> Result { + pub fn try_from_settings( + settings: PayloadDispatcherSettings, + metrics: DispatcherMetrics, + ) -> Result { let adapter = ChainTxAdapterFactory::build( &settings.chain_conf, &settings.raw_chain_conf, @@ -54,7 +69,8 @@ impl PayloadDispatcherState { let rocksdb = Arc::new(HyperlaneRocksDB::new(&settings.domain, db)); let payload_db = rocksdb.clone() as Arc; let tx_db = rocksdb as Arc; - Ok(Self::new(payload_db, tx_db, adapter)) + let domain = settings.domain.to_string(); + Ok(Self::new(payload_db, tx_db, adapter, metrics, domain)) } pub(crate) async fn update_status_for_payloads( @@ -75,10 +91,27 @@ impl PayloadDispatcherState { "Error updating payload status in the database" ); } + self.update_payload_metric_if_dropped(&status); info!(?details, new_status=?status, "Updated payload status"); } } + fn update_payload_metric_if_dropped(&self, status: &PayloadStatus) { + match status { + PayloadStatus::InTransaction(TransactionStatus::Dropped(ref reason)) => { + self.metrics.update_dropped_payloads_metric( + &format!("DroppedInTransaction({reason:?})"), + &self.domain, + ); + } + PayloadStatus::Dropped(ref reason) => { + self.metrics + .update_dropped_payloads_metric(&format!("{reason:?}"), &self.domain); + } + _ => {} + } + } + pub(crate) async fn store_tx(&self, tx: &Transaction) { if let Err(err) = self.tx_db.store_transaction_by_id(tx).await { error!( diff --git a/rust/main/submitter/src/payload_dispatcher/stages/utils.rs b/rust/main/submitter/src/payload_dispatcher/stages/utils.rs index 706b6a62076..6342fda2222 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/utils.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/utils.rs @@ -5,6 +5,7 @@ use tracing::{error, info}; use crate::{ error::{IsRetryable, SubmitterError}, + payload_dispatcher::metrics::DispatcherMetrics, transaction::{Transaction, TransactionStatus}, }; @@ -13,6 +14,7 @@ use super::PayloadDispatcherState; pub async fn call_until_success_or_nonretryable_error( f: F, action: &str, + state: &PayloadDispatcherState, ) -> Result where F: Fn() -> Fut, @@ -28,6 +30,11 @@ where } else { return Err(SubmitterError::NonRetryableError(err.to_string())); } + state.metrics.update_call_retries_metric( + &err.to_metrics_label(), + action, + state.domain.as_str(), + ); } } } @@ -38,8 +45,29 @@ pub async fn update_tx_status( tx: &mut Transaction, new_status: TransactionStatus, ) -> Result<(), SubmitterError> { + // return early to avoid double counting metrics + if new_status == tx.status { + return Ok(()); + } info!(?tx, ?new_status, "Updating tx status"); tx.status = new_status; state.store_tx(tx).await; + + // these metric updates assume a transaction can only be finalized once and dropped once. + // note that a transaction may be counted as `finalized` initially, and then later + // also counted as `dropped` if it was reorged out. + match tx.status { + TransactionStatus::Finalized => { + state + .metrics + .update_finalized_transactions_metric(&state.domain); + } + TransactionStatus::Dropped(ref reason) => { + state + .metrics + .update_dropped_transactions_metric(&format!("{reason:?}"), &state.domain); + } + _ => {} + } Ok(()) } diff --git a/rust/main/submitter/src/payload_dispatcher/tests.rs b/rust/main/submitter/src/payload_dispatcher/tests.rs index 25d926279da..ac82f2ca8ac 100644 --- a/rust/main/submitter/src/payload_dispatcher/tests.rs +++ b/rust/main/submitter/src/payload_dispatcher/tests.rs @@ -5,29 +5,39 @@ use tokio::{sync::Mutex, time::sleep}; use crate::{ chain_tx_adapter::TxBuildingResult, payload_dispatcher::{ + metrics::DispatcherMetrics, test_utils::{dummy_tx, tmp_dbs, MockAdapter}, BuildingStageQueue, PayloadDbLoader, PayloadDispatcherState, }, - Entrypoint, FullPayload, PayloadDispatcher, PayloadDispatcherEntrypoint, PayloadStatus, - TransactionStatus, + Entrypoint, FullPayload, PayloadDispatcher, PayloadDispatcherEntrypoint, PayloadId, + PayloadStatus, TransactionStatus, }; +use super::PayloadDb; + #[tokio::test] async fn test_entrypoint_send_is_detected_by_loader() { let (payload_db, tx_db) = tmp_dbs(); let building_stage_queue = Arc::new(Mutex::new(VecDeque::new())); - let payload_db_loader = PayloadDbLoader::new(payload_db.clone(), building_stage_queue.clone()); + let domain = "dummy_domain".to_string(); + let payload_db_loader = PayloadDbLoader::new( + payload_db.clone(), + building_stage_queue.clone(), + domain.clone(), + ); let mut payload_iterator = payload_db_loader.into_iterator().await; + let metrics = DispatcherMetrics::dummy_instance(); let adapter = Arc::new(MockAdapter::new()); - let state = PayloadDispatcherState::new(payload_db, tx_db, adapter); + let state = + PayloadDispatcherState::new(payload_db, tx_db, adapter, metrics.clone(), domain.clone()); let dispatcher_entrypoint = PayloadDispatcherEntrypoint { inner: state.clone(), }; let _payload_db_loader = tokio::spawn(async move { payload_iterator - .load_from_db() + .load_from_db(metrics.clone()) .await .expect("Payload loader crashed"); }); @@ -54,13 +64,201 @@ async fn test_entrypoint_send_is_finalized_by_dispatcher() { .with_max_level(tracing::Level::DEBUG) .try_init(); + let payload = FullPayload::random(); + + let adapter = MockAdapter::new(); + let adapter = mock_adapter_methods(adapter, payload.clone()); + let adapter = Arc::new(adapter); + let (entrypoint, dispatcher) = mock_entrypoint_and_dispatcher(adapter.clone()).await; + let metrics = dispatcher.inner.metrics.clone(); + + let _payload_dispatcher = tokio::spawn(async move { dispatcher.spawn().await }); + entrypoint.send_payload(&payload).await.unwrap(); + + // wait until the payload status is InTransaction(Finalized) + wait_until_payload_status( + entrypoint.inner.payload_db.clone(), + payload.id(), + |payload_status| { + matches!( + payload_status, + PayloadStatus::InTransaction(TransactionStatus::Finalized) + ) + }, + ) + .await; + sleep(Duration::from_millis(200)).await; // Wait for the metrics to be updated + + let metrics_assertion = MetricsAssertion { + domain: entrypoint.inner.domain.clone(), + finalized_txs: 1, + building_stage_queue_length: 0, + inclusion_stage_pool_length: 0, + finality_stage_pool_length: 0, + dropped_payloads: 0, + dropped_transactions: 0, + dropped_payload_reason: "".to_string(), + dropped_transaction_reason: "".to_string(), + }; + assert_metrics(metrics, metrics_assertion); +} + +#[tokio::test] +async fn test_entrypoint_send_is_dropped_by_dispatcher() { + let _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .try_init(); + + let payload = FullPayload::random(); + + let mut adapter = MockAdapter::new(); + let mut counter = 0; + adapter.expect_simulate_tx().returning(move |_| { + counter += 1; + if counter == 1 { + // simulation is successful the first time around, and the payload makes it into a tx + Ok(true) + } else { + // the second time around, the simulation fails, say due to a network race condition + // where the payload was delivered by someone else and now it reverts + Ok(false) + } + }); + let adapter = mock_adapter_methods(adapter, payload.clone()); + let adapter = Arc::new(adapter); + let (entrypoint, dispatcher) = mock_entrypoint_and_dispatcher(adapter.clone()).await; + let metrics = dispatcher.inner.metrics.clone(); + + let _payload_dispatcher = tokio::spawn(async move { dispatcher.spawn().await }); + entrypoint.send_payload(&payload).await.unwrap(); + + // wait until the payload status is InTransaction(Finalized) + wait_until_payload_status( + entrypoint.inner.payload_db.clone(), + payload.id(), + |payload_status| { + matches!( + payload_status, + PayloadStatus::InTransaction(TransactionStatus::Dropped(_)) + ) + }, + ) + .await; + sleep(Duration::from_millis(200)).await; // Wait for the metrics to be updated + + let metrics_assertion = MetricsAssertion { + domain: entrypoint.inner.domain.clone(), + finalized_txs: 0, + building_stage_queue_length: 0, + inclusion_stage_pool_length: 0, + finality_stage_pool_length: 0, + dropped_payloads: 1, + dropped_transactions: 1, + dropped_payload_reason: "DroppedInTransaction(FailedSimulation)".to_string(), + dropped_transaction_reason: "FailedSimulation".to_string(), + }; + assert_metrics(metrics, metrics_assertion); +} + +#[tokio::test] +async fn test_entrypoint_payload_fails_simulation() { + let _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .try_init(); + + let payload = FullPayload::random(); + + let mut adapter = MockAdapter::new(); + // the payload always fails simulation + adapter.expect_simulate_tx().returning(move |_| Ok(false)); + let adapter = mock_adapter_methods(adapter, payload.clone()); + let adapter = Arc::new(adapter); + let (entrypoint, dispatcher) = mock_entrypoint_and_dispatcher(adapter.clone()).await; + let metrics = dispatcher.inner.metrics.clone(); + + let _payload_dispatcher = tokio::spawn(async move { dispatcher.spawn().await }); + entrypoint.send_payload(&payload).await.unwrap(); + + // wait until the payload status is InTransaction(Finalized) + wait_until_payload_status( + entrypoint.inner.payload_db.clone(), + payload.id(), + |payload_status| matches!(payload_status, PayloadStatus::Dropped(_)), + ) + .await; + sleep(Duration::from_millis(200)).await; // Wait for the metrics to be updated + + let metrics_assertion = MetricsAssertion { + domain: entrypoint.inner.domain.clone(), + finalized_txs: 0, + building_stage_queue_length: 0, + inclusion_stage_pool_length: 0, + finality_stage_pool_length: 0, + dropped_payloads: 1, + dropped_transactions: 0, + dropped_payload_reason: "FailedSimulation".to_string(), + dropped_transaction_reason: "".to_string(), + }; + assert_metrics(metrics, metrics_assertion); +} + +async fn mock_entrypoint_and_dispatcher( + adapter: Arc, +) -> (PayloadDispatcherEntrypoint, PayloadDispatcher) { let (payload_db, tx_db) = tmp_dbs(); let building_stage_queue = Arc::new(Mutex::new(VecDeque::new())); - let payload_db_loader = PayloadDbLoader::new(payload_db.clone(), building_stage_queue.clone()); + let domain = "test_domain".to_string(); + let payload_db_loader = PayloadDbLoader::new( + payload_db.clone(), + building_stage_queue.clone(), + domain.clone(), + ); let mut payload_iterator = payload_db_loader.into_iterator().await; - let payload = FullPayload::random(); - let mut adapter = MockAdapter::new(); + let metrics = DispatcherMetrics::dummy_instance(); + + let state = + PayloadDispatcherState::new(payload_db, tx_db, adapter, metrics.clone(), domain.clone()); + let dispatcher_entrypoint = PayloadDispatcherEntrypoint { + inner: state.clone(), + }; + + let metrics_to_move = metrics.clone(); + let _payload_db_loader = tokio::spawn(async move { + payload_iterator + .load_from_db(metrics_to_move) + .await + .expect("Payload loader crashed"); + }); + + let payload_dispatcher = PayloadDispatcher { + inner: state.clone(), + domain: domain.clone(), + }; + (dispatcher_entrypoint, payload_dispatcher) +} + +async fn wait_until_payload_status( + payload_db: Arc, + payload_id: &PayloadId, + status_check: F, +) where + F: Fn(&PayloadStatus) -> bool, +{ + loop { + let stored_payload = payload_db + .retrieve_payload_by_id(payload_id) + .await + .unwrap() + .unwrap(); + if status_check(&stored_payload.status) { + break; + } + sleep(Duration::from_millis(100)).await; + } +} + +fn mock_adapter_methods(mut adapter: MockAdapter, payload: FullPayload) -> MockAdapter { adapter .expect_estimated_block_time() .return_const(Duration::from_millis(100)); @@ -89,40 +287,76 @@ async fn test_entrypoint_send_is_finalized_by_dispatcher() { adapter.expect_simulate_tx().returning(|_| Ok(true)); adapter.expect_submit().returning(|_| Ok(())); + adapter +} - let adapter = Arc::new(adapter); - - let state = PayloadDispatcherState::new(payload_db, tx_db, adapter); - let dispatcher_entrypoint = PayloadDispatcherEntrypoint { - inner: state.clone(), - }; - - let _payload_db_loader = tokio::spawn(async move { - payload_iterator - .load_from_db() - .await - .expect("Payload loader crashed"); - }); - - let payload_dispatcher = PayloadDispatcher { - inner: state.clone(), - domain: "dummy_destination".to_string(), - }; - let _payload_dispatcher = tokio::spawn(async move { payload_dispatcher.spawn().await }); +struct MetricsAssertion { + domain: String, + finalized_txs: u64, + building_stage_queue_length: i64, + inclusion_stage_pool_length: i64, + finality_stage_pool_length: i64, + dropped_payloads: u64, + dropped_transactions: u64, + dropped_payload_reason: String, + dropped_transaction_reason: String, +} - dispatcher_entrypoint.send_payload(&payload).await.unwrap(); +fn assert_metrics(metrics: DispatcherMetrics, assertion: MetricsAssertion) { + // check metrics + let gathered_metrics = metrics.gather().unwrap(); + let metrics_str = String::from_utf8(gathered_metrics).unwrap(); + println!("Metrics: {}", metrics_str); - // wait until the payload status is InTransaction(Finalized) - loop { - let stored_payload = state - .payload_db - .retrieve_payload_by_id(payload.id()) - .await - .unwrap() - .unwrap(); - if stored_payload.status == PayloadStatus::InTransaction(TransactionStatus::Finalized) { - break; - } - sleep(Duration::from_millis(100)).await; - } + let finalized_txs = metrics + .finalized_transactions + .with_label_values(&[&assertion.domain]) + .get(); + assert_eq!( + finalized_txs, assertion.finalized_txs, + "Finalized transactions metric is incorrect for domain {}", + assertion.domain + ); + let building_stage_queue_length = metrics + .building_stage_queue_length + .with_label_values(&[&assertion.domain]) + .get(); + assert_eq!( + building_stage_queue_length, assertion.building_stage_queue_length, + "Building stage queue length metric is incorrect" + ); + let inclusion_stage_pool_length = metrics + .inclusion_stage_pool_length + .with_label_values(&[&assertion.domain]) + .get(); + assert_eq!( + inclusion_stage_pool_length, assertion.inclusion_stage_pool_length, + "Inclusion stage pool length metric is incorrect" + ); + let finality_stage_pool_length = metrics + .finality_stage_pool_length + .with_label_values(&[&assertion.domain]) + .get(); + assert_eq!( + finality_stage_pool_length, assertion.finality_stage_pool_length, + "Finality stage pool length metric is incorrect" + ); + let dropped_payloads = metrics + .dropped_payloads + .with_label_values(&[&assertion.domain, &assertion.dropped_payload_reason]) + .get(); + assert_eq!( + dropped_payloads, assertion.dropped_payloads, + "Dropped payloads metric is incorrect for domain {}", + assertion.domain + ); + let dropped_transactions = metrics + .dropped_transactions + .with_label_values(&[&assertion.domain, &assertion.dropped_transaction_reason]) + .get(); + assert_eq!( + dropped_transactions, assertion.dropped_transactions, + "Dropped transactions metric is incorrect for domain {}", + assertion.domain + ); } diff --git a/rust/main/utils/run-locally/src/config.rs b/rust/main/utils/run-locally/src/config.rs index 31eaae1613f..3d8178c9abc 100644 --- a/rust/main/utils/run-locally/src/config.rs +++ b/rust/main/utils/run-locally/src/config.rs @@ -1,7 +1,7 @@ use std::env; use std::sync::Arc; -#[derive(Debug)] +#[derive(Debug, Clone, Default)] pub struct Config { pub is_ci_env: bool, pub ci_mode: bool, diff --git a/rust/main/utils/run-locally/src/invariants/termination_invariants.rs b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs index 0144f7e430b..56593578df1 100644 --- a/rust/main/utils/run-locally/src/invariants/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs @@ -10,6 +10,7 @@ use relayer::GAS_EXPENDITURE_LOG_MESSAGE; use crate::logging::log; use crate::{fetch_metric, AGENT_LOGGING_DIR, RELAYER_METRICS_PORT, SCRAPER_METRICS_PORT}; +#[derive(Clone)] pub struct RelayerTerminationInvariantParams<'a> { pub config: &'a Config, pub starting_relayer_balance: f64, diff --git a/rust/main/utils/run-locally/src/sealevel/mod.rs b/rust/main/utils/run-locally/src/sealevel/mod.rs index 58253483568..66535ab50e8 100644 --- a/rust/main/utils/run-locally/src/sealevel/mod.rs +++ b/rust/main/utils/run-locally/src/sealevel/mod.rs @@ -9,6 +9,7 @@ use std::{ time::{Duration, Instant}, }; +use hyperlane_core::SubmitterType; use tempfile::tempdir; use crate::SHUTDOWN; @@ -31,6 +32,7 @@ use crate::{ // sent before and after the relayer spins up, to avoid rounding errors. pub const SOL_MESSAGES_EXPECTED: u32 = 10; pub const SOL_MESSAGES_WITH_NON_MATCHING_IGP: u32 = 1; +pub const SUBMITTER_TYPE: SubmitterType = SubmitterType::Lander; /// These private keys are from the solana-test-validator network const RELAYER_KEYS: &[&str] = &[ @@ -98,8 +100,8 @@ fn run_locally() { .hyp_env("DB", relayer_db.to_str().unwrap()) .hyp_env("CHAINS_SEALEVELTEST1_SIGNER_KEY", RELAYER_KEYS[0]) .hyp_env("CHAINS_SEALEVELTEST2_SIGNER_KEY", RELAYER_KEYS[1]) - .hyp_env("CHAINS_SEALEVELTEST1_SUBMITTER", "Lander") - .hyp_env("CHAINS_SEALEVELTEST2_SUBMITTER", "Lander") + .hyp_env("CHAINS_SEALEVELTEST1_SUBMITTER", SUBMITTER_TYPE.to_string()) + .hyp_env("CHAINS_SEALEVELTEST2_SUBMITTER", SUBMITTER_TYPE.to_string()) .hyp_env("RELAYCHAINS", "invalidchain,otherinvalid") .hyp_env("ALLOWLOCALCHECKPOINTSYNCERS", "true") .hyp_env( @@ -309,6 +311,7 @@ fn run_locally() { starting_relayer_balance, &solana_programs_path, &solana_config_path, + SUBMITTER_TYPE, ) }, || !SHUTDOWN.load(Ordering::Relaxed), diff --git a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs index 110054be230..8682ae26d66 100644 --- a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs @@ -1,9 +1,11 @@ -use std::path::Path; +use std::{collections::HashMap, path::Path}; +use hyperlane_core::SubmitterType; use maplit::hashmap; use crate::{ config::Config, + fetch_metric, invariants::{ provider_metrics_invariant_met, relayer_termination_invariants_met, scraper_termination_invariants_met, RelayerTerminationInvariantParams, @@ -23,6 +25,7 @@ pub fn termination_invariants_met( starting_relayer_balance: f64, solana_cli_tools_path: &Path, solana_config_path: &Path, + submitter_type: SubmitterType, ) -> eyre::Result { log!("Checking sealevel termination invariants"); let sol_messages_expected = SOL_MESSAGES_EXPECTED; @@ -37,7 +40,7 @@ pub fn termination_invariants_met( let msg_processed_count = fetch_relayer_message_processed_count()?; let gas_payment_events_count = fetch_relayer_gas_payment_event_count()?; - let params = RelayerTerminationInvariantParams { + let relayer_invariant_params = RelayerTerminationInvariantParams { config, starting_relayer_balance, msg_processed_count, @@ -49,7 +52,7 @@ pub fn termination_invariants_met( non_matching_igp_message_count: 0, double_insertion_message_count: sol_messages_with_non_matching_igp, }; - if !relayer_termination_invariants_met(params)? { + if !relayer_termination_invariants_met(relayer_invariant_params.clone())? { log!("Relayer termination invariants not met"); return Ok(false); } @@ -80,6 +83,154 @@ pub fn termination_invariants_met( return Ok(false); } + if matches!(submitter_type, SubmitterType::Lander) + && !submitter_metrics_invariants_met( + relayer_invariant_params, + RELAYER_METRICS_PORT, + &hashmap! {"destination" => "sealeveltest2"}, + )? + { + log!("Submitter metrics invariants not met"); + return Ok(false); + } + log!("Termination invariants have been meet"); Ok(true) } + +fn submitter_metrics_invariants_met( + params: RelayerTerminationInvariantParams, + relayer_port: &str, + filter_hashmap: &HashMap<&str, &str>, +) -> eyre::Result { + let finalized_transactions = fetch_metric( + relayer_port, + "hyperlane_lander_finalized_transactions", + filter_hashmap, + )? + .iter() + .sum::(); + + let building_stage_queue_length = fetch_metric( + relayer_port, + "hyperlane_lander_building_stage_queue_length", + filter_hashmap, + )? + .iter() + .sum::(); + + let inclusion_stage_pool_length = fetch_metric( + relayer_port, + "hyperlane_lander_inclusion_stage_pool_length", + filter_hashmap, + )? + .iter() + .sum::(); + let finality_stage_pool_length = fetch_metric( + relayer_port, + "hyperlane_lander_finality_stage_pool_length", + filter_hashmap, + )? + .iter() + .sum::(); + let dropped_payloads = fetch_metric( + relayer_port, + "hyperlane_lander_dropped_payloads", + filter_hashmap, + )? + .iter() + .sum::(); + let dropped_transactions = fetch_metric( + relayer_port, + "hyperlane_lander_dropped_transactions", + filter_hashmap, + )? + .iter() + .sum::(); + + if finalized_transactions < params.total_messages_expected { + log!( + "hyperlane_lander_finalized_transactions {} count, expected {}", + finalized_transactions, + params.total_messages_expected + ); + return Ok(false); + } + if building_stage_queue_length != 0 { + log!( + "hyperlane_lander_building_stage_queue_length {} count, expected {}", + building_stage_queue_length, + 0 + ); + return Ok(false); + } + if inclusion_stage_pool_length != 0 { + log!( + "hyperlane_lander_inclusion_stage_pool_length {} count, expected {}", + inclusion_stage_pool_length, + 0 + ); + return Ok(false); + } + if finality_stage_pool_length != 0 { + log!( + "hyperlane_lander_finality_stage_pool_length {} count, expected {}", + finality_stage_pool_length, + 0 + ); + return Ok(false); + } + if dropped_payloads != 0 { + log!( + "hyperlane_lander_dropped_payloads {} count, expected {}", + dropped_payloads, + 0 + ); + return Ok(false); + } + if dropped_transactions != 0 { + log!( + "hyperlane_lander_dropped_transactions {} count, expected {}", + dropped_transactions, + 0 + ); + return Ok(false); + } + + Ok(true) +} + +#[cfg(test)] +mod tests { + use maplit::hashmap; + + #[test] + fn submitter_metrics_are_correct() { + let relayer_metrics_port = 9092; + let filter_hashmap = hashmap! { + "destination" => "sealeveltest2", + }; + let params = super::RelayerTerminationInvariantParams { + total_messages_expected: 10, + // the rest are not used + config: &crate::config::Config::load(), + starting_relayer_balance: 0.0, + msg_processed_count: 0, + gas_payment_events_count: 0, + total_messages_dispatched: 0, + failed_message_count: 0, + submitter_queue_length_expected: 0, + non_matching_igp_message_count: 0, + double_insertion_message_count: 0, + }; + assert_eq!( + super::submitter_metrics_invariants_met( + params, + &relayer_metrics_port.to_string(), + &filter_hashmap + ) + .unwrap(), + true + ); + } +} From 049d3247523e7d9135fda2c67ab7d5afdb42acc4 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Thu, 24 Apr 2025 14:07:10 +0100 Subject: [PATCH 060/223] fix: Set log level INFO for AWS Smithy Runtime (#6001) ### Description Set log level INFO for AWS Smithy Runtime so that it is less chatty in the logs ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/hyperlane-base/src/settings/trace/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/main/hyperlane-base/src/settings/trace/mod.rs b/rust/main/hyperlane-base/src/settings/trace/mod.rs index bae93d9697b..e00bb46f07f 100644 --- a/rust/main/hyperlane-base/src/settings/trace/mod.rs +++ b/rust/main/hyperlane-base/src/settings/trace/mod.rs @@ -75,12 +75,10 @@ impl TracingConfig { .with_target("tower", Level::Info) .with_target("tendermint", Level::Info) .with_target("tokio", Level::Debug) - .with_target("tokio_util", Level::Debug) - .with_target("aws_sdk_s3", Level::Info) - .with_target("aws_smithy_runtime_api", Level::Info) // Enable Trace level for Tokio if you want to use tokio-console // .with_target("tokio", Level::Trace) - // .with_target("tokio_util", Level::Trace) + .with_target("aws_sdk_s3", Level::Info) + .with_target("aws_smithy_runtime", Level::Info) .with_target("ethers_providers", Level::Debug); } From 5131846be0310153a90826629e74e36321e71c63 Mon Sep 17 00:00:00 2001 From: Jamin <57451149+yjamin@users.noreply.github.com> Date: Thu, 24 Apr 2025 15:40:53 +0200 Subject: [PATCH 061/223] feat(infra): check public validator rpc script (#6008) ### Description Added infra script to check public RPCs on validator sets. Uses the `metadata_latest.json` for each validator in a given set and matches the entries against the `hyperlane-registry`. We flag every matching RPC to the registry as public. ### Drive-by changes ### Related issues ### Backward compatibility Yes ### Testing Local testing --- .../scripts/check/check-validator-rpcs.ts | 110 ++++++++++++++++++ typescript/sdk/src/aws/s3.ts | 10 +- typescript/utils/src/types.ts | 2 + 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 typescript/infra/scripts/check/check-validator-rpcs.ts diff --git a/typescript/infra/scripts/check/check-validator-rpcs.ts b/typescript/infra/scripts/check/check-validator-rpcs.ts new file mode 100644 index 00000000000..b9a96e0c8f9 --- /dev/null +++ b/typescript/infra/scripts/check/check-validator-rpcs.ts @@ -0,0 +1,110 @@ +import { ethers } from 'ethers'; + +import { + defaultMultisigConfigs, + getValidatorFromStorageLocation, +} from '@hyperlane-xyz/sdk'; + +import { isEthereumProtocolChain } from '../../src/utils/utils.js'; +import { getArgs, withChains } from '../agent-utils.js'; +import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; + +enum CheckResult { + OK = '✅', + WARNING = '🚨', +} + +async function main() { + const { environment, chains } = await withChains(getArgs()).argv; + const config = getEnvironmentConfig(environment); + const { core } = await getHyperlaneCore(environment); + + // Ensure we skip lumia, as we don't have the addresses in registry. + const targetNetworks = ( + chains && chains.length > 0 ? chains : config.supportedChainNames + ).filter((chain) => isEthereumProtocolChain(chain) && chain !== 'lumia'); + + // set useSecrets to `false` to compare with public RPCs instead of private ones + const registry = await config.getRegistry(false); + const metadata = await registry.getMetadata(); + + const publicRpcs: string[] = []; + + for (const chain of targetNetworks) { + const chainMetadata = metadata[chain]; + if (!chainMetadata) { + throw new Error(`No metadata for ${chain}`); + } + publicRpcs.push( + ...chainMetadata.rpcUrls.map((rpc) => + ethers.utils.solidityKeccak256(['string'], [rpc.http]), + ), + ); + if (chainMetadata.grpcUrls) + publicRpcs.push( + ...chainMetadata.grpcUrls.map((rpc) => + ethers.utils.solidityKeccak256(['string'], [rpc.http]), + ), + ); + } + const output: { + chain: string; + validator: string; + status: CheckResult; + rpcs: string; + private: string; + }[] = []; + + await Promise.all( + targetNetworks.map(async (chain) => { + const validatorAnnounce = core.getContracts(chain).validatorAnnounce; + const defaultValidatorConfigs = + defaultMultisigConfigs[chain].validators || []; + const validators = defaultValidatorConfigs.map((v) => v.address); + + const storageLocations = + await validatorAnnounce.getAnnouncedStorageLocations(validators); + + for (let i = 0; i < defaultValidatorConfigs.length; i++) { + const { address: validator, alias } = defaultValidatorConfigs[i]; + const location = storageLocations[i][storageLocations[i].length - 1]; + + try { + const validatorInstance = await getValidatorFromStorageLocation( + location, + ); + const metadata = await validatorInstance.getMetadata(); + + const matchCount = publicRpcs.filter((rpc) => + metadata.rpcs?.some((x) => x == rpc), + ).length; + const rpcCount = metadata.rpcs?.length; + + output.push({ + chain, + validator: alias ?? validator, + status: CheckResult.OK, + rpcs: `${rpcCount ?? '?'}`, + private: !rpcCount ? '?/?' : `${rpcCount - matchCount}/${rpcCount}`, + }); + } catch { + output.push({ + chain, + validator: alias ?? validator, + status: CheckResult.WARNING, + rpcs: '?', + private: '?/?', + }); + } + } + + return { + chain, + }; + }), + ); + + console.table(output); +} + +main().catch(console.error); diff --git a/typescript/sdk/src/aws/s3.ts b/typescript/sdk/src/aws/s3.ts index 462aa26feef..f56cecb68f9 100644 --- a/typescript/sdk/src/aws/s3.ts +++ b/typescript/sdk/src/aws/s3.ts @@ -34,7 +34,15 @@ export class S3Wrapper { } constructor(readonly config: S3Config) { - this.client = new S3Client(config); + this.client = new S3Client({ + ...config, + // explicitly set empty credentials to allow usage without env vars + credentials: { + accessKeyId: '', + secretAccessKey: '', + }, + signer: { sign: async (req) => req }, + }); if (config.caching) { this.cache = {}; } diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index 361dfa486a8..39b0cec8746 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -123,4 +123,6 @@ export type Annotated = T & { export type ValidatorMetadata = { git_sha: string; + rpcs?: string[]; + allows_public_rpcs?: boolean; }; From 24fe342edcb72fc5ca73472026daa4ca750e9baa Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:28:33 +0100 Subject: [PATCH 062/223] chore(metadata): clean up aggregation fast path (#6011) ### Description - the logic for building aggregation ISM metadata via the fast path has been cleaned up and broken into smaller functions - previously when the fast path couldn't be used, a misleading warning about a fast path failure was printed. (An example of that is when the threshold was > 1). Since being unable to use the fast path is normal in those cases, now a debug log is printed instead Fixes https://linear.app/hyperlane-xyz/issue/BACK-171/relayer-metadata-fast-path-change-should-be-cleaned-up-especially-the ### Backward compatibility Yes ### Testing E2E covers this --- .../relayer/src/msg/metadata/aggregation.rs | 105 ++++++++++++------ 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/aggregation.rs b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs index e5df0ccffba..640e5ed46ff 100644 --- a/rust/main/agents/relayer/src/msg/metadata/aggregation.rs +++ b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs @@ -4,7 +4,7 @@ use futures_util::future::join_all; use derive_new::new; use itertools::{Either, Itertools}; -use tracing::{info, instrument}; +use tracing::{debug, info, instrument}; use {hyperlane_base::cache::FunctionCallCache, tracing::warn}; use hyperlane_core::{ @@ -191,41 +191,78 @@ impl AggregationIsmMetadataBuilder { params: MessageMetadataBuildParams, threshold: usize, ism_addresses: Vec, - ) -> Result { + ) -> Result, MetadataBuildError> { if threshold > 1 { - return Err(MetadataBuildError::FastPathError( - "Aggregation ISM threshold > 1".to_string(), - )); + debug!( + ?threshold, + reason = "Aggregation ISM threshold > 1", + "Fast path is not available" + ); + return Ok(None); } - let sub_isms = join_all(ism_addresses.iter().map(|sub_ism_address| { - message_builder::ism_and_module_type(self.base.clone(), *sub_ism_address) + let Some(( + message_id_multisig_ism_index, + message_id_multisig_ism, + message_id_multisig_ism_address, + )) = self + .try_find_message_id_multisig_ism(ism_addresses.clone()) + .await + else { + debug!( + ?threshold, + reason = "Aggregation ISM does not have a MessageIdMultisig submodule", + "Fast path is not available" + ); + return Ok(None); + }; + let metadata = self + .build_message_id_aggregation_metadata( + message, + params.clone(), + message_id_multisig_ism, + message_id_multisig_ism_index, + message_id_multisig_ism_address, + ism_addresses.len(), + ) + .await?; + + Ok(Some(metadata)) + } + + async fn try_find_message_id_multisig_ism( + &self, + ism_addresses: Vec, + ) -> Option<(usize, Box, H256)> { + let sub_isms = join_all(ism_addresses.iter().map(|sub_ism_address| async { + let ism_and_module_type = + message_builder::ism_and_module_type(self.base.clone(), *sub_ism_address).await; + (ism_and_module_type, *sub_ism_address) })) .await; - let (message_id_multisig_ism_index, message_id_multisig_ism) = sub_isms - .into_iter() - .enumerate() - .find_map(|(index, ism)| { - if let Ok((ism, ModuleType::MessageIdMultisig)) = ism { - Some((index, ism)) - } else { - None - } - }) - .ok_or(MetadataBuildError::FastPathError( - "No MessageIdMultisigIsm submodule in aggregation ISM".to_string(), - ))?; - let message_id_multisig_ism_address = ism_addresses - .get(message_id_multisig_ism_index) - .ok_or(MetadataBuildError::FastPathError(format!( - "No ism address found for messageIdMultisig index {}", - message_id_multisig_ism_index - )))?; + sub_isms.into_iter().enumerate().find_map(|(index, ism)| { + if let (Ok((ism, ModuleType::MessageIdMultisig)), address) = ism { + Some((index, ism, address)) + } else { + None + } + }) + } + + async fn build_message_id_aggregation_metadata( + &self, + message: &HyperlaneMessage, + params: MessageMetadataBuildParams, + ism: Box, + ism_index: usize, + ism_address: H256, + ism_count: usize, + ) -> Result { let sub_module_and_meta = message_builder::build_message_metadata( self.base.clone(), - *message_id_multisig_ism_address, + ism_address, message, params.clone(), - Some((message_id_multisig_ism, ModuleType::MessageIdMultisig)), + Some((ism, ModuleType::MessageIdMultisig)), ) .await?; @@ -243,11 +280,8 @@ impl AggregationIsmMetadataBuilder { "Fast path metadata failed dry run (returned None)".to_string(), )); } - let sub_module_metadata = SubModuleMetadata::new(message_id_multisig_ism_index, metadata); - let metadata = Metadata::new(Self::format_metadata( - &mut [sub_module_metadata], - ism_addresses.len(), - )); + let sub_module_metadata = SubModuleMetadata::new(ism_index, metadata); + let metadata = Metadata::new(Self::format_metadata(&mut [sub_module_metadata], ism_count)); Ok(metadata) } } @@ -276,7 +310,7 @@ impl MetadataBuilder for AggregationIsmMetadataBuilder { .try_build_fast_path(message, params.clone(), threshold, ism_addresses.clone()) .await { - Ok(metadata) => { + Ok(Some(metadata)) => { info!("Built metadata using fast path"); return Ok(metadata); } @@ -286,6 +320,9 @@ impl MetadataBuilder for AggregationIsmMetadataBuilder { "Fast path failed, falling back to the other submodules in the aggregation ISM" ); } + _ => { + // The fast path is not available, a debug log has already been printed so try the slow path + } } let sub_modules_and_metas = join_all(ism_addresses.iter().map(|sub_ism_address| { From 8871fad798190c35ce3e44a427ae91b4165d0215 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Thu, 24 Apr 2025 12:35:10 -0400 Subject: [PATCH 063/223] feat: test cli cross platform compatibility in ci (#6002) ### Description Updates the `test-cli-install` action to install the cli on macos and windows too and with different node.js version ### Drive-by changes - None ### Related issues ### Backward compatibility - YES ### Testing - Manual --------- Co-authored-by: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> --- .github/workflows/release.yml | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dfedb241a12..34e95b91a13 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,31 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: + cli-install-cross-platform-release-test: + strategy: + matrix: + os: [depot-ubuntu-latest, depot-macos-latest, depot-windows-2022] + node-version: [18, 19, 20, 21, 22, 23] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: install-hyperlane-cli + id: install-hyperlane-cli + uses: ./.github/actions/install-cli + with: + ref: ${{ github.sha }} + + - name: Test run the CLI + run: hyperlane --version + release: + needs: + - cli-install-cross-platform-release-test permissions: id-token: write contents: write @@ -21,9 +45,9 @@ jobs: - name: Checkout Repo uses: actions/checkout@v4 with: - # check out full history - fetch-depth: 0 - submodules: recursive + # check out full history + fetch-depth: 0 + submodules: recursive - name: Setup Node.js 18.x uses: actions/setup-node@v4 From f4a84d1541cd602d3a0ce37c14d44f5b937801d9 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 24 Apr 2025 18:46:24 +0100 Subject: [PATCH 064/223] fix: rm old warp route id (#6014) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/warp/warpIds.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index 44eb32b710a..7e7cc07ae18 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -41,7 +41,6 @@ export enum WarpRouteIds { EthereumVictionUSDT = 'USDT/ethereum-viction', BerachainEthereumSwellUnichainZircuitPZETH = 'PZETH/berachain-ethereum-swell-unichain-zircuit', BerachainEthereumSwellUnichainZircuitPZETHSTAGE = 'PZETHSTAGE/berachain-ethereum-swell-unichain-zircuit', - EthereumBscLumiaprismLUMIA = 'LUMIA/bsc-ethereum-lumiaprism', EthereumZircuitRe7LRT = 'Re7LRT/ethereum-zircuit', InevmInjectiveINJ = 'INJ/inevm-injective', ArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIA = 'LUMIA/arbitrum-avalanche-base-bsc-ethereum-lumiaprism-optimism-polygon', From 0277de58498c91cba9907d64104f4b082817bfa7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:54:34 -0400 Subject: [PATCH 065/223] Version Packages (#5985) 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/sdk@12.2.0 ### Minor Changes - c7934f711: Adds the isRevokeApprovalRequired method on the token adapters to check if the user should revoke any previously set allowances on the token to transfer to avoid approvals failing like in the case of USDT - ecbacbdf2: Add EvmHypRebaseCollateralAdapter and EvmHypSyntheticRebaseAdapter ### Patch Changes - @hyperlane-xyz/utils@12.2.0 - @hyperlane-xyz/core@7.1.1 ## @hyperlane-xyz/core@7.1.1 ### Patch Changes - @hyperlane-xyz/utils@12.2.0 ## @hyperlane-xyz/cosmos-sdk@12.2.0 ### Patch Changes - @hyperlane-xyz/cosmos-types@12.2.0 ## @hyperlane-xyz/helloworld@12.2.0 ### Patch Changes - Updated dependencies [c7934f711] - Updated dependencies [ecbacbdf2] - @hyperlane-xyz/sdk@12.2.0 - @hyperlane-xyz/core@7.1.1 ## @hyperlane-xyz/widgets@12.2.0 ### Patch Changes - Updated dependencies [c7934f711] - Updated dependencies [ecbacbdf2] - @hyperlane-xyz/sdk@12.2.0 - @hyperlane-xyz/utils@12.2.0 ## @hyperlane-xyz/cli@12.2.0 ## @hyperlane-xyz/cosmos-types@12.2.0 ## @hyperlane-xyz/utils@12.2.0 ## @hyperlane-xyz/infra@12.2.0 ### Patch Changes - Updated dependencies [c7934f711] - Updated dependencies [ecbacbdf2] - @hyperlane-xyz/sdk@12.2.0 - @hyperlane-xyz/helloworld@12.2.0 - @hyperlane-xyz/utils@12.2.0 ## @hyperlane-xyz/ccip-server@12.2.0 ## @hyperlane-xyz/github-proxy@12.2.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/shiny-turtles-hide.md | 5 ---- .changeset/stupid-trainers-poke.md | 5 ---- solidity/CHANGELOG.md | 6 +++++ solidity/contracts/PackageVersioned.sol | 2 +- solidity/package.json | 4 +-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 2 ++ typescript/cli/package.json | 6 ++--- typescript/cli/src/version.ts | 2 +- typescript/cosmos-sdk/CHANGELOG.md | 7 +++++ typescript/cosmos-sdk/package.json | 4 +-- typescript/cosmos-types/CHANGELOG.md | 2 ++ typescript/cosmos-types/package.json | 2 +- typescript/github-proxy/CHANGELOG.md | 2 ++ typescript/github-proxy/package.json | 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 | 12 +++++++++ typescript/sdk/package.json | 6 ++--- typescript/utils/CHANGELOG.md | 2 ++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 9 +++++++ typescript/widgets/package.json | 6 ++--- yarn.lock | 36 ++++++++++++------------- 27 files changed, 107 insertions(+), 54 deletions(-) delete mode 100644 .changeset/shiny-turtles-hide.md delete mode 100644 .changeset/stupid-trainers-poke.md create mode 100644 typescript/cosmos-sdk/CHANGELOG.md diff --git a/.changeset/shiny-turtles-hide.md b/.changeset/shiny-turtles-hide.md deleted file mode 100644 index f8238bcea29..00000000000 --- a/.changeset/shiny-turtles-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Adds the isRevokeApprovalRequired method on the token adapters to check if the user should revoke any previously set allowances on the token to transfer to avoid approvals failing like in the case of USDT diff --git a/.changeset/stupid-trainers-poke.md b/.changeset/stupid-trainers-poke.md deleted file mode 100644 index 02a55435ac0..00000000000 --- a/.changeset/stupid-trainers-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Add EvmHypRebaseCollateralAdapter and EvmHypSyntheticRebaseAdapter diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index 162ffdadb95..6101adf534c 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/core +## 7.1.1 + +### Patch Changes + +- @hyperlane-xyz/utils@12.2.0 + ## 7.1.0 ### Minor Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index b479179c235..20e3f5dfd71 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.1.0"; + string public constant PACKAGE_VERSION = "7.1.1"; } diff --git a/solidity/package.json b/solidity/package.json index 47e6db51788..1352b31ff81 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.1.0", + "version": "7.1.1", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.1.0", + "@hyperlane-xyz/utils": "12.2.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 873cf4885b0..9997bf5d7a0 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 12.2.0 + ## 12.1.0 ## 12.0.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index d01f5485f55..7c574e5d4bb 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.1.0", + "version": "12.2.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 38a58bb2fd5..14bfc8bcee4 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cli +## 12.2.0 + ## 12.1.0 ### Patch Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index bb3fbeec1ff..bd16150dd5a 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.1.0", + "version": "12.2.0", "description": "A command-line utility for common Hyperlane operations", "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", @@ -9,8 +9,8 @@ "@ethersproject/abi": "*", "@ethersproject/providers": "*", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.1.0", - "@hyperlane-xyz/utils": "12.1.0", + "@hyperlane-xyz/sdk": "12.2.0", + "@hyperlane-xyz/utils": "12.2.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index e04a4acc82b..0e42922c30c 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.1.0'; +export const VERSION = '12.2.0'; diff --git a/typescript/cosmos-sdk/CHANGELOG.md b/typescript/cosmos-sdk/CHANGELOG.md new file mode 100644 index 00000000000..3d6d4caa31d --- /dev/null +++ b/typescript/cosmos-sdk/CHANGELOG.md @@ -0,0 +1,7 @@ +# @hyperlane-xyz/cosmos-sdk + +## 12.2.0 + +### Patch Changes + +- @hyperlane-xyz/cosmos-types@12.2.0 diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 74993fd98c0..28ae24ad414 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-sdk", - "version": "1.0.0", + "version": "12.2.0", "description": "Hyperlane TypeScript SDK for the Cosmos Hyperlane SDK module", "type": "module", "exports": { @@ -46,6 +46,6 @@ }, "dependencies": { "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/cosmos-types": "11.0.0" + "@hyperlane-xyz/cosmos-types": "12.2.0" } } diff --git a/typescript/cosmos-types/CHANGELOG.md b/typescript/cosmos-types/CHANGELOG.md index fd9a3c2a0fa..835c42364fd 100644 --- a/typescript/cosmos-types/CHANGELOG.md +++ b/typescript/cosmos-types/CHANGELOG.md @@ -1,3 +1,5 @@ # @hyperlane-xyz/cosmos-types +## 12.2.0 + ## 11.0.0 diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index dd8ca846c59..015b08832a5 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-types", - "version": "11.0.0", + "version": "12.2.0", "description": "Hyperlane TypeScript SDK types for the Cosmos Hyperlane SDK module", "type": "module", "exports": { diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index 7933757440f..e97da119a6a 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 12.2.0 + ## 12.1.0 ## 12.0.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index c5afc67488c..b0ce0bf4334 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.1.0", + "version": "12.2.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index d62f1670bb1..5c3b6fe3497 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/helloworld +## 12.2.0 + +### Patch Changes + +- Updated dependencies [c7934f711] +- Updated dependencies [ecbacbdf2] + - @hyperlane-xyz/sdk@12.2.0 + - @hyperlane-xyz/core@7.1.1 + ## 12.1.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index b16fc5d4472..24345de2ad6 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": "12.1.0", + "version": "12.2.0", "dependencies": { - "@hyperlane-xyz/core": "7.1.0", + "@hyperlane-xyz/core": "7.1.1", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.1.0", + "@hyperlane-xyz/sdk": "12.2.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 7cd8eab6e2e..d7702c6db3a 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/infra +## 12.2.0 + +### Patch Changes + +- Updated dependencies [c7934f711] +- Updated dependencies [ecbacbdf2] + - @hyperlane-xyz/sdk@12.2.0 + - @hyperlane-xyz/helloworld@12.2.0 + - @hyperlane-xyz/utils@12.2.0 + ## 12.1.0 ### Minor Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 125b2247328..e89891aef69 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": "12.1.0", + "version": "12.2.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.1.0", + "@hyperlane-xyz/helloworld": "12.2.0", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.1.0", - "@hyperlane-xyz/utils": "12.1.0", + "@hyperlane-xyz/sdk": "12.2.0", + "@hyperlane-xyz/utils": "12.2.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index d5415ce0e52..38a3c03a312 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,17 @@ # @hyperlane-xyz/sdk +## 12.2.0 + +### Minor Changes + +- c7934f711: Adds the isRevokeApprovalRequired method on the token adapters to check if the user should revoke any previously set allowances on the token to transfer to avoid approvals failing like in the case of USDT +- ecbacbdf2: Add EvmHypRebaseCollateralAdapter and EvmHypSyntheticRebaseAdapter + +### Patch Changes + +- @hyperlane-xyz/utils@12.2.0 +- @hyperlane-xyz/core@7.1.1 + ## 12.1.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index a1a7da696dd..1b09219ed96 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,15 +1,15 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.1.0", + "version": "12.2.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.14", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.1.0", - "@hyperlane-xyz/utils": "12.1.0", + "@hyperlane-xyz/core": "7.1.1", + "@hyperlane-xyz/utils": "12.2.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 4aa277d315b..d1a1dd35d8a 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/utils +## 12.2.0 + ## 12.1.0 ## 12.0.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 07adc835b50..ed4df96d74c 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": "12.1.0", + "version": "12.2.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index eb882f68d7e..b76376511b3 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/widgets +## 12.2.0 + +### Patch Changes + +- Updated dependencies [c7934f711] +- Updated dependencies [ecbacbdf2] + - @hyperlane-xyz/sdk@12.2.0 + - @hyperlane-xyz/utils@12.2.0 + ## 12.1.0 ### Patch Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 57d067f40e5..c4b55522222 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.1.0", + "version": "12.2.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -9,8 +9,8 @@ "dependencies": { "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/sdk": "12.1.0", - "@hyperlane-xyz/utils": "12.1.0", + "@hyperlane-xyz/sdk": "12.2.0", + "@hyperlane-xyz/utils": "12.2.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index 0bbd6a14944..887f5fae3db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7651,8 +7651,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.1.0" - "@hyperlane-xyz/utils": "npm:12.1.0" + "@hyperlane-xyz/sdk": "npm:12.2.0" + "@hyperlane-xyz/utils": "npm:12.2.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7692,14 +7692,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.1.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.1, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.1.0" + "@hyperlane-xyz/utils": "npm:12.2.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7745,7 +7745,7 @@ __metadata: dependencies: "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/cosmos-types": "npm:11.0.0" + "@hyperlane-xyz/cosmos-types": "npm:12.2.0" "@types/mocha": "npm:^10.0.1" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" @@ -7761,7 +7761,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-types@npm:11.0.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": +"@hyperlane-xyz/cosmos-types@npm:12.2.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" dependencies: @@ -7795,14 +7795,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.1.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:12.2.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.0" + "@hyperlane-xyz/core": "npm:7.1.1" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.1.0" + "@hyperlane-xyz/sdk": "npm:12.2.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7851,10 +7851,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.1.0" + "@hyperlane-xyz/helloworld": "npm:12.2.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.1.0" - "@hyperlane-xyz/utils": "npm:12.1.0" + "@hyperlane-xyz/sdk": "npm:12.2.0" + "@hyperlane-xyz/utils": "npm:12.2.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7925,7 +7925,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.1.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:12.2.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7935,8 +7935,8 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.0" - "@hyperlane-xyz/utils": "npm:12.1.0" + "@hyperlane-xyz/core": "npm:7.1.1" + "@hyperlane-xyz/utils": "npm:12.2.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -7981,7 +7981,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.1.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:12.2.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8023,8 +8023,8 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.1.0" - "@hyperlane-xyz/utils": "npm:12.1.0" + "@hyperlane-xyz/sdk": "npm:12.2.0" + "@hyperlane-xyz/utils": "npm:12.2.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From 6101959f7799faa1e92056fb4afb39e3edf8d867 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Thu, 24 Apr 2025 19:44:08 +0100 Subject: [PATCH 066/223] feat: check new chains using remoteRouters (#5939) ### Description This PR changes the new chains check implemented in #5914 to allow non-fully connected warp routes. We now use the `remoteRouters` field in the deployment configuration. When `remoteRouters` is present, the checker uses the chains listed there to verify enrollment. When `remoteRouters` is absent from the deployment config, the checker assumes the route is fully connected and verifies enrollment for all the chains in the route. ### Related issues Resolves #5440 Fixes ENG-1061 ### Backward compatibility The new behavior of the check is optional, only triggered by `remoteRouters` field which isn't currently present on any deployment configs in the registry. ### Testing Manually running `check-warp-deploy` to verify the check works correctly with `remoteRouters` on the following routes: - USDC/arbitrum-base-ethereum-lisk-optimism-polygon-zeronetwork - USDT/arbitrum-ethereum-mantle-mode-polygon-scroll-zeronetwork --- .changeset/large-masks-burn.md | 6 + ...ptimismPolygonZeroNetworkUSDCWarpConfig.ts | 191 ++++++++++-------- ...PolygonScrollZeroNetworkUSDTWarpConfig.ts} | 18 ++ typescript/infra/config/registry.ts | 34 ++-- typescript/infra/config/warp.ts | 6 +- typescript/infra/scripts/check/check-utils.ts | 10 +- .../infra/scripts/check/check-warp-deploy.ts | 28 +-- .../sdk/src/router/HyperlaneRouterChecker.ts | 9 +- .../sdk/src/router/ProxiedRouterChecker.ts | 7 +- typescript/sdk/src/token/checker.ts | 10 +- 10 files changed, 177 insertions(+), 142 deletions(-) create mode 100644 .changeset/large-masks-burn.md rename typescript/infra/config/environments/mainnet3/warp/configGetters/{getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts => getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts} (83%) diff --git a/.changeset/large-masks-burn.md b/.changeset/large-masks-burn.md new file mode 100644 index 00000000000..c8db0b79e55 --- /dev/null +++ b/.changeset/large-masks-burn.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/infra': minor +'@hyperlane-xyz/sdk': minor +--- + +Enhanced the router enrollment check to support non-fully connected warp routes using the `remoteRouters` property from the deployment config. diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts index 49fc6289494..86096b4f792 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts @@ -13,102 +13,123 @@ import { } from '../../../../../src/config/warp.js'; import { timelocks } from '../../owners.js'; -export const getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC = async ( - routerConfig: ChainMap, - abacusWorksEnvOwnerConfig: ChainMap, -): Promise> => { - const ISM_CONFIG = ethers.constants.AddressZero; +export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig = + async ( + routerConfig: ChainMap, + abacusWorksEnvOwnerConfig: ChainMap, + ): Promise> => { + const ISM_CONFIG = ethers.constants.AddressZero; - const arbitrum: HypTokenRouterConfig = { - ...routerConfig.arbitrum, - owner: abacusWorksEnvOwnerConfig.arbitrum.owner, - proxyAdmin: { - address: '0x02317D525FA7ceb5ea388244b4618f0c8Ac1CeC2', - owner: timelocks.arbitrum, - }, - type: TokenType.collateral, - token: tokens.arbitrum.USDC, - interchainSecurityModule: ISM_CONFIG, - }; + const arbitrum: HypTokenRouterConfig = { + ...routerConfig.arbitrum, + owner: abacusWorksEnvOwnerConfig.arbitrum.owner, + proxyAdmin: { + address: '0x02317D525FA7ceb5ea388244b4618f0c8Ac1CeC2', + owner: timelocks.arbitrum, + }, + type: TokenType.collateral, + token: tokens.arbitrum.USDC, + interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + }, + }; - const base: HypTokenRouterConfig = { - ...routerConfig.base, - owner: abacusWorksEnvOwnerConfig.base.owner, - proxyAdmin: { + const base: HypTokenRouterConfig = { + ...routerConfig.base, owner: abacusWorksEnvOwnerConfig.base.owner, - address: '0xB6E9331576C5aBF69376AF6989eA61b7C7ea67F1', - }, - type: TokenType.collateral, - token: tokens.base.USDC, - interchainSecurityModule: ISM_CONFIG, - }; + proxyAdmin: { + owner: abacusWorksEnvOwnerConfig.base.owner, + address: '0xB6E9331576C5aBF69376AF6989eA61b7C7ea67F1', + }, + type: TokenType.collateral, + token: tokens.base.USDC, + interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + }, + }; - const optimism: HypTokenRouterConfig = { - ...routerConfig.optimism, - owner: abacusWorksEnvOwnerConfig.optimism.owner, - proxyAdmin: { + const optimism: HypTokenRouterConfig = { + ...routerConfig.optimism, owner: abacusWorksEnvOwnerConfig.optimism.owner, - address: '0xca9e64761C97b049901dF4E7a5926464969528b1', - }, - type: TokenType.collateral, - token: tokens.optimism.USDC, - interchainSecurityModule: ISM_CONFIG, - }; + proxyAdmin: { + owner: abacusWorksEnvOwnerConfig.optimism.owner, + address: '0xca9e64761C97b049901dF4E7a5926464969528b1', + }, + type: TokenType.collateral, + token: tokens.optimism.USDC, + interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + }, + }; - const polygon: HypTokenRouterConfig = { - ...routerConfig.polygon, - owner: abacusWorksEnvOwnerConfig.polygon.owner, - proxyAdmin: { + const polygon: HypTokenRouterConfig = { + ...routerConfig.polygon, owner: abacusWorksEnvOwnerConfig.polygon.owner, - address: '0x7fd5be37d560626625f395A2e6E30eA89150cc98', - }, - type: TokenType.collateral, - token: tokens.polygon.USDC, - interchainSecurityModule: ISM_CONFIG, - }; + proxyAdmin: { + owner: abacusWorksEnvOwnerConfig.polygon.owner, + address: '0x7fd5be37d560626625f395A2e6E30eA89150cc98', + }, + type: TokenType.collateral, + token: tokens.polygon.USDC, + interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + }, + }; - const zeronetwork: HypTokenRouterConfig = { - ...routerConfig.zeronetwork, - owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, - proxyAdmin: { + const zeronetwork: HypTokenRouterConfig = { + ...routerConfig.zeronetwork, owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, - address: '0x6E906d8AeEBE9025a410887EAafc58C2561705e0', - }, - type: TokenType.collateral, - token: tokens.zeronetwork.USDC, - interchainSecurityModule: ISM_CONFIG, - }; + proxyAdmin: { + owner: abacusWorksEnvOwnerConfig.zeronetwork.owner, + address: '0x6E906d8AeEBE9025a410887EAafc58C2561705e0', + }, + type: TokenType.collateral, + token: tokens.zeronetwork.USDC, + interchainSecurityModule: ISM_CONFIG, + }; - const ethereum: HypTokenRouterConfig = { - ...routerConfig.ethereum, - owner: abacusWorksEnvOwnerConfig.ethereum.owner, - proxyAdmin: { + const ethereum: HypTokenRouterConfig = { + ...routerConfig.ethereum, owner: abacusWorksEnvOwnerConfig.ethereum.owner, - address: '0x81063D413Ed6Eac3FCf0521eea14906fD27fEb1A', - }, - type: TokenType.collateral, - token: tokens.ethereum.USDC, - interchainSecurityModule: ISM_CONFIG, - }; + proxyAdmin: { + owner: abacusWorksEnvOwnerConfig.ethereum.owner, + address: '0x81063D413Ed6Eac3FCf0521eea14906fD27fEb1A', + }, + type: TokenType.collateral, + token: tokens.ethereum.USDC, + interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + }, + }; - const lisk: HypTokenRouterConfig = { - ...routerConfig.lisk, - owner: abacusWorksEnvOwnerConfig.lisk.owner, - proxyAdmin: { - ...abacusWorksEnvOwnerConfig.lisk, - address: '0x81Db8B4Bc6F2e95781eeA2a21D0A453Ac046eFc0', - }, - type: TokenType.synthetic, - interchainSecurityModule: ISM_CONFIG, - }; + const lisk: HypTokenRouterConfig = { + ...routerConfig.lisk, + owner: abacusWorksEnvOwnerConfig.lisk.owner, + proxyAdmin: { + ...abacusWorksEnvOwnerConfig.lisk, + address: '0x81Db8B4Bc6F2e95781eeA2a21D0A453Ac046eFc0', + }, + type: TokenType.synthetic, + interchainSecurityModule: ISM_CONFIG, + }; - return { - arbitrum, - base, - ethereum, - optimism, - polygon, - zeronetwork, - lisk, + return { + arbitrum, + base, + ethereum, + optimism, + polygon, + zeronetwork, + lisk, + }; }; -}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts similarity index 83% rename from typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts rename to typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts index bb25b14036b..883116bf51f 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts @@ -31,6 +31,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig type: TokenType.collateral, token: tokens.arbitrum.USDT, interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + }, }; const ethereum: HypTokenRouterConfig = { @@ -43,6 +46,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig type: TokenType.collateral, token: tokens.ethereum.USDT, interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + }, }; const mantle: HypTokenRouterConfig = { @@ -55,6 +61,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig type: TokenType.collateral, token: tokens.mantle.USDT, interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + }, }; const mode: HypTokenRouterConfig = { @@ -67,6 +76,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig type: TokenType.collateral, token: tokens.mode.USDT, interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + }, }; const polygon: HypTokenRouterConfig = { @@ -79,6 +91,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig type: TokenType.collateral, token: tokens.polygon.USDT, interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + }, }; const scroll: HypTokenRouterConfig = { @@ -91,6 +106,9 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig type: TokenType.collateral, token: tokens.scroll.USDT, interchainSecurityModule: ISM_CONFIG, + remoteRouters: { + zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + }, }; const zeronetwork: HypTokenRouterConfig = { diff --git a/typescript/infra/config/registry.ts b/typescript/infra/config/registry.ts index 49ee31a663c..492590d7a6e 100644 --- a/typescript/infra/config/registry.ts +++ b/typescript/infra/config/registry.ts @@ -3,6 +3,7 @@ import { fileURLToPath } from 'url'; import { ChainAddresses, + IRegistry, MergedRegistry, PartialRegistry, warpConfigToWarpAddresses, @@ -16,7 +17,6 @@ import { ChainMetadata, ChainName, WarpCoreConfig, - WarpRouteDeployConfig, getDomainId as resolveDomainId, getReorgPeriod as resolveReorgPeriod, } from '@hyperlane-xyz/sdk'; @@ -65,6 +65,14 @@ export function getRegistry(): FileSystemRegistry { return registry; } +function getRegistryFromUris(registryUris?: string[]): IRegistry { + if (registryUris && registryUris.length > 0) { + return getMergedRegistry({ registryUris, enableProxy: true }); + } else { + return getRegistry(); + } +} + export function getChains(): ChainName[] { return getRegistry().getChains(); } @@ -115,30 +123,18 @@ export function getWarpAddresses( return warpConfigToWarpAddresses(warpCoreConfig); } -export async function getWarpCoreConfigFromMergedRegistry( +export async function getWarpAddressesFrom( warpRouteId: string, - registryUris: string[], -): Promise { - const registry = getMergedRegistry({ registryUris, enableProxy: true }); + registryUris?: string[], +): Promise> { + const registry = getRegistryFromUris(registryUris); const warpRouteConfig = await registry.getWarpRoute(warpRouteId); - if (!warpRouteConfig) { throw new Error( - `Warp route config for ${warpRouteId} not found in registry`, + `Warp route config for ${warpRouteId} not found in ${registry.uri}`, ); } - return warpRouteConfig; -} - -export async function getWarpAddressesFromMergedRegistry( - warpRouteId: string, - registryUris: string[], -): Promise> { - const warpCoreConfig = await getWarpCoreConfigFromMergedRegistry( - warpRouteId, - registryUris, - ); - return warpConfigToWarpAddresses(warpCoreConfig); + return warpConfigToWarpAddresses(warpRouteConfig); } export function getEnvChains(env: DeployEnvironment): ChainName[] { diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index d74e16e90b8..2619afac7f4 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -27,8 +27,8 @@ import { getArbitrumBaseEthereumLumiaprismOptimismPolygonETHGnosisSafeBuilderStrategyConfig, getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig, } from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumLumiaprismOptimismPolygonETHWarpConfig.js'; -import { getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC } from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.js'; -import { getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.js'; +import { getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.js'; +import { getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.js'; import { getArbitrumEthereumSolanaTreasureSMOLWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.js'; import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js'; import { getBaseEthereumSuperseedCBBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBaseEthereumSuperseedCBBTCWarpConfig.js'; @@ -112,7 +112,7 @@ export const warpConfigGetterMap: Record = { [WarpRouteIds.ArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDT]: getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig, [WarpRouteIds.ArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDC]: - getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC, + getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig, [WarpRouteIds.ArbitrumBaseBlastBscEthereumGnosisLiskMantleModeOptimismPolygonScrollZeroNetworkZoraMainnet]: getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig, [WarpRouteIds.EclipseStrideTIA]: getEclipseStrideTiaWarpConfig, diff --git a/typescript/infra/scripts/check/check-utils.ts b/typescript/infra/scripts/check/check-utils.ts index 57e0e4fb409..85691679cbd 100644 --- a/typescript/infra/scripts/check/check-utils.ts +++ b/typescript/infra/scripts/check/check-utils.ts @@ -23,10 +23,7 @@ import { eqAddress, objFilter } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { DEPLOYER } from '../../config/environments/mainnet3/owners.js'; -import { - getWarpAddresses, - getWarpAddressesFromMergedRegistry, -} from '../../config/registry.js'; +import { getWarpAddressesFrom } from '../../config/registry.js'; import { getWarpConfig } from '../../config/warp.js'; import { chainsToSkip } from '../../src/config/chain.js'; import { DeployEnvironment } from '../../src/config/environment.js'; @@ -207,9 +204,8 @@ export async function getGovernor( warpRouteId, registryUris, ); - const warpAddresses = registryUris - ? await getWarpAddressesFromMergedRegistry(warpRouteId, registryUris) - : getWarpAddresses(warpRouteId); + const warpAddresses = await getWarpAddressesFrom(warpRouteId, registryUris); + const filteredAddresses = Object.keys(warpAddresses) // filter out changes not in config .filter((key) => key in config) .reduce((obj, key) => { diff --git a/typescript/infra/scripts/check/check-warp-deploy.ts b/typescript/infra/scripts/check/check-warp-deploy.ts index 27d1f38d281..c0f40bbbf63 100644 --- a/typescript/infra/scripts/check/check-warp-deploy.ts +++ b/typescript/infra/scripts/check/check-warp-deploy.ts @@ -9,7 +9,7 @@ import { WarpRouteIds } from '../../config/environments/mainnet3/warp/warpIds.js import { DEFAULT_REGISTRY_URI } from '../../config/registry.js'; import { getWarpConfigMapFromMergedRegistry } from '../../config/warp.js'; import { submitMetrics } from '../../src/utils/metrics.js'; -import { Modules, getWarpRouteIdsInteractive } from '../agent-utils.js'; +import { Modules } from '../agent-utils.js'; import { getEnvironmentConfig } from '../core-utils.js'; import { @@ -20,15 +20,8 @@ import { } from './check-utils.js'; async function main() { - const { - environment, - asDeployer, - chains, - fork, - context, - pushMetrics, - interactive, - } = await getCheckWarpDeployArgs().argv; + const { environment, asDeployer, chains, fork, context, pushMetrics } = + await getCheckWarpDeployArgs().argv; const metricsRegister = new Registry(); const checkerViolationsGauge = new Gauge( @@ -47,17 +40,12 @@ async function main() { registries, ); - let warpIdsToCheck: string[]; - if (interactive) { - warpIdsToCheck = await getWarpRouteIdsInteractive(); - } else { - console.log(chalk.yellow('Skipping the following warp routes:')); - routesToSkip.forEach((route) => console.log(chalk.yellow(`- ${route}`))); + console.log(chalk.yellow('Skipping the following warp routes:')); + routesToSkip.forEach((route) => console.log(chalk.yellow(`- ${route}`))); - warpIdsToCheck = Object.keys(warpCoreConfigMap).filter( - (warpRouteId) => !routesToSkip.includes(warpRouteId), - ); - } + const warpIdsToCheck = Object.keys(warpCoreConfigMap).filter( + (warpRouteId) => !routesToSkip.includes(warpRouteId), + ); // Get all the chains from warpCoreConfigMap. Used to initialize the MultiProvider. const warpConfigChains = warpIdsToCheck.reduce((chains, warpRouteId) => { diff --git a/typescript/sdk/src/router/HyperlaneRouterChecker.ts b/typescript/sdk/src/router/HyperlaneRouterChecker.ts index 1210c709ceb..99508281666 100644 --- a/typescript/sdk/src/router/HyperlaneRouterChecker.ts +++ b/typescript/sdk/src/router/HyperlaneRouterChecker.ts @@ -126,13 +126,12 @@ export class HyperlaneRouterChecker< } } - async checkEnrolledRouters(chain: ChainName): Promise { + async checkEnrolledRouters( + chain: ChainName, + expectedRemoteChains: ChainName[] = [], + ): Promise { const router = this.app.router(this.app.getContracts(chain)); const actualRemoteChains = await this.app.remoteChains(chain); - const expectedRemoteChains = this.app - .chains() - .filter((c) => c !== chain) - .sort(); const currentRouters: ChainMap = {}; const expectedRouters: ChainMap = {}; diff --git a/typescript/sdk/src/router/ProxiedRouterChecker.ts b/typescript/sdk/src/router/ProxiedRouterChecker.ts index 5ae5c16c4b4..2d30cc6c010 100644 --- a/typescript/sdk/src/router/ProxiedRouterChecker.ts +++ b/typescript/sdk/src/router/ProxiedRouterChecker.ts @@ -44,9 +44,12 @@ export abstract class ProxiedRouterChecker< ); } - async checkChain(chain: ChainName): Promise { + async checkChain( + chain: ChainName, + expectedChains?: ChainName[], + ): Promise { await super.checkMailboxClient(chain); - await super.checkEnrolledRouters(chain); + await super.checkEnrolledRouters(chain, expectedChains); await this.checkProxiedContracts(chain); await this.checkOwnership(chain); } diff --git a/typescript/sdk/src/token/checker.ts b/typescript/sdk/src/token/checker.ts index a5716b5cab9..2a517e1268d 100644 --- a/typescript/sdk/src/token/checker.ts +++ b/typescript/sdk/src/token/checker.ts @@ -37,7 +37,15 @@ export class HypERC20Checker extends ProxiedRouterChecker< HypTokenRouterConfig > { async checkChain(chain: ChainName): Promise { - await super.checkChain(chain); + let expectedChains: string[]; + expectedChains = Object.keys(this.configMap); + const thisChainConfig = this.configMap[chain]; + if (thisChainConfig?.remoteRouters) { + expectedChains = Object.keys(thisChainConfig.remoteRouters); + } + expectedChains = expectedChains.filter((chn) => chn !== chain).sort(); + + await super.checkChain(chain, expectedChains); await this.checkToken(chain); } From 89e65c24f4c40e854c8e715ecc51fc57a5a1b2d3 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:27:13 +0100 Subject: [PATCH 067/223] chore: fix xlayer chunk size, new relayer image (#6017) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/config/mainnet_config.json | 1 + typescript/infra/config/environments/mainnet3/agent.ts | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index d575ea258a1..0b9cbfbc213 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -3259,6 +3259,7 @@ "gasCurrencyCoinGeckoId": "okb", "gnosisSafeTransactionServiceUrl": "https://app.safe.global/welcome?chain=xlayer", "index": { + "chunk": 999, "from": 3387690 }, "interchainAccountIsm": "0x29B37088724B745C0ABcE591449Cf042772160C2", diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 764fea69805..24efa5c1eb3 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -826,9 +826,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - // Using an older image to ensure low-volume chains don't hit an edge case, - // see https://hyperlaneworkspace.slack.com/archives/C08GR6PBPGT/p1745272027027899?thread_ts=1745262374.703859&cid=C08GR6PBPGT - tag: '7b0f5a0-20250418-120540', + tag: '24fe342-20250424-164437', }, blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement: gasPaymentEnforcement, @@ -868,7 +866,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '7b0f5a0-20250418-120540', + tag: '24fe342-20250424-164437', }, blacklist: [...blacklist, ...vanguardMatchingList], // We're temporarily (ab)using the RC relayer as a way to increase @@ -906,7 +904,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '7b0f5a0-20250418-120540', + tag: '24fe342-20250424-164437', }, blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement, @@ -933,7 +931,7 @@ const getVanguardRootAgentConfig = (index: number): RootAgentConfig => ({ docker: { repo, // includes gasPriceCap overrides + per-chain maxSubmitQueueLength - tag: '4569591-20250421-224434', + tag: '24fe342-20250424-164437', }, whitelist: vanguardMatchingList, // Not specifying a blacklist for optimization purposes -- all the message IDs From 74db5a3a23bb65bfca36a17c32eb052023a6b702 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:01:48 +0100 Subject: [PATCH 068/223] feat: script to recover vanguard funds (#6015) ### Description feat: script to recover vanguard funds ### Drive-by changes ### Related issues ### Backward compatibility ### Testing - recovered testnet4 funds - recovered mainnet3 funds - output: https://app.warp.dev/block/neYHaUx8zfJf0gjTIy1B9Z before: ![image](https://github.com/user-attachments/assets/b335ac8a-dd2c-45ca-aac5-2ecf3d79021f) after: ![image](https://github.com/user-attachments/assets/fbe16b5a-f894-4e41-bda2-b1a8dddca5f5) --- .../scripts/funding/reclaim-vanguard-funds.sh | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100755 typescript/infra/scripts/funding/reclaim-vanguard-funds.sh diff --git a/typescript/infra/scripts/funding/reclaim-vanguard-funds.sh b/typescript/infra/scripts/funding/reclaim-vanguard-funds.sh new file mode 100755 index 00000000000..86a83ff7edb --- /dev/null +++ b/typescript/infra/scripts/funding/reclaim-vanguard-funds.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +if [ -f "$HOME/.bashrc" ]; then + source "$HOME/.bashrc" +fi + +# Check if environment argument is provided +if [ "$1" != "mainnet3" ] && [ "$1" != "testnet4" ]; then + echo "Usage: $0 " + exit 1 +fi + +ENVIRONMENT=$1 + +# Set the deployer address and vanguard addresses based on environment +# Define the chains to check for each environment +if [ "$ENVIRONMENT" = "mainnet3" ]; then + DEPLOYER="0xa7eccdb9be08178f896c26b7bbd8c3d4e844d9ba" + CHAINS=("base" "bsc" "arbitrum" "optimism" "ethereum") + VANGUARD_ADDRESSES=("0xbe2e6b1ce045422a08a3662fffa3fc5f114efc3d" + "0xdbcd22e5223f5d0040398e66dbb525308f27c655" + "0x226b721316ea44aad50a10f4cc67fc30658ab4a9" + "0xcdd728647ecd9d75413c9b780de303b1d1eb12a5" + "0x5401627b69f317da9adf3d6e1e1214724ce49032" + "0x6fd953d1cbdf3a79663b4238898147a6cf36d459") +else + DEPLOYER="0xfaD1C94469700833717Fa8a3017278BC1cA8031C" + CHAINS=("basesepolia" "bsctestnet" "arbitrumsepolia" "optimismsepolia" "sepolia") + VANGUARD_ADDRESSES=("0x2c9209efcaff2778d945e18fb24174e16845dc62" + "0x939043d9db00f6ada1b742239beb7ddd5bf82096" + "0x45b58e4d46a89c003cc7126bd971eb3794a66aeb" + "0x1f4fdb150e8c9fda70687a2fd481e305af1e7f8e" + "0xe41b227e7aaaf7bbd1d60258de0dd76a11a0c3fc" + "0xb1d77c39166972c0873b6ae016d1a54ec3ce289b" + "0x59f4ee751c5ef680382bdf0bebfa92f278e17284" + "0xd4df81362263d4fbb9ccf6002b0a028b893701b0") +fi + +# Function to get AWS credentials from gcloud secrets +get_aws_credentials() { + local vanguard=$1 + + # Get AWS credentials from gcloud secrets + export AWS_ACCESS_KEY_ID=$(gcloud secrets versions access latest --secret="${vanguard}-${ENVIRONMENT}-relayer-aws-access-key-id") + export AWS_SECRET_ACCESS_KEY=$(gcloud secrets versions access latest --secret="${vanguard}-${ENVIRONMENT}-relayer-aws-secret-access-key") + export AWS_KMS_KEY_ID="alias/${vanguard}-${ENVIRONMENT}-key-relayer" +} + +# Function to check balance and send funds +check_and_send() { + local vanguard_index=$1 + local chain=$2 + local vanguard_address=${VANGUARD_ADDRESSES[$vanguard_index]} + + echo "Checking vanguard$vanguard_index ($vanguard_address) on $chain..." + + # Get the balance of the current wallet + balance=$(cast balance $vanguard_address --rpc-url $(rpc $ENVIRONMENT $chain)) + pretty_balance=$(cast fw $balance) + echo "Balance: $pretty_balance" + + # If pretty_balance is greater than 0, send funds to deployer + if [ $(echo "$pretty_balance > 0" | bc) -eq 1 ]; then + echo "Sending $pretty_balance funds from vanguard$vanguard_index ($vanguard_address) to $DEPLOYER on $chain..." + # Subtract 0.01 ETH (10000000000000000 wei) for sepolia, 0.001 ETH (1000000000000000 wei) for ethereum, 0.0001 ETH (100000000000000 wei) for other chains + if [ "$chain" = "sepolia" ]; then + adjusted_balance=$(echo "$balance - 10000000000000000" | bc) + elif [ "$chain" = "ethereum" ]; then + adjusted_balance=$(echo "$balance - 1000000000000000" | bc) + else + adjusted_balance=$(echo "$balance - 100000000000000" | bc) + fi + if [ $(echo "$adjusted_balance > 0" | bc) -eq 1 ]; then + cast send $DEPLOYER --value $adjusted_balance --rpc-url $(rpc $ENVIRONMENT $chain) --aws + else + echo "Insufficient balance after gas cost deduction." + fi + fi + echo "------------------------------------------------------------------------------------------------------------------------------------------------------" +} + +# Set the range based on environment +if [ "$ENVIRONMENT" = "mainnet3" ]; then + RANGE="1 2 3 4 5" +else + RANGE="0 1 2 3 4 5 6 7" +fi + +# Iterate through vanguards based on environment +for i in $RANGE; do + echo "######################################################################################################################################################" + echo "Processing vanguard$i..." + + # Get AWS credentials once per vanguard + get_aws_credentials "vanguard$i" + + # Check each chain + for chain in "${CHAINS[@]}"; do + check_and_send $i $chain + done +done + +echo "Funds reclamation process completed!" From 21dd4870fb215e2b25f2fbf5e56026042e4f25ac Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 25 Apr 2025 17:04:59 +0100 Subject: [PATCH 069/223] feat: Store all payload ids for message id (#6016) ### Description * Store all payload ids for message id * Store as a vector * Extend the vector with new payload ids ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5892 ### Backward compatibility Yes ### Testing Unit test for encoding/decoding --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../agents/relayer/src/msg/op_submitter.rs | 66 +++++++++++++------ .../agents/relayer/src/msg/pending_message.rs | 4 +- rust/main/agents/relayer/src/msg/processor.rs | 4 +- rust/main/agents/validator/src/submit.rs | 4 +- rust/main/hyperlane-base/src/db/mod.rs | 8 +-- .../src/db/rocks/hyperlane_db.rs | 10 +-- rust/main/hyperlane-core/src/traits/encode.rs | 47 +++++++++++++ .../src/traits/pending_operation.rs | 2 +- 8 files changed, 110 insertions(+), 35 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index a516356287b..f66f459e917 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -18,11 +18,12 @@ use tracing::{debug, error, info, info_span, instrument, trace, warn, Instrument use hyperlane_base::db::{HyperlaneDb, HyperlaneRocksDB}; use hyperlane_base::CoreMetrics; use hyperlane_core::{ - ConfirmReason::{self, *}, - HyperlaneDomain, HyperlaneDomainProtocol, PendingOperationResult, PendingOperationStatus, - QueueOperation, ReprepareReason, + ConfirmReason, HyperlaneDomain, HyperlaneDomainProtocol, PendingOperationResult, + PendingOperationStatus, QueueOperation, ReprepareReason, +}; +use submitter::{ + Entrypoint, FullPayload, PayloadDispatcherEntrypoint, PayloadId, PayloadStatus, SubmitterError, }; -use submitter::{Entrypoint, FullPayload, PayloadDispatcherEntrypoint, PayloadId}; use crate::msg::pending_message::CONFIRM_DELAY; use crate::server::MessageRetryRequest; @@ -516,9 +517,9 @@ async fn submit_via_lander( return; } - if let Err(e) = db.store_payload_id_by_message_id(&message_id, &payload.details.id) { - let reason = ReprepareReason::ErrorStoringPayloadIdByMessageId; - let msg = "Error storing mapping from message id to payload id"; + if let Err(e) = db.store_payload_ids_by_message_id(&message_id, vec![payload.details.id]) { + let reason = ReprepareReason::ErrorStoringPayloadIdsByMessageId; + let msg = "Error storing mapping from message id to payload ids"; prepare_op(op, prepare_queue, e, msg, reason).await; return; } @@ -582,6 +583,8 @@ async fn confirm_op( confirm_queue: &OpQueue, metrics: &SerialSubmitterMetrics, ) { + use ConfirmReason::SubmittedBySelf; + let destination = op.destination_domain().clone(); debug!(?op, "Operation submitted"); op.set_next_attempt_after(CONFIRM_DELAY); @@ -668,7 +671,7 @@ async fn confirm_lander_task( .into_iter() .map(|op| { let message_id = op.id(); - (op, db.retrieve_payload_id_by_message_id(&message_id)) + (op, db.retrieve_payload_ids_by_message_id(&message_id)) }) .collect::>(); @@ -677,14 +680,24 @@ async fn confirm_lander_task( .map(|(op, result)| async { let message_id = op.id(); match result { - Ok(Some(payload_id)) => Some((op, entrypoint.payload_status(payload_id).await)), - Ok(None) | Err(_) => { + Ok(Some(ids)) if !ids.is_empty() => { + let op_futures = ids + .into_iter() + .map(|id| async { + let status = entrypoint.payload_status(id.clone()).await; + (id, status) + }) + .collect::>(); + let op_results = join_all(op_futures).await; + Some((op, op_results)) + } + Ok(Some(_)) | Ok(None) | Err(_) => { error!( ?op, %message_id, "Error retrieving payload id by message id", ); - send_back_on_failed_submisison( + send_back_on_failed_submission( op, prepare_queue.clone(), &metrics, @@ -706,10 +719,13 @@ async fn confirm_lander_task( let confirmed_operations = Arc::new(Mutex::new(0)); let confirm_futures = payload_status_results .into_iter() - .map(|(op, status_result)| async { - let Ok(payload_status) = status_result else { + .map(|(op, status_results)| async { + let status_results_len = status_results.len(); + let successes = filter_status_results(status_results); + + if status_results_len - successes.len() > 0 { warn!(?op, "Error retrieving payload status",); - send_back_on_failed_submisison( + send_back_on_failed_submission( op, prepare_queue.clone(), &metrics, @@ -717,8 +733,11 @@ async fn confirm_lander_task( ) .await; return; - }; - if payload_status.is_finalized() { + } + + let finalized = !successes.iter().any(|(_, status)| !status.is_finalized()); + + if finalized { { let mut lock = confirmed_operations.lock().await; *lock += 1; @@ -732,7 +751,7 @@ async fn confirm_lander_task( ) .await; } else { - info!(?op, ?payload_status, "Operation not finalized yet"); + info!(?op, ?successes, "Operation not finalized yet"); process_confirm_result( op, prepare_queue.clone(), @@ -753,6 +772,15 @@ async fn confirm_lander_task( } } +fn filter_status_results( + status_results: Vec<(PayloadId, Result)>, +) -> Vec<(PayloadId, PayloadStatus)> { + status_results + .into_iter() + .filter_map(|(id, result)| Some((id, result.ok()?))) + .collect::>() +} + async fn confirm_operation( mut op: QueueOperation, domain: HyperlaneDomain, @@ -790,7 +818,7 @@ async fn process_confirm_result( .await; } PendingOperationResult::Reprepare(reason) => { - send_back_on_failed_submisison(op, prepare_queue.clone(), &metrics, Some(reason)).await; + send_back_on_failed_submission(op, prepare_queue.clone(), &metrics, Some(reason)).await; } PendingOperationResult::Drop => { metrics.ops_dropped.inc(); @@ -800,7 +828,7 @@ async fn process_confirm_result( operation_result } -async fn send_back_on_failed_submisison( +async fn send_back_on_failed_submission( op: QueueOperation, prepare_queue: OpQueue, metrics: &SerialSubmitterMetrics, diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index 5f7c09b024b..ff7ea82b2d3 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -1169,8 +1169,8 @@ mod test { ) -> DbResult>; fn store_highest_seen_message_nonce_number(&self, nonce: &u32) -> DbResult<()>; fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; - fn store_payload_id_by_message_id(&self, message_id: &H256, payload_id: &UniqueIdentifier) -> DbResult<()>; - fn retrieve_payload_id_by_message_id(&self, message_id: &H256) -> DbResult>; + fn store_payload_ids_by_message_id(&self, message_id: &H256, payload_ids: Vec) -> DbResult<()>; + fn retrieve_payload_ids_by_message_id(&self, message_id: &H256) -> DbResult>>; } } diff --git a/rust/main/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs index 9b6943d0836..f874818101b 100644 --- a/rust/main/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -824,9 +824,9 @@ pub mod test { /// Retrieve the nonce of the highest processed message we're aware of fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; - fn store_payload_id_by_message_id(&self, message_id: &H256, payload_id: &UniqueIdentifier) -> DbResult<()>; + fn store_payload_ids_by_message_id(&self, message_id: &H256, payload_ids: Vec) -> DbResult<()>; - fn retrieve_payload_id_by_message_id(&self, message_id: &H256) -> DbResult>; + fn retrieve_payload_ids_by_message_id(&self, message_id: &H256) -> DbResult>>; } } diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs index f7ce326f036..3d3c1319fa4 100644 --- a/rust/main/agents/validator/src/submit.rs +++ b/rust/main/agents/validator/src/submit.rs @@ -588,8 +588,8 @@ mod test { ) -> DbResult>; fn store_highest_seen_message_nonce_number(&self, nonce: &u32) -> DbResult<()>; fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; - fn store_payload_id_by_message_id(&self, message_id: &H256, payload_id: &UniqueIdentifier) -> DbResult<()>; - fn retrieve_payload_id_by_message_id(&self, message_id: &H256) -> DbResult>; + fn store_payload_ids_by_message_id(&self, message_id: &H256, payload_ids: Vec) -> DbResult<()>; + fn retrieve_payload_ids_by_message_id(&self, message_id: &H256) -> DbResult>>; } } diff --git a/rust/main/hyperlane-base/src/db/mod.rs b/rust/main/hyperlane-base/src/db/mod.rs index 83a39386a27..338945c3f2f 100644 --- a/rust/main/hyperlane-base/src/db/mod.rs +++ b/rust/main/hyperlane-base/src/db/mod.rs @@ -162,15 +162,15 @@ pub trait HyperlaneDb: Send + Sync { fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; /// Store payload id by message id - fn store_payload_id_by_message_id( + fn store_payload_ids_by_message_id( &self, message_id: &H256, - payload_id: &UniqueIdentifier, + payloads_id: Vec, ) -> DbResult<()>; /// Retrieve payload id by message id - fn retrieve_payload_id_by_message_id( + fn retrieve_payload_ids_by_message_id( &self, message_id: &H256, - ) -> DbResult>; + ) -> DbResult>>; } diff --git a/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs index ef9cd44275d..70bb2979655 100644 --- a/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -659,18 +659,18 @@ impl HyperlaneDb for HyperlaneRocksDB { self.retrieve_value_by_key(HIGHEST_SEEN_MESSAGE_NONCE, &bool::default()) } - fn store_payload_id_by_message_id( + fn store_payload_ids_by_message_id( &self, message_id: &H256, - payload_id: &UniqueIdentifier, + payload_ids: Vec, ) -> DbResult<()> { - self.store_value_by_key(PAYLOAD_ID_BY_MESSAGE_ID, message_id, payload_id) + self.store_value_by_key(PAYLOAD_ID_BY_MESSAGE_ID, message_id, &payload_ids) } - fn retrieve_payload_id_by_message_id( + fn retrieve_payload_ids_by_message_id( &self, message_id: &H256, - ) -> DbResult> { + ) -> DbResult>> { self.retrieve_value_by_key(PAYLOAD_ID_BY_MESSAGE_ID, message_id) } } diff --git a/rust/main/hyperlane-core/src/traits/encode.rs b/rust/main/hyperlane-core/src/traits/encode.rs index bfdfc88795a..bc922c4273f 100644 --- a/rust/main/hyperlane-core/src/traits/encode.rs +++ b/rust/main/hyperlane-core/src/traits/encode.rs @@ -303,8 +303,45 @@ impl Decode for Indexed { } } +impl Encode for Vec { + fn write_to(&self, writer: &mut W) -> std::io::Result + where + W: std::io::Write, + { + let mut written = 0; + // Write the length of the vector as a u32 + written += (self.len() as u64).write_to(writer)?; + + // Write each `T` in the vector using its `Encode` implementation + written += self.iter().try_fold(0, |acc, item| { + item.write_to(writer).map(|bytes| acc + bytes) + })?; + Ok(written) + } +} + +impl Decode for Vec { + fn read_from(reader: &mut R) -> Result + where + R: std::io::Read, + { + // Read the length of the vector + let len = u64::read_from(reader)? as usize; + + // Read each `T` using its `Decode` implementation + let vec = (0..len).try_fold(vec![], |mut acc, _| { + let item = T::read_from(reader)?; + acc.push(item); + Ok::, HyperlaneProtocolError>(acc) + })?; + Ok(vec) + } +} + #[cfg(test)] mod test { + use std::io::Cursor; + use crate::{Decode, Encode, Indexed, H256}; #[test] @@ -327,4 +364,14 @@ mod test { let decoded = super::InterchainGasPayment::read_from(&mut &encoded[..]).unwrap(); assert_eq!(payment, decoded); } + + #[test] + fn test_encoding_vec_u32() { + let vec: Vec = vec![1, 2, 3, 4, 5]; + let mut buf = vec![]; + let encoded_length = vec.write_to(&mut buf).unwrap(); + let decoded = Vec::::read_from(&mut Cursor::new(buf)).unwrap(); + assert_eq!(vec, decoded); + assert_eq!(encoded_length, 8 + 4 * vec.len()); + } } diff --git a/rust/main/hyperlane-core/src/traits/pending_operation.rs b/rust/main/hyperlane-core/src/traits/pending_operation.rs index b75d599ffe5..5101c20a083 100644 --- a/rust/main/hyperlane-core/src/traits/pending_operation.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation.rs @@ -278,7 +278,7 @@ pub enum ReprepareReason { ErrorCreatingPayload, #[strum(to_string = "Failed to store payload id by message id")] /// Failed to store payload id by message id - ErrorStoringPayloadIdByMessageId, + ErrorStoringPayloadIdsByMessageId, #[strum(to_string = "Failed to retrieve payload id by message id")] /// Failed to retrieve payload id by message id ErrorRetrievingPayloadId, From 8c4a33bbc2dc213e5807ddf4e57889d6b95c93bb Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 25 Apr 2025 13:46:44 -0400 Subject: [PATCH 070/223] feat: don't default to 0 index when comparing checkpoint indexes (#6024) ### Description - instead of defaulting to 0 when comparing checkpoint indexes, we check if its None. - if it's None, then we write checkpoint, if not, then we do checkpoint index comparison ### Drive-by changes - remove GcsStorageClient override of `update_latest_index()` ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4260 ### Backward compatibility Yes/No ### Testing None --- .../hyperlane-base/src/traits/checkpoint_syncer.rs | 12 +++++++++--- rust/main/hyperlane-base/src/types/gcs_storage.rs | 10 ---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs b/rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs index 66e2999267b..a982032b210 100644 --- a/rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs +++ b/rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs @@ -14,9 +14,15 @@ pub trait CheckpointSyncer: Debug + Send + Sync { async fn write_latest_index(&self, index: u32) -> Result<()>; /// Update the latest index of this syncer if necessary async fn update_latest_index(&self, index: u32) -> Result<()> { - let curr = self.latest_index().await?.unwrap_or(0); - if index > curr { - self.write_latest_index(index).await?; + match self.latest_index().await? { + None => { + self.write_latest_index(index).await?; + } + Some(curr) => { + if index > curr { + self.write_latest_index(index).await?; + } + } } Ok(()) } diff --git a/rust/main/hyperlane-base/src/types/gcs_storage.rs b/rust/main/hyperlane-base/src/types/gcs_storage.rs index 9b8a804fab1..fd6b4ca81bb 100644 --- a/rust/main/hyperlane-base/src/types/gcs_storage.rs +++ b/rust/main/hyperlane-base/src/types/gcs_storage.rs @@ -206,16 +206,6 @@ impl CheckpointSyncer for GcsStorageClient { self.upload_and_log(LATEST_INDEX_KEY, data).await } - /// Update the latest index of this syncer if necessary - #[instrument(skip(self, index))] - async fn update_latest_index(&self, index: u32) -> Result<()> { - let curr = self.latest_index().await?.unwrap_or(0); - if index > curr { - self.write_latest_index(index).await?; - } - Ok(()) - } - /// Attempt to fetch the signed (checkpoint, messageId) tuple at this index #[instrument(skip(self, index))] async fn fetch_checkpoint(&self, index: u32) -> Result> { From ffb0cf48ed69c40787a0f6cde7895c0962e68450 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Sat, 26 Apr 2025 17:43:41 +0100 Subject: [PATCH 071/223] feat: add milkyway (#6036) ### Description feat: add milkyway ### Drive-by changes fix: price fetching from cosmos registry ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/789 ### Backward compatibility ### Testing manual --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 64 +++++++++++++++++++ .../config/environments/mainnet3/agent.ts | 11 +++- .../mainnet3/aw-validators/hyperlane.json | 3 + .../mainnet3/balances/dailyRelayerBurn.json | 1 + .../balances/desiredRelayerBalances.json | 1 + .../balances/highUrgencyRelayerBalance.json | 1 + .../lowUrgencyEngKeyFunderBalance.json | 1 + .../balances/lowUrgencyKeyFunderBalance.json | 1 + .../environments/mainnet3/gasPrices.json | 4 ++ .../config/environments/mainnet3/owners.ts | 3 + .../mainnet3/supportedChainNames.ts | 1 + .../environments/mainnet3/tokenPrices.json | 1 + .../environments/mainnet3/validators.ts | 11 ++++ typescript/sdk/src/consts/multisigIsm.ts | 10 +++ typescript/sdk/src/gas/utils.ts | 5 +- 16 files changed, 115 insertions(+), 5 deletions(-) diff --git a/.registryrc b/.registryrc index c736e2e3f9d..03cc05b4db4 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -0bb72738327f4abf3af81963ede5f0da79ae2dc1 +f83414e788906cf9bbd2f3670b115bff5bd5eda8 diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 0b9cbfbc213..c15716de24d 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -9070,6 +9070,70 @@ "index": { "from": 226924 } + }, + "milkyway": { + "bech32Prefix": "milk", + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": 1 + }, + "canonicalAsset": "umilk", + "chainId": "milkyway", + "contractAddressBytes": 32, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "MilkyWay", + "domainId": 1835625579, + "gasCurrencyCoinGeckoId": "temp-milkyway-milk", + "gasPrice": { + "denom": "umilk", + "amount": "0.03" + }, + "grpcUrls": [ + { + "http": "https://grpc.mainnet.milkyway.zone/" + } + ], + "index": { + "chunk": 10, + "from": 2454618 + }, + "name": "milkyway", + "nativeToken": { + "decimals": 6, + "denom": "umilk", + "name": "MILK", + "symbol": "MILK" + }, + "protocol": "cosmosnative", + "restUrls": [ + { + "http": "https://lcd.mainnet.milkyway.zone/" + } + ], + "rpcUrls": [ + { + "http": "https://rpc.mainnet.milkyway.zone/" + } + ], + "signer": { + "key": "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26", + "prefix": "milk", + "type": "cosmosKey" + }, + "slip44": 118, + "technicalStack": "other", + "interchainGasPaymaster": "0x726f757465725f706f73745f6469737061746368000000040000000000000000", + "interchainSecurityModule": "0x726f757465725f69736d00000000000000000000000000010000000000000000", + "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", + "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000001", + "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000", + "transactionOverrides": { + "gasPrice": "0.03" + } } }, "defaultRpcConsensusType": "fallback" diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 24efa5c1eb3..aa7f186b046 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -139,6 +139,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< merlin: true, metal: true, metis: true, + milkyway: true, mint: true, mode: true, molten: true, @@ -279,6 +280,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< merlin: true, metal: true, metis: true, + milkyway: true, mint: true, mode: true, molten: true, @@ -419,6 +421,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< merlin: true, metal: true, metis: true, + milkyway: true, mint: true, mode: true, molten: true, @@ -583,6 +586,8 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ { originDomain: getDomainId('infinityvm') }, // Temporary workaround due to funky Zeronetwork gas amounts. { destinationDomain: getDomainId('zeronetwork') }, + // Temporary workaround during testing of MilkyWay. + { originDomain: getDomainId('milkyway') }, // Temporary workaround for some high gas amount estimates on Treasure ...warpRouteMatchingList(WarpRouteIds.ArbitrumTreasureMAGIC), ], @@ -840,7 +845,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: '385b307-20250418-150728', + tag: '62073e3-20250426-080512', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -851,7 +856,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '8ab7c80-20250326-191115', + tag: '62073e3-20250426-080512', }, resources: scraperResources, }, @@ -866,7 +871,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '24fe342-20250424-164437', + tag: '62073e3-20250426-080512', }, blacklist: [...blacklist, ...vanguardMatchingList], // We're temporarily (ab)using the RC relayer as a way to increase diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index f7e3eee1272..15ed37dd927 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -251,6 +251,9 @@ "metis": { "validators": ["0xc4a3d25107060e800a43842964546db508092260"] }, + "milkyway": { + "validators": ["0x9985e0c6df8e25b655b46a317af422f5e7756875"] + }, "mint": { "validators": ["0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9"] }, diff --git a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json index 0ae62de58ae..ee5e84912f4 100644 --- a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json +++ b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json @@ -72,6 +72,7 @@ "merlin": 0.000172, "metal": 0.00791, "metis": 0.229, + "milkyway": 3.13, "mint": 0.00272, "mode": 0.0229, "molten": 24.4, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index 7b83a2b9a42..92c1ee7303c 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -72,6 +72,7 @@ "merlin": 0.002, "metal": 0.0633, "metis": 3, + "milkyway": 25, "mint": 0.05, "mode": 0.2, "molten": 195, diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index a57e55ab69b..a63c91da30c 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -71,6 +71,7 @@ "merlin": 0.000344, "metal": 0.0158, "metis": 1, + "milkyway": 6.26, "mint": 0.00544, "mode": 0.0458, "molten": 48.8, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index d19b9f5f158..e6c69a531dd 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -71,6 +71,7 @@ "merlin": 0.00103, "metal": 0.0475, "metis": 2.5, + "milkyway": 18.8, "mint": 0.0163, "mode": 0.137, "molten": 146, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index 007b0aa1ca5..c798b612809 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -71,6 +71,7 @@ "merlin": 0.003, "metal": 0.1, "metis": 6, + "milkyway": 37.6, "mint": 0.1, "mode": 0.4, "molten": 293, diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index bc265a7db57..9f418e22531 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -291,6 +291,10 @@ "amount": "3.76552424", "decimals": 9 }, + "milkyway": { + "amount": "0.03", + "decimals": 1 + }, "mint": { "amount": "0.001000252", "decimals": 9 diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index b10d98a4c55..092b2378d7e 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -84,6 +84,9 @@ export const chainOwners: ChainMap = { osmosis: { owner: 'n/a - nothing owned here', }, + milkyway: { + owner: 'TODO: configure milkyway owner', + }, soon: { // Squads vault owner: 'E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62', diff --git a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts index 30c8c5bf692..0ec17fc3b0e 100644 --- a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts +++ b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts @@ -76,6 +76,7 @@ export const mainnet3SupportedChainNames = [ 'merlin', 'metal', 'metis', + 'milkyway', 'mint', 'mode', 'molten', diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index b5a3096bdd9..06046488e83 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -72,6 +72,7 @@ "merlin": "85091", "metal": "1599.53", "metis": "13.62", + "milkyway": "1", "mint": "1599.53", "mode": "1599.53", "molten": "0.127992", diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index 8da0262591c..f0bdf5983fc 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -1698,5 +1698,16 @@ export const validatorChainConfig = ( 'reactive', ), }, + + milkyway: { + interval: 5, + reorgPeriod: getReorgPeriod('milkyway'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x9985e0c6df8e25b655b46a317af422f5e7756875'], + }, + 'milkyway', + ), + }, }; }; diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index 65e75d5ab9e..d7fd9e8b2e5 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -1404,6 +1404,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + milkyway: { + threshold: 1, + validators: [ + { + address: '0x9985e0c6df8e25b655b46a317af422f5e7756875', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + mint: { threshold: 2, validators: [ diff --git a/typescript/sdk/src/gas/utils.ts b/typescript/sdk/src/gas/utils.ts index 22397095bcb..142ce0a0061 100644 --- a/typescript/sdk/src/gas/utils.ts +++ b/typescript/sdk/src/gas/utils.ts @@ -74,7 +74,10 @@ export async function getCosmosChainGasPrice( if (!metadata) { throw new Error(`No metadata found for Cosmos chain ${chain}`); } - if (metadata.protocol !== ProtocolType.Cosmos) { + if ( + metadata.protocol !== ProtocolType.Cosmos && + metadata.protocol !== ProtocolType.CosmosNative + ) { throw new Error(`Chain ${chain} is not a Cosmos chain`); } From 5db39f4932177a918429610ee08bfcf9d2acd7d6 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Sat, 26 Apr 2025 17:54:29 +0100 Subject: [PATCH 072/223] feat: cosmosnative fixes (#6039) ### Description feat: cosmosnative fixes - correctly skip non-EVM chains when doing a warp apply - adjust the `ZHash` zod regex to support cosmos addresses more explicitly - required because setting valid cosmos addresses such as `milk1326ley07fm6rpeqgxmxevnqevrsjfew2akzupg` or `neutron1fqf5mprg3f5hytvzp3t7spmsum6rjrw80mq8zgkc0h6rxga0dtzqws3uu7` as an owner in the warp deploy config would break the schema parsing ### Drive-by changes - bring `CosmosNative` parity in more conditions where `Cosmos` protocol type exists - don't fail balance thresholds test if an alert threshold is configured in grafana but undefined locally - this is common when doing new chain deploys, and we should not be breaking CI for everyone else just because 1 PR adds a new chain ### Related issues ### Backward compatibility ### Testing manual cli warp apply of the MILK/bsc-milkyway route --- .changeset/twenty-eggs-rescue.md | 6 ++++++ typescript/cli/src/deploy/warp.ts | 5 +++++ typescript/infra/test/balance-alerts.test.ts | 20 ++++++++++++++++++- typescript/sdk/src/consts/igp.ts | 2 ++ typescript/sdk/src/contracts/contracts.ts | 1 + typescript/sdk/src/gas/utils.ts | 3 ++- typescript/sdk/src/metadata/agentConfig.ts | 11 ++++++++-- .../sdk/src/metadata/chainMetadataTypes.ts | 12 +++++++---- typescript/sdk/src/metadata/customZodTypes.ts | 2 +- 9 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 .changeset/twenty-eggs-rescue.md diff --git a/.changeset/twenty-eggs-rescue.md b/.changeset/twenty-eggs-rescue.md new file mode 100644 index 00000000000..07e7d715df4 --- /dev/null +++ b/.changeset/twenty-eggs-rescue.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +Fixes to support CosmosNative and warp apply with foreign deployments. diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 5175a5fbedc..a094877c2ed 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -652,6 +652,11 @@ async function updateExistingWarpRoute( await promiseObjAll( objMap(expandedWarpDeployConfig, async (chain, config) => { + if (multiProvider.getProtocol(chain) !== ProtocolType.Ethereum) { + logBlue(`Skipping non-EVM chain ${chain}`); + return; + } + await retryAsync(async () => { const deployedTokenRoute = deployedRoutersAddresses[chain]; assert(deployedTokenRoute, `Missing artifacts for ${chain}.`); diff --git a/typescript/infra/test/balance-alerts.test.ts b/typescript/infra/test/balance-alerts.test.ts index 9533116eb81..570e9c157f0 100644 --- a/typescript/infra/test/balance-alerts.test.ts +++ b/typescript/infra/test/balance-alerts.test.ts @@ -24,6 +24,7 @@ describe('Balance Alert Thresholds', async function () { try { saToken = await fetchGrafanaServiceAccountToken(); } catch (error) { + // eslint-disable-next-line no-console console.log( 'Error fetching grafana service account token, skipping test', error, @@ -32,6 +33,7 @@ describe('Balance Alert Thresholds', async function () { } const alertsToCheck = Object.values(AlertType); const mismatches: string[] = []; + const warnings: string[] = []; for (const alert of alertsToCheck) { // Fetch alert rule from Grafana @@ -62,7 +64,15 @@ describe('Balance Alert Thresholds', async function () { const current = currentThresholds[chain]; const proposed = proposedThresholds[chain]; - if (current !== proposed) { + if (current === undefined) { + warnings.push( + `${alert} - ${chain}: threshold exists in config but not in Grafana`, + ); + } else if (proposed === undefined) { + warnings.push( + `${alert} - ${chain}: threshold exists in Grafana (${current}) but not in config`, + ); + } else if (current !== proposed) { mismatches.push( `${alert} - ${chain}: current=${current}, proposed=${proposed}`, ); @@ -70,6 +80,14 @@ describe('Balance Alert Thresholds', async function () { } } + if (warnings.length > 0) { + // eslint-disable-next-line no-console + console.warn( + 'Found thresholds that exist in one place but not the other:\n' + + warnings.join('\n'), + ); + } + if (mismatches.length > 0) { expect.fail( 'Found mismatches between Grafana alerts and config files:\n' + diff --git a/typescript/sdk/src/consts/igp.ts b/typescript/sdk/src/consts/igp.ts index 9dcc662399a..8e547ebe66f 100644 --- a/typescript/sdk/src/consts/igp.ts +++ b/typescript/sdk/src/consts/igp.ts @@ -35,6 +35,8 @@ export function getProtocolExchangeRateDecimals( return TOKEN_EXCHANGE_RATE_DECIMALS_SEALEVEL; case ProtocolType.Cosmos: return TOKEN_EXCHANGE_RATE_DECIMALS_COSMOS; + case ProtocolType.CosmosNative: + return TOKEN_EXCHANGE_RATE_DECIMALS_COSMOS; default: throw new Error(`Unsupported protocol type: ${protocolType}`); } diff --git a/typescript/sdk/src/contracts/contracts.ts b/typescript/sdk/src/contracts/contracts.ts index c0d331f6be0..204eee4b494 100644 --- a/typescript/sdk/src/contracts/contracts.ts +++ b/typescript/sdk/src/contracts/contracts.ts @@ -188,6 +188,7 @@ export function attachContractsMapAndGetForeignDeployments< throw new Error('Ethereum chain should not have foreign deployments'); case ProtocolType.Cosmos: + case ProtocolType.CosmosNative: return router; case ProtocolType.Sealevel: diff --git a/typescript/sdk/src/gas/utils.ts b/typescript/sdk/src/gas/utils.ts index 142ce0a0061..09ad8b6156f 100644 --- a/typescript/sdk/src/gas/utils.ts +++ b/typescript/sdk/src/gas/utils.ts @@ -47,7 +47,8 @@ export async function getGasPrice( decimals: 9, }; } - case ProtocolType.Cosmos: { + case ProtocolType.Cosmos: + case ProtocolType.CosmosNative: { const { amount } = await getCosmosChainGasPrice(chain, mpp); return { amount, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 6ca87dda348..9df383cc757 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -235,6 +235,7 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( break; case ProtocolType.Cosmos: + case ProtocolType.CosmosNative: if (![AgentSignerKeyType.Cosmos].includes(signerType)) { return false; } @@ -251,7 +252,10 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( } // If the protocol type is Cosmos, require everything in AgentCosmosChainMetadataSchema - if (metadata.protocol === ProtocolType.Cosmos) { + if ( + metadata.protocol === ProtocolType.Cosmos || + metadata.protocol === ProtocolType.CosmosNative + ) { if (!AgentCosmosChainMetadataSchema.safeParse(metadata).success) { return false; } @@ -548,7 +552,10 @@ export function buildAgentConfig( const chainConfigs: ChainMap = {}; for (const chain of [...chains].sort()) { const metadata = multiProvider.tryGetChainMetadata(chain); - if (metadata?.protocol === ProtocolType.Cosmos) { + if ( + metadata?.protocol === ProtocolType.Cosmos || + metadata?.protocol === ProtocolType.CosmosNative + ) { // 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; diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index 76d9415eff3..3ec922e1b01 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -336,7 +336,8 @@ export const ChainMetadataSchema = ChainMetadataSchemaExtensible.refine( ) return false; else if ( - metadata.protocol === ProtocolType.Cosmos && + (metadata.protocol === ProtocolType.Cosmos || + metadata.protocol === ProtocolType.CosmosNative) && typeof metadata.chainId !== 'string' ) return false; @@ -355,7 +356,8 @@ export const ChainMetadataSchema = ChainMetadataSchemaExtensible.refine( .refine( (metadata) => { if ( - metadata.protocol === ProtocolType.Cosmos && + (metadata.protocol === ProtocolType.Cosmos || + metadata.protocol === ProtocolType.CosmosNative) && (!metadata.bech32Prefix || !metadata.slip44) ) return false; @@ -369,7 +371,8 @@ export const ChainMetadataSchema = ChainMetadataSchemaExtensible.refine( .refine( (metadata) => { if ( - metadata.protocol === ProtocolType.Cosmos && + (metadata.protocol === ProtocolType.Cosmos || + metadata.protocol === ProtocolType.CosmosNative) && (!metadata.restUrls || !metadata.grpcUrls) ) return false; @@ -383,7 +386,8 @@ export const ChainMetadataSchema = ChainMetadataSchemaExtensible.refine( .refine( (metadata) => { if ( - metadata.protocol === ProtocolType.Cosmos && + (metadata.protocol === ProtocolType.Cosmos || + metadata.protocol === ProtocolType.CosmosNative) && metadata.nativeToken && !metadata.nativeToken.denom ) diff --git a/typescript/sdk/src/metadata/customZodTypes.ts b/typescript/sdk/src/metadata/customZodTypes.ts index bfabed48664..b70ca0f7d6a 100644 --- a/typescript/sdk/src/metadata/customZodTypes.ts +++ b/typescript/sdk/src/metadata/customZodTypes.ts @@ -14,7 +14,7 @@ export const ZUWei = z.union([ZUint.safe(), z.string().regex(/^\d+$/)]); export const ZHash = z .string() .regex( - /^(0x([0-9a-fA-F]{32}|[0-9a-fA-F]{40}|[0-9a-fA-F]{64}|[0-9a-fA-F]{128}))|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{32})$/, + /^(0x([0-9a-fA-F]{32}|[0-9a-fA-F]{40}|[0-9a-fA-F]{64}|[0-9a-fA-F]{128}))|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{32})|([a-z]{1,10}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38,58})$/, ); /** Zod ChainName schema */ export const ZChainName = z.string().regex(/^[a-z][a-z0-9]*$/); From 2edb767f21ff8d063a0fdede7b0d5e2cd5244732 Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:27:50 +0200 Subject: [PATCH 073/223] chore: cosmos addresses (#6022) ### Description This PR improves the way cosmos addresses are displayed. In cosmos there is like in Ethereum a private public key pair, but for every cosmos chain there is a different address prefix. Example Osmosis `osmo1sduu...`, Cosmos Hub `cosmos1j48f33..`, KYVE `kyve1jhf738...` and so on. Right now always the address of the first chain gets displayed which often has nothing to do with the chain the user has funds on and wants to swap from/to. This has caused confusion in the past. This PR addresses this issue by showing the Cosmos Hub address by default if the user is connected with a cosmos wallet since Cosmos Hub is still the standard in the cosmos ecosystem. Once the user selects a cosmos origin chain that address is displayed instead. This PR can be tested by checking out the Warp UI PR https://github.com/hyperlane-xyz/hyperlane-warp-ui-template/pull/536 ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual Testing with Warp UI --------- Co-authored-by: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> --- .changeset/soft-mails-drive.md | 5 +++ .../src/walletIntegrations/AccountList.tsx | 17 ++++--- .../ConnectWalletButton.tsx | 25 ++++++----- .../src/walletIntegrations/multiProtocol.tsx | 44 ++++++++++++++++++- 4 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 .changeset/soft-mails-drive.md diff --git a/.changeset/soft-mails-drive.md b/.changeset/soft-mails-drive.md new file mode 100644 index 00000000000..88976191e76 --- /dev/null +++ b/.changeset/soft-mails-drive.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/widgets': minor +--- + +improve how cosmos addresses are displayed diff --git a/typescript/widgets/src/walletIntegrations/AccountList.tsx b/typescript/widgets/src/walletIntegrations/AccountList.tsx index 63d08baada8..3bfdfcf51dd 100644 --- a/typescript/widgets/src/walletIntegrations/AccountList.tsx +++ b/typescript/widgets/src/walletIntegrations/AccountList.tsx @@ -1,7 +1,7 @@ import { clsx } from 'clsx'; import React, { ButtonHTMLAttributes } from 'react'; -import { MultiProtocolProvider } from '@hyperlane-xyz/sdk'; +import { ChainName, MultiProtocolProvider } from '@hyperlane-xyz/sdk'; import { ProtocolType, objKeys } from '@hyperlane-xyz/utils'; import { Button } from '../components/Button.js'; @@ -13,6 +13,7 @@ import { widgetLogger } from '../logger.js'; import { tryClipboardSet } from '../utils/clipboard.js'; import { WalletLogo } from '../walletIntegrations/WalletLogo.js'; import { + getAddressFromAccountAndChain, useAccounts, useDisconnectFns, useWalletDetails, @@ -27,11 +28,13 @@ export function AccountList({ onClickConnectWallet, onCopySuccess, className, + chainName, }: { multiProvider: MultiProtocolProvider; onClickConnectWallet: () => void; onCopySuccess?: () => void; className?: string; + chainName?: string; }) { const { readyAccounts } = useAccounts(multiProvider); const disconnectFns = useDisconnectFns(); @@ -61,6 +64,7 @@ export function AccountList({ walletDetails={walletDetails[acc.protocol]} onCopySuccess={onCopySuccess} onClickDisconnect={() => onClickDisconnect(acc.protocol)} + chainName={chainName} /> ))} diff --git a/typescript/widgets/src/walletIntegrations/ConnectWalletButton.tsx b/typescript/widgets/src/walletIntegrations/ConnectWalletButton.tsx index 1d64e9cf70b..e11cb5dbec9 100644 --- a/typescript/widgets/src/walletIntegrations/ConnectWalletButton.tsx +++ b/typescript/widgets/src/walletIntegrations/ConnectWalletButton.tsx @@ -1,7 +1,7 @@ import { clsx } from 'clsx'; import React, { ButtonHTMLAttributes } from 'react'; -import { MultiProtocolProvider } from '@hyperlane-xyz/sdk'; +import { ChainName, MultiProtocolProvider } from '@hyperlane-xyz/sdk'; import { ProtocolType, shortenAddress } from '@hyperlane-xyz/utils'; import { Button } from '../components/Button.js'; @@ -10,13 +10,18 @@ import { WalletIcon } from '../icons/Wallet.js'; import { useIsSsr } from '../utils/ssr.js'; import { WalletLogo } from './WalletLogo.js'; -import { useAccounts, useWalletDetails } from './multiProtocol.js'; +import { + getAddressFromAccountAndChain, + useAccounts, + useWalletDetails, +} from './multiProtocol.js'; type Props = { multiProvider: MultiProtocolProvider; onClickWhenConnected: () => void; onClickWhenUnconnected: () => void; countClassName?: string; + chainName?: ChainName; } & ButtonHTMLAttributes; export function ConnectWalletButton({ @@ -25,6 +30,7 @@ export function ConnectWalletButton({ onClickWhenUnconnected, className, countClassName, + chainName, ...rest }: Props) { const isSsr = useIsSsr(); @@ -34,6 +40,12 @@ export function ConnectWalletButton({ const numReady = readyAccounts.length; const firstAccount = readyAccounts[0]; + + const shownAddress = shortenAddress( + getAddressFromAccountAndChain(firstAccount, chainName), + true, + ); + const firstWallet = walletDetails[firstAccount?.protocol || ProtocolType.Ethereum]; @@ -71,14 +83,7 @@ export function ConnectWalletButton({
{firstWallet.name || 'Wallet'}
-
- {readyAccounts[0].addresses.length - ? shortenAddress( - readyAccounts[0].addresses[0].address, - true, - ) - : 'Unknown'} -
+
{shownAddress}
diff --git a/typescript/widgets/src/walletIntegrations/multiProtocol.tsx b/typescript/widgets/src/walletIntegrations/multiProtocol.tsx index 80ac26dc30d..5bbff73a63b 100644 --- a/typescript/widgets/src/walletIntegrations/multiProtocol.tsx +++ b/typescript/widgets/src/walletIntegrations/multiProtocol.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react'; +import { cosmoshub } from '@hyperlane-xyz/registry'; import { ChainName, MultiProtocolProvider } from '@hyperlane-xyz/sdk'; import { Address, HexString, ProtocolType } from '@hyperlane-xyz/utils'; @@ -127,7 +128,10 @@ export function getAccountAddressForChain( if (!chainName || !accounts) return undefined; const protocol = multiProvider.getProtocol(chainName); const account = accounts[protocol]; - if (protocol === ProtocolType.Cosmos) { + if ( + protocol === ProtocolType.Cosmos || + protocol === ProtocolType.CosmosNative + ) { return account?.addresses.find((a) => a.chainName === chainName)?.address; } else { // Use first because only cosmos has the notion of per-chain addresses @@ -135,6 +139,44 @@ export function getAccountAddressForChain( } } +export function getAddressFromAccountAndChain( + account?: AccountInfo, + chainName?: ChainName, +) { + if (!account || !chainName) { + return 'Unknown'; + } + + // by default display the first address of the account + let address = account.addresses[0]?.address ?? 'Unknown'; + + // only in cosmos there are multiple addresses per account, in this + // case we display the cosmos hub address by default. If the user + // selects a cosmos based origin chain in the swap form that cosmos + // address is displayed instead + if (account?.protocol === ProtocolType.Cosmos) { + // chainName can be an EVM chain here, therefore if no + // cosmos address was found we search for the cosmos hub + // address below + const cosmosAddress = account?.addresses?.find( + (a) => a.chainName === chainName, + )?.address; + + if (cosmosAddress) { + // set cosmos address if one has been found for the chain name + address = cosmosAddress; + } else { + // search for the cosmos hub address if no address has been found + // for the chain name + address = + account?.addresses?.find((a) => a.chainName === cosmoshub.name) + ?.address ?? 'Unknown'; + } + } + + return address; +} + export function getAccountAddressAndPubKey( multiProvider: MultiProtocolProvider, chainName?: ChainName, From 7500bd6fe2c81b546458336ba76a6d06d4537b39 Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:40:12 +0200 Subject: [PATCH 074/223] feat: cosmos token adapter (#5888) ### Description This PR implements the newly added Protocol Type `cosmosnative` and adds the Cosmos Token Adapter. Furthermore, this PR has been tested with the Hyperlane warp ui template to verify if everything works. Important decisions which should be reviewed: - Protocol Type Cosmos stays `cosmosnative` - Provider Type Cosmos is `cosmjs-native` - Token Standard for Cosmos Hyp Collateral is `CosmosNativeHypCollateral` - Token Standard for Cosmos Hyp Synthetic is `CosmosNativeHypSynthetic` ### Drive-by changes Added new protocol type to typescript/utils and typescript/widgets ### Related issues ### Backward compatibility Yes ### Testing Manual testing by locally linking the hyperlane sdk with the hyperlane warp ui template: https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/troy/cosmos-token-adapter. Can be reproduced by checking out the following branch and replacing the links in the package.json with your local hyperlane monorepo path. --- .changeset/wicked-phones-develop.md | 7 + typescript/sdk/package.json | 3 +- typescript/sdk/src/app/MultiProtocolApp.ts | 9 + typescript/sdk/src/core/MultiProtocolCore.ts | 2 + .../core/adapters/CosmNativeCoreAdapter.ts | 94 ++++++ .../src/metadata/chainMetadataConversion.ts | 38 ++- .../sdk/src/metadata/chainMetadataTypes.ts | 9 + .../src/providers/MultiProtocolProvider.ts | 10 + typescript/sdk/src/providers/ProviderType.ts | 33 ++- .../sdk/src/providers/providerBuilders.ts | 16 +- typescript/sdk/src/providers/rpcHealthTest.ts | 9 +- .../src/providers/transactionFeeEstimators.ts | 46 +++ typescript/sdk/src/token/Token.test.ts | 3 + typescript/sdk/src/token/Token.ts | 12 + typescript/sdk/src/token/TokenStandard.ts | 19 +- .../adapters/CosmosModuleTokenAdapter.ts | 273 ++++++++++++++++++ typescript/sdk/src/warp/WarpCore.ts | 5 +- typescript/utils/src/addresses.ts | 8 + typescript/widgets/package.json | 2 + .../widgets/src/walletIntegrations/cosmos.ts | 29 +- yarn.lock | 97 ++++--- 21 files changed, 658 insertions(+), 66 deletions(-) create mode 100644 .changeset/wicked-phones-develop.md create mode 100644 typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts create mode 100644 typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts diff --git a/.changeset/wicked-phones-develop.md b/.changeset/wicked-phones-develop.md new file mode 100644 index 00000000000..a36bc6c8253 --- /dev/null +++ b/.changeset/wicked-phones-develop.md @@ -0,0 +1,7 @@ +--- +'@hyperlane-xyz/widgets': minor +'@hyperlane-xyz/utils': minor +'@hyperlane-xyz/sdk': minor +--- + +implemented cosmos protocol type and cosmos token adapter diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 1b09219ed96..0dd6fbb9695 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -5,10 +5,11 @@ "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", - "@chain-registry/types": "^0.50.14", + "@chain-registry/types": "^0.50.122", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", "@hyperlane-xyz/core": "7.1.1", + "@hyperlane-xyz/cosmos-sdk": "1.0.0", "@hyperlane-xyz/utils": "12.2.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", diff --git a/typescript/sdk/src/app/MultiProtocolApp.ts b/typescript/sdk/src/app/MultiProtocolApp.ts index 6444d68a164..8f2baa322e2 100644 --- a/typescript/sdk/src/app/MultiProtocolApp.ts +++ b/typescript/sdk/src/app/MultiProtocolApp.ts @@ -12,6 +12,7 @@ import { import { ChainMetadata } from '../metadata/chainMetadataTypes.js'; import { MultiProtocolProvider } from '../providers/MultiProtocolProvider.js'; import { + CosmJsNativeProvider, CosmJsProvider, CosmJsWasmProvider, EthersV5Provider, @@ -68,6 +69,14 @@ export class BaseCosmosAdapter extends BaseAppAdapter { } } +export class BaseCosmNativeAdapter extends BaseAppAdapter { + public readonly protocol: ProtocolType = ProtocolType.CosmosNative; + + public getProvider(): CosmJsNativeProvider['provider'] { + return this.multiProvider.getCosmJsNativeProvider(this.chainName); + } +} + export class BaseSealevelAdapter extends BaseAppAdapter { public readonly protocol: ProtocolType = ProtocolType.Sealevel; diff --git a/typescript/sdk/src/core/MultiProtocolCore.ts b/typescript/sdk/src/core/MultiProtocolCore.ts index 560567b727b..f230d4c7b68 100644 --- a/typescript/sdk/src/core/MultiProtocolCore.ts +++ b/typescript/sdk/src/core/MultiProtocolCore.ts @@ -5,6 +5,7 @@ import { MultiProtocolProvider } from '../providers/MultiProtocolProvider.js'; import { TypedTransactionReceipt } from '../providers/ProviderType.js'; import { ChainMap, ChainName } from '../types.js'; +import { CosmNativeCoreAdapter } from './adapters/CosmNativeCoreAdapter.js'; import { CosmWasmCoreAdapter } from './adapters/CosmWasmCoreAdapter.js'; import { EvmCoreAdapter } from './adapters/EvmCoreAdapter.js'; import { SealevelCoreAdapter } from './adapters/SealevelCoreAdapter.js'; @@ -39,6 +40,7 @@ export class MultiProtocolCore extends MultiProtocolApp< if (protocol === ProtocolType.Ethereum) return EvmCoreAdapter; if (protocol === ProtocolType.Sealevel) return SealevelCoreAdapter; if (protocol === ProtocolType.Cosmos) return CosmWasmCoreAdapter; + if (protocol === ProtocolType.CosmosNative) return CosmNativeCoreAdapter; throw new Error(`No adapter for protocol ${protocol}`); } diff --git a/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts b/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts new file mode 100644 index 00000000000..2991764f143 --- /dev/null +++ b/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts @@ -0,0 +1,94 @@ +import { + Address, + HexString, + assert, + ensure0x, + messageId, + pollAsync, +} from '@hyperlane-xyz/utils'; + +import { BaseCosmosAdapter } from '../../app/MultiProtocolApp.js'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; +import { + ProviderType, + TypedTransactionReceipt, +} from '../../providers/ProviderType.js'; +import { ChainName } from '../../types.js'; + +import { ICoreAdapter } from './types.js'; + +const MESSAGE_DISPATCH_EVENT_TYPE = 'hyperlane.core.v1.Dispatch'; +const MESSAGE_ATTRIBUTE_KEY = 'message'; +const MESSAGE_DESTINATION_ATTRIBUTE_KEY = 'destination'; + +export class CosmNativeCoreAdapter + extends BaseCosmosAdapter + implements ICoreAdapter +{ + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { mailbox: Address }, + ) { + super(chainName, multiProvider, addresses); + } + + extractMessageIds( + sourceTx: TypedTransactionReceipt, + ): Array<{ messageId: string; destination: ChainName }> { + assert( + sourceTx.type === ProviderType.CosmJsNative, + `Unsupported provider type for CosmNativeCoreAdapter ${sourceTx.type}`, + ); + + const dispatchEvents = sourceTx.receipt.events.filter( + (e) => e.type === MESSAGE_DISPATCH_EVENT_TYPE, + ); + + return dispatchEvents.map((event) => { + const findAttribute = (key: string) => + event.attributes.find((a) => a.key === key); + + const messageAttribute = findAttribute(MESSAGE_ATTRIBUTE_KEY); + const destAttribute = findAttribute(MESSAGE_DESTINATION_ATTRIBUTE_KEY); + + assert(messageAttribute, 'No message attribute found in dispatch event'); + assert(destAttribute, 'No destination attribute found in dispatch event'); + + return { + messageId: ensure0x(messageId(messageAttribute.value)), + destination: this.multiProvider.getChainName(destAttribute.value), + }; + }); + } + + async waitForMessageProcessed( + messageId: HexString, + destination: ChainName, + delayMs?: number, + maxAttempts?: number, + ): Promise { + const provider = await this.multiProvider.getCosmJsNativeProvider( + destination, + ); + + await pollAsync( + async () => { + this.logger.debug(`Checking if message ${messageId} was processed`); + const { delivered } = await provider.query.core.Delivered({ + id: this.addresses.mailbox, + message_id: messageId, + }); + + assert(delivered, `Message ${messageId} not yet processed`); + + this.logger.info(`Message ${messageId} was processed`); + return delivered; + }, + delayMs, + maxAttempts, + ); + + return true; + } +} diff --git a/typescript/sdk/src/metadata/chainMetadataConversion.ts b/typescript/sdk/src/metadata/chainMetadataConversion.ts index 15cd34ee466..08e6fe8d1f4 100644 --- a/typescript/sdk/src/metadata/chainMetadataConversion.ts +++ b/typescript/sdk/src/metadata/chainMetadataConversion.ts @@ -48,6 +48,7 @@ export function chainMetadataToCosmosChain(metadata: ChainMetadata): { nativeToken, bech32Prefix, slip44, + gasPrice, } = metadata; if (!nativeToken) throw new Error(`Missing native token for ${name}`); @@ -68,10 +69,22 @@ export function chainMetadataToCosmosChain(metadata: ChainMetadata): { : [], }, fees: { - fee_tokens: [{ denom: 'token' }], + fee_tokens: [ + { + denom: gasPrice?.denom ?? nativeToken.denom!, + ...(gasPrice?.amount + ? { + fixed_min_gas_price: parseInt(gasPrice.amount), + low_gas_price: parseInt(gasPrice.amount), + average_gas_price: parseInt(gasPrice.amount) * 1.5, + high_gas_price: parseInt(gasPrice.amount) * 3, + } + : {}), + }, + ], }, staking: { - staking_tokens: [{ denom: 'stake' }], + staking_tokens: [{ denom: nativeToken.denom! }], }, }; @@ -80,20 +93,13 @@ export function chainMetadataToCosmosChain(metadata: ChainMetadata): { assets: [ { description: `The native token of ${displayName || name} chain.`, - denom_units: [{ denom: 'token', exponent: nativeToken.decimals }], - base: 'token', - name: 'token', - display: 'token', - symbol: 'token', - type_asset: 'sdk.coin', - }, - { - description: `The native token of ${displayName || name} chain.`, - denom_units: [{ denom: 'token', exponent: nativeToken.decimals }], - base: 'stake', - name: 'stake', - display: 'stake', - symbol: 'stake', + denom_units: [ + { denom: nativeToken.denom!, exponent: nativeToken.decimals }, + ], + base: nativeToken.denom!, + name: nativeToken.name, + display: nativeToken.denom!, + symbol: nativeToken.symbol, type_asset: 'sdk.coin', }, ], diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index 3ec922e1b01..319711a3c8c 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -134,6 +134,11 @@ export const NativeTokenSchema = z.object({ denom: z.string().optional(), }); +export const GasPriceSchema = z.object({ + denom: z.string(), + amount: z.string(), +}); + export const DisabledChainSchema = z.object({ status: z .literal(ChainStatus.Disabled) @@ -320,6 +325,10 @@ export const ChainMetadataSchemaObject = z.object({ .record(z.any()) .optional() .describe('Properties to include when forming transaction requests.'), + + gasPrice: GasPriceSchema.optional().describe( + 'The gas price of Cosmos chains.', + ), }); // Passthrough allows for extra fields to remain in the object (such as extensions consumers may want like `mailbox`) diff --git a/typescript/sdk/src/providers/MultiProtocolProvider.ts b/typescript/sdk/src/providers/MultiProtocolProvider.ts index 6148f76a000..ffcecd9fe59 100644 --- a/typescript/sdk/src/providers/MultiProtocolProvider.ts +++ b/typescript/sdk/src/providers/MultiProtocolProvider.ts @@ -17,6 +17,7 @@ import type { ChainMap, ChainName, ChainNameOrId } from '../types.js'; import { MultiProvider, MultiProviderOptions } from './MultiProvider.js'; import { + CosmJsNativeProvider, CosmJsProvider, CosmJsWasmProvider, EthersV5Provider, @@ -205,6 +206,15 @@ export class MultiProtocolProvider< ); } + getCosmJsNativeProvider( + chainNameOrId: ChainNameOrId, + ): CosmJsNativeProvider['provider'] { + return this.getSpecificProvider( + chainNameOrId, + ProviderType.CosmJsNative, + ); + } + setProvider( chainNameOrId: ChainNameOrId, provider: TypedProvider, diff --git a/typescript/sdk/src/providers/ProviderType.ts b/typescript/sdk/src/providers/ProviderType.ts index 83dc465fe53..44b0c2dfb97 100644 --- a/typescript/sdk/src/providers/ProviderType.ts +++ b/typescript/sdk/src/providers/ProviderType.ts @@ -34,6 +34,7 @@ import { types as zkSyncTypes, } from 'zksync-ethers'; +import { HyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; import { Annotated, ProtocolType } from '@hyperlane-xyz/utils'; export enum ProviderType { @@ -41,6 +42,7 @@ export enum ProviderType { Viem = 'viem', SolanaWeb3 = 'solana-web3', CosmJs = 'cosmjs', + CosmJsNative = 'cosmjs-native', CosmJsWasm = 'cosmjs-wasm', GnosisTxBuilder = 'gnosis-txBuilder', Starknet = 'starknet', @@ -54,7 +56,7 @@ export const PROTOCOL_TO_DEFAULT_PROVIDER_TYPE: Record< [ProtocolType.Ethereum]: ProviderType.EthersV5, [ProtocolType.Sealevel]: ProviderType.SolanaWeb3, [ProtocolType.Cosmos]: ProviderType.CosmJsWasm, - [ProtocolType.CosmosNative]: ProviderType.CosmJsWasm, + [ProtocolType.CosmosNative]: ProviderType.CosmJsNative, [ProtocolType.Starknet]: ProviderType.Starknet, }; @@ -80,10 +82,10 @@ type ProtocolTypesMapping = { receipt: CosmJsWasmTransactionReceipt; }; [ProtocolType.CosmosNative]: { - transaction: CosmJsWasmTransaction; - provider: CosmJsWasmProvider; - contract: CosmJsWasmContract; - receipt: CosmJsWasmTransactionReceipt; + transaction: CosmJsNativeTransaction; + provider: CosmJsNativeProvider; + contract: null; + receipt: CosmJsNativeTransactionReceipt; }; [ProtocolType.Starknet]: { transaction: StarknetJsTransaction; @@ -152,6 +154,12 @@ export interface CosmJsWasmProvider provider: Promise; } +export interface CosmJsNativeProvider + extends TypedProviderBase> { + type: ProviderType.CosmJsNative; + provider: Promise; +} + export interface StarknetJsProvider extends TypedProviderBase { type: ProviderType.Starknet; @@ -170,6 +178,7 @@ export type TypedProvider = | SolanaWeb3Provider | CosmJsProvider | CosmJsWasmProvider + | CosmJsNativeProvider | StarknetJsProvider | ZKSyncProvider; @@ -270,6 +279,12 @@ export interface CosmJsWasmTransaction transaction: ExecuteInstruction; } +export interface CosmJsNativeTransaction + extends TypedTransactionBase { + type: ProviderType.CosmJsNative; + transaction: CmTransaction; +} + export interface StarknetJsTransaction extends TypedTransactionBase { type: ProviderType.Starknet; @@ -289,6 +304,7 @@ export type TypedTransaction = | SolanaWeb3Transaction | CosmJsTransaction | CosmJsWasmTransaction + | CosmJsNativeTransaction | StarknetJsTransaction | ZKSyncTransaction; @@ -331,6 +347,12 @@ export interface CosmJsWasmTransactionReceipt receipt: DeliverTxResponse; } +export interface CosmJsNativeTransactionReceipt + extends TypedTransactionReceiptBase { + type: ProviderType.CosmJsNative; + receipt: DeliverTxResponse; +} + export interface StarknetJsTransactionReceipt extends TypedTransactionReceiptBase { type: ProviderType.Starknet; @@ -349,5 +371,6 @@ export type TypedTransactionReceipt = | SolanaWeb3TransactionReceipt | CosmJsTransactionReceipt | CosmJsWasmTransactionReceipt + | CosmJsNativeTransactionReceipt | StarknetJsTransactionReceipt | ZKSyncTransactionReceipt; diff --git a/typescript/sdk/src/providers/providerBuilders.ts b/typescript/sdk/src/providers/providerBuilders.ts index 74107faafd5..fbb0522bd87 100644 --- a/typescript/sdk/src/providers/providerBuilders.ts +++ b/typescript/sdk/src/providers/providerBuilders.ts @@ -6,11 +6,13 @@ import { RpcProvider as StarknetRpcProvider } from 'starknet'; import { createPublicClient, http } from 'viem'; import { Provider as ZKProvider } from 'zksync-ethers'; +import { HyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; import { ProtocolType, assert, isNumeric } from '@hyperlane-xyz/utils'; import { ChainMetadata, RpcUrl } from '../metadata/chainMetadataTypes.js'; import { + CosmJsNativeProvider, CosmJsProvider, CosmJsWasmProvider, EthersV5Provider, @@ -113,6 +115,17 @@ export function defaultCosmJsWasmProviderBuilder( }; } +export function defaultCosmJsNativeProviderBuilder( + rpcUrls: RpcUrl[], + _network: number | string, +): CosmJsNativeProvider { + if (!rpcUrls.length) throw new Error('No RPC URLs provided'); + return { + type: ProviderType.CosmJsNative, + provider: HyperlaneModuleClient.connect(rpcUrls[0].http), + }; +} + export function defaultStarknetJsProviderBuilder( rpcUrls: RpcUrl[], ): StarknetJsProvider { @@ -158,6 +171,7 @@ export const defaultProviderBuilderMap: ProviderBuilderMap = { [ProviderType.SolanaWeb3]: defaultSolProviderBuilder, [ProviderType.CosmJs]: defaultCosmJsProviderBuilder, [ProviderType.CosmJsWasm]: defaultCosmJsWasmProviderBuilder, + [ProviderType.CosmJsNative]: defaultCosmJsNativeProviderBuilder, [ProviderType.Starknet]: defaultStarknetJsProviderBuilder, [ProviderType.ZkSync]: defaultZKSyncProviderBuilder, }; @@ -169,6 +183,6 @@ export const protocolToDefaultProviderBuilder: Record< [ProtocolType.Ethereum]: defaultEthersV5ProviderBuilder, [ProtocolType.Sealevel]: defaultSolProviderBuilder, [ProtocolType.Cosmos]: defaultCosmJsWasmProviderBuilder, - [ProtocolType.CosmosNative]: defaultCosmJsWasmProviderBuilder, + [ProtocolType.CosmosNative]: defaultCosmJsNativeProviderBuilder, [ProtocolType.Starknet]: defaultStarknetJsProviderBuilder, }; diff --git a/typescript/sdk/src/providers/rpcHealthTest.ts b/typescript/sdk/src/providers/rpcHealthTest.ts index 47b9ece24ad..57b8e4579dc 100644 --- a/typescript/sdk/src/providers/rpcHealthTest.ts +++ b/typescript/sdk/src/providers/rpcHealthTest.ts @@ -4,6 +4,7 @@ import { Address, rootLogger } from '@hyperlane-xyz/utils'; import { ChainMetadata } from '../metadata/chainMetadataTypes.js'; import { + CosmJsNativeProvider, CosmJsProvider, CosmJsWasmProvider, EthersV5Provider, @@ -25,7 +26,8 @@ export async function isRpcHealthy( return isSolanaWeb3ProviderHealthy(provider.provider, metadata); else if ( provider.type === ProviderType.CosmJsWasm || - provider.type === ProviderType.CosmJs + provider.type === ProviderType.CosmJs || + provider.type === ProviderType.CosmJsNative ) return isCosmJsProviderHealthy(provider.provider, metadata); else @@ -74,7 +76,10 @@ export async function isSolanaWeb3ProviderHealthy( } export async function isCosmJsProviderHealthy( - provider: CosmJsProvider['provider'] | CosmJsWasmProvider['provider'], + provider: + | CosmJsProvider['provider'] + | CosmJsWasmProvider['provider'] + | CosmJsNativeProvider['provider'], metadata: ChainMetadata, ): Promise { const readyProvider = await provider; diff --git a/typescript/sdk/src/providers/transactionFeeEstimators.ts b/typescript/sdk/src/providers/transactionFeeEstimators.ts index f689fe78af0..56fa4885e74 100644 --- a/typescript/sdk/src/providers/transactionFeeEstimators.ts +++ b/typescript/sdk/src/providers/transactionFeeEstimators.ts @@ -11,6 +11,8 @@ import { Address, HexString, Numberish, assert } from '@hyperlane-xyz/utils'; import { ChainMetadata } from '../metadata/chainMetadataTypes.js'; import { + CosmJsNativeProvider, + CosmJsNativeTransaction, CosmJsProvider, CosmJsTransaction, CosmJsWasmProvider, @@ -222,6 +224,35 @@ export async function estimateTransactionFeeCosmJsWasm({ }); } +export async function estimateTransactionFeeCosmJsNative({ + transaction, + provider, + estimatedGasPrice, + sender, + senderPubKey, + memo, +}: { + transaction: CosmJsNativeTransaction; + provider: CosmJsNativeProvider; + estimatedGasPrice: Numberish; + sender: Address; + senderPubKey: HexString; + memo?: string; +}): Promise { + const client = await provider.provider; + const message = client.registry.encodeAsAny(transaction.transaction); + const pubKey = encodeSecp256k1Pubkey(Buffer.from(senderPubKey, 'hex')); + + const gasUnits = await client.simulate(sender, pubKey, [message], memo); + const gasPrice = parseFloat(estimatedGasPrice.toString()); + + return { + gasUnits, + gasPrice, + fee: Math.floor(gasUnits * gasPrice), + }; +} + export function estimateTransactionFee({ transaction, provider, @@ -280,6 +311,21 @@ export function estimateTransactionFee({ sender, senderPubKey, }); + } else if ( + transaction.type === ProviderType.CosmJsNative && + provider.type === ProviderType.CosmJsNative + ) { + const { transactionOverrides } = chainMetadata; + const estimatedGasPrice = transactionOverrides?.gasPrice as Numberish; + assert(estimatedGasPrice, 'gasPrice required for CosmJS gas estimation'); + assert(senderPubKey, 'senderPubKey required for CosmJS gas estimation'); + return estimateTransactionFeeCosmJsNative({ + transaction, + provider, + estimatedGasPrice, + sender, + senderPubKey, + }); } else { throw new Error( `Unsupported transaction type ${transaction.type} or provider type ${provider.type} for gas estimation`, diff --git a/typescript/sdk/src/token/Token.test.ts b/typescript/sdk/src/token/Token.test.ts index e5f26d4abf5..d4bd4daef26 100644 --- a/typescript/sdk/src/token/Token.test.ts +++ b/typescript/sdk/src/token/Token.test.ts @@ -217,6 +217,9 @@ const STANDARD_TO_TOKEN: Record = { }, [TokenStandard.CwHypSynthetic]: null, + [TokenStandard.CosmNativeHypCollateral]: null, + [TokenStandard.CosmNativeHypSynthetic]: null, + //TODO: check this and manage it. [TokenStandard.StarknetHypCollateral]: null, [TokenStandard.StarknetHypNative]: null, diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts index 3c5f3a55363..a09be1b85aa 100644 --- a/typescript/sdk/src/token/Token.ts +++ b/typescript/sdk/src/token/Token.ts @@ -33,6 +33,10 @@ import { CwNativeTokenAdapter, CwTokenAdapter, } from './adapters/CosmWasmTokenAdapter.js'; +import { + CosmNativeHypCollateralAdapter, + CosmNativeHypSyntheticAdapter, +} from './adapters/CosmosModuleTokenAdapter.js'; import { CosmIbcToWarpTokenAdapter, CosmIbcTokenAdapter, @@ -284,6 +288,14 @@ export class Token implements IToken { const connection = this.getConnectionForChain(destination); assert(connection, `No connection found for chain ${destination}`); return this.getIbcAdapter(multiProvider, connection); + } else if (standard === TokenStandard.CosmNativeHypCollateral) { + return new CosmNativeHypCollateralAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.CosmNativeHypSynthetic) { + return new CosmNativeHypSyntheticAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); } else { throw new Error(`No hyp adapter found for token standard: ${standard}`); } diff --git a/typescript/sdk/src/token/TokenStandard.ts b/typescript/sdk/src/token/TokenStandard.ts index 6f1efec3d9b..536ba85e1f5 100644 --- a/typescript/sdk/src/token/TokenStandard.ts +++ b/typescript/sdk/src/token/TokenStandard.ts @@ -46,6 +46,10 @@ export enum TokenStandard { CwHypCollateral = 'CwHypCollateral', CwHypSynthetic = 'CwHypSynthetic', + // Cosmos Native + CosmNativeHypCollateral = 'CosmosNativeHypCollateral', + CosmNativeHypSynthetic = 'CosmosNativeHypSynthetic', + //Starknet StarknetHypNative = 'StarknetHypNative', StarknetHypCollateral = 'StarknetHypCollateral', @@ -84,6 +88,10 @@ export const TOKEN_STANDARD_TO_PROTOCOL: Record = { CosmosNative: ProtocolType.Cosmos, CosmosIbc: ProtocolType.Cosmos, + // Cosmos Native + CosmosNativeHypCollateral: ProtocolType.CosmosNative, + CosmosNativeHypSynthetic: ProtocolType.CosmosNative, + // CosmWasm CW20: ProtocolType.Cosmos, CWNative: ProtocolType.Cosmos, @@ -102,7 +110,13 @@ export const TOKEN_STANDARD_TO_PROVIDER_TYPE: Record< TokenStandard, ProviderType > = objMap(TOKEN_STANDARD_TO_PROTOCOL, (k, v) => { - if (k.startsWith('Cosmos')) return ProviderType.CosmJs; + if (k.startsWith('CosmosNative')) { + return ProviderType.CosmJsNative; + } + if (k.startsWith('Cosmos')) { + return ProviderType.CosmJs; + } + return PROTOCOL_TO_DEFAULT_PROVIDER_TYPE[v]; }); @@ -120,6 +134,7 @@ export const TOKEN_COLLATERALIZED_STANDARDS = [ TokenStandard.SealevelHypNative, TokenStandard.CwHypCollateral, TokenStandard.CwHypNative, + TokenStandard.CosmNativeHypCollateral, TokenStandard.EvmHypXERC20Lockbox, TokenStandard.EvmHypVSXERC20Lockbox, ]; @@ -156,6 +171,8 @@ export const TOKEN_HYP_STANDARDS = [ TokenStandard.CwHypNative, TokenStandard.CwHypCollateral, TokenStandard.CwHypSynthetic, + TokenStandard.CosmNativeHypCollateral, + TokenStandard.CosmNativeHypSynthetic, TokenStandard.StarknetHypNative, TokenStandard.StarknetHypCollateral, TokenStandard.StarknetHypSynthetic, diff --git a/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts new file mode 100644 index 00000000000..c90b7803f6b --- /dev/null +++ b/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts @@ -0,0 +1,273 @@ +import { MsgSendEncodeObject } from '@cosmjs/stargate'; + +import { MsgRemoteTransferEncodeObject } from '@hyperlane-xyz/cosmos-sdk'; +import { + Address, + Domain, + ProtocolType, + addressToBytes32, + convertToProtocolAddress, + isAddressCosmos, +} from '@hyperlane-xyz/utils'; + +import { BaseCosmNativeAdapter } from '../../app/MultiProtocolApp.js'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; +import { ChainName } from '../../types.js'; +import { PROTOCOL_TO_DEFAULT_NATIVE_TOKEN } from '../nativeTokenMetadata.js'; +import { TokenMetadata } from '../types.js'; + +import { + IHypTokenAdapter, + ITokenAdapter, + InterchainGasQuote, + TransferParams, + TransferRemoteParams, +} from './ITokenAdapter.js'; + +const COSMOS_TYPE_URL_SEND = '/cosmos.bank.v1beta1.MsgSend'; + +class CosmosModuleTokenAdapter + extends BaseCosmNativeAdapter + implements ITokenAdapter +{ + // use getter so Tokens which extend this base class + // can overwrite this denom + protected async getDenom(): Promise { + return this.properties.denom; + } + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: Record, + public readonly properties: { + denom: string; + }, + ) { + if (!properties.denom) { + throw new Error('Missing properties for CosmNativeTokenAdapter'); + } + + super(chainName, multiProvider, addresses); + } + + async getBalance(address: string): Promise { + const provider = await this.getProvider(); + const denom = await this.getDenom(); + + if (isAddressCosmos(address)) { + const coin = await provider.getBalance(address, denom); + return BigInt(coin.amount); + } else { + const { bridged_supply } = await provider.query.warp.BridgedSupply({ + id: address, + }); + return BigInt(bridged_supply?.amount ?? '0'); + } + } + + async getMetadata(): Promise { + const token = await this.multiProvider.getNativeToken(this.chainName); + + return { + symbol: token.symbol, + name: token.name, + decimals: token.decimals, + }; + } + + async getMinimumTransferAmount(_recipient: Address): Promise { + return 0n; + } + + async isApproveRequired(): Promise { + return false; + } + + populateApproveTx( + _transferParams: TransferParams, + ): Promise { + throw new Error('Approve not required for native tokens'); + } + + async isRevokeApprovalRequired(_: Address, __: Address): Promise { + return false; + } + + async populateTransferTx( + transferParams: TransferParams, + ): Promise { + const denom = await this.getDenom(); + return { + typeUrl: COSMOS_TYPE_URL_SEND, + value: { + fromAddress: transferParams.fromAccountOwner, + toAddress: transferParams.recipient, + amount: [ + { + denom, + amount: transferParams.weiAmountOrId.toString(), + }, + ], + }, + }; + } + + async getTotalSupply(): Promise { + const provider = await this.getProvider(); + const denom = await this.getDenom(); + const supply = await provider.query.bank.supplyOf(denom); + return BigInt(supply.amount); + } +} + +export class CosmNativeHypCollateralAdapter + extends CosmosModuleTokenAdapter + implements + IHypTokenAdapter +{ + protected tokenId: string; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address }, + ) { + super(chainName, multiProvider, addresses, { + denom: PROTOCOL_TO_DEFAULT_NATIVE_TOKEN[ProtocolType.CosmosNative].denom!, + }); + this.tokenId = addresses.token; + } + + protected async getDenom(): Promise { + const provider = await this.getProvider(); + const { token } = await provider.query.warp.Token({ id: this.tokenId }); + + return token?.origin_denom ?? ''; + } + + async getDomains(): Promise { + const provider = await this.getProvider(); + const remoteRouters = await provider.query.warp.RemoteRouters({ + id: this.tokenId, + }); + + return remoteRouters.remote_routers.map((router) => router.receiver_domain); + } + + async getRouterAddress(domain: Domain): Promise { + const provider = await this.getProvider(); + const remoteRouters = await provider.query.warp.RemoteRouters({ + id: this.tokenId, + }); + + const router = remoteRouters.remote_routers.find( + (router) => router.receiver_domain === domain, + ); + + if (!router) { + throw new Error(`Router with domain "${domain}" not found`); + } + + return Buffer.from(router.receiver_contract); + } + + async getAllRouters(): Promise> { + const provider = await this.getProvider(); + const remoteRouters = await provider.query.warp.RemoteRouters({ + id: this.tokenId, + }); + + return remoteRouters.remote_routers.map((router) => ({ + domain: router.receiver_domain, + address: Buffer.from(router.receiver_contract), + })); + } + + async getBridgedSupply(): Promise { + const provider = await this.getProvider(); + const { bridged_supply } = await provider.query.warp.BridgedSupply({ + id: this.tokenId, + }); + + if (!bridged_supply) { + return undefined; + } + + return BigInt(bridged_supply.amount); + } + + async quoteTransferRemoteGas( + destination: Domain, + _sender?: Address, + ): Promise { + const provider = await this.getProvider(); + const { gas_payment } = await provider.query.warp.QuoteRemoteTransfer({ + id: this.tokenId, + destination_domain: destination.toString(), + }); + + return { + addressOrDenom: gas_payment[0].denom, + amount: BigInt(gas_payment[0].amount), + }; + } + + async populateTransferRemoteTx( + params: TransferRemoteParams, + ): Promise { + if (!params.interchainGas) { + params.interchainGas = await this.quoteTransferRemoteGas( + params.destination, + ); + } + + const provider = await this.getProvider(); + + const { remote_routers } = await provider.query.warp.RemoteRouters({ + id: this.tokenId, + }); + + const router = remote_routers.find( + (router) => router.receiver_domain === params.destination, + ); + + if (!router) { + throw new Error( + `Failed to find remote router for token id and destination: ${this.tokenId},${params.destination}`, + ); + } + + if (!params.interchainGas.addressOrDenom) { + throw new Error( + `Require denom for max fee, didn't receive and denom in the interchainGas quote`, + ); + } + + const msg: MsgRemoteTransferEncodeObject = { + typeUrl: '/hyperlane.warp.v1.MsgRemoteTransfer', + value: { + sender: params.fromAccountOwner, + recipient: addressToBytes32( + convertToProtocolAddress(params.recipient, ProtocolType.Ethereum), + ProtocolType.Ethereum, + ), + amount: params.weiAmountOrId.toString(), + token_id: this.tokenId, + destination_domain: params.destination, + gas_limit: router.gas, + max_fee: { + denom: params.interchainGas.addressOrDenom || '', + amount: params.interchainGas.amount.toString(), + }, + }, + }; + return msg; + } +} + +export class CosmNativeHypSyntheticAdapter extends CosmNativeHypCollateralAdapter { + protected async getTokenDenom(): Promise { + return `hyperlane/${this.tokenId}`; + } +} diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index a3d06f84e3c..28e94115f4e 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -672,7 +672,10 @@ export class WarpCore { return { recipient: 'Invalid recipient' }; // Also ensure the address denom is correct if the dest protocol is Cosmos - if (protocol === ProtocolType.Cosmos) { + if ( + protocol === ProtocolType.Cosmos || + protocol === ProtocolType.CosmosNative + ) { if (!bech32Prefix) { this.logger.error(`No bech32 prefix found for chain ${destination}`); return { destination: 'Invalid chain data' }; diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index 63c28cf5e08..d6a4f92c8a4 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -118,6 +118,7 @@ export function isValidAddress(address: Address, protocol?: ProtocolType) { [ProtocolType.Ethereum]: isValidAddressEvm, [ProtocolType.Sealevel]: isValidAddressSealevel, [ProtocolType.Cosmos]: isValidAddressCosmos, + [ProtocolType.CosmosNative]: isValidAddressCosmos, }, address, false, @@ -158,6 +159,7 @@ export function normalizeAddress(address: Address, protocol?: ProtocolType) { [ProtocolType.Ethereum]: normalizeAddressEvm, [ProtocolType.Sealevel]: normalizeAddressSealevel, [ProtocolType.Cosmos]: normalizeAddressCosmos, + [ProtocolType.CosmosNative]: normalizeAddressCosmos, }, address, address, @@ -186,6 +188,7 @@ export function eqAddress(a1: Address, a2: Address) { [ProtocolType.Ethereum]: (_a1) => eqAddressEvm(_a1, a2), [ProtocolType.Sealevel]: (_a1) => eqAddressSol(_a1, a2), [ProtocolType.Cosmos]: (_a1) => eqAddressCosmos(_a1, a2), + [ProtocolType.CosmosNative]: (_a1) => eqAddressCosmos(_a1, a2), }, a1, false, @@ -212,6 +215,8 @@ export function isValidTransactionHash(input: string, protocol: ProtocolType) { return isValidTransactionHashSealevel(input); } else if (protocol === ProtocolType.Cosmos) { return isValidTransactionHashCosmos(input); + } else if (protocol === ProtocolType.CosmosNative) { + return isValidTransactionHashCosmos(input); } else { return false; } @@ -275,6 +280,7 @@ export function addressToBytes( [ProtocolType.Ethereum]: addressToBytesEvm, [ProtocolType.Sealevel]: addressToBytesSol, [ProtocolType.Cosmos]: addressToBytesCosmos, + [ProtocolType.CosmosNative]: addressToBytesCosmos, }, address, new Uint8Array(), @@ -358,6 +364,8 @@ export function bytesToProtocolAddress( return bytesToAddressSol(bytes); } else if (toProtocol === ProtocolType.Cosmos) { return bytesToAddressCosmos(bytes, prefix!); + } else if (toProtocol === ProtocolType.CosmosNative) { + return bytesToAddressCosmos(bytes, prefix!); } else { throw new Error(`Unsupported protocol for address ${toProtocol}`); } diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index c4b55522222..49cbb29a3b2 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -7,8 +7,10 @@ "react-dom": "^18" }, "dependencies": { + "@cosmjs/stargate": "^0.32.4", "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", + "@hyperlane-xyz/cosmos-sdk": "1.0.0", "@hyperlane-xyz/sdk": "12.2.0", "@hyperlane-xyz/utils": "12.2.0", "@interchain-ui/react": "^1.23.28", diff --git a/typescript/widgets/src/walletIntegrations/cosmos.ts b/typescript/widgets/src/walletIntegrations/cosmos.ts index 7b54c61cf1d..5ce233d31fb 100644 --- a/typescript/widgets/src/walletIntegrations/cosmos.ts +++ b/typescript/widgets/src/walletIntegrations/cosmos.ts @@ -4,9 +4,11 @@ import type { ExecuteResult, IndexedTx, } from '@cosmjs/cosmwasm-stargate'; +import { GasPrice } from '@cosmjs/stargate'; import { useChain, useChains } from '@cosmos-kit/react'; import { useCallback, useMemo } from 'react'; +import { SigningHyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; import { cosmoshub } from '@hyperlane-xyz/registry'; import { ChainMetadata, @@ -134,8 +136,12 @@ export function useCosmosTransactionFns( await onSwitchNetwork(chainName); logger.debug(`Sending tx on chain ${chainName}`); - const { getSigningCosmWasmClient, getSigningStargateClient } = - chainContext; + const { + getSigningCosmWasmClient, + getSigningStargateClient, + getOfflineSigner, + chain, + } = chainContext; let result: ExecuteResult | DeliverTxResponse; let txDetails: IndexedTx | null; if (tx.type === ProviderType.CosmJsWasm) { @@ -152,6 +158,24 @@ export function useCosmosTransactionFns( // It seems the signAndBroadcast method uses a default fee multiplier of 1.4 // https://github.com/cosmos/cosmjs/blob/e819a1fc0e99a3e5320d8d6667a08d3b92e5e836/packages/stargate/src/signingstargateclient.ts#L115 // A multiplier of 1.6 was insufficient for Celestia -> Neutron|Cosmos -> XXX transfers, but 2 worked. + result = await client.signAndBroadcast( + chainContext.address, + [tx.transaction], + 2, + ); + txDetails = await client.getTx(result.transactionHash); + } else if (tx.type === ProviderType.CosmJsNative) { + const signer = getOfflineSigner(); + const client = await SigningHyperlaneModuleClient.connectWithSigner( + chain.apis!.rpc![0].address, + signer, + { + // set zero gas price here so it does not error. actual gas price + // will be injected from the wallet registry like Keplr or Leap + gasPrice: GasPrice.fromString('0token'), + }, + ); + result = await client.signAndBroadcast( chainContext.address, [tx.transaction], @@ -182,6 +206,7 @@ function getCosmosChains( ): ChainMetadata[] { return [ ...getChainsForProtocol(multiProvider, ProtocolType.Cosmos), + ...getChainsForProtocol(multiProvider, ProtocolType.CosmosNative), cosmoshub, ]; } diff --git a/yarn.lock b/yarn.lock index 887f5fae3db..f1718aec09b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4339,6 +4339,13 @@ __metadata: languageName: node linkType: hard +"@chain-registry/types@npm:^0.50.122": + version: 0.50.122 + resolution: "@chain-registry/types@npm:0.50.122" + checksum: 10/c416f737f40cf4180e6f05bd86d5a636a2549f3947ba4f2c9208a12895597a658047cb017775bca5a4f947b6be9b6fa5785c2fed8dc2ef53a64eaff90ede8a28 + languageName: node + linkType: hard + "@chain-registry/types@npm:^0.50.13": version: 0.50.13 resolution: "@chain-registry/types@npm:0.50.13" @@ -4346,13 +4353,6 @@ __metadata: languageName: node linkType: hard -"@chain-registry/types@npm:^0.50.14": - version: 0.50.14 - resolution: "@chain-registry/types@npm:0.50.14" - checksum: 10/4a1e6978de66d50f98a804174fb8bfa712e42284473edc9e85e6c308dac3fcf381d1888248579128e7a78cec7539378bf7a149b20654741daa85b1c88b8beb8c - languageName: node - linkType: hard - "@chain-registry/utils@npm:^1.51.13": version: 1.51.13 resolution: "@chain-registry/utils@npm:1.51.13" @@ -5276,9 +5276,9 @@ __metadata: languageName: node linkType: hard -"@cosmos-kit/core@npm:^2.15.0": - version: 2.15.0 - resolution: "@cosmos-kit/core@npm:2.15.0" +"@cosmos-kit/core@npm:^2.16.0": + version: 2.16.0 + resolution: "@cosmos-kit/core@npm:2.16.0" dependencies: "@chain-registry/client": "npm:^1.49.11" "@chain-registry/keplr": "npm:^1.69.13" @@ -5287,48 +5287,48 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.3" "@cosmjs/proto-signing": "npm:^0.32.3" "@cosmjs/stargate": "npm:^0.32.3" - "@dao-dao/cosmiframe": "npm:^0.1.0" + "@dao-dao/cosmiframe": "npm:^1.0.0-rc.1" "@walletconnect/types": "npm:2.11.0" bowser: "npm:2.11.0" cosmjs-types: "npm:^0.9.0" events: "npm:3.3.0" nock: "npm:13.5.4" uuid: "npm:^9.0.1" - checksum: 10/fe13203a71390cbbcb014454ab28c9440281fdeedb23f8bca0984c0b756c4049d0716e104be7eeae809726ebf55bb1a67fc92cd3159e19b9d864177e0bf83215 + checksum: 10/6c516ce5f03581047d5163ec690fca21ac0bdc20f9264aa33874eb8443a18a5e61f635bfa6a82f1bfddf142f2aabda69e7ceb23ef49e1ce690fcf10432f77c80 languageName: node linkType: hard -"@cosmos-kit/react-lite@npm:^2.15.1": - version: 2.15.1 - resolution: "@cosmos-kit/react-lite@npm:2.15.1" +"@cosmos-kit/react-lite@npm:^2.16.0": + version: 2.16.0 + resolution: "@cosmos-kit/react-lite@npm:2.16.0" dependencies: "@chain-registry/types": "npm:^0.46.11" - "@cosmos-kit/core": "npm:^2.15.0" - "@dao-dao/cosmiframe": "npm:^0.1.0" + "@cosmos-kit/core": "npm:^2.16.0" + "@dao-dao/cosmiframe": "npm:^1.0.0-rc.1" peerDependencies: - "@types/react": ">= 17" - "@types/react-dom": ">= 17" - react: ^18 - react-dom: ^18 - checksum: 10/3b3c3b7f9fea92c65d1fbb5585d397b09a06fb0a91089a6e4960259fa43d96fdf85ad4d285512df33373ee35fb85e487dace701700afb846960e505e795d80cb + "@types/react": "*" + "@types/react-dom": "*" + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10/a5a9ef8ec6164bdfe1b0bb306ab0790270b0b65f8a0d43390db0fd00606dbe93835cf321ca69c258f8f0a06f03705d3536610267c97f6bc4039df09b785a0932 languageName: node linkType: hard "@cosmos-kit/react@npm:^2.18.0": - version: 2.20.1 - resolution: "@cosmos-kit/react@npm:2.20.1" + version: 2.22.0 + resolution: "@cosmos-kit/react@npm:2.22.0" dependencies: "@chain-registry/types": "npm:^0.46.11" - "@cosmos-kit/core": "npm:^2.15.0" - "@cosmos-kit/react-lite": "npm:^2.15.1" + "@cosmos-kit/core": "npm:^2.16.0" + "@cosmos-kit/react-lite": "npm:^2.16.0" "@react-icons/all-files": "npm:^4.1.0" peerDependencies: - "@interchain-ui/react": ^1.23.9 - "@types/react": ">= 17" - "@types/react-dom": ">= 17" - react: ^18 - react-dom: ^18 - checksum: 10/dc66aedf2cf485262cac805b128f49cf8466d9e16916c242684e47468b5b9f7dec94d396926ae4be808532446864de48987311578d4fd1343d4abdf4876205d2 + "@interchain-ui/react": ^1.26.3 + "@types/react": "*" + "@types/react-dom": "*" + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10/92fdb00e1b43cb1959663cbec26ee29c9b1c19fcdee8b4585ffd897178cfe610e8d69969d3cee8753fa1866a89580883ee3fffe7196d15dbaabc81c8888d610a languageName: node linkType: hard @@ -5341,15 +5341,15 @@ __metadata: languageName: node linkType: hard -"@dao-dao/cosmiframe@npm:^0.1.0": - version: 0.1.0 - resolution: "@dao-dao/cosmiframe@npm:0.1.0" +"@dao-dao/cosmiframe@npm:^1.0.0-rc.1": + version: 1.0.0-rc.1 + resolution: "@dao-dao/cosmiframe@npm:1.0.0-rc.1" dependencies: uuid: "npm:^9.0.1" peerDependencies: "@cosmjs/amino": "*" "@cosmjs/proto-signing": "*" - checksum: 10/7a53a9047b3deecf1ad9b9aa80467d7d9e91f7187bfdd97b8fde77cdfaf4e828f9c9a85cacfb8f13ebe9ad30f578d1e652c7afc4dcd13b30f04440ff50c3a1d0 + checksum: 10/5dcb6c1d654a184e489269bf942906acd82108f0dfc5d89aba165e0700c8cd314f6a3938d1080d7c73e0abf7ea6d8309b7d2f0c3b05c60107fb41927d51c052f languageName: node linkType: hard @@ -7739,6 +7739,16 @@ __metadata: languageName: unknown linkType: soft +"@hyperlane-xyz/cosmos-sdk@npm:1.0.0": + version: 1.0.0 + resolution: "@hyperlane-xyz/cosmos-sdk@npm:1.0.0" + dependencies: + "@cosmjs/stargate": "npm:^0.32.4" + "@hyperlane-xyz/cosmos-types": "npm:11.0.0" + checksum: 10/86218b58b7559d934b02740b5c575c00b8574d1ed41471871ce8be648c8838b507bd911c52d5b69833691febfb7d26b88e011828c454ab64ac3c0a90b40e3cd9 + languageName: node + linkType: hard + "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk" @@ -7761,6 +7771,16 @@ __metadata: languageName: unknown linkType: soft +"@hyperlane-xyz/cosmos-types@npm:11.0.0": + version: 11.0.0 + resolution: "@hyperlane-xyz/cosmos-types@npm:11.0.0" + dependencies: + long: "npm:^5.2.4" + protobufjs: "npm:^7.4.0" + checksum: 10/18bf66a72cf0fb84d2d612f0b7f859675ea9aa9a9d5d5eea9e5698bd1f016cffcdf53ad17a015cc668f8d715ab8abaccd35bc0ad25d09cf9b8b69449b2372cb8 + languageName: node + linkType: hard + "@hyperlane-xyz/cosmos-types@npm:12.2.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" @@ -7931,11 +7951,12 @@ __metadata: dependencies: "@arbitrum/sdk": "npm:^4.0.0" "@aws-sdk/client-s3": "npm:^3.577.0" - "@chain-registry/types": "npm:^0.50.14" + "@chain-registry/types": "npm:^0.50.122" "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" "@hyperlane-xyz/core": "npm:7.1.1" + "@hyperlane-xyz/cosmos-sdk": "npm:1.0.0" "@hyperlane-xyz/utils": "npm:12.2.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -8017,11 +8038,13 @@ __metadata: dependencies: "@chakra-ui/react": "npm:^2.8.2" "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" + "@cosmjs/stargate": "npm:^0.32.4" "@cosmos-kit/react": "npm:^2.18.0" "@emotion/react": "npm:^11.13.3" "@emotion/styled": "npm:^11.13.0" "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" + "@hyperlane-xyz/cosmos-sdk": "npm:1.0.0" "@hyperlane-xyz/registry": "npm:11.1.0" "@hyperlane-xyz/sdk": "npm:12.2.0" "@hyperlane-xyz/utils": "npm:12.2.0" From 737b8a2b1d710a8e4c0923a5617c60d44d9d960a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 18:36:38 +0000 Subject: [PATCH 075/223] Version Packages (#6018) 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@12.3.0 ### Minor Changes - 5db39f493: Fixes to support CosmosNative and warp apply with foreign deployments. ## @hyperlane-xyz/sdk@12.3.0 ### Minor Changes - 6101959f7: Enhanced the router enrollment check to support non-fully connected warp routes using the `remoteRouters` property from the deployment config. - 5db39f493: Fixes to support CosmosNative and warp apply with foreign deployments. - 7500bd6fe: implemented cosmos protocol type and cosmos token adapter ### Patch Changes - Updated dependencies [7500bd6fe] - @hyperlane-xyz/utils@12.3.0 - @hyperlane-xyz/core@7.1.2 - @hyperlane-xyz/cosmos-sdk@12.3.0 ## @hyperlane-xyz/utils@12.3.0 ### Minor Changes - 7500bd6fe: implemented cosmos protocol type and cosmos token adapter ## @hyperlane-xyz/widgets@12.3.0 ### Minor Changes - 2edb767f2: improve how cosmos addresses are displayed - 7500bd6fe: implemented cosmos protocol type and cosmos token adapter ### Patch Changes - Updated dependencies [6101959f7] - Updated dependencies [5db39f493] - Updated dependencies [7500bd6fe] - @hyperlane-xyz/sdk@12.3.0 - @hyperlane-xyz/utils@12.3.0 - @hyperlane-xyz/cosmos-sdk@12.3.0 ## @hyperlane-xyz/core@7.1.2 ### Patch Changes - Updated dependencies [7500bd6fe] - @hyperlane-xyz/utils@12.3.0 ## @hyperlane-xyz/cosmos-sdk@12.3.0 ### Patch Changes - @hyperlane-xyz/cosmos-types@12.3.0 ## @hyperlane-xyz/helloworld@12.3.0 ### Patch Changes - Updated dependencies [6101959f7] - Updated dependencies [5db39f493] - Updated dependencies [7500bd6fe] - @hyperlane-xyz/sdk@12.3.0 - @hyperlane-xyz/core@7.1.2 ## @hyperlane-xyz/cosmos-types@12.3.0 ## @hyperlane-xyz/infra@12.3.0 ### Minor Changes - 6101959f7: Enhanced the router enrollment check to support non-fully connected warp routes using the `remoteRouters` property from the deployment config. ### Patch Changes - Updated dependencies [6101959f7] - Updated dependencies [5db39f493] - Updated dependencies [7500bd6fe] - @hyperlane-xyz/sdk@12.3.0 - @hyperlane-xyz/utils@12.3.0 - @hyperlane-xyz/helloworld@12.3.0 ## @hyperlane-xyz/ccip-server@12.3.0 ## @hyperlane-xyz/github-proxy@12.3.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/large-masks-burn.md | 6 --- .changeset/soft-mails-drive.md | 5 -- .changeset/twenty-eggs-rescue.md | 6 --- .changeset/wicked-phones-develop.md | 7 --- solidity/CHANGELOG.md | 7 +++ solidity/contracts/PackageVersioned.sol | 2 +- solidity/package.json | 4 +- typescript/ccip-server/CHANGELOG.md | 2 + typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 6 +++ typescript/cli/package.json | 6 +-- typescript/cli/src/version.ts | 2 +- typescript/cosmos-sdk/CHANGELOG.md | 6 +++ typescript/cosmos-sdk/package.json | 4 +- typescript/cosmos-types/CHANGELOG.md | 2 + typescript/cosmos-types/package.json | 2 +- typescript/github-proxy/CHANGELOG.md | 2 + typescript/github-proxy/package.json | 2 +- typescript/helloworld/CHANGELOG.md | 10 ++++ typescript/helloworld/package.json | 6 +-- typescript/infra/CHANGELOG.md | 15 ++++++ typescript/infra/package.json | 8 ++-- typescript/sdk/CHANGELOG.md | 15 ++++++ typescript/sdk/package.json | 8 ++-- typescript/utils/CHANGELOG.md | 6 +++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 16 +++++++ typescript/widgets/package.json | 8 ++-- yarn.lock | 62 +++++++++---------------- 29 files changed, 136 insertions(+), 93 deletions(-) delete mode 100644 .changeset/large-masks-burn.md delete mode 100644 .changeset/soft-mails-drive.md delete mode 100644 .changeset/twenty-eggs-rescue.md delete mode 100644 .changeset/wicked-phones-develop.md diff --git a/.changeset/large-masks-burn.md b/.changeset/large-masks-burn.md deleted file mode 100644 index c8db0b79e55..00000000000 --- a/.changeset/large-masks-burn.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/infra': minor -'@hyperlane-xyz/sdk': minor ---- - -Enhanced the router enrollment check to support non-fully connected warp routes using the `remoteRouters` property from the deployment config. diff --git a/.changeset/soft-mails-drive.md b/.changeset/soft-mails-drive.md deleted file mode 100644 index 88976191e76..00000000000 --- a/.changeset/soft-mails-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/widgets': minor ---- - -improve how cosmos addresses are displayed diff --git a/.changeset/twenty-eggs-rescue.md b/.changeset/twenty-eggs-rescue.md deleted file mode 100644 index 07e7d715df4..00000000000 --- a/.changeset/twenty-eggs-rescue.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -Fixes to support CosmosNative and warp apply with foreign deployments. diff --git a/.changeset/wicked-phones-develop.md b/.changeset/wicked-phones-develop.md deleted file mode 100644 index a36bc6c8253..00000000000 --- a/.changeset/wicked-phones-develop.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/widgets': minor -'@hyperlane-xyz/utils': minor -'@hyperlane-xyz/sdk': minor ---- - -implemented cosmos protocol type and cosmos token adapter diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index 6101adf534c..d3e252ea475 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperlane-xyz/core +## 7.1.2 + +### Patch Changes + +- Updated dependencies [7500bd6fe] + - @hyperlane-xyz/utils@12.3.0 + ## 7.1.1 ### Patch Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index 20e3f5dfd71..d57ddf8b2ac 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.1.1"; + string public constant PACKAGE_VERSION = "7.1.2"; } diff --git a/solidity/package.json b/solidity/package.json index 1352b31ff81..48550e9ba24 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.1.1", + "version": "7.1.2", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.2.0", + "@hyperlane-xyz/utils": "12.3.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 9997bf5d7a0..e5a5d1596a4 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 12.3.0 + ## 12.2.0 ## 12.1.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 7c574e5d4bb..34a812d6f89 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.2.0", + "version": "12.3.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 14bfc8bcee4..a208b3996c5 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/cli +## 12.3.0 + +### Minor Changes + +- 5db39f493: Fixes to support CosmosNative and warp apply with foreign deployments. + ## 12.2.0 ## 12.1.0 diff --git a/typescript/cli/package.json b/typescript/cli/package.json index bd16150dd5a..97ae7f09fbe 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.2.0", + "version": "12.3.0", "description": "A command-line utility for common Hyperlane operations", "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", @@ -9,8 +9,8 @@ "@ethersproject/abi": "*", "@ethersproject/providers": "*", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.2.0", - "@hyperlane-xyz/utils": "12.2.0", + "@hyperlane-xyz/sdk": "12.3.0", + "@hyperlane-xyz/utils": "12.3.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 0e42922c30c..f18f474793d 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.2.0'; +export const VERSION = '12.3.0'; diff --git a/typescript/cosmos-sdk/CHANGELOG.md b/typescript/cosmos-sdk/CHANGELOG.md index 3d6d4caa31d..95f5fe51563 100644 --- a/typescript/cosmos-sdk/CHANGELOG.md +++ b/typescript/cosmos-sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/cosmos-sdk +## 12.3.0 + +### Patch Changes + +- @hyperlane-xyz/cosmos-types@12.3.0 + ## 12.2.0 ### Patch Changes diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 28ae24ad414..40751d4f777 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-sdk", - "version": "12.2.0", + "version": "12.3.0", "description": "Hyperlane TypeScript SDK for the Cosmos Hyperlane SDK module", "type": "module", "exports": { @@ -46,6 +46,6 @@ }, "dependencies": { "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/cosmos-types": "12.2.0" + "@hyperlane-xyz/cosmos-types": "12.3.0" } } diff --git a/typescript/cosmos-types/CHANGELOG.md b/typescript/cosmos-types/CHANGELOG.md index 835c42364fd..dd0922cad4e 100644 --- a/typescript/cosmos-types/CHANGELOG.md +++ b/typescript/cosmos-types/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cosmos-types +## 12.3.0 + ## 12.2.0 ## 11.0.0 diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index 015b08832a5..b00db5eb1dc 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-types", - "version": "12.2.0", + "version": "12.3.0", "description": "Hyperlane TypeScript SDK types for the Cosmos Hyperlane SDK module", "type": "module", "exports": { diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index e97da119a6a..6e3ece0b848 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 12.3.0 + ## 12.2.0 ## 12.1.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index b0ce0bf4334..2c50f1e2008 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.2.0", + "version": "12.3.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 5c3b6fe3497..253fe0ab870 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/helloworld +## 12.3.0 + +### Patch Changes + +- Updated dependencies [6101959f7] +- Updated dependencies [5db39f493] +- Updated dependencies [7500bd6fe] + - @hyperlane-xyz/sdk@12.3.0 + - @hyperlane-xyz/core@7.1.2 + ## 12.2.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 24345de2ad6..29e721a073b 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": "12.2.0", + "version": "12.3.0", "dependencies": { - "@hyperlane-xyz/core": "7.1.1", + "@hyperlane-xyz/core": "7.1.2", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.2.0", + "@hyperlane-xyz/sdk": "12.3.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index d7702c6db3a..6188612c182 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,20 @@ # @hyperlane-xyz/infra +## 12.3.0 + +### Minor Changes + +- 6101959f7: Enhanced the router enrollment check to support non-fully connected warp routes using the `remoteRouters` property from the deployment config. + +### Patch Changes + +- Updated dependencies [6101959f7] +- Updated dependencies [5db39f493] +- Updated dependencies [7500bd6fe] + - @hyperlane-xyz/sdk@12.3.0 + - @hyperlane-xyz/utils@12.3.0 + - @hyperlane-xyz/helloworld@12.3.0 + ## 12.2.0 ### Patch Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index e89891aef69..2d29fb97eab 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": "12.2.0", + "version": "12.3.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.2.0", + "@hyperlane-xyz/helloworld": "12.3.0", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.2.0", - "@hyperlane-xyz/utils": "12.2.0", + "@hyperlane-xyz/sdk": "12.3.0", + "@hyperlane-xyz/utils": "12.3.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 38a3c03a312..a357fc83c70 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,20 @@ # @hyperlane-xyz/sdk +## 12.3.0 + +### Minor Changes + +- 6101959f7: Enhanced the router enrollment check to support non-fully connected warp routes using the `remoteRouters` property from the deployment config. +- 5db39f493: Fixes to support CosmosNative and warp apply with foreign deployments. +- 7500bd6fe: implemented cosmos protocol type and cosmos token adapter + +### Patch Changes + +- Updated dependencies [7500bd6fe] + - @hyperlane-xyz/utils@12.3.0 + - @hyperlane-xyz/core@7.1.2 + - @hyperlane-xyz/cosmos-sdk@12.3.0 + ## 12.2.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 0dd6fbb9695..b3137442ad5 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,16 +1,16 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.2.0", + "version": "12.3.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.122", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.1.1", - "@hyperlane-xyz/cosmos-sdk": "1.0.0", - "@hyperlane-xyz/utils": "12.2.0", + "@hyperlane-xyz/core": "7.1.2", + "@hyperlane-xyz/cosmos-sdk": "12.3.0", + "@hyperlane-xyz/utils": "12.3.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index d1a1dd35d8a..c1f963643a3 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/utils +## 12.3.0 + +### Minor Changes + +- 7500bd6fe: implemented cosmos protocol type and cosmos token adapter + ## 12.2.0 ## 12.1.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index ed4df96d74c..4e34e0206a1 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": "12.2.0", + "version": "12.3.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index b76376511b3..a39b520f8ed 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,21 @@ # @hyperlane-xyz/widgets +## 12.3.0 + +### Minor Changes + +- 2edb767f2: improve how cosmos addresses are displayed +- 7500bd6fe: implemented cosmos protocol type and cosmos token adapter + +### Patch Changes + +- Updated dependencies [6101959f7] +- Updated dependencies [5db39f493] +- Updated dependencies [7500bd6fe] + - @hyperlane-xyz/sdk@12.3.0 + - @hyperlane-xyz/utils@12.3.0 + - @hyperlane-xyz/cosmos-sdk@12.3.0 + ## 12.2.0 ### Patch Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 49cbb29a3b2..24996339f77 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.2.0", + "version": "12.3.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -10,9 +10,9 @@ "@cosmjs/stargate": "^0.32.4", "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/cosmos-sdk": "1.0.0", - "@hyperlane-xyz/sdk": "12.2.0", - "@hyperlane-xyz/utils": "12.2.0", + "@hyperlane-xyz/cosmos-sdk": "12.3.0", + "@hyperlane-xyz/sdk": "12.3.0", + "@hyperlane-xyz/utils": "12.3.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index f1718aec09b..1f93090c8c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7651,8 +7651,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.2.0" - "@hyperlane-xyz/utils": "npm:12.2.0" + "@hyperlane-xyz/sdk": "npm:12.3.0" + "@hyperlane-xyz/utils": "npm:12.3.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7692,14 +7692,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.1.1, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.2, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.2.0" + "@hyperlane-xyz/utils": "npm:12.3.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7739,23 +7739,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-sdk@npm:1.0.0": - version: 1.0.0 - resolution: "@hyperlane-xyz/cosmos-sdk@npm:1.0.0" - dependencies: - "@cosmjs/stargate": "npm:^0.32.4" - "@hyperlane-xyz/cosmos-types": "npm:11.0.0" - checksum: 10/86218b58b7559d934b02740b5c575c00b8574d1ed41471871ce8be648c8838b507bd911c52d5b69833691febfb7d26b88e011828c454ab64ac3c0a90b40e3cd9 - languageName: node - linkType: hard - -"@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": +"@hyperlane-xyz/cosmos-sdk@npm:12.3.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk" dependencies: "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/cosmos-types": "npm:12.2.0" + "@hyperlane-xyz/cosmos-types": "npm:12.3.0" "@types/mocha": "npm:^10.0.1" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" @@ -7771,17 +7761,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-types@npm:11.0.0": - version: 11.0.0 - resolution: "@hyperlane-xyz/cosmos-types@npm:11.0.0" - dependencies: - long: "npm:^5.2.4" - protobufjs: "npm:^7.4.0" - checksum: 10/18bf66a72cf0fb84d2d612f0b7f859675ea9aa9a9d5d5eea9e5698bd1f016cffcdf53ad17a015cc668f8d715ab8abaccd35bc0ad25d09cf9b8b69449b2372cb8 - languageName: node - linkType: hard - -"@hyperlane-xyz/cosmos-types@npm:12.2.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": +"@hyperlane-xyz/cosmos-types@npm:12.3.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" dependencies: @@ -7815,14 +7795,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.2.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:12.3.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.1" + "@hyperlane-xyz/core": "npm:7.1.2" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.2.0" + "@hyperlane-xyz/sdk": "npm:12.3.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7871,10 +7851,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.2.0" + "@hyperlane-xyz/helloworld": "npm:12.3.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.2.0" - "@hyperlane-xyz/utils": "npm:12.2.0" + "@hyperlane-xyz/sdk": "npm:12.3.0" + "@hyperlane-xyz/utils": "npm:12.3.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7945,7 +7925,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.2.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:12.3.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7955,9 +7935,9 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.1" - "@hyperlane-xyz/cosmos-sdk": "npm:1.0.0" - "@hyperlane-xyz/utils": "npm:12.2.0" + "@hyperlane-xyz/core": "npm:7.1.2" + "@hyperlane-xyz/cosmos-sdk": "npm:12.3.0" + "@hyperlane-xyz/utils": "npm:12.3.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -8002,7 +7982,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.2.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:12.3.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8044,10 +8024,10 @@ __metadata: "@emotion/styled": "npm:^11.13.0" "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" - "@hyperlane-xyz/cosmos-sdk": "npm:1.0.0" + "@hyperlane-xyz/cosmos-sdk": "npm:12.3.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.2.0" - "@hyperlane-xyz/utils": "npm:12.2.0" + "@hyperlane-xyz/sdk": "npm:12.3.0" + "@hyperlane-xyz/utils": "npm:12.3.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From 4d7af781e3ed4de65a75b429eae43875f0f7d622 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:07:28 +0100 Subject: [PATCH 076/223] chore: upgrade prettier (#6004) ### Description This PR upgrades Prettier and the `@trivago/prettier-plugin-sort-imports` plugin to support the now-standard JSON import syntax. This upgrade is required for another PR that transitions to the new import syntax. The CI job that runs Prettier has been updated to check the formatting of only the files modified by the current PR. When this job runs on pushes to the `main` branch, it verifies all files without raising errors. The formatting check results will still appear as warnings in the annotations section of the job run page. ### Related Issues Resolves ENG-1335. ### Backward Compatibility This change is not entirely backward compatible, as the latest Prettier version introduces different formatting in some cases. The updated formatter will only apply to modified files. Remaining formatting inconsistencies will be addressed in a future PR. --- .github/workflows/test.yml | 38 +- .prettierignore | 8 + package.json | 5 +- solidity/package.json | 10 +- typescript/ccip-server/package.json | 2 +- typescript/cli/package.json | 2 +- typescript/cosmos-sdk/package.json | 2 +- typescript/cosmos-types/package.json | 4 +- typescript/github-proxy/package.json | 2 +- typescript/helloworld/package.json | 10 +- typescript/infra/package.json | 2 +- typescript/sdk/package.json | 2 +- typescript/utils/package.json | 2 +- typescript/widgets/package.json | 2 +- yarn.lock | 1550 +++++++++++--------------- 15 files changed, 700 insertions(+), 941 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6cd22da9035..af2c47bb505 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,8 +65,10 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - # check out full history - fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc # Build required before linting or the intra-monorepo package cycle checking won't work - name: yarn-build @@ -77,14 +79,30 @@ jobs: - name: lint run: yarn lint - - name: prettier + - name: Get changed PR files + if: github.event_name == 'pull_request' run: | - yarn prettier - CHANGES=$(git status -s) - if [[ ! -z $CHANGES ]]; then - echo "Changes found: $CHANGES" - exit 1 - fi + gh pr view ${{ github.event.pull_request.number }} --json files -q '.files[] | select (.status != "removed") | .path' > changed_files.txt + echo "Changed files: $(cat changed_files.txt)" + env: + GH_TOKEN: ${{ github.token }} + + - name: Run prettier (changed files) + if: github.event_name == 'pull_request' + run: >- + cat changed_files.txt | + xargs -r yarn exec prettier --check --ignore-unknown + + - name: Run prettier (all files) + if: github.event_name != 'pull_request' + run: >- + yarn exec prettier --check --ignore-unknown . + continue-on-error: true + + - name: Clean up + if: always() + run: | + rm -f changed_files.txt yarn-test: runs-on: ubuntu-latest @@ -235,7 +253,7 @@ jobs: run: | cd typescript/infra yarn tsx ./scripts/agents/update-agent-config.ts -e ${{ matrix.environment }} - CHANGES=$(git status -s) + CHANGES=$(git status -s . :/rust/main/config) if [[ ! -z $CHANGES ]]; then echo "Changes found in agent config: $CHANGES" exit 1 diff --git a/.prettierignore b/.prettierignore index a37cd3e5141..f2f47dff36d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,11 @@ *.Dockerfile Dockerfile + +typescript/*/dist/ + +rust/ +tools/ +typescript/infra/helm/**/templates/ +typescript/infra/src/infrastructure/external-secrets/helm/templates/ +vectors/ diff --git a/package.json b/package.json index 7605c93cf91..5938b5b8bcb 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "devDependencies": { "@eslint/js": "^9.15.0", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@typescript-eslint/eslint-plugin": "^8.1.6", "@typescript-eslint/parser": "^8.1.6", "eslint": "^9.15.0", @@ -14,7 +14,8 @@ "eslint-plugin-jest": "^28.2.0", "husky": "^8.0.0", "lint-staged": "^12.4.3", - "prettier": "^2.8.8", + "prettier": "^3.5.3", + "prettier-plugin-solidity": "^1.4.2", "syncpack": "^13.0.0", "tsx": "^4.19.1" }, diff --git a/solidity/package.json b/solidity/package.json index 48550e9ba24..a8ab079c144 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -29,10 +29,10 @@ "hardhat": "^2.22.2", "hardhat-gas-reporter": "^1.0.9", "hardhat-ignore-warnings": "^0.2.11", - "prettier": "^2.8.8", - "prettier-plugin-solidity": "^1.1.3", - "solhint": "^4.5.4", - "solhint-plugin-prettier": "^0.0.5", + "prettier": "^3.5.3", + "prettier-plugin-solidity": "^1.4.2", + "solhint": "^5.0.5", + "solhint-plugin-prettier": "^0.1.0", "solidity-bytes-utils": "^0.8.0", "solidity-coverage": "^0.8.3", "ts-generator": "^0.1.1", @@ -90,7 +90,7 @@ "slither": "slither .", "storage": "./storage.sh", "version:update": "sh ./bytecodeversion.sh", - "version:changed": "yarn version:update && git diff --exit-code", + "version:changed": "yarn version:update && git diff --exit-code contracts/PackageVersioned.sol", "version:exhaustive": "yarn tsx ./test/exhaustiveversion.test.ts" }, "peerDependencies": { diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 34a812d6f89..6f441f0cfed 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -27,7 +27,7 @@ "eslint": "^9.15.0", "jest": "^29.7.0", "nodemon": "^3.0.3", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "ts-jest": "^29.1.2", "ts-node": "^10.8.0", "tsx": "^4.19.1", diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 97ae7f09fbe..4f7f6aac59f 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -35,7 +35,7 @@ "ethers": "^5.7.2", "latest-version": "^8.0.0", "mocha": "^10.2.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "terminal-link": "^3.0.0", "tsx": "^4.19.1", "typescript": "5.3.3", diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 40751d4f777..3e4e53febf9 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -40,7 +40,7 @@ "eslint-plugin-import": "^2.31.0", "mocha": "^10.2.0", "mocha-steps": "^1.3.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "typescript": "5.3.3", "typescript-eslint": "^8.23.0" }, diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index b00db5eb1dc..f4e4a9b7c40 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -21,7 +21,7 @@ ], "license": "Apache-2.0", "scripts": { - "build": "rm -rf ./dist && tsc && prettier --write ./src --loglevel silent", + "build": "rm -rf ./dist && tsc && prettier --write ./src --log-level silent", "format": "prettier --write .", "lint": "eslint -c ./eslint.config.mjs", "prettier": "prettier --write ./src", @@ -37,7 +37,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "typescript": "5.3.3", "typescript-eslint": "^8.23.0" }, diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index 2c50f1e2008..4f1c71fed3c 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -28,7 +28,7 @@ "@cloudflare/workers-types": "^4.20240821.1", "@faker-js/faker": "^8.4.1", "chai": "^4.5.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "typescript": "5.3.3", "vitest": "1.4.0", "wrangler": "^3.74.0" diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 29e721a073b..0cff7eff8df 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -13,7 +13,7 @@ "@eslint/js": "^9.15.0", "@nomiclabs/hardhat-ethers": "^2.2.3", "@nomiclabs/hardhat-waffle": "^2.0.6", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@typechain/ethers-v5": "^11.1.2", "@typechain/ethers-v6": "^0.5.1", "@typechain/hardhat": "^9.1.0", @@ -27,10 +27,10 @@ "ethereum-waffle": "^4.0.10", "hardhat": "^2.22.2", "hardhat-gas-reporter": "^1.0.9", - "prettier": "^2.8.8", - "prettier-plugin-solidity": "^1.1.3", - "solhint": "^4.5.4", - "solhint-plugin-prettier": "^0.0.5", + "prettier": "^3.5.3", + "prettier-plugin-solidity": "^1.4.2", + "solhint": "^5.0.5", + "solhint-plugin-prettier": "^0.1.0", "solidity-coverage": "^0.8.3", "ts-node": "^10.8.0", "typechain": "patch:typechain@npm%3A8.3.2#~/.yarn/patches/typechain-npm-8.3.2-b02e27439e.patch", diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 2d29fb97eab..40a34191f65 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -49,7 +49,7 @@ "ethers": "^5.7.2", "hardhat": "^2.22.2", "mocha": "^10.2.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "tsx": "^4.19.1", "typescript": "5.3.3" }, diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index b3137442ad5..90370f614f0 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -46,7 +46,7 @@ "ethereum-waffle": "^4.0.10", "hardhat": "^2.22.2", "mocha": "^10.2.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "sinon": "^13.0.2", "ts-node": "^10.8.0", "tsx": "^4.19.1", diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 4e34e0206a1..e35ad8b3df7 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -25,7 +25,7 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", "mocha": "^10.2.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "sinon": "^13.0.2", "typescript": "5.3.3" }, diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 24996339f77..a6d3ec53a8c 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -57,7 +57,7 @@ "eslint-plugin-storybook": "^0.11.1", "framer-motion": "^10.16.4", "postcss": "^8.4.21", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "react": "^18.2.0", "react-dom": "^18.2.0", "storybook": "^7.6.14", diff --git a/yarn.lock b/yarn.lock index 1f93090c8c2..c0b9cf768f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2301,7 +2301,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.25.9": +"@babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.2": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -2372,17 +2372,6 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:7.17.7": - version: 7.17.7 - resolution: "@babel/generator@npm:7.17.7" - dependencies: - "@babel/types": "npm:^7.17.0" - jsesc: "npm:^2.5.1" - source-map: "npm:^0.5.0" - checksum: 10/3303afa2b1310e67071d6c998121b2af1038d6450df266d24fe9a86329fb6288b8ab38bb71787917b3f81a1c4edbc5bc7e6735683fe1d0aa288b33e93daacd60 - languageName: node - linkType: hard - "@babel/generator@npm:^7.23.0": version: 7.23.3 resolution: "@babel/generator@npm:7.23.3" @@ -2432,6 +2421,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.26.5, @babel/generator@npm:^7.27.0": + version: 7.27.0 + resolution: "@babel/generator@npm:7.27.0" + dependencies: + "@babel/parser": "npm:^7.27.0" + "@babel/types": "npm:^7.27.0" + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + jsesc: "npm:^3.0.2" + checksum: 10/5447c402b1d841132534a0a9715e89f4f28b6f2886a23e70aaa442150dba4a1e29e4e2351814f439ee1775294dccdef9ab0a4192b6e6a5ad44e24233b3611da2 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-annotate-as-pure@npm:7.24.7" @@ -2913,6 +2915,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.27.0": + version: 7.27.0 + resolution: "@babel/parser@npm:7.27.0" + dependencies: + "@babel/types": "npm:^7.27.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10/0fee9f05c6db753882ca9d10958301493443da9f6986d7020ebd7a696b35886240016899bc0b47d871aea2abcafd64632343719742e87432c8145e0ec2af2a03 + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.0" @@ -4067,21 +4080,21 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.5.5": - version: 7.23.4 - resolution: "@babel/runtime@npm:7.23.4" +"@babel/runtime@npm:^7.22.6": + version: 7.23.1 + resolution: "@babel/runtime@npm:7.23.1" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/6ef4f6dcc4ec4d74cb9f6c26a26e92d016b36debd167be48cae293fbd990b3157fb1d8d21c531285da15a5bda9ccb23e651b56234941e03d91c8af69d4c593a9 + checksum: 10/a9fdd322ae1f5d5e3446b6181745300f863164a30acb35ee296c6989cf0ecfd57598a7a6ef209b414575cabe81ef17756412052b3d85fcaf8729332b5b70c45f languageName: node linkType: hard -"@babel/runtime@npm:^7.22.6": - version: 7.23.1 - resolution: "@babel/runtime@npm:7.23.1" +"@babel/runtime@npm:^7.5.5": + version: 7.23.4 + resolution: "@babel/runtime@npm:7.23.4" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/a9fdd322ae1f5d5e3446b6181745300f863164a30acb35ee296c6989cf0ecfd57598a7a6ef209b414575cabe81ef17756412052b3d85fcaf8729332b5b70c45f + checksum: 10/6ef4f6dcc4ec4d74cb9f6c26a26e92d016b36debd167be48cae293fbd990b3157fb1d8d21c531285da15a5bda9ccb23e651b56234941e03d91c8af69d4c593a9 languageName: node linkType: hard @@ -4129,21 +4142,14 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:7.23.2": - version: 7.23.2 - resolution: "@babel/traverse@npm:7.23.2" +"@babel/template@npm:^7.27.0": + version: 7.27.0 + resolution: "@babel/template@npm:7.27.0" dependencies: - "@babel/code-frame": "npm:^7.22.13" - "@babel/generator": "npm:^7.23.0" - "@babel/helper-environment-visitor": "npm:^7.22.20" - "@babel/helper-function-name": "npm:^7.23.0" - "@babel/helper-hoist-variables": "npm:^7.22.5" - "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.23.0" - "@babel/types": "npm:^7.23.0" - debug: "npm:^4.1.0" - globals: "npm:^11.1.0" - checksum: 10/e4fcb8f8395804956df4ae1301230a14b6eb35b74a7058a0e0b40f6f4be7281e619e6dafe400e833d4512da5d61cf17ea177d04b00a8f7cf3d8d69aff83ca3d8 + "@babel/code-frame": "npm:^7.26.2" + "@babel/parser": "npm:^7.27.0" + "@babel/types": "npm:^7.27.0" + checksum: 10/7159ca1daea287ad34676d45a7146675444d42c7664aca3e617abc9b1d9548c8f377f35a36bb34cf956e1d3610dcb7acfcfe890aebf81880d35f91a7bd273ee5 languageName: node linkType: hard @@ -4195,13 +4201,18 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:7.17.0": - version: 7.17.0 - resolution: "@babel/types@npm:7.17.0" +"@babel/traverse@npm:^7.26.7": + version: 7.27.0 + resolution: "@babel/traverse@npm:7.27.0" dependencies: - "@babel/helper-validator-identifier": "npm:^7.16.7" - to-fast-properties: "npm:^2.0.0" - checksum: 10/535ccef360d0c74e2bb685050f3a45e6ab30f66c740bbdd0858148ed502043f1ae2006a9d0269ac3b7356b690091ae313efd912e408bc0198d80a14b2a6f1537 + "@babel/code-frame": "npm:^7.26.2" + "@babel/generator": "npm:^7.27.0" + "@babel/parser": "npm:^7.27.0" + "@babel/template": "npm:^7.27.0" + "@babel/types": "npm:^7.27.0" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: 10/b0675bc16bd87187e8b090557b0650135de56a621692ad8614b20f32621350ae0fc2e1129b73b780d64a9ed4beab46849a17f90d5267b6ae6ce09ec8412a12c7 languageName: node linkType: hard @@ -4216,16 +4227,6 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.17.0, @babel/types@npm:^7.8.3": - version: 7.18.4 - resolution: "@babel/types@npm:7.18.4" - dependencies: - "@babel/helper-validator-identifier": "npm:^7.16.7" - to-fast-properties: "npm:^2.0.0" - checksum: 10/950430807ef92ee972b82bc676d2b923de3dd85bf9caa08247b9dc9b31fb8a14bae9e6a2feffef29dafbd058766693101b0cb22891ba7f2ff05c362a88b75687 - languageName: node - linkType: hard - "@babel/types@npm:^7.18.9, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.25.0, @babel/types@npm:^7.4.4": version: 7.25.0 resolution: "@babel/types@npm:7.25.0" @@ -4280,6 +4281,26 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.26.7, @babel/types@npm:^7.27.0": + version: 7.27.0 + resolution: "@babel/types@npm:7.27.0" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10/2c322bce107c8a534dc4a23be60d570e6a4cc7ca2e44d4f0eee08c0b626104eb7e60ab8de03463bc5da1773a2f69f1e6edec1648d648d65461d6520a7f3b0770 + languageName: node + linkType: hard + +"@babel/types@npm:^7.8.3": + version: 7.18.4 + resolution: "@babel/types@npm:7.18.4" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.16.7" + to-fast-properties: "npm:^2.0.0" + checksum: 10/950430807ef92ee972b82bc676d2b923de3dd85bf9caa08247b9dc9b31fb8a14bae9e6a2feffef29dafbd058766693101b0cb22891ba7f2ff05c362a88b75687 + languageName: node + linkType: hard + "@balena/dockerignore@npm:^1.0.2": version: 1.0.2 resolution: "@balena/dockerignore@npm:1.0.2" @@ -4532,15 +4553,15 @@ __metadata: languageName: node linkType: hard -"@changesets/apply-release-plan@npm:^6.1.4": - version: 6.1.4 - resolution: "@changesets/apply-release-plan@npm:6.1.4" +"@changesets/apply-release-plan@npm:^7.0.12": + version: 7.0.12 + resolution: "@changesets/apply-release-plan@npm:7.0.12" dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/config": "npm:^2.3.1" - "@changesets/get-version-range-type": "npm:^0.3.2" - "@changesets/git": "npm:^2.0.0" - "@changesets/types": "npm:^5.2.1" + "@changesets/config": "npm:^3.1.1" + "@changesets/get-version-range-type": "npm:^0.4.0" + "@changesets/git": "npm:^3.0.4" + "@changesets/should-skip-package": "npm:^0.1.2" + "@changesets/types": "npm:^6.1.0" "@manypkg/get-packages": "npm:^1.1.3" detect-indent: "npm:^6.0.0" fs-extra: "npm:^7.0.1" @@ -4549,7 +4570,7 @@ __metadata: prettier: "npm:^2.7.1" resolve-from: "npm:^5.0.0" semver: "npm:^7.5.3" - checksum: 10/882858d37e988f789102d2aed93b37077461a36f9e7b81ff011726d162bbfd3850d9bda9b900544394cc1f72112581d101ea40edf911a1ca0f6e59a77f482726 + checksum: 10/3ce05caa73b7b96a8a6be943507591925c44b22f209da001fb9d83df1d7a4569659e889373f5f7a208a121b3cf7bc17788969b8849bddaf13c27d6720e4e1c47 languageName: node linkType: hard @@ -4574,20 +4595,6 @@ __metadata: languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^5.2.4": - version: 5.2.4 - resolution: "@changesets/assemble-release-plan@npm:5.2.4" - dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/errors": "npm:^0.1.4" - "@changesets/get-dependents-graph": "npm:^1.3.6" - "@changesets/types": "npm:^5.2.1" - "@manypkg/get-packages": "npm:^1.1.3" - semver: "npm:^7.5.3" - checksum: 10/fc1db5ace2bd96b285719f11c30610b0a66a1ee5c6716cb897c62be6b8d91192f415d3c164a6d02a4efff4ba4a57ea0d29491cb10594a0c365cd0d809633c34b - languageName: node - linkType: hard - "@changesets/assemble-release-plan@npm:^6.0.5": version: 6.0.5 resolution: "@changesets/assemble-release-plan@npm:6.0.5" @@ -4602,12 +4609,17 @@ __metadata: languageName: node linkType: hard -"@changesets/changelog-git@npm:^0.1.14": - version: 0.1.14 - resolution: "@changesets/changelog-git@npm:0.1.14" +"@changesets/assemble-release-plan@npm:^6.0.6": + version: 6.0.6 + resolution: "@changesets/assemble-release-plan@npm:6.0.6" dependencies: - "@changesets/types": "npm:^5.2.1" - checksum: 10/7dde49aced9760c425e10f3c2e83b2fce08bced455476bbaddf929b96d35ee62d2e6bec442b98dc9ea99a771bfdda3706587111578c42d6025e15d32d8f164e8 + "@changesets/errors": "npm:^0.2.0" + "@changesets/get-dependents-graph": "npm:^2.1.3" + "@changesets/should-skip-package": "npm:^0.1.2" + "@changesets/types": "npm:^6.1.0" + "@manypkg/get-packages": "npm:^1.1.3" + semver: "npm:^7.5.3" + checksum: 10/b6c7ce7231e4c1801255d15e99355c700dc6fd62abb5330817231e2f45edd06fa7d31aac0ed3b1908a6cde33ef0c5bf2c1e71f2e03d37435131f2a4d4d48aaf8 languageName: node linkType: hard @@ -4620,6 +4632,15 @@ __metadata: languageName: node linkType: hard +"@changesets/changelog-git@npm:^0.2.1": + version: 0.2.1 + resolution: "@changesets/changelog-git@npm:0.2.1" + dependencies: + "@changesets/types": "npm:^6.1.0" + checksum: 10/c22f3c0baf50c102a6890046351ee42f65ff6d58747ba4f75e5e40da1ed5fbcfd0dc2d11cdfb86acbb3262e58acb93f096c798827cac570c1e22e8f32f58a30f + languageName: node + linkType: hard + "@changesets/changelog-github@npm:^0.5.0": version: 0.5.0 resolution: "@changesets/changelog-github@npm:0.5.0" @@ -4632,45 +4653,40 @@ __metadata: linkType: hard "@changesets/cli@npm:^2.26.2": - version: 2.26.2 - resolution: "@changesets/cli@npm:2.26.2" - dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/apply-release-plan": "npm:^6.1.4" - "@changesets/assemble-release-plan": "npm:^5.2.4" - "@changesets/changelog-git": "npm:^0.1.14" - "@changesets/config": "npm:^2.3.1" - "@changesets/errors": "npm:^0.1.4" - "@changesets/get-dependents-graph": "npm:^1.3.6" - "@changesets/get-release-plan": "npm:^3.0.17" - "@changesets/git": "npm:^2.0.0" - "@changesets/logger": "npm:^0.0.5" - "@changesets/pre": "npm:^1.0.14" - "@changesets/read": "npm:^0.5.9" - "@changesets/types": "npm:^5.2.1" - "@changesets/write": "npm:^0.2.3" + version: 2.29.2 + resolution: "@changesets/cli@npm:2.29.2" + dependencies: + "@changesets/apply-release-plan": "npm:^7.0.12" + "@changesets/assemble-release-plan": "npm:^6.0.6" + "@changesets/changelog-git": "npm:^0.2.1" + "@changesets/config": "npm:^3.1.1" + "@changesets/errors": "npm:^0.2.0" + "@changesets/get-dependents-graph": "npm:^2.1.3" + "@changesets/get-release-plan": "npm:^4.0.10" + "@changesets/git": "npm:^3.0.4" + "@changesets/logger": "npm:^0.1.1" + "@changesets/pre": "npm:^2.0.2" + "@changesets/read": "npm:^0.6.5" + "@changesets/should-skip-package": "npm:^0.1.2" + "@changesets/types": "npm:^6.1.0" + "@changesets/write": "npm:^0.4.0" "@manypkg/get-packages": "npm:^1.1.3" - "@types/is-ci": "npm:^3.0.0" - "@types/semver": "npm:^7.5.0" ansi-colors: "npm:^4.1.3" - chalk: "npm:^2.1.0" - enquirer: "npm:^2.3.0" + ci-info: "npm:^3.7.0" + enquirer: "npm:^2.4.1" external-editor: "npm:^3.1.0" fs-extra: "npm:^7.0.1" - human-id: "npm:^1.0.2" - is-ci: "npm:^3.0.1" - meow: "npm:^6.0.0" - outdent: "npm:^0.5.0" + mri: "npm:^1.2.0" p-limit: "npm:^2.2.0" - preferred-pm: "npm:^3.0.0" + package-manager-detector: "npm:^0.2.0" + picocolors: "npm:^1.1.0" resolve-from: "npm:^5.0.0" semver: "npm:^7.5.3" - spawndamnit: "npm:^2.0.0" + spawndamnit: "npm:^3.0.1" term-size: "npm:^2.1.0" - tty-table: "npm:^4.1.5" bin: changeset: bin.js - checksum: 10/3ea8c1a69c7a0c254e3054d10a5ada503ae17b8863f9de8de5b25f2e475d7213555cca133361ab9b97c69e06574a609449f7177134b3c810919f85f78c4c2758 + checksum: 10/6c3e02c6449f0fdce675849c9bff04e2ff65d12ae38632cf941b1bfdc2e5e6e6497a949c1087d09ce494e8acf96460a8740c18c15e76123eb2d3d938abfef1bb languageName: node linkType: hard @@ -4712,21 +4728,6 @@ __metadata: languageName: node linkType: hard -"@changesets/config@npm:^2.3.1": - version: 2.3.1 - resolution: "@changesets/config@npm:2.3.1" - dependencies: - "@changesets/errors": "npm:^0.1.4" - "@changesets/get-dependents-graph": "npm:^1.3.6" - "@changesets/logger": "npm:^0.0.5" - "@changesets/types": "npm:^5.2.1" - "@manypkg/get-packages": "npm:^1.1.3" - fs-extra: "npm:^7.0.1" - micromatch: "npm:^4.0.2" - checksum: 10/5b19831829b8c984620594c89f3ead47494b3dd497c1785d6fc3e06ccb62fd95509219e093f4f325d1f3168d98dc97a84611e4bbea17357cbdb466dbe9a0af8c - languageName: node - linkType: hard - "@changesets/config@npm:^3.0.5": version: 3.0.5 resolution: "@changesets/config@npm:3.0.5" @@ -4742,12 +4743,18 @@ __metadata: languageName: node linkType: hard -"@changesets/errors@npm:^0.1.4": - version: 0.1.4 - resolution: "@changesets/errors@npm:0.1.4" +"@changesets/config@npm:^3.1.1": + version: 3.1.1 + resolution: "@changesets/config@npm:3.1.1" dependencies: - extendable-error: "npm:^0.1.5" - checksum: 10/10734f1379715bf5a70b566dd42b50a75964d76f382bb67332776614454deda6d04a43dd7e727cd7cba56d7f2f7c95a07c7c0a19dd5d64fb1980b28322840733 + "@changesets/errors": "npm:^0.2.0" + "@changesets/get-dependents-graph": "npm:^2.1.3" + "@changesets/logger": "npm:^0.1.1" + "@changesets/types": "npm:^6.1.0" + "@manypkg/get-packages": "npm:^1.1.3" + fs-extra: "npm:^7.0.1" + micromatch: "npm:^4.0.8" + checksum: 10/9500e02b68801f052478b3e10523bd3a39b9e5e989e718832832537c9da965580f496262c2bc3f6e23a4e6fb4303f730a69dcbf2041f68d2fa7bd03dd1f82db0 languageName: node linkType: hard @@ -4760,28 +4767,27 @@ __metadata: languageName: node linkType: hard -"@changesets/get-dependents-graph@npm:^1.3.6": - version: 1.3.6 - resolution: "@changesets/get-dependents-graph@npm:1.3.6" +"@changesets/get-dependents-graph@npm:^2.1.2": + version: 2.1.2 + resolution: "@changesets/get-dependents-graph@npm:2.1.2" dependencies: - "@changesets/types": "npm:^5.2.1" + "@changesets/types": "npm:^6.0.0" "@manypkg/get-packages": "npm:^1.1.3" - chalk: "npm:^2.1.0" - fs-extra: "npm:^7.0.1" + picocolors: "npm:^1.1.0" semver: "npm:^7.5.3" - checksum: 10/04626cdc4039fee66b3b828f1c29026c5607d2d35fa05ce6489f257178ab1283ebffe8c9b9b19378d40d310674538677cdc21d10cdf4953b0d86edeb7265f06b + checksum: 10/36d9877b0b071183b253d894e0dbef56f764fe2ff592064489d4f122c419ab97f0d023c9e078849d0f48b4129f5018c7c81cb380b02d975db5e0768ab29226c1 languageName: node linkType: hard -"@changesets/get-dependents-graph@npm:^2.1.2": - version: 2.1.2 - resolution: "@changesets/get-dependents-graph@npm:2.1.2" +"@changesets/get-dependents-graph@npm:^2.1.3": + version: 2.1.3 + resolution: "@changesets/get-dependents-graph@npm:2.1.3" dependencies: - "@changesets/types": "npm:^6.0.0" + "@changesets/types": "npm:^6.1.0" "@manypkg/get-packages": "npm:^1.1.3" picocolors: "npm:^1.1.0" semver: "npm:^7.5.3" - checksum: 10/36d9877b0b071183b253d894e0dbef56f764fe2ff592064489d4f122c419ab97f0d023c9e078849d0f48b4129f5018c7c81cb380b02d975db5e0768ab29226c1 + checksum: 10/33f2bb5dc88443b68fd796fd3b019a553fb3e21cb957a8a117db2a6770ad81f7c156ebdc3b12cfa75169de918f11271a71f61034aec48a53bf1a936d6d783e3d languageName: node linkType: hard @@ -4795,18 +4801,17 @@ __metadata: languageName: node linkType: hard -"@changesets/get-release-plan@npm:^3.0.17": - version: 3.0.17 - resolution: "@changesets/get-release-plan@npm:3.0.17" +"@changesets/get-release-plan@npm:^4.0.10": + version: 4.0.10 + resolution: "@changesets/get-release-plan@npm:4.0.10" dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/assemble-release-plan": "npm:^5.2.4" - "@changesets/config": "npm:^2.3.1" - "@changesets/pre": "npm:^1.0.14" - "@changesets/read": "npm:^0.5.9" - "@changesets/types": "npm:^5.2.1" + "@changesets/assemble-release-plan": "npm:^6.0.6" + "@changesets/config": "npm:^3.1.1" + "@changesets/pre": "npm:^2.0.2" + "@changesets/read": "npm:^0.6.5" + "@changesets/types": "npm:^6.1.0" "@manypkg/get-packages": "npm:^1.1.3" - checksum: 10/0b85dcca9a671cdf1ca1a11cf69f909351507c23961b2948ec49ddd982616ae4f844aff1fd526f3f2960876c7705ce4ccb837395621e2d3b1877669d02e3f0d9 + checksum: 10/372087faf29262bc1373721b5793090828fd3dabd9ff64f0fadf2c7dcb6dfcba19d690d9ea86ca3cab9d5c7a45878d64e302cc30f59e159e7074034cf9e806e7 languageName: node linkType: hard @@ -4824,13 +4829,6 @@ __metadata: languageName: node linkType: hard -"@changesets/get-version-range-type@npm:^0.3.2": - version: 0.3.2 - resolution: "@changesets/get-version-range-type@npm:0.3.2" - checksum: 10/2b82db1eb373546cca646d57da0e32f24455bcb74b7c2dfc262e8e7a744b0aef3d669e2141c08a17192637594466f55cb6ff04f4eb4dec972656646d331c99aa - languageName: node - linkType: hard - "@changesets/get-version-range-type@npm:^0.4.0": version: 0.4.0 resolution: "@changesets/get-version-range-type@npm:0.4.0" @@ -4838,21 +4836,6 @@ __metadata: languageName: node linkType: hard -"@changesets/git@npm:^2.0.0": - version: 2.0.0 - resolution: "@changesets/git@npm:2.0.0" - dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/errors": "npm:^0.1.4" - "@changesets/types": "npm:^5.2.1" - "@manypkg/get-packages": "npm:^1.1.3" - is-subdir: "npm:^1.1.1" - micromatch: "npm:^4.0.2" - spawndamnit: "npm:^2.0.0" - checksum: 10/91811806ef609a3667260c111b5b248a5853cba1f6468184b025aa1a3f19fb4a4b54c7c286a4920f6877599e1641e06a7ee011332c152de5a66df422ac6b55c6 - languageName: node - linkType: hard - "@changesets/git@npm:^3.0.2": version: 3.0.2 resolution: "@changesets/git@npm:3.0.2" @@ -4866,12 +4849,16 @@ __metadata: languageName: node linkType: hard -"@changesets/logger@npm:^0.0.5": - version: 0.0.5 - resolution: "@changesets/logger@npm:0.0.5" +"@changesets/git@npm:^3.0.4": + version: 3.0.4 + resolution: "@changesets/git@npm:3.0.4" dependencies: - chalk: "npm:^2.1.0" - checksum: 10/f0edc1edd6bef23d78f4f3fd2028e5230c67d74c00a7318a3ae2aac167a46edaf0701c2cabd441dc10081722e9d6b85ad13e6103a1b08d7fa3b5aca6f5db65b3 + "@changesets/errors": "npm:^0.2.0" + "@manypkg/get-packages": "npm:^1.1.3" + is-subdir: "npm:^1.1.1" + micromatch: "npm:^4.0.8" + spawndamnit: "npm:^3.0.1" + checksum: 10/4f5a1f3354ec39d530df78b198eaaf2a8ef6cca873dd18efb8706aae09cab04e0d985abd236288644fac5d10cc5cb6ba2538c3e0be023c4d80790ff841f39fa6 languageName: node linkType: hard @@ -4884,16 +4871,6 @@ __metadata: languageName: node linkType: hard -"@changesets/parse@npm:^0.3.16": - version: 0.3.16 - resolution: "@changesets/parse@npm:0.3.16" - dependencies: - "@changesets/types": "npm:^5.2.1" - js-yaml: "npm:^3.13.1" - checksum: 10/769eaceff362748bbfcf3f6a0790cd56b7ee01abee59e03d0a150d66cfcd55e85d276e13c18dd4a9c68cb48140f1cebcabf94c49e72e734febc8eaf34b3e72f8 - languageName: node - linkType: hard - "@changesets/parse@npm:^0.4.0": version: 0.4.0 resolution: "@changesets/parse@npm:0.4.0" @@ -4904,16 +4881,13 @@ __metadata: languageName: node linkType: hard -"@changesets/pre@npm:^1.0.14": - version: 1.0.14 - resolution: "@changesets/pre@npm:1.0.14" +"@changesets/parse@npm:^0.4.1": + version: 0.4.1 + resolution: "@changesets/parse@npm:0.4.1" dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/errors": "npm:^0.1.4" - "@changesets/types": "npm:^5.2.1" - "@manypkg/get-packages": "npm:^1.1.3" - fs-extra: "npm:^7.0.1" - checksum: 10/fb81617af819bd47938c566558194b618dc60d0c8febf856e74076182d1733ed7dec72f486e0ed6589ef8e6ccaaa13008bbec7ee7c79375921a8f175222e14b4 + "@changesets/types": "npm:^6.1.0" + js-yaml: "npm:^3.13.1" + checksum: 10/2973ab8f38592a80efea589e148e5bdfd6ed3af86aa9206f941b5b3955f68464bf70a5965349f642667c708ebae60e4266be538328cd27075cace3f7cc1022e3 languageName: node linkType: hard @@ -4929,19 +4903,15 @@ __metadata: languageName: node linkType: hard -"@changesets/read@npm:^0.5.9": - version: 0.5.9 - resolution: "@changesets/read@npm:0.5.9" +"@changesets/pre@npm:^2.0.2": + version: 2.0.2 + resolution: "@changesets/pre@npm:2.0.2" dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/git": "npm:^2.0.0" - "@changesets/logger": "npm:^0.0.5" - "@changesets/parse": "npm:^0.3.16" - "@changesets/types": "npm:^5.2.1" - chalk: "npm:^2.1.0" + "@changesets/errors": "npm:^0.2.0" + "@changesets/types": "npm:^6.1.0" + "@manypkg/get-packages": "npm:^1.1.3" fs-extra: "npm:^7.0.1" - p-filter: "npm:^2.1.0" - checksum: 10/f12ee06dec2def36d3f6b6d0166fdfcbb95593c6eb911ba516989c304029d4fe9fcb60d3edd36c07f12e95cfa8a807c9b0096d45c74876d896a50ee8dfb721f8 + checksum: 10/daaedd2747492ced61f107d38f90e535607bcb073b10ffac3d9e3bcad1a4cc082370884224fc6785af2d92d37f6b0a3bf853f9759b8fda294878d00d24344415 languageName: node linkType: hard @@ -4960,6 +4930,21 @@ __metadata: languageName: node linkType: hard +"@changesets/read@npm:^0.6.5": + version: 0.6.5 + resolution: "@changesets/read@npm:0.6.5" + dependencies: + "@changesets/git": "npm:^3.0.4" + "@changesets/logger": "npm:^0.1.1" + "@changesets/parse": "npm:^0.4.1" + "@changesets/types": "npm:^6.1.0" + fs-extra: "npm:^7.0.1" + p-filter: "npm:^2.1.0" + picocolors: "npm:^1.1.0" + checksum: 10/fec0ac28801e0560fae0eb1d21250dd2a48aaff67bddd1b446a960afd761690d5873dca6eff369d43763bec61f1023d38a38876d5824e316e6de622dc52a24f3 + languageName: node + linkType: hard + "@changesets/should-skip-package@npm:^0.1.1": version: 0.1.1 resolution: "@changesets/should-skip-package@npm:0.1.1" @@ -4970,6 +4955,16 @@ __metadata: languageName: node linkType: hard +"@changesets/should-skip-package@npm:^0.1.2": + version: 0.1.2 + resolution: "@changesets/should-skip-package@npm:0.1.2" + dependencies: + "@changesets/types": "npm:^6.1.0" + "@manypkg/get-packages": "npm:^1.1.3" + checksum: 10/d09fcf1200ee201f0dd5b8049d90e8b5e0cfd34cc94f5c661c4cdab182a8263628733f9bc5886550a92f6f7857339d79fc77f12ffd53559b029a2bf9a2fa7ace + languageName: node + linkType: hard + "@changesets/types@npm:^4.0.1": version: 4.1.0 resolution: "@changesets/types@npm:4.1.0" @@ -4977,13 +4972,6 @@ __metadata: languageName: node linkType: hard -"@changesets/types@npm:^5.2.1": - version: 5.2.1 - resolution: "@changesets/types@npm:5.2.1" - checksum: 10/0783de5c1544c56c926efdbc1e9f04500e09395156e971e60e8de07a43627328a61d432bade108f15a12cd07776d866cc88fa5c61705dcae8640701327449674 - languageName: node - linkType: hard - "@changesets/types@npm:^6.0.0": version: 6.0.0 resolution: "@changesets/types@npm:6.0.0" @@ -4991,16 +4979,10 @@ __metadata: languageName: node linkType: hard -"@changesets/write@npm:^0.2.3": - version: 0.2.3 - resolution: "@changesets/write@npm:0.2.3" - dependencies: - "@babel/runtime": "npm:^7.20.1" - "@changesets/types": "npm:^5.2.1" - fs-extra: "npm:^7.0.1" - human-id: "npm:^1.0.2" - prettier: "npm:^2.7.1" - checksum: 10/40858ffcda3827f312312fbededbdd58d7ecb20547a501c8eaeedf88453fd3102de431f174beaf8b87adf382528951e223e93af77fc81cf34d184a543d77de26 +"@changesets/types@npm:^6.1.0": + version: 6.1.0 + resolution: "@changesets/types@npm:6.1.0" + checksum: 10/2dcd00712cb85d0c53afdd8d0e856b4bf9c0ce8dc36c838c918d44799aacd9ba8659b9ff610ff92b94fc03c8fd2b52c5b05418fcf8a1bd138cd9182414ede373 languageName: node linkType: hard @@ -5016,6 +4998,18 @@ __metadata: languageName: node linkType: hard +"@changesets/write@npm:^0.4.0": + version: 0.4.0 + resolution: "@changesets/write@npm:0.4.0" + dependencies: + "@changesets/types": "npm:^6.1.0" + fs-extra: "npm:^7.0.1" + human-id: "npm:^4.1.1" + prettier: "npm:^2.7.1" + checksum: 10/bcea8431a09e282bdf66adbd8411d5d3cc19b4a2df519a42586c912b23a7b3ef18d1d0765e2d1a27ff175e2dfc9ef4c2df95cfa920dd4dd2972aaaf662afc6b9 + languageName: node + linkType: hard + "@cloudflare/kv-asset-handler@npm:0.3.4": version: 0.3.4 resolution: "@cloudflare/kv-asset-handler@npm:0.3.4" @@ -5369,14 +5363,14 @@ __metadata: languageName: node linkType: hard -"@effect/schema@npm:0.71.1": - version: 0.71.1 - resolution: "@effect/schema@npm:0.71.1" +"@effect/schema@npm:^0.75.5": + version: 0.75.5 + resolution: "@effect/schema@npm:0.75.5" dependencies: fast-check: "npm:^3.21.0" peerDependencies: - effect: ^3.6.5 - checksum: 10/d8ef78980409ce6fbe3de5ccba976cdcce86014916613a12dc9826ee27ad64e8f94579718a3099c6a9fc460aaafd471f573e75f74dfeaafb89b98275b4d2ed70 + effect: ^3.9.2 + checksum: 10/597f33a7b678d43f461c03a432c23a02e823aaa6ca49fad1feec2215c7e5898bff6e238c044d6788d645a2c6b4b803aac4f32731f1b0dc1b649949162360d884 languageName: node linkType: hard @@ -7633,7 +7627,7 @@ __metadata: ethers: "npm:^5.7.2" jest: "npm:^29.7.0" nodemon: "npm:^3.0.3" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" ts-jest: "npm:^29.1.2" ts-node: "npm:^10.8.0" tsx: "npm:^4.19.1" @@ -7677,7 +7671,7 @@ __metadata: ethers: "npm:^5.7.2" latest-version: "npm:^8.0.0" mocha: "npm:^10.2.0" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" terminal-link: "npm:^3.0.0" tsx: "npm:^4.19.1" typescript: "npm:5.3.3" @@ -7720,10 +7714,10 @@ __metadata: hardhat: "npm:^2.22.2" hardhat-gas-reporter: "npm:^1.0.9" hardhat-ignore-warnings: "npm:^0.2.11" - prettier: "npm:^2.8.8" - prettier-plugin-solidity: "npm:^1.1.3" - solhint: "npm:^4.5.4" - solhint-plugin-prettier: "npm:^0.0.5" + prettier: "npm:^3.5.3" + prettier-plugin-solidity: "npm:^1.4.2" + solhint: "npm:^5.0.5" + solhint-plugin-prettier: "npm:^0.1.0" solidity-bytes-utils: "npm:^0.8.0" solidity-coverage: "npm:^0.8.3" ts-generator: "npm:^0.1.1" @@ -7755,7 +7749,7 @@ __metadata: eslint-plugin-import: "npm:^2.31.0" mocha: "npm:^10.2.0" mocha-steps: "npm:^1.3.0" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" typescript: "npm:5.3.3" typescript-eslint: "npm:^8.23.0" languageName: unknown @@ -7773,7 +7767,7 @@ __metadata: eslint-import-resolver-typescript: "npm:^3.6.3" eslint-plugin-import: "npm:^2.31.0" long: "npm:^5.2.4" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" protobufjs: "npm:^7.4.0" typescript: "npm:5.3.3" typescript-eslint: "npm:^8.23.0" @@ -7788,7 +7782,7 @@ __metadata: "@cloudflare/workers-types": "npm:^4.20240821.1" "@faker-js/faker": "npm:^8.4.1" chai: "npm:^4.5.0" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" typescript: "npm:5.3.3" vitest: "npm:1.4.0" wrangler: "npm:^3.74.0" @@ -7806,7 +7800,7 @@ __metadata: "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" - "@trivago/prettier-plugin-sort-imports": "npm:^4.2.1" + "@trivago/prettier-plugin-sort-imports": "npm:^5.2.2" "@typechain/ethers-v5": "npm:^11.1.2" "@typechain/ethers-v6": "npm:^0.5.1" "@typechain/hardhat": "npm:^9.1.0" @@ -7821,10 +7815,10 @@ __metadata: ethers: "npm:^5.7.2" hardhat: "npm:^2.22.2" hardhat-gas-reporter: "npm:^1.0.9" - prettier: "npm:^2.8.8" - prettier-plugin-solidity: "npm:^1.1.3" - solhint: "npm:^4.5.4" - solhint-plugin-prettier: "npm:^0.0.5" + prettier: "npm:^3.5.3" + prettier-plugin-solidity: "npm:^1.4.2" + solhint: "npm:^5.0.5" + solhint-plugin-prettier: "npm:^0.1.0" solidity-coverage: "npm:^0.8.3" ts-node: "npm:^10.8.0" typechain: "patch:typechain@npm%3A8.3.2#~/.yarn/patches/typechain-npm-8.3.2-b02e27439e.patch" @@ -7881,7 +7875,7 @@ __metadata: json-stable-stringify: "npm:^1.1.1" mocha: "npm:^10.2.0" postgres: "npm:^3.4.5" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" prom-client: "npm:^14.0.1" prompts: "npm:^2.4.2" tsx: "npm:^4.19.1" @@ -7899,7 +7893,7 @@ __metadata: dependencies: "@changesets/cli": "npm:^2.26.2" "@eslint/js": "npm:^9.15.0" - "@trivago/prettier-plugin-sort-imports": "npm:^4.2.1" + "@trivago/prettier-plugin-sort-imports": "npm:^5.2.2" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" eslint: "npm:^9.15.0" @@ -7909,7 +7903,8 @@ __metadata: eslint-plugin-jest: "npm:^28.2.0" husky: "npm:^8.0.0" lint-staged: "npm:^12.4.3" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" + prettier-plugin-solidity: "npm:^1.4.2" syncpack: "npm:^13.0.0" tsx: "npm:^4.19.1" languageName: unknown @@ -7966,7 +7961,7 @@ __metadata: hardhat: "npm:^2.22.2" mocha: "npm:^10.2.0" pino: "npm:^8.19.0" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" sinon: "npm:^13.0.2" starknet: "npm:^6.23.1" ts-node: "npm:^10.8.0" @@ -8005,7 +8000,7 @@ __metadata: lodash-es: "npm:^4.17.21" mocha: "npm:^10.2.0" pino: "npm:^8.19.0" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" sinon: "npm:^13.0.2" typescript: "npm:5.3.3" yaml: "npm:2.4.5" @@ -8060,7 +8055,7 @@ __metadata: eslint-plugin-storybook: "npm:^0.11.1" framer-motion: "npm:^10.16.4" postcss: "npm:^8.4.21" - prettier: "npm:^2.8.8" + prettier: "npm:^3.5.3" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" react-tooltip: "npm:^5.28.0" @@ -10628,6 +10623,15 @@ __metadata: languageName: node linkType: hard +"@prettier/sync@npm:^0.3.0": + version: 0.3.0 + resolution: "@prettier/sync@npm:0.3.0" + peerDependencies: + prettier: ^3.0.0 + checksum: 10/10520d2479830c18256bf2b18349a15a8964f968059f7ccaa1f6d455c133a26e6c96bd7aea7c64ef6701700e8007ad68619b16ec83391b356b5d4a741cbae504 + languageName: node + linkType: hard + "@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": version: 1.1.2 resolution: "@protobufjs/aspromise@npm:1.1.2" @@ -14662,19 +14666,10 @@ __metadata: languageName: node linkType: hard -"@solidity-parser/parser@npm:^0.16.2": - version: 0.16.2 - resolution: "@solidity-parser/parser@npm:0.16.2" - dependencies: - antlr4ts: "npm:^0.5.0-alpha.4" - checksum: 10/a95b0c45331623e587e938e69d49832814ab8867371bd58f9946346b99a18705ee47b98a1006c3da6dae33ec8c8c1bf7d203a4acfa1ced0bfd68632acf2cd19c - languageName: node - linkType: hard - -"@solidity-parser/parser@npm:^0.18.0": - version: 0.18.0 - resolution: "@solidity-parser/parser@npm:0.18.0" - checksum: 10/3b600b584f49bd84d6d27aeeb453c49c279df49324e104bda00d12cd3b26f18cb6230ff63db6a0ba1f383868620d318b15b7417a92aa8c580099449adaa13d76 +"@solidity-parser/parser@npm:^0.19.0": + version: 0.19.0 + resolution: "@solidity-parser/parser@npm:0.19.0" + checksum: 10/2136708ecc988b534efcf836e95f4f02a1452ab0c026438014c35ce31b26dc011cc8c512d502fc7bcb968f850ab7e524838292bc36cad6a144fedb4c29685587 languageName: node linkType: hard @@ -14848,6 +14843,13 @@ __metadata: languageName: node linkType: hard +"@standard-schema/spec@npm:^1.0.0": + version: 1.0.0 + resolution: "@standard-schema/spec@npm:1.0.0" + checksum: 10/aee780cc1431888ca4b9aba9b24ffc8f3073fc083acc105e3951481478a2f4dc957796931b2da9e2d8329584cf211e4542275f188296c1cdff3ed44fd93a8bc8 + languageName: node + linkType: hard + "@starknet-io/get-starknet-core@npm:4.0.6, @starknet-io/get-starknet-core@npm:^4.0.4": version: 4.0.6 resolution: "@starknet-io/get-starknet-core@npm:4.0.6" @@ -15827,23 +15829,29 @@ __metadata: languageName: node linkType: hard -"@trivago/prettier-plugin-sort-imports@npm:^4.2.1": - version: 4.3.0 - resolution: "@trivago/prettier-plugin-sort-imports@npm:4.3.0" +"@trivago/prettier-plugin-sort-imports@npm:^5.2.2": + version: 5.2.2 + resolution: "@trivago/prettier-plugin-sort-imports@npm:5.2.2" dependencies: - "@babel/generator": "npm:7.17.7" - "@babel/parser": "npm:^7.20.5" - "@babel/traverse": "npm:7.23.2" - "@babel/types": "npm:7.17.0" - javascript-natural-sort: "npm:0.7.1" + "@babel/generator": "npm:^7.26.5" + "@babel/parser": "npm:^7.26.7" + "@babel/traverse": "npm:^7.26.7" + "@babel/types": "npm:^7.26.7" + javascript-natural-sort: "npm:^0.7.1" lodash: "npm:^4.17.21" peerDependencies: "@vue/compiler-sfc": 3.x prettier: 2.x - 3.x + prettier-plugin-svelte: 3.x + svelte: 4.x || 5.x peerDependenciesMeta: "@vue/compiler-sfc": optional: true - checksum: 10/eb25cbeeaf85d3acd54019d1f3563447337a2faee7a35558adb69dff44ce3b93714a5b64ba4d0374f3df3191c32c993d441493fdc43a2c97c9b8a0e3d58702cf + prettier-plugin-svelte: + optional: true + svelte: + optional: true + checksum: 10/b169a2b122366790303981cb5368bc884e6072e350ec8f8eebeeac9d6124089eec6a2e47e98f71332765b798b2a9a11cf6f164dafbc7bc98d1e63ba456156c39 languageName: node linkType: hard @@ -16296,15 +16304,6 @@ __metadata: languageName: node linkType: hard -"@types/is-ci@npm:^3.0.0": - version: 3.0.4 - resolution: "@types/is-ci@npm:3.0.4" - dependencies: - ci-info: "npm:^3.1.0" - checksum: 10/5cb58dd3b64830bf2ce577017f554139cd35e3250a3feb3c2d5e5a2cb261cca909cf68faab6f31dde0c054719c7b360dd0f46d3a83a05b1e78453a9872d056c5 - languageName: node - linkType: hard - "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" @@ -16330,7 +16329,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.5": +"@types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.5": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7 @@ -16454,13 +16453,6 @@ __metadata: languageName: node linkType: hard -"@types/minimist@npm:^1.2.0": - version: 1.2.5 - resolution: "@types/minimist@npm:1.2.5" - checksum: 10/477047b606005058ab0263c4f58097136268007f320003c348794f74adedc3166ffc47c80ec3e94687787f2ab7f4e72c468223946e79892cf0fd9e25e9970a90 - languageName: node - linkType: hard - "@types/mkdirp@npm:^0.5.2": version: 0.5.2 resolution: "@types/mkdirp@npm:0.5.2" @@ -16784,13 +16776,6 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.5.0": - version: 7.5.6 - resolution: "@types/semver@npm:7.5.6" - checksum: 10/e77282b17f74354e17e771c0035cccb54b94cc53d0433fa7e9ba9d23fd5d7edcd14b6c8b7327d58bbd89e83b1c5eda71dfe408e06b929007e2b89586e9b63459 - languageName: node - linkType: hard - "@types/send@npm:*": version: 0.17.4 resolution: "@types/send@npm:0.17.4" @@ -17035,16 +17020,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/scope-manager@npm:6.21.0" - dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - checksum: 10/fe91ac52ca8e09356a71dc1a2f2c326480f3cccfec6b2b6d9154c1a90651ab8ea270b07c67df5678956c3bbf0bbe7113ab68f68f21b20912ea528b1214197395 - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:8.16.0": version: 8.16.0 resolution: "@typescript-eslint/scope-manager@npm:8.16.0" @@ -17065,6 +17040,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/scope-manager@npm:8.31.0" + dependencies: + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/visitor-keys": "npm:8.31.0" + checksum: 10/4ca30db2e6186415bcfa5bba24f55f3508c383d755cc3599c08087b04587276620b5d094439cd3df3e88bce25ad0f5bd2a4a7473ae59410c8ff9e72f87d7648e + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.16.0": version: 8.16.0 resolution: "@typescript-eslint/type-utils@npm:8.16.0" @@ -17097,13 +17082,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/types@npm:6.21.0" - checksum: 10/e26da86d6f36ca5b6ef6322619f8ec55aabcd7d43c840c977ae13ae2c964c3091fc92eb33730d8be08927c9de38466c5323e78bfb270a9ff1d3611fe821046c5 - languageName: node - linkType: hard - "@typescript-eslint/types@npm:8.16.0": version: 8.16.0 resolution: "@typescript-eslint/types@npm:8.16.0" @@ -17118,22 +17096,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" - dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - minimatch: "npm:9.0.3" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10/b32fa35fca2a229e0f5f06793e5359ff9269f63e9705e858df95d55ca2cd7fdb5b3e75b284095a992c48c5fc46a1431a1a4b6747ede2dd08929dc1cbacc589b8 +"@typescript-eslint/types@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/types@npm:8.31.0" + checksum: 10/937eca69241850ad94a5c93221191f2cbc448951f1672e913d106efe2bdd30d188c54d2502cbff5d4d9b3a95becf16387a20644239b1fee7458198cbdac4f923 languageName: node linkType: hard @@ -17174,6 +17140,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.31.0" + dependencies: + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/visitor-keys": "npm:8.31.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.0.1" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10/e2155504e2231e69c909e0268b63979e3829d4e5b3845c4272b72de3cb855d225c26639d9dc23b2753464a9f6c5c8a31665640a90e10da20eb9462eff9115261 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.16.0, @typescript-eslint/utils@npm:^8.8.1": version: 8.16.0 resolution: "@typescript-eslint/utils@npm:8.16.0" @@ -17206,30 +17190,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:^6.0.0": - version: 6.21.0 - resolution: "@typescript-eslint/utils@npm:6.21.0" +"@typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0": + version: 8.31.0 + resolution: "@typescript-eslint/utils@npm:8.31.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@types/json-schema": "npm:^7.0.12" - "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" - semver: "npm:^7.5.4" + "@typescript-eslint/scope-manager": "npm:8.31.0" + "@typescript-eslint/types": "npm:8.31.0" + "@typescript-eslint/typescript-estree": "npm:8.31.0" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: 10/b404a2c55a425a79d054346ae123087d30c7ecf7ed7abcf680c47bf70c1de4fabadc63434f3f460b2fa63df76bc9e4a0b9fa2383bb8a9fcd62733fb5c4e4f3e3 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" - dependencies: - "@typescript-eslint/types": "npm:6.21.0" - eslint-visitor-keys: "npm:^3.4.1" - checksum: 10/30422cdc1e2ffad203df40351a031254b272f9c6f2b7e02e9bfa39e3fc2c7b1c6130333b0057412968deda17a3a68a578a78929a8139c6acef44d9d841dc72e1 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10/9e8fcef36bff920ba4eacc4289efc74a9aa65462849061d37d3014286948c8318b031a852555c7a7fe9cdf646458a2f82f7138171f7072ac595293979d5fd3a4 languageName: node linkType: hard @@ -17253,6 +17225,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.31.0": + version: 8.31.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.31.0" + dependencies: + "@typescript-eslint/types": "npm:8.31.0" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10/85417c4fb44735ace29201afa446e71bbdef074bf4543701c149eda22d51bf7b01c4da3ffc574dd9ef8b33ac4b5dea35a50326e413f223d2f5e73e4dc8e3c8ee + languageName: node + linkType: hard + "@vanilla-extract/css-utils@npm:^0.1.4": version: 0.1.4 resolution: "@vanilla-extract/css-utils@npm:0.1.4" @@ -18820,7 +18802,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.flat@npm:^1.2.3, array.prototype.flat@npm:^1.3.1, array.prototype.flat@npm:^1.3.2": +"array.prototype.flat@npm:^1.3.1, array.prototype.flat@npm:^1.3.2": version: 1.3.2 resolution: "array.prototype.flat@npm:1.3.2" dependencies: @@ -18901,13 +18883,6 @@ __metadata: languageName: node linkType: hard -"arrify@npm:^1.0.1": - version: 1.0.1 - resolution: "arrify@npm:1.0.1" - checksum: 10/745075dd4a4624ff0225c331dacb99be501a515d39bcb7c84d24660314a6ec28e68131b137e6f7e16318170842ce97538cd298fc4cd6b2cc798e0b957f2747e7 - languageName: node - linkType: hard - "as-table@npm:^1.0.36": version: 1.0.55 resolution: "as-table@npm:1.0.55" @@ -19741,15 +19716,6 @@ __metadata: languageName: node linkType: hard -"breakword@npm:^1.0.5": - version: 1.0.6 - resolution: "breakword@npm:1.0.6" - dependencies: - wcwidth: "npm:^1.0.1" - checksum: 10/e8a3f308c0214986e1b768ca4460a798ffe4bbe08c375576de526431a01a9738318710cc05e309486ac5809d77d9f33d957f80939a890e07be5e89baad9816f8 - languageName: node - linkType: hard - "brorand@npm:^1.0.1, brorand@npm:^1.1.0": version: 1.1.0 resolution: "brorand@npm:1.1.0" @@ -20179,17 +20145,6 @@ __metadata: languageName: node linkType: hard -"camelcase-keys@npm:^6.2.2": - version: 6.2.2 - resolution: "camelcase-keys@npm:6.2.2" - dependencies: - camelcase: "npm:^5.3.1" - map-obj: "npm:^4.0.0" - quick-lru: "npm:^4.0.1" - checksum: 10/c1999f5b6d03bee7be9a36e48eef3da9e93e51b000677348ec8d15d51fc4418375890fb6c7155e387322d2ebb2a2cdebf9cd96607a6753d1d6c170d9b1e2eed5 - languageName: node - linkType: hard - "camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" @@ -20380,7 +20335,7 @@ __metadata: languageName: node linkType: hard -"chalk-template@npm:1.1.0": +"chalk-template@npm:^1.1.0": version: 1.1.0 resolution: "chalk-template@npm:1.1.0" dependencies: @@ -20389,14 +20344,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:5.3.0, chalk@npm:^5.2.0, chalk@npm:^5.3.0": - version: 5.3.0 - resolution: "chalk@npm:5.3.0" - checksum: 10/6373caaab21bd64c405bfc4bd9672b145647fc9482657b5ea1d549b3b2765054e9d3d928870cdf764fb4aad67555f5061538ff247b8310f110c5c888d92397ea - languageName: node - linkType: hard - -"chalk@npm:^2.0.0, chalk@npm:^2.1.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": +"chalk@npm:^2.0.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -20427,6 +20375,20 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.2.0, chalk@npm:^5.3.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 10/6373caaab21bd64c405bfc4bd9672b145647fc9482657b5ea1d549b3b2765054e9d3d928870cdf764fb4aad67555f5061538ff247b8310f110c5c888d92397ea + languageName: node + linkType: hard + +"chalk@npm:^5.4.1": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: 10/29df3ffcdf25656fed6e95962e2ef86d14dfe03cd50e7074b06bad9ffbbf6089adbb40f75c00744d843685c8d008adaf3aed31476780312553caf07fa86e5bc7 + languageName: node + linkType: hard + "char-regex@npm:^1.0.2": version: 1.0.2 resolution: "char-regex@npm:1.0.2" @@ -20549,7 +20511,7 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0, ci-info@npm:^3.7.0": +"ci-info@npm:^3.2.0, ci-info@npm:^3.7.0": version: 3.9.0 resolution: "ci-info@npm:3.9.0" checksum: 10/75bc67902b4d1c7b435497adeb91598f6d52a3389398e44294f6601b20cfef32cf2176f7be0eb961d9e085bb333a8a5cae121cb22f81cf238ae7f58eb80e9397 @@ -20653,12 +20615,12 @@ __metadata: languageName: node linkType: hard -"cli-cursor@npm:^4.0.0": - version: 4.0.0 - resolution: "cli-cursor@npm:4.0.0" +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" dependencies: - restore-cursor: "npm:^4.0.0" - checksum: 10/ab3f3ea2076e2176a1da29f9d64f72ec3efad51c0960898b56c8a17671365c26e67b735920530eaf7328d61f8bd41c27f46b9cf6e4e10fe2fa44b5e8c0e392cc + restore-cursor: "npm:^5.0.0" + checksum: 10/1eb9a3f878b31addfe8d82c6d915ec2330cec8447ab1f117f4aa34f0137fbb3137ec3466e1c9a65bcb7557f6e486d343f2da57f253a2f668d691372dfa15c090 languageName: node linkType: hard @@ -20909,9 +20871,9 @@ __metadata: linkType: hard "colorette@npm:^2.0.16": - version: 2.0.17 - resolution: "colorette@npm:2.0.17" - checksum: 10/2de04abe276beff3ddf3b3f93206bd0062cf6468aaa3f6866b0f390c4cf0c1ff18b79f93d5e61deebf2f1d177fff1a2f206f84248c998b21b306eb78ff14a380 + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 10/0b8de48bfa5d10afc160b8eaa2b9938f34a892530b2f7d7897e0458d9535a066e3998b49da9d21161c78225b272df19ae3a64d6df28b4c9734c0e55bbd02406f languageName: node linkType: hard @@ -20962,13 +20924,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:12.1.0, commander@npm:^12.1.0": - version: 12.1.0 - resolution: "commander@npm:12.1.0" - checksum: 10/cdaeb672d979816853a4eed7f1310a9319e8b976172485c2a6b437ed0db0a389a44cfb222bfbde772781efa9f215bdd1b936f80d6b249485b465c6cb906e1f93 - languageName: node - linkType: hard - "commander@npm:3.0.2": version: 3.0.2 resolution: "commander@npm:3.0.2" @@ -20983,6 +20938,20 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10/cdaeb672d979816853a4eed7f1310a9319e8b976172485c2a6b437ed0db0a389a44cfb222bfbde772781efa9f215bdd1b936f80d6b249485b465c6cb906e1f93 + languageName: node + linkType: hard + +"commander@npm:^13.1.0": + version: 13.1.0 + resolution: "commander@npm:13.1.0" + checksum: 10/d3b4b79e6be8471ddadacbb8cd441fe82154d7da7393b50e76165a9e29ccdb74fa911a186437b9a211d0fc071db6051915c94fb8ef16d77511d898e9dbabc6af + languageName: node + linkType: hard + "commander@npm:^2.20.3": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -21012,9 +20981,9 @@ __metadata: linkType: hard "commander@npm:^9.3.0": - version: 9.3.0 - resolution: "commander@npm:9.3.0" - checksum: 10/18c49c9d7329847720c5eb453b1b2720db11dc44182abb0e814820c6598fa82184ac52aca26f4b4a57131ff91713326eff351ae8ad02b0c49222626cf8cacc3d + version: 9.5.0 + resolution: "commander@npm:9.5.0" + checksum: 10/41c49b3d0f94a1fbeb0463c85b13f15aa15a9e0b4d5e10a49c0a1d58d4489b549d62262b052ae0aa6cfda53299bee487bfe337825df15e342114dde543f82906 languageName: node linkType: hard @@ -21238,23 +21207,6 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:9.0.0": - version: 9.0.0 - resolution: "cosmiconfig@npm:9.0.0" - dependencies: - env-paths: "npm:^2.2.1" - import-fresh: "npm:^3.3.0" - js-yaml: "npm:^4.1.0" - parse-json: "npm:^5.2.0" - peerDependencies: - typescript: ">=4.9.5" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10/8bdf1dfbb6fdb3755195b6886dc0649a3c742ec75afa4cb8da7b070936aed22a4f4e5b7359faafe03180358f311dbc300d248fd6586c458203d376a40cc77826 - languageName: node - linkType: hard - "cosmiconfig@npm:^7.0.0": version: 7.1.0 resolution: "cosmiconfig@npm:7.1.0" @@ -21285,6 +21237,23 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:^9.0.0": + version: 9.0.0 + resolution: "cosmiconfig@npm:9.0.0" + dependencies: + env-paths: "npm:^2.2.1" + import-fresh: "npm:^3.3.0" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.2.0" + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/8bdf1dfbb6fdb3755195b6886dc0649a3c742ec75afa4cb8da7b070936aed22a4f4e5b7359faafe03180358f311dbc300d248fd6586c458203d376a40cc77826 + languageName: node + linkType: hard + "cosmjs-types@npm:^0.9.0": version: 0.9.0 resolution: "cosmjs-types@npm:0.9.0" @@ -21381,17 +21350,6 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^5.1.0": - version: 5.1.0 - resolution: "cross-spawn@npm:5.1.0" - dependencies: - lru-cache: "npm:^4.0.1" - shebang-command: "npm:^1.2.0" - which: "npm:^1.2.9" - checksum: 10/726939c9954fc70c20e538923feaaa33bebc253247d13021737c3c7f68cdc3e0a57f720c0fe75057c0387995349f3f12e20e9bfdbf12274db28019c7ea4ec166 - languageName: node - linkType: hard - "cross-spawn@npm:^6.0.5": version: 6.0.5 resolution: "cross-spawn@npm:6.0.5" @@ -21487,39 +21445,6 @@ __metadata: languageName: node linkType: hard -"csv-generate@npm:^3.4.3": - version: 3.4.3 - resolution: "csv-generate@npm:3.4.3" - checksum: 10/93f18eb1897a886ca5e45d22e82b6c026ac3e9dc3f04918e797d14b1f8e22234a14c018bbbf55a3cc2cfe4284bfa6b8a45097f902451af738911f0e2b0c6d0ed - languageName: node - linkType: hard - -"csv-parse@npm:^4.16.3": - version: 4.16.3 - resolution: "csv-parse@npm:4.16.3" - checksum: 10/b873dd2d312ac0329200f13788176bae3073862241483b0339a4777c9eddcebd9f2f48f13d02dc0baf4bc02e957f886ea03a9cb22160d70836b0017432f8fa41 - languageName: node - linkType: hard - -"csv-stringify@npm:^5.6.5": - version: 5.6.5 - resolution: "csv-stringify@npm:5.6.5" - checksum: 10/efed94869b8426e6a983f2237bd74eff15953e2e27affee9c1324f66a67dabe948573c4c21a8661a79aa20b58efbcafcf11c34e80bdd532a43f35e9cde5985b9 - languageName: node - linkType: hard - -"csv@npm:^5.5.3": - version: 5.5.3 - resolution: "csv@npm:5.5.3" - dependencies: - csv-generate: "npm:^3.4.3" - csv-parse: "npm:^4.16.3" - csv-stringify: "npm:^5.6.5" - stream-transform: "npm:^2.1.3" - checksum: 10/3928e1d88b98f0c3aa26e078cfca36086e0953afa5e83f45fa769b0f6fb4f79e82b4dfd400e8c61637edf144b2650f6ba8c585ec1aad11a6cda84aa04da5bc38 - languageName: node - linkType: hard - "d@npm:1, d@npm:^1.0.1": version: 1.0.1 resolution: "d@npm:1.0.1" @@ -21660,17 +21585,7 @@ __metadata: languageName: node linkType: hard -"decamelize-keys@npm:^1.1.0": - version: 1.1.1 - resolution: "decamelize-keys@npm:1.1.1" - dependencies: - decamelize: "npm:^1.1.0" - map-obj: "npm:^1.0.0" - checksum: 10/71d5898174f17a8d2303cecc98ba0236e842948c4d042a8180d5e749be8442220bca2d16dd93bebd7b49e86c807814273212e4da0fae67be7c58c282ff76057a - languageName: node - linkType: hard - -"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": +"decamelize@npm:^1.2.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" checksum: 10/ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa @@ -22349,10 +22264,13 @@ __metadata: languageName: node linkType: hard -"effect@npm:3.6.5": - version: 3.6.5 - resolution: "effect@npm:3.6.5" - checksum: 10/e722cc1d262dfcff85b3e43d11edafb03d68e0acf670ed0d709d32218e6bf2ae7084ac627430094b1be6aee6ffdeec061b1d097d2216fce18ebc7264087ab2f0 +"effect@npm:^3.13.7": + version: 3.14.13 + resolution: "effect@npm:3.14.13" + dependencies: + "@standard-schema/spec": "npm:^1.0.0" + fast-check: "npm:^3.23.1" + checksum: 10/a55a0427a022be300d7288aea53053216c47c050b109e9c72bc7d1bcc3ac9ca63f8f44afc0a9bb5b308985259a97cd9ec593e984f958b9a53921faa49bc600a6 languageName: node linkType: hard @@ -22534,22 +22452,22 @@ __metadata: languageName: node linkType: hard -"enquirer@npm:2.4.1, enquirer@npm:^2.3.6, enquirer@npm:^2.4.1": - version: 2.4.1 - resolution: "enquirer@npm:2.4.1" +"enquirer@npm:^2.3.0": + version: 2.3.6 + resolution: "enquirer@npm:2.3.6" dependencies: ansi-colors: "npm:^4.1.1" - strip-ansi: "npm:^6.0.1" - checksum: 10/b3726486cd98f0d458a851a03326a2a5dd4d84f37ff94ff2a2960c915e0fc865865da3b78f0877dc36ac5c1189069eca603e82ec63d5bc6b0dd9985bf6426d7a + checksum: 10/751d14f037eb7683997e696fb8d5fe2675e0b0cde91182c128cf598acf3f5bd9005f35f7c2a9109e291140af496ebec237b6dac86067d59a9b44f3688107f426 languageName: node linkType: hard -"enquirer@npm:^2.3.0": - version: 2.3.6 - resolution: "enquirer@npm:2.3.6" +"enquirer@npm:^2.3.6, enquirer@npm:^2.4.1": + version: 2.4.1 + resolution: "enquirer@npm:2.4.1" dependencies: ansi-colors: "npm:^4.1.1" - checksum: 10/751d14f037eb7683997e696fb8d5fe2675e0b0cde91182c128cf598acf3f5bd9005f35f7c2a9109e291140af496ebec237b6dac86067d59a9b44f3688107f426 + strip-ansi: "npm:^6.0.1" + checksum: 10/b3726486cd98f0d458a851a03326a2a5dd4d84f37ff94ff2a2960c915e0fc865865da3b78f0877dc36ac5c1189069eca603e82ec63d5bc6b0dd9985bf6426d7a languageName: node linkType: hard @@ -23421,12 +23339,12 @@ __metadata: linkType: hard "eslint-plugin-jest@npm:^28.2.0": - version: 28.2.0 - resolution: "eslint-plugin-jest@npm:28.2.0" + version: 28.11.0 + resolution: "eslint-plugin-jest@npm:28.11.0" dependencies: - "@typescript-eslint/utils": "npm:^6.0.0" + "@typescript-eslint/utils": "npm:^6.0.0 || ^7.0.0 || ^8.0.0" peerDependencies: - "@typescript-eslint/eslint-plugin": ^6.0.0 || ^7.0.0 + "@typescript-eslint/eslint-plugin": ^6.0.0 || ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 jest: "*" peerDependenciesMeta: @@ -23434,7 +23352,7 @@ __metadata: optional: true jest: optional: true - checksum: 10/029a3d140a561d941580cbfee15ccacf4584971975f61111f07b87f01bf64c9739607cbe8e6fd3888429179ea8fd733e655ccd87b3b83b3b5cee2187e2355a4e + checksum: 10/7f3896ec2dc03110688bb9f359a7aa1ba1a6d9a60ffbc3642361c4aaf55afcba9ce36b6609b20b1507028c2170ffe29b0f3e9cc9b7fe12fdd233740a2f9ce0a1 languageName: node linkType: hard @@ -23505,13 +23423,6 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.4.1": - version: 3.4.1 - resolution: "eslint-visitor-keys@npm:3.4.1" - checksum: 10/92641e7ccde470065aa2931161a6a053690a54aae35ae08f38e376ecfd7c012573c542b37a3baecf921eb951fd57943411392f464c2b8f3399adee4723a1369f - languageName: node - linkType: hard - "eslint-visitor-keys@npm:^4.2.0": version: 4.2.0 resolution: "eslint-visitor-keys@npm:4.2.0" @@ -24300,21 +24211,21 @@ __metadata: languageName: node linkType: hard -"fast-check@npm:3.21.0": - version: 3.21.0 - resolution: "fast-check@npm:3.21.0" +"fast-check@npm:^3.21.0": + version: 3.23.1 + resolution: "fast-check@npm:3.23.1" dependencies: pure-rand: "npm:^6.1.0" - checksum: 10/64e221858d5d98c6ea10c81e6a1a66760565bca41883466891974197a5439c7f0fe1dc293b8d887eaf959d0ff85197cc9d76813ae6215b018cde313254db7d53 + checksum: 10/03720c2d4adf02701a2e974b83d6439477851a6524c5980df0870dc0032f0200cc5e157f47641afa79dc42733b05058f2333df54291d5ac39d108d317a62e6c0 languageName: node linkType: hard -"fast-check@npm:^3.21.0": - version: 3.23.1 - resolution: "fast-check@npm:3.23.1" +"fast-check@npm:^3.23.1, fast-check@npm:^3.23.2": + version: 3.23.2 + resolution: "fast-check@npm:3.23.2" dependencies: pure-rand: "npm:^6.1.0" - checksum: 10/03720c2d4adf02701a2e974b83d6439477851a6524c5980df0870dc0032f0200cc5e157f47641afa79dc42733b05058f2333df54291d5ac39d108d317a62e6c0 + checksum: 10/dab344146b778e8bc2973366ea55528d1b58d3e3037270262b877c54241e800c4d744957722c24705c787020d702aece11e57c9e3dbd5ea19c3e10926bf1f3fe languageName: node linkType: hard @@ -24365,6 +24276,19 @@ __metadata: languageName: node linkType: hard +"fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10/dcc6432b269762dd47381d8b8358bf964d8f4f60286ac6aa41c01ade70bda459ff2001b516690b96d5365f68a49242966112b5d5cc9cd82395fa8f9d017c90ad + languageName: node + linkType: hard + "fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -24624,16 +24548,6 @@ __metadata: languageName: node linkType: hard -"find-yarn-workspace-root2@npm:1.2.16": - version: 1.2.16 - resolution: "find-yarn-workspace-root2@npm:1.2.16" - dependencies: - micromatch: "npm:^4.0.2" - pkg-dir: "npm:^4.2.0" - checksum: 10/398aa473ac245d9c9e9af5a75806b5a6828bd9a759f138faf4666f00c5fcb78af679d43f5cfbe73fe667cf6ec3ef6c9e157b09400181e5b9edc3adc47080e9bb - languageName: node - linkType: hard - "find-yarn-workspace-root@npm:^2.0.0": version: 2.0.0 resolution: "find-yarn-workspace-root@npm:2.0.0" @@ -25641,20 +25555,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:14.0.2": - version: 14.0.2 - resolution: "globby@npm:14.0.2" - dependencies: - "@sindresorhus/merge-streams": "npm:^2.1.0" - fast-glob: "npm:^3.3.2" - ignore: "npm:^5.2.4" - path-type: "npm:^5.0.0" - slash: "npm:^5.1.0" - unicorn-magic: "npm:^0.1.0" - checksum: 10/67660da70fc1223f7170c1a62ba6c373385e9e39765d952b6518606dec15ed8c7958e9dae6ba5752a31dbc1e9126f146938b830ad680fe794141734ffc3fbb75 - languageName: node - linkType: hard - "globby@npm:^10.0.1": version: 10.0.2 resolution: "globby@npm:10.0.2" @@ -25671,7 +25571,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.0.0, globby@npm:^11.0.1, globby@npm:^11.0.2, globby@npm:^11.1.0": +"globby@npm:^11.0.0, globby@npm:^11.0.1, globby@npm:^11.0.2": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -25685,6 +25585,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:^14.1.0": + version: 14.1.0 + resolution: "globby@npm:14.1.0" + dependencies: + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.3" + ignore: "npm:^7.0.3" + path-type: "npm:^6.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.3.0" + checksum: 10/e527ff54f0dddf60abfabd0d9e799768619d957feecd8b13ef60481f270bfdce0d28f6b09267c60f8064798fb3003b8ec991375f7fe0233fbce5304e1741368c + languageName: node + linkType: hard + "google-auth-library@npm:^9.3.0": version: 9.10.0 resolution: "google-auth-library@npm:9.10.0" @@ -25823,13 +25737,6 @@ __metadata: languageName: node linkType: hard -"grapheme-splitter@npm:^1.0.4": - version: 1.0.4 - resolution: "grapheme-splitter@npm:1.0.4" - checksum: 10/fdb2f51fd430ce881e18e44c4934ad30e59736e46213f7ad35ea5970a9ebdf7d0fe56150d15cc98230d55d2fd48c73dc6781494c38d8cf2405718366c36adb88 - languageName: node - linkType: hard - "graphemer@npm:^1.4.0": version: 1.4.0 resolution: "graphemer@npm:1.4.0" @@ -25941,13 +25848,6 @@ __metadata: languageName: node linkType: hard -"hard-rejection@npm:^2.1.0": - version: 2.1.0 - resolution: "hard-rejection@npm:2.1.0" - checksum: 10/7baaf80a0c7fff4ca79687b4060113f1529589852152fa935e6787a2bc96211e784ad4588fb3048136ff8ffc9dfcf3ae385314a5b24db32de20bea0d1597f9dc - languageName: node - linkType: hard - "hardhat-contract-sizer@npm:^2.1.1": version: 2.10.0 resolution: "hardhat-contract-sizer@npm:2.10.0" @@ -26371,12 +26271,12 @@ __metadata: languageName: node linkType: hard -"hosted-git-info@npm:^7.0.0": - version: 7.0.2 - resolution: "hosted-git-info@npm:7.0.2" +"hosted-git-info@npm:^8.0.0": + version: 8.1.0 + resolution: "hosted-git-info@npm:8.1.0" dependencies: lru-cache: "npm:^10.0.1" - checksum: 10/8f085df8a4a637d995f357f48b1e3f6fc1f9f92e82b33fb406415b5741834ed431a510a09141071001e8deea2eee43ce72786463e2aa5e5a70db8648c0eedeab + checksum: 10/872a1f3b5da6bff9d99410b96cf7ecb6415ef7d8c8842579cfb690144f40be4581cc4ea50d978829a5fc1ef0b1097151a722d14f905beaf3f09330e8ca40fa4c languageName: node linkType: hard @@ -26525,6 +26425,15 @@ __metadata: languageName: node linkType: hard +"human-id@npm:^4.1.1": + version: 4.1.1 + resolution: "human-id@npm:4.1.1" + bin: + human-id: dist/cli.js + checksum: 10/84fef1edd470fc155a34161107beed8baf77bafd20bf515c3fadfbce3690ecc9aa0bacf3fcf4cf9add3c274772ead3ef64aa6531374538ffebe8129fccfb0015 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -26549,11 +26458,11 @@ __metadata: linkType: hard "husky@npm:^8.0.0": - version: 8.0.1 - resolution: "husky@npm:8.0.1" + version: 8.0.3 + resolution: "husky@npm:8.0.3" bin: husky: lib/bin.js - checksum: 10/0f4c1162845cd8c4ecf13af85fe62a1c9de3d4277053004a59dae90a36346fe7eeb7eff3bc7a8484c4f365f6eccf7fd17aa6935198028980c9d6e95306455f3c + checksum: 10/b754cf70fdc97c3b60fec5b80056b9c11436464953b1691bf2b5dcf0081fb6685d2c5f47abb8b2b1c49f504aabea5321fdd6496f8b755d9f6e7525a493406abb languageName: node linkType: hard @@ -26644,6 +26553,13 @@ __metadata: languageName: node linkType: hard +"ignore@npm:^7.0.3": + version: 7.0.3 + resolution: "ignore@npm:7.0.3" + checksum: 10/ce5e812af3acd6607a3fe0a9f9b5f01d53f009a5ace8cbf5b6491d05a481b55d65186e6a7eaa13126e93f15276bcf3d1e8d6ff3ce5549c312f9bb313fff33365 + languageName: node + linkType: hard + "immediate@npm:^3.2.3": version: 3.3.0 resolution: "immediate@npm:3.3.0" @@ -26960,17 +26876,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^3.0.1": - version: 3.0.1 - resolution: "is-ci@npm:3.0.1" - dependencies: - ci-info: "npm:^3.2.0" - bin: - is-ci: bin.js - checksum: 10/192c66dc7826d58f803ecae624860dccf1899fc1f3ac5505284c0a5cf5f889046ffeb958fa651e5725d5705c5bcb14f055b79150ea5fcad7456a9569de60260e - languageName: node - linkType: hard - "is-core-module@npm:^2.13.0": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" @@ -27681,7 +27586,7 @@ __metadata: languageName: node linkType: hard -"javascript-natural-sort@npm:0.7.1": +"javascript-natural-sort@npm:^0.7.1": version: 0.7.1 resolution: "javascript-natural-sort@npm:0.7.1" checksum: 10/7bf6eab67871865d347f09a95aa770f9206c1ab0226bcda6fdd9edec340bf41111a7f82abac30556aa16a21cfa3b2b1ca4a362c8b73dd5ce15220e5d31f49d79 @@ -28260,7 +28165,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:3.x, js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1": +"js-yaml@npm:3.x, js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" dependencies: @@ -28459,7 +28364,7 @@ __metadata: languageName: node linkType: hard -"jsonc-parser@npm:3.3.1": +"jsonc-parser@npm:^3.3.1": version: 3.3.1 resolution: "jsonc-parser@npm:3.3.1" checksum: 10/9b0dc391f20b47378f843ef1e877e73ec652a5bdc3c5fa1f36af0f119a55091d147a86c1ee86a232296f55c929bba174538c2bf0312610e0817a22de131cc3f4 @@ -28643,7 +28548,7 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": +"kind-of@npm:^6.0.2": version: 6.0.3 resolution: "kind-of@npm:6.0.3" checksum: 10/5873d303fb36aad875b7538798867da2ae5c9e328d67194b0162a3659a627d22f742fc9c4ae95cd1704132a24b00cae5041fc00c0f6ef937dc17080dc4dbb962 @@ -28678,13 +28583,6 @@ __metadata: languageName: node linkType: hard -"kleur@npm:^4.1.5": - version: 4.1.5 - resolution: "kleur@npm:4.1.5" - checksum: 10/44d84cc4eedd4311099402ef6d4acd9b2d16e08e499d6ef3bb92389bd4692d7ef09e35248c26e27f98acac532122acb12a1bfee645994ae3af4f0a37996da7df - languageName: node - linkType: hard - "latest-version@npm:^7.0.0": version: 7.0.0 resolution: "latest-version@npm:7.0.0" @@ -29034,18 +28932,6 @@ __metadata: languageName: node linkType: hard -"load-yaml-file@npm:^0.2.0": - version: 0.2.0 - resolution: "load-yaml-file@npm:0.2.0" - dependencies: - graceful-fs: "npm:^4.1.5" - js-yaml: "npm:^3.13.0" - pify: "npm:^4.0.1" - strip-bom: "npm:^3.0.0" - checksum: 10/b1bfa7e80114933e43ccc1cf3772582b7e13c8a71dc8d560de2aeecdabf545014daf8a5afabe634c1e9f71c75f6f8528bbd944c9cbbbdf2ab8c927118bd48fd2 - languageName: node - linkType: hard - "loader-utils@npm:^2.0.0": version: 2.0.4 resolution: "loader-utils@npm:2.0.4" @@ -29315,16 +29201,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^4.0.1": - version: 4.1.5 - resolution: "lru-cache@npm:4.1.5" - dependencies: - pseudomap: "npm:^1.0.2" - yallist: "npm:^2.1.2" - checksum: 10/9ec7d73f11a32cba0e80b7a58fdf29970814c0c795acaee1a6451ddfd609bae6ef9df0837f5bbeabb571ecd49c1e2d79e10e9b4ed422cfba17a0cb6145b018a9 - languageName: node - linkType: hard - "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -29468,20 +29344,6 @@ __metadata: languageName: node linkType: hard -"map-obj@npm:^1.0.0": - version: 1.0.1 - resolution: "map-obj@npm:1.0.1" - checksum: 10/f8e6fc7f6137329c376c4524f6d25b3c243c17019bc8f621d15a2dcb855919e482a9298a78ae58b00dbd0e76b640bf6533aa343a9e993cfc16e0346a2507e7f8 - languageName: node - linkType: hard - -"map-obj@npm:^4.0.0": - version: 4.3.0 - resolution: "map-obj@npm:4.3.0" - checksum: 10/fbc554934d1a27a1910e842bc87b177b1a556609dd803747c85ece420692380827c6ae94a95cce4407c054fa0964be3bf8226f7f2cb2e9eeee432c7c1985684e - languageName: node - linkType: hard - "map-or-similar@npm:^1.5.0": version: 1.5.0 resolution: "map-or-similar@npm:1.5.0" @@ -29603,25 +29465,6 @@ __metadata: languageName: node linkType: hard -"meow@npm:^6.0.0": - version: 6.1.1 - resolution: "meow@npm:6.1.1" - dependencies: - "@types/minimist": "npm:^1.2.0" - camelcase-keys: "npm:^6.2.2" - decamelize-keys: "npm:^1.1.0" - hard-rejection: "npm:^2.1.0" - minimist-options: "npm:^4.0.2" - normalize-package-data: "npm:^2.5.0" - read-pkg-up: "npm:^7.0.1" - redent: "npm:^3.0.0" - trim-newlines: "npm:^3.0.0" - type-fest: "npm:^0.13.1" - yargs-parser: "npm:^18.1.3" - checksum: 10/507ea2e7d61f6afe17e8f57323e190ddc8bd4ad0921db75920cf9d6f0f686828d3fe3b18a0a09ee6a5c27e070a3ca69133a7a095e57703b2e8d46eb56ee7d66f - languageName: node - linkType: hard - "merge-descriptors@npm:1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" @@ -29789,6 +29632,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10/eb5893c99e902ccebbc267c6c6b83092966af84682957f79313311edb95e8bb5f39fb048d77132b700474d1c86d90ccc211e99bae0935447a4834eb4c882982c + languageName: node + linkType: hard + "mimic-response@npm:^1.0.0": version: 1.0.1 resolution: "mimic-response@npm:1.0.1" @@ -29896,15 +29746,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.3": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10/c81b47d28153e77521877649f4bab48348d10938df9e8147a58111fe00ef89559a2938de9f6632910c4f7bf7bb5cd81191a546167e58d357f0cfb1e18cecc1c5 - languageName: node - linkType: hard - "minimatch@npm:9.0.5, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -29932,17 +29773,6 @@ __metadata: languageName: node linkType: hard -"minimist-options@npm:^4.0.2": - version: 4.1.0 - resolution: "minimist-options@npm:4.1.0" - dependencies: - arrify: "npm:^1.0.1" - is-plain-obj: "npm:^1.1.0" - kind-of: "npm:^6.0.3" - checksum: 10/8c040b3068811e79de1140ca2b708d3e203c8003eb9a414c1ab3cd467fc5f17c9ca02a5aef23bedc51a7f8bfbe77f87e9a7e31ec81fba304cda675b019496f4e - languageName: node - linkType: hard - "minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.6 resolution: "minimist@npm:1.2.6" @@ -30072,13 +29902,6 @@ __metadata: languageName: node linkType: hard -"mixme@npm:^0.5.1": - version: 0.5.10 - resolution: "mixme@npm:0.5.10" - checksum: 10/b0834a462f0960eaa6ec161bb2be56d75a13ad3b2ffefa092960c91dd456e335f181316de5206965f160ce33815b2b559c9dccc1042041738c3bcc1075dbfad2 - languageName: node - linkType: hard - "mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": version: 0.5.3 resolution: "mkdirp-classic@npm:0.5.3" @@ -30887,15 +30710,15 @@ __metadata: languageName: node linkType: hard -"npm-package-arg@npm:11.0.3": - version: 11.0.3 - resolution: "npm-package-arg@npm:11.0.3" +"npm-package-arg@npm:^12.0.2": + version: 12.0.2 + resolution: "npm-package-arg@npm:12.0.2" dependencies: - hosted-git-info: "npm:^7.0.0" - proc-log: "npm:^4.0.0" + hosted-git-info: "npm:^8.0.0" + proc-log: "npm:^5.0.0" semver: "npm:^7.3.5" - validate-npm-package-name: "npm:^5.0.0" - checksum: 10/bacc863907edf98940286edc2fd80327901c1e8b34426d538cdc708ed66bc6567f06d742d838eaf35db6804347bb4ba56ca9cef032c4b52743b33e7a22a2678e + validate-npm-package-name: "npm:^6.0.0" + checksum: 10/f61dacb42c02dfa00f97d9fbd7f3696b8fe651cf7dd0d8f4e7766b5835290d0967c20ec148b31b10d7f2931108c507813d9d2624b279769b1b2e4a356914d561 languageName: node linkType: hard @@ -31006,13 +30829,20 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0, object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.12.0, object-inspect@npm:^1.9.0": version: 1.12.2 resolution: "object-inspect@npm:1.12.2" checksum: 10/aa11100d45fa919b36448347d4f7c8a78b0247886881db56a2026b512c4042a9749e64894519b00a4db8c6e2b713a965b5ceaa3b59324aeb3da007c54a33bc58 languageName: node linkType: hard +"object-inspect@npm:^1.12.2": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 10/aa13b1190ad3e366f6c83ad8a16ed37a19ed57d267385aa4bfdccda833d7b90465c057ff6c55d035a6b2e52c1a2295582b294217a0a3a1ae7abdd6877ef781fb + languageName: node + linkType: hard + "object-inspect@npm:^1.13.1": version: 1.13.1 resolution: "object-inspect@npm:1.13.1" @@ -31240,6 +31070,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10/eb08d2da9339819e2f9d52cab9caf2557d80e9af8c7d1ae86e1a0fef027d00a88e9f5bd67494d350df360f7c559fbb44e800b32f310fb989c860214eacbb561c + languageName: node + linkType: hard + "open@npm:^7.4.2": version: 7.4.2 resolution: "open@npm:7.4.2" @@ -31289,23 +31128,6 @@ __metadata: languageName: node linkType: hard -"ora@npm:8.0.1": - version: 8.0.1 - resolution: "ora@npm:8.0.1" - dependencies: - chalk: "npm:^5.3.0" - cli-cursor: "npm:^4.0.0" - cli-spinners: "npm:^2.9.2" - is-interactive: "npm:^2.0.0" - is-unicode-supported: "npm:^2.0.0" - log-symbols: "npm:^6.0.0" - stdin-discarder: "npm:^0.2.1" - string-width: "npm:^7.0.0" - strip-ansi: "npm:^7.1.0" - checksum: 10/3d37bb3f53e965e5176004af319f82feef7323ee0b2428db5ee6f689b9b9ba939d7b1e81691d4614333c4fb9e294790eb049db9c1e990b14b9bbe150c6f09993 - languageName: node - linkType: hard - "ora@npm:^5.4.1": version: 5.4.1 resolution: "ora@npm:5.4.1" @@ -31323,6 +31145,23 @@ __metadata: languageName: node linkType: hard +"ora@npm:^8.2.0": + version: 8.2.0 + resolution: "ora@npm:8.2.0" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10/cea932fdcb29549cd7b5af81f427760986429cadc752b1dd4bf31bc6821f5ba137e1ef9a18cde7bdfbe5b4e3d3201e76b048765c51a27b15d18c57ac0e0a909a + languageName: node + linkType: hard + "os-tmpdir@npm:~1.0.2": version: 1.0.2 resolution: "os-tmpdir@npm:1.0.2" @@ -31759,10 +31598,10 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^5.0.0": - version: 5.0.0 - resolution: "path-type@npm:5.0.0" - checksum: 10/15ec24050e8932c2c98d085b72cfa0d6b4eeb4cbde151a0a05726d8afae85784fc5544f733d8dfc68536587d5143d29c0bd793623fad03d7e61cc00067291cd5 +"path-type@npm:^6.0.0": + version: 6.0.0 + resolution: "path-type@npm:6.0.0" + checksum: 10/b9f6eaf7795c48d5c9bc4c6bc3ac61315b8d36975a73497ab2e02b764c0836b71fb267ea541863153f633a069a1c2ed3c247cb781633842fc571c655ac57c00e languageName: node linkType: hard @@ -32206,18 +32045,6 @@ __metadata: languageName: node linkType: hard -"preferred-pm@npm:^3.0.0": - version: 3.1.2 - resolution: "preferred-pm@npm:3.1.2" - dependencies: - find-up: "npm:^5.0.0" - find-yarn-workspace-root2: "npm:1.2.16" - path-exists: "npm:^4.0.0" - which-pm: "npm:2.0.0" - checksum: 10/d66019f36765c4e241197cd34e2718c03d7eff953b94dacb278df9326767ccc2744d4e0bcab265fd9bb036f704ed5f3909d02594cd5663bd640a160fe4c1446c - languageName: node - linkType: hard - "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -32248,16 +32075,15 @@ __metadata: languageName: node linkType: hard -"prettier-plugin-solidity@npm:^1.1.3": - version: 1.2.0 - resolution: "prettier-plugin-solidity@npm:1.2.0" +"prettier-plugin-solidity@npm:^1.4.2": + version: 1.4.2 + resolution: "prettier-plugin-solidity@npm:1.4.2" dependencies: - "@solidity-parser/parser": "npm:^0.16.2" - semver: "npm:^7.5.4" - solidity-comments-extractor: "npm:^0.0.7" + "@solidity-parser/parser": "npm:^0.19.0" + semver: "npm:^7.6.3" peerDependencies: prettier: ">=2.3.0" - checksum: 10/5b9a77ae33292b6093f5dfcbeb9948dbaa0f7f89917a4be5940f0d29a6103238bc7c5d61919e15ab9fa92aeefe7c86fb7870e6421353b984b9ec71ab4c00ebf2 + checksum: 10/5f4ca400275c860bbca7ba3ee316682bec04a760a816e903f6a528acc61ef3d2eda9a81edb7e38449b79196d2946db44424fbab78944d2e8bb32f8fed31363bc languageName: node linkType: hard @@ -32270,7 +32096,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.7.1, prettier@npm:^2.8.0, prettier@npm:^2.8.3, prettier@npm:^2.8.8": +"prettier@npm:^2.7.1, prettier@npm:^2.8.0, prettier@npm:^2.8.3": version: 2.8.8 resolution: "prettier@npm:2.8.8" bin: @@ -32279,6 +32105,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.5.3": + version: 3.5.3 + resolution: "prettier@npm:3.5.3" + bin: + prettier: bin/prettier.cjs + checksum: 10/7050c08f674d9e49fbd9a4c008291d0715471f64e94cc5e4b01729affce221dfc6875c8de7e66b728c64abc9352eefb7eaae071b5f79d30081be207b53774b78 + languageName: node + linkType: hard + "pretty-format@npm:^27.0.2": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" @@ -32315,10 +32150,10 @@ __metadata: languageName: node linkType: hard -"proc-log@npm:^4.0.0": - version: 4.2.0 - resolution: "proc-log@npm:4.2.0" - checksum: 10/4e1394491b717f6c1ade15c570ecd4c2b681698474d3ae2d303c1e4b6ab9455bd5a81566211e82890d5a5ae9859718cc6954d5150bb18b09b72ecb297beae90a +"proc-log@npm:^5.0.0": + version: 5.0.0 + resolution: "proc-log@npm:5.0.0" + checksum: 10/35610bdb0177d3ab5d35f8827a429fb1dc2518d9e639f2151ac9007f01a061c30e0c635a970c9b00c39102216160f6ec54b62377c92fac3b7bfc2ad4b98d195c languageName: node linkType: hard @@ -32392,7 +32227,7 @@ __metadata: languageName: node linkType: hard -"prompts@npm:2.4.2, prompts@npm:^2.0.1, prompts@npm:^2.4.0, prompts@npm:^2.4.2": +"prompts@npm:^2.0.1, prompts@npm:^2.4.0, prompts@npm:^2.4.2": version: 2.4.2 resolution: "prompts@npm:2.4.2" dependencies: @@ -32562,13 +32397,6 @@ __metadata: languageName: node linkType: hard -"pseudomap@npm:^1.0.2": - version: 1.0.2 - resolution: "pseudomap@npm:1.0.2" - checksum: 10/856c0aae0ff2ad60881168334448e898ad7a0e45fe7386d114b150084254c01e200c957cf378378025df4e052c7890c5bd933939b0e0d2ecfcc1dc2f0b2991f5 - languageName: node - linkType: hard - "psl@npm:^1.1.28": version: 1.8.0 resolution: "psl@npm:1.8.0" @@ -32864,13 +32692,6 @@ __metadata: languageName: node linkType: hard -"quick-lru@npm:^4.0.1": - version: 4.0.1 - resolution: "quick-lru@npm:4.0.1" - checksum: 10/5c7c75f1c696750f619b165cc9957382f919e4207dabf04597a64f0298861391cdc5ee91a1dde1a5d460ecf7ee1af7fc36fef6d155bef2be66f05d43fd63d4f0 - languageName: node - linkType: hard - "quick-lru@npm:^5.1.1": version: 5.1.1 resolution: "quick-lru@npm:5.1.1" @@ -33324,16 +33145,6 @@ __metadata: languageName: node linkType: hard -"read-yaml-file@npm:2.1.0": - version: 2.1.0 - resolution: "read-yaml-file@npm:2.1.0" - dependencies: - js-yaml: "npm:^4.0.0" - strip-bom: "npm:^4.0.0" - checksum: 10/52765eb183e79466f51eebeb19b933cc0f0e907052d062d67300b97e79910064a24b370cdb0b5dd8b05afff3d0ec57282670fd9070dc608e13b11820ac79183d - languageName: node - linkType: hard - "read-yaml-file@npm:^1.1.0": version: 1.1.0 resolution: "read-yaml-file@npm:1.1.0" @@ -33346,6 +33157,16 @@ __metadata: languageName: node linkType: hard +"read-yaml-file@npm:^2.1.0": + version: 2.1.0 + resolution: "read-yaml-file@npm:2.1.0" + dependencies: + js-yaml: "npm:^4.0.0" + strip-bom: "npm:^4.0.0" + checksum: 10/52765eb183e79466f51eebeb19b933cc0f0e907052d062d67300b97e79910064a24b370cdb0b5dd8b05afff3d0ec57282670fd9070dc608e13b11820ac79183d + languageName: node + linkType: hard + "readable-stream@npm:^2.0.0, readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" @@ -33961,13 +33782,13 @@ __metadata: languageName: node linkType: hard -"restore-cursor@npm:^4.0.0": - version: 4.0.0 - resolution: "restore-cursor@npm:4.0.0" +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" dependencies: - onetime: "npm:^5.1.0" - signal-exit: "npm:^3.0.2" - checksum: 10/5b675c5a59763bf26e604289eab35711525f11388d77f409453904e1e69c0d37ae5889295706b2c81d23bd780165084d040f9b68fffc32cc921519031c4fa4af + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10/838dd54e458d89cfbc1a923b343c1b0f170a04100b4ce1733e97531842d7b440463967e521216e8ab6c6f8e89df877acc7b7f4c18ec76e99fb9bf5a60d358d2c languageName: node linkType: hard @@ -34004,9 +33825,9 @@ __metadata: linkType: hard "rfdc@npm:^1.3.0": - version: 1.3.0 - resolution: "rfdc@npm:1.3.0" - checksum: 10/76dedd9700cdf132947fde7ce1a8838c9cbb7f3e8f9188af0aaf97194cce745f42094dd2cf547426934cc83252ee2c0e432b2e0222a4415ab0db32de82665c69 + version: 1.4.1 + resolution: "rfdc@npm:1.4.1" + checksum: 10/2f3d11d3d8929b4bfeefc9acb03aae90f971401de0add5ae6c5e38fec14f0405e6a4aad8fdb76344bfdd20c5193110e3750cbbd28ba86d73729d222b6cf4a729 languageName: node linkType: hard @@ -34327,11 +34148,11 @@ __metadata: linkType: hard "rxjs@npm:^7.5.5": - version: 7.5.5 - resolution: "rxjs@npm:7.5.5" + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" dependencies: tslib: "npm:^2.1.0" - checksum: 10/9c8af134bc557b0c51aff8fd4d8190cbbb1f3ca4602f46cdded04a0d68bb2581e61ae2fbf583aea4f99ee66dac6cf6c4b31856022a9b929f37c521c048f48465 + checksum: 10/03dff09191356b2b87d94fbc1e97c4e9eb3c09d4452399dddd451b09c2f1ba8d56925a40af114282d7bc0c6fe7514a2236ca09f903cf70e4bbf156650dddb49d languageName: node linkType: hard @@ -34519,15 +34340,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.3, semver@npm:^7.3.8, semver@npm:^7.5.0, semver@npm:^7.5.1, semver@npm:^7.6.0, semver@npm:^7.6.3": - version: 7.6.3 - resolution: "semver@npm:7.6.3" - bin: - semver: bin/semver.js - checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 - languageName: node - linkType: hard - "semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.7.0": version: 5.7.1 resolution: "semver@npm:5.7.1" @@ -34566,6 +34378,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.8, semver@npm:^7.5.0, semver@npm:^7.5.1, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 + languageName: node + linkType: hard + "semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" @@ -34586,6 +34407,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.1": + version: 7.7.1 + resolution: "semver@npm:7.7.1" + bin: + semver: bin/semver.js + checksum: 10/4cfa1eb91ef3751e20fc52e47a935a0118d56d6f15a837ab814da0c150778ba2ca4f1a4d9068b33070ea4273629e615066664c2cfcd7c272caf7a8a0f6518b2c + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -34995,22 +34825,6 @@ __metadata: languageName: node linkType: hard -"smartwrap@npm:^2.0.2": - version: 2.0.2 - resolution: "smartwrap@npm:2.0.2" - dependencies: - array.prototype.flat: "npm:^1.2.3" - breakword: "npm:^1.0.5" - grapheme-splitter: "npm:^1.0.4" - strip-ansi: "npm:^6.0.0" - wcwidth: "npm:^1.0.1" - yargs: "npm:^15.1.0" - bin: - smartwrap: src/terminal-adapter.js - checksum: 10/dcc7b9082b74a0ce0f391fce8a4be72f56d1b6e78fbfed9b4191da89d66d82f62a7b44c727d7e68714f0faf1ed1fce0be498563b0d4ef8aad80e2433983b0603 - languageName: node - linkType: hard - "socket.io-client@npm:^4.5.1": version: 4.8.1 resolution: "socket.io-client@npm:4.8.1" @@ -35090,23 +34904,24 @@ __metadata: languageName: node linkType: hard -"solhint-plugin-prettier@npm:^0.0.5": - version: 0.0.5 - resolution: "solhint-plugin-prettier@npm:0.0.5" +"solhint-plugin-prettier@npm:^0.1.0": + version: 0.1.0 + resolution: "solhint-plugin-prettier@npm:0.1.0" dependencies: + "@prettier/sync": "npm:^0.3.0" prettier-linter-helpers: "npm:^1.0.0" peerDependencies: - prettier: ^1.15.0 || ^2.0.0 - prettier-plugin-solidity: ^1.0.0-alpha.14 - checksum: 10/ca721e327daf49a4d9ef0ee5c9622482a8c5563d600eedfd3856c69ce67e416dd77da5166a033e2e641c9cdd7a0f2cbc7913b0eb1712081b3c7e8c633eef82a5 + prettier: ^3.0.0 + prettier-plugin-solidity: ^1.0.0 + checksum: 10/241caa07b9d1570117cf0cc56371cc81c69fb17706dbc68136dfb112279c8c1cf815dbaa70c146acd06876e16d9a7385312b63302f2381868c02c3bdfa23715b languageName: node linkType: hard -"solhint@npm:^4.5.4": - version: 4.5.4 - resolution: "solhint@npm:4.5.4" +"solhint@npm:^5.0.5": + version: 5.0.5 + resolution: "solhint@npm:5.0.5" dependencies: - "@solidity-parser/parser": "npm:^0.18.0" + "@solidity-parser/parser": "npm:^0.19.0" ajv: "npm:^6.12.6" antlr4: "npm:^4.13.1-patch-1" ast-parents: "npm:^0.0.1" @@ -35130,7 +34945,7 @@ __metadata: optional: true bin: solhint: solhint.js - checksum: 10/0d839f4c81b83ec2fa9db5003971623b4ca6895348d55874bd9189c4af06c8c5328789410a2859496b7cbdee0ed0321694d63d83f5c94955f4d00bc15e317e41 + checksum: 10/07cce3796e0123fda1e27bb0f81fb94290fb0d4bddc703a10236c81403a3a09fe1bfcbf37c583590778509546b92d43c0799456b4ae9dd183233fd8fe6e88554 languageName: node linkType: hard @@ -35167,13 +34982,6 @@ __metadata: languageName: node linkType: hard -"solidity-comments-extractor@npm:^0.0.7": - version: 0.0.7 - resolution: "solidity-comments-extractor@npm:0.0.7" - checksum: 10/a5cedf2310709969bc1783a6c336171478536f2f0ea96ad88437e0ef1e8844c0b37dd75591b0a824ec9c30640ea7e31b5f03128e871e6235bef3426617ce96c4 - languageName: node - linkType: hard - "solidity-comments-freebsd-x64@npm:0.0.2": version: 0.0.2 resolution: "solidity-comments-freebsd-x64@npm:0.0.2" @@ -35346,7 +35154,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.0, source-map@npm:^0.5.7": +"source-map@npm:^0.5.7": version: 0.5.7 resolution: "source-map@npm:0.5.7" checksum: 10/9b4ac749ec5b5831cad1f8cc4c19c4298ebc7474b24a0acf293e2f040f03f8eeccb3d01f12aa0f90cf46d555c887e03912b83a042c627f419bda5152d89c5269 @@ -35383,16 +35191,6 @@ __metadata: languageName: node linkType: hard -"spawndamnit@npm:^2.0.0": - version: 2.0.0 - resolution: "spawndamnit@npm:2.0.0" - dependencies: - cross-spawn: "npm:^5.1.0" - signal-exit: "npm:^3.0.2" - checksum: 10/c74b5e264ee5bc13d55692fd422d74c282e4607eb04ac64d19d06796718d89b14921620fa4237ec5635e7acdff21461670ff19850f210225410a353cad0d7fed - languageName: node - linkType: hard - "spawndamnit@npm:^3.0.1": version: 3.0.1 resolution: "spawndamnit@npm:3.0.1" @@ -35610,7 +35408,7 @@ __metadata: languageName: node linkType: hard -"stdin-discarder@npm:^0.2.1": +"stdin-discarder@npm:^0.2.2": version: 0.2.2 resolution: "stdin-discarder@npm:0.2.2" checksum: 10/642ffd05bd5b100819d6b24a613d83c6e3857c6de74eb02fc51506fa61dc1b0034665163831873868157c4538d71e31762bcf319be86cea04c3aba5336470478 @@ -35675,15 +35473,6 @@ __metadata: languageName: node linkType: hard -"stream-transform@npm:^2.1.3": - version: 2.1.3 - resolution: "stream-transform@npm:2.1.3" - dependencies: - mixme: "npm:^0.5.1" - checksum: 10/3167bf23a96b3fc7f991d4a8224cd0701c9234be8acd8450f8692c431bed4548d1ef90d1b410fdeff567fa740c3db97b52d5f3c4ad4485fc0e0b8be655800ab7 - languageName: node - linkType: hard - "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -35706,9 +35495,9 @@ __metadata: linkType: hard "string-argv@npm:^0.3.1": - version: 0.3.1 - resolution: "string-argv@npm:0.3.1" - checksum: 10/47c637e3f47b3f5a6430036315e65564483fcf7745341d474943f0c2046f188681275fc1f2948db75c7a7e68134b1446e0dcceda60a7be1ee0c3fb026c0d90c4 + version: 0.3.2 + resolution: "string-argv@npm:0.3.2" + checksum: 10/f9d3addf887026b4b5f997a271149e93bf71efc8692e7dc0816e8807f960b18bcb9787b45beedf0f97ff459575ee389af3f189d8b649834cac602f2e857e75af languageName: node linkType: hard @@ -35783,7 +35572,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^7.0.0": +"string-width@npm:^7.2.0": version: 7.2.0 resolution: "string-width@npm:7.2.0" dependencies: @@ -36176,9 +35965,9 @@ __metadata: linkType: hard "supports-color@npm:^9.2.2": - version: 9.2.2 - resolution: "supports-color@npm:9.2.2" - checksum: 10/976d84877402fc38c1d43b1fde20b0a8dc0283273f21cfebe4ff7507d27543cdfbeec7db108a96b82d694465f06d64e8577562b05d0520b41710088e0a33cc50 + version: 9.4.0 + resolution: "supports-color@npm:9.4.0" + checksum: 10/cb8ff8daeaf1db642156f69a9aa545b6c01dd9c4def4f90a49f46cbf24be0c245d392fcf37acd119cd1819b99dad2cc9b7e3260813f64bcfd7f5b18b5a1eefb8 languageName: node linkType: hard @@ -36262,27 +36051,27 @@ __metadata: linkType: hard "syncpack@npm:^13.0.0": - version: 13.0.0 - resolution: "syncpack@npm:13.0.0" - dependencies: - "@effect/schema": "npm:0.71.1" - chalk: "npm:5.3.0" - chalk-template: "npm:1.1.0" - commander: "npm:12.1.0" - cosmiconfig: "npm:9.0.0" - effect: "npm:3.6.5" - enquirer: "npm:2.4.1" - fast-check: "npm:3.21.0" - globby: "npm:14.0.2" - jsonc-parser: "npm:3.3.1" + version: 13.0.3 + resolution: "syncpack@npm:13.0.3" + dependencies: + "@effect/schema": "npm:^0.75.5" + chalk: "npm:^5.4.1" + chalk-template: "npm:^1.1.0" + commander: "npm:^13.1.0" + cosmiconfig: "npm:^9.0.0" + effect: "npm:^3.13.7" + enquirer: "npm:^2.4.1" + fast-check: "npm:^3.23.2" + globby: "npm:^14.1.0" + jsonc-parser: "npm:^3.3.1" minimatch: "npm:9.0.5" - npm-package-arg: "npm:11.0.3" - ora: "npm:8.0.1" - prompts: "npm:2.4.2" - read-yaml-file: "npm:2.1.0" - semver: "npm:7.6.3" + npm-package-arg: "npm:^12.0.2" + ora: "npm:^8.2.0" + prompts: "npm:^2.4.2" + read-yaml-file: "npm:^2.1.0" + semver: "npm:^7.7.1" tightrope: "npm:0.2.0" - ts-toolbelt: "npm:9.6.0" + ts-toolbelt: "npm:^9.6.0" bin: syncpack: dist/bin.js syncpack-fix-mismatches: dist/bin-fix-mismatches/index.js @@ -36294,7 +36083,7 @@ __metadata: syncpack-prompt: dist/bin-prompt/index.js syncpack-set-semver-ranges: dist/bin-set-semver-ranges/index.js syncpack-update: dist/bin-update/index.js - checksum: 10/c80f60faaad640f38de4e1fb5e7daf5dac62963def211b7b3989186c503dec8800c197d3051798553998331c0b0e05ab4b96dd765cd51c7c6cd3651f87c559ed + checksum: 10/702c734cbb9970a218419dec7e1f2c9bb478f6a8f3dcee22f4368878f243cfcb7565dd4eae8b2e162c3fb2476b5b5b6ccf7d4b40d29b19e6283e68519c7adcfe languageName: node linkType: hard @@ -36834,13 +36623,6 @@ __metadata: languageName: node linkType: hard -"trim-newlines@npm:^3.0.0": - version: 3.0.1 - resolution: "trim-newlines@npm:3.0.1" - checksum: 10/b530f3fadf78e570cf3c761fb74fef655beff6b0f84b29209bac6c9622db75ad1417f4a7b5d54c96605dcd72734ad44526fef9f396807b90839449eb543c6206 - languageName: node - linkType: hard - "trpc-browser@npm:^1.3.2": version: 1.4.4 resolution: "trpc-browser@npm:1.4.4" @@ -36851,15 +36633,6 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.0.1": - version: 1.3.0 - resolution: "ts-api-utils@npm:1.3.0" - peerDependencies: - typescript: ">=4.2.0" - checksum: 10/3ee44faa24410cd649b5c864e068d438aa437ef64e9e4a66a41646a6d3024d3097a695eeb3fb26ee364705d3cb9653a65756d009e6a53badb6066a5f447bf7ed - languageName: node - linkType: hard - "ts-api-utils@npm:^1.3.0": version: 1.4.1 resolution: "ts-api-utils@npm:1.4.1" @@ -37019,7 +36792,7 @@ __metadata: languageName: node linkType: hard -"ts-toolbelt@npm:9.6.0": +"ts-toolbelt@npm:^9.6.0": version: 9.6.0 resolution: "ts-toolbelt@npm:9.6.0" checksum: 10/2c2dea2631dbd7372a79cccc6d09a377a6ca2f319f767fd239d2e312cd1d9165a90f8c1777a047227bfdcda6aeba3addbadce88fdfc7f43caf4534d385a43c82 @@ -37103,23 +36876,6 @@ __metadata: languageName: node linkType: hard -"tty-table@npm:^4.1.5": - version: 4.2.3 - resolution: "tty-table@npm:4.2.3" - dependencies: - chalk: "npm:^4.1.2" - csv: "npm:^5.5.3" - kleur: "npm:^4.1.5" - smartwrap: "npm:^2.0.2" - strip-ansi: "npm:^6.0.1" - wcwidth: "npm:^1.0.1" - yargs: "npm:^17.7.1" - bin: - tty-table: adapters/terminal-adapter.js - checksum: 10/8532c784027a833bd805de5962be469faaee0ec314cc1c01e77d06ec89d44f18992455969b29ec460abbc7798ea584d805966cbd6480f5a5ffd29865e8de2501 - languageName: node - linkType: hard - "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -37189,13 +36945,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^0.13.1": - version: 0.13.1 - resolution: "type-fest@npm:0.13.1" - checksum: 10/11e9476dc85bf97a71f6844fb67ba8e64a4c7e445724c0f3bd37eb2ddf4bc97c1dc9337bd880b28bce158de1c0cb275c2d03259815a5bf64986727197126ab56 - languageName: node - linkType: hard - "type-fest@npm:^0.16.0": version: 0.16.0 resolution: "type-fest@npm:0.16.0" @@ -37659,10 +37408,10 @@ __metadata: languageName: node linkType: hard -"unicorn-magic@npm:^0.1.0": - version: 0.1.0 - resolution: "unicorn-magic@npm:0.1.0" - checksum: 10/9b4d0e9809807823dc91d0920a4a4c0cff2de3ebc54ee87ac1ee9bc75eafd609b09d1f14495e0173aef26e01118706196b6ab06a75fe0841028b3983a8af313f +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10/bdd7d7c522f9456f32a0b77af23f8854f9a7db846088c3868ec213f9550683ab6a2bdf3803577eacbafddb4e06900974385841ccb75338d17346ccef45f9cb01 languageName: node linkType: hard @@ -38154,10 +37903,10 @@ __metadata: languageName: node linkType: hard -"validate-npm-package-name@npm:^5.0.0": - version: 5.0.1 - resolution: "validate-npm-package-name@npm:5.0.1" - checksum: 10/0d583a1af23aeffea7748742cf22b6802458736fb8b60323ba5949763824d46f796474b0e1b9206beb716f9d75269e19dbd7795d6b038b29d561be95dd827381 +"validate-npm-package-name@npm:^6.0.0": + version: 6.0.0 + resolution: "validate-npm-package-name@npm:6.0.0" + checksum: 10/4d018c4fa07f95534a5fea667adc653b1ef52f08bf56aff066c28394499d0a6949c0b00edbd7077c4dc1e041da9220af7c742ced67d7d2d6a1b07d10cbe91b29 languageName: node linkType: hard @@ -38997,16 +38746,6 @@ __metadata: languageName: node linkType: hard -"which-pm@npm:2.0.0": - version: 2.0.0 - resolution: "which-pm@npm:2.0.0" - dependencies: - load-yaml-file: "npm:^0.2.0" - path-exists: "npm:^4.0.0" - checksum: 10/8f9dc47ab1302d536458a3d28b891907540d67e18b95d8cf0a41ba768b679c2bc7b64c17d9b80c85443c4b300a3e2d5c29ae1e9c7c6ad2833760070fbdbd3b6f - languageName: node - linkType: hard - "which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13": version: 1.1.13 resolution: "which-typed-array@npm:1.1.13" @@ -39519,13 +39258,6 @@ __metadata: languageName: node linkType: hard -"yallist@npm:^2.1.2": - version: 2.1.2 - resolution: "yallist@npm:2.1.2" - checksum: 10/75fc7bee4821f52d1c6e6021b91b3e079276f1a9ce0ad58da3c76b79a7e47d6f276d35e206a96ac16c1cf48daee38a8bb3af0b1522a3d11c8ffe18f898828832 - languageName: node - linkType: hard - "yallist@npm:^3.0.0, yallist@npm:^3.0.2, yallist@npm:^3.1.1": version: 3.1.1 resolution: "yallist@npm:3.1.1" @@ -39582,7 +39314,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^18.1.2, yargs-parser@npm:^18.1.3": +"yargs-parser@npm:^18.1.2": version: 18.1.3 resolution: "yargs-parser@npm:18.1.3" dependencies: @@ -39662,7 +39394,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^15.1.0, yargs@npm:^15.3.1": +"yargs@npm:^15.3.1": version: 15.4.1 resolution: "yargs@npm:15.4.1" dependencies: @@ -39681,7 +39413,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": +"yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 54cd8360a202d397cad598cac255432bf119f40b Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:51:41 +0100 Subject: [PATCH 077/223] chore: fix agent-configs tests (#6041) ### Description chore: fix agent-configs tests ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 5 +---- rust/main/config/testnet_config.json | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.registryrc b/.registryrc index 03cc05b4db4..7601abe379d 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -f83414e788906cf9bbd2f3670b115bff5bd5eda8 +0bf5f930af73dd1213d25e445a0999d1d1b5ed6e diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index c15716de24d..dc33a7ce34e 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -9130,10 +9130,7 @@ "interchainSecurityModule": "0x726f757465725f69736d00000000000000000000000000010000000000000000", "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000001", - "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": "0.03" - } + "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000" } }, "defaultRpcConsensusType": "fallback" diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index 7b5cb2c9d45..0a5acf0c64b 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -2945,10 +2945,7 @@ { "http": "https://grpc-raw.kaon.kyve.network" } - ], - "transactionOverrides": { - "gasPrice": "2.0" - } + ] } }, "defaultRpcConsensusType": "fallback" From cbbd2ea6ba9079a1086f8f58eedb7834205f87b8 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:00:44 +0100 Subject: [PATCH 078/223] fix(cosmos-native): sort sigs in ascending order by validator address (#6043) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/chains/hyperlane-cosmos-native/src/ism.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rust/main/chains/hyperlane-cosmos-native/src/ism.rs b/rust/main/chains/hyperlane-cosmos-native/src/ism.rs index b892026c421..ec0f33c885a 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/ism.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/ism.rs @@ -115,11 +115,15 @@ impl MultisigIsm for CosmosNativeIsm { t if t == MerkleRootMultisigIsm::type_url() => { let ism = MerkleRootMultisigIsm::decode(ism.value.as_slice()) .map_err(HyperlaneCosmosError::from)?; - let validators = ism + let mut validators = ism .validators .iter() .map(|v| H160::from_str(v).map(H256::from)) .collect::, _>>()?; + // on cosmos native, the ISM expects the checkpoints in the metadata to be sorted + // in ascending order of the validator address. So we sort the validators here, + // which will determine the order of the checkpoints in the metadata. + validators.sort(); Ok((validators, ism.threshold as u8)) } _ => Err(ChainCommunicationError::from_other_str(&format!( From 83fcd39344eb178cddb76c8777e2b1726be23948 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:34:33 +0100 Subject: [PATCH 079/223] feat: add auroratestnet, milkywaytestnet (#6023) ### Description feat: add auroratestnet, milkywaytestnet ### Drive-by changes ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/787 ### Backward compatibility ### Testing manual --- .registryrc | 2 +- rust/main/config/testnet_config.json | 195 ++++++++++++++---- .../config/environments/testnet4/agent.ts | 28 ++- .../testnet4/aw-validators/hyperlane.json | 7 +- .../testnet4/core/verification.json | 70 +++++++ .../config/environments/testnet4/funding.ts | 4 +- .../environments/testnet4/gasPrices.json | 24 ++- .../testnet4/ism/verification.json | 86 ++++++++ .../config/environments/testnet4/owners.ts | 3 + .../testnet4/supportedChainNames.ts | 2 + .../environments/testnet4/tokenPrices.json | 3 +- .../environments/testnet4/validators.ts | 21 ++ typescript/sdk/src/consts/multisigIsm.ts | 20 ++ 13 files changed, 413 insertions(+), 52 deletions(-) diff --git a/.registryrc b/.registryrc index 7601abe379d..bef059f9517 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -0bf5f930af73dd1213d25e445a0999d1d1b5ed6e +ab67983b3c146f03284abd0268655520023c08e1 diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index 0a5acf0c64b..b9e5494d051 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -31,7 +31,7 @@ "interchainAccountIsm": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", "interchainAccountRouter": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", - "interchainSecurityModule": "0xCF55470D7616D3257B1516703584B5d4280da4ED", + "interchainSecurityModule": "0x1116DC6960b430a3deAdF670A0cfe7f01b5280a3", "isTestnet": true, "mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "merkleTreeHook": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", @@ -94,7 +94,7 @@ "from": 49690504 }, "interchainGasPaymaster": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "interchainSecurityModule": "0x43C997061a5C222EFdCbf16426DdA0B6B32e7158", + "interchainSecurityModule": "0x67dDC5cD2f82C18D7638d660e6765E9B5A39648D", "isTestnet": true, "mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", "merkleTreeHook": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", @@ -162,7 +162,7 @@ "from": 13851043 }, "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x2820C18D0C5F90d3eC6850b9808BCaAE102C6F93", + "interchainSecurityModule": "0x4259f88Ec1790dE3E9054a363dE963316e5d3EF0", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", @@ -232,7 +232,7 @@ "interchainAccountIsm": "0xa9D8Ec959F34272B1a56D09AF00eeee58970d3AE", "interchainAccountRouter": "0x6d2B3e304E58c2a19f1492E7cf15CaF63Ce6e0d2", "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", - "interchainSecurityModule": "0x3778EeeAf9492451d9031AfDBB4004626cbccE1B", + "interchainSecurityModule": "0x62f08d1B492022A392c5216182C1c03991b56130", "isTestnet": true, "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", @@ -301,7 +301,7 @@ "from": 4950 }, "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", - "interchainSecurityModule": "0xeE511CEae36cd8A8Adc531E72D9803ba37a85E18", + "interchainSecurityModule": "0xCf71BD48503504B74B62c6eaBfA45D1d45CC27a0", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", @@ -402,7 +402,7 @@ "from": 1606754 }, "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x7d2e30B43aA1582Bda31C58985c6743230e8ac62", + "interchainSecurityModule": "0x705dd1E03A281027d728D4F567eb4A0eA4c9BB01", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", @@ -468,7 +468,7 @@ "interchainAccountIsm": "0xfaB4815BDC5c60c6bD625459C8577aFdD79D9311", "interchainAccountRouter": "0xeEF6933122894fF217a7dd07510b3D64b747e29b", "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "interchainSecurityModule": "0x3aeBc0206666CFcE29d6E678ff9c1e0D053e8077", + "interchainSecurityModule": "0x8cDb96f843C3442a2AB358c164725685781C1F3D", "isTestnet": true, "mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", "merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", @@ -534,7 +534,7 @@ "from": 1543015 }, "interchainGasPaymaster": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", - "interchainSecurityModule": "0x86B0AD09B05840Bcdb00ff4323f8Eb39cfc7e732", + "interchainSecurityModule": "0xB553bBC7Fa0bb3540794500876C0D238B006EE1c", "isTestnet": true, "mailbox": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", "merkleTreeHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", @@ -599,7 +599,7 @@ "from": 15833917 }, "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x62C1f1A1Fcc0C24eC1425c55317843259387EAb0", + "interchainSecurityModule": "0x4aA92b296F5FB271A825C9d903A6e52Ec9cB4A0b", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", @@ -727,7 +727,7 @@ "from": 10634605 }, "interchainGasPaymaster": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", - "interchainSecurityModule": "0xbb1977DB0fA1a0a9F3BeA022C18039708412B863", + "interchainSecurityModule": "0x1f0Ec7Cc255D8DB89196BaC2326723A4b60c7DBE", "isTestnet": true, "mailbox": "0x54148470292C24345fb828B003461a9444414517", "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", @@ -802,7 +802,7 @@ "interchainAccountIsm": "0xE023239c8dfc172FF008D8087E7442d3eBEd9350", "interchainAccountRouter": "0xe17c37212d785760E8331D4A4395B17b34Ba8cDF", "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "interchainSecurityModule": "0xd67E6C921A4285D9e57D3AC780a8aD7D928771f8", + "interchainSecurityModule": "0x10f397fBB5c9D3eE1E81d1888B635fd5670c228A", "isTestnet": true, "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", @@ -880,7 +880,7 @@ "interchainAccountIsm": "0x83a3068B719F764d413625dA77468ED74789ae02", "interchainAccountRouter": "0x8e131c8aE5BF1Ed38D05a00892b6001a7d37739d", "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", - "interchainSecurityModule": "0x85f13A4D3BE151e036A807D76893F70A03B96d58", + "interchainSecurityModule": "0xe839cf3AD5C01c687FcCE7de121a9987C568E818", "isTestnet": true, "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", @@ -991,7 +991,7 @@ "from": 3111622 }, "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", - "interchainSecurityModule": "0x8707f99c8B9C35Bd7795818394Fd6adC62B3E236", + "interchainSecurityModule": "0xA7aD4a35492e64b54EdCD6F5f9B8cd4D2aEd3184", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", @@ -1069,7 +1069,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x97b74242477D581F13944732811e42F4d46a287D", + "interchainSecurityModule": "0x8C241675A4c755F78B9D4473e7D546169635FA53", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1137,7 +1137,7 @@ "interchainAccountIsm": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", "interchainAccountRouter": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", "interchainGasPaymaster": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", - "interchainSecurityModule": "0x5ef15024646fd93f454426931E5339b0cBd7Eb8f", + "interchainSecurityModule": "0xfcF18EdA46855C1b7e319B4B2033Bf1f844B3534", "mailbox": "0xB08d78F439e55D02C398519eef61606A5926245F", "merkleTreeHook": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "pausableHook": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", @@ -1198,7 +1198,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x8af28EB954c5EcBEEB1f7D38d6C9E13d14c74A15", + "interchainSecurityModule": "0x2e1e05Ec996ed6A51A7d972B5c63C2BE2034100F", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1266,7 +1266,7 @@ "interchainAccountIsm": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", "interchainAccountRouter": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", "interchainGasPaymaster": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", - "interchainSecurityModule": "0x781d146C2887031cEA4093163e5E53949198E31E", + "interchainSecurityModule": "0xA20bA09A791B92Fb81d66BeEC391AfF38fDCf2cf", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x1b33611fCc073aB0737011d5512EF673Bff74962", "pausableHook": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", @@ -1327,7 +1327,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x96C669123224074F31efa9119a86E9c08c1939Cc", + "interchainSecurityModule": "0x1df25C93E719602EA6A6e0Ba94516FcfF040C9cd", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1391,7 +1391,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0xF6DB5f2A6E521985d658bdF405b9727964424eA8", + "interchainSecurityModule": "0x1CE1Dd90fd90C891e7FDc3933e07a3Fcc8a41989", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1642,7 +1642,7 @@ "interchainAccountIsm": "0x39c85C84876479694A2470c0E8075e9d68049aFc", "interchainAccountRouter": "0x80fE4Cb8c70fc60B745d4ffD4403c27a8cBC9e02", "interchainGasPaymaster": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", - "interchainSecurityModule": "0x4421F6B0F4775e7E1cFe9C4F6d29bFc40D5126bf", + "interchainSecurityModule": "0x912F3785d42B574d52c37Fef293a9545fcF07C19", "mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", "merkleTreeHook": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", "pausableHook": "0x4fE19d49F45854Da50b6009258929613EC92C147", @@ -1705,7 +1705,7 @@ "interchainAccountIsm": "0x3ca332A585FDB9d4FF51f2FA8999eA32184D3606", "interchainAccountRouter": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", "interchainGasPaymaster": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", - "interchainSecurityModule": "0xBc460Beee56F193E29Aa42d9eB99c4329f87AcfC", + "interchainSecurityModule": "0x17EB751b02CE7fbe8B70EA4ac013c5ba007841dD", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", "pausableHook": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", @@ -1768,7 +1768,7 @@ "interchainAccountIsm": "0xBF2C366530C1269d531707154948494D3fF4AcA7", "interchainAccountRouter": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", "interchainGasPaymaster": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", - "interchainSecurityModule": "0xc9d10b90057268F7b37cDB94f5e8eD6f10A77a81", + "interchainSecurityModule": "0x58Ca79d52F023E9a4a382F69F5D5C296cc91CEdC", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", "pausableHook": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", @@ -1839,7 +1839,7 @@ "interchainAccountIsm": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", "interchainAccountRouter": "0xe036768e48Cb0D42811d2bF0748806FCcBfCd670", "interchainGasPaymaster": "0x867f2089D09903f208AeCac84E599B90E5a4A821", - "interchainSecurityModule": "0x932AA65dB1555619BaB4Cc2219efdd415dD22a1b", + "interchainSecurityModule": "0xf57F816c65A01e5FAa6Dfd2EED656fFac2EADA63", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", "pausableHook": "0x7483faD0Bc297667664A43A064bA7c9911659f57", @@ -1898,7 +1898,7 @@ "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", "fallbackRoutingHook": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainGasPaymaster": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", - "interchainSecurityModule": "0x31dc078391518a8eC56aadCB54158714b596975a", + "interchainSecurityModule": "0xCf4d7d78F6A1Afe046cB996aFdE18bFB72F2c70C", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x4fE19d49F45854Da50b6009258929613EC92C147", "pausableHook": "0x01812D60958798695391dacF092BAc4a715B1718", @@ -2163,7 +2163,7 @@ "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "fallbackRoutingHook": "0xCB3c489a2FB67a7Cd555D47B3a9A0E654784eD16", "interchainGasPaymaster": "0x39c85C84876479694A2470c0E8075e9d68049aFc", - "interchainSecurityModule": "0xF1F44a3F92F7dd8D03c4D461a9b2a6d83ddc6A5d", + "interchainSecurityModule": "0xCc747d6EFB9e71D92dcdea7c8503D3419935D64C", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x843908541D24d9F6Fa30C8Bb1c39038C947D08fC", "pausableHook": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", @@ -2232,7 +2232,7 @@ "interchainAccountIsm": "0x507C18fa4e3b0ce6beBD494488D62d1ed0fB0555", "interchainAccountRouter": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", "interchainGasPaymaster": "0x39c85C84876479694A2470c0E8075e9d68049aFc", - "interchainSecurityModule": "0x00C69bE4bff7775b3DF6Da748e087dbD93F1e07C", + "interchainSecurityModule": "0x6E993E7485aC28fBB92780e24f64F6ABF2E98E5B", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x843908541D24d9F6Fa30C8Bb1c39038C947D08fC", "pausableHook": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", @@ -2297,7 +2297,7 @@ "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "fallbackRoutingHook": "0x39c85C84876479694A2470c0E8075e9d68049aFc", "interchainGasPaymaster": "0xB589407cf6bEA5CD81AD0946b9F1467933ede74c", - "interchainSecurityModule": "0xd847d1EAf388659e92475ad07e01aAe24C20507C", + "interchainSecurityModule": "0x21676D1882D8D702A8f428C2eBeFDC28ec297590", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", "pausableHook": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", @@ -2351,7 +2351,7 @@ "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "fallbackRoutingHook": "0x39c85C84876479694A2470c0E8075e9d68049aFc", "interchainGasPaymaster": "0xB589407cf6bEA5CD81AD0946b9F1467933ede74c", - "interchainSecurityModule": "0x13A179e08D9538Eb51DA4659E157f587F535Ff07", + "interchainSecurityModule": "0x90e41dCcB1cE47439ABcb1A90507a47DD2565B1f", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", "pausableHook": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", @@ -2417,7 +2417,7 @@ "interchainAccountIsm": "0x4da6f7E710137657008D5BCeF26151aac5c9884f", "interchainAccountRouter": "0x919Af376D02751bFCaD9CBAD6bad0c3089dAE33f", "interchainGasPaymaster": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", - "interchainSecurityModule": "0x63a0e3a65293Ce106AAbD5A2124e4E318DE667aB", + "interchainSecurityModule": "0xE59E61BdC48A85E291e0C0DA78c2e99A83F1749F", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", "pausableHook": "0x8d4f112cffa338D3c3Ef2Cf443179C5a48E678e4", @@ -2482,7 +2482,7 @@ "interchainAccountIsm": "0x4da6f7E710137657008D5BCeF26151aac5c9884f", "interchainAccountRouter": "0x919Af376D02751bFCaD9CBAD6bad0c3089dAE33f", "interchainGasPaymaster": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", - "interchainSecurityModule": "0x2380ecc0e745b2a06E890DbfD78E7c0f49c4037b", + "interchainSecurityModule": "0xF8845C39CE09101c2AfD4df30039a4ED31b08E8a", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", "pausableHook": "0x8d4f112cffa338D3c3Ef2Cf443179C5a48E678e4", @@ -2549,7 +2549,7 @@ "interchainAccountIsm": "0xD9dc83Ea22C6F1A224e51562B32b580695905A1A", "interchainAccountRouter": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", "interchainGasPaymaster": "0xce0e13f67399375eF0a7acb741E815145A6AAf67", - "interchainSecurityModule": "0x899F40E204be29060aaFec03c2cDcfd219fc8734", + "interchainSecurityModule": "0xE15869efE56Fc8F49aF9af310E20B93b12E5Bd73", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xb3D796584fDeBE2321894eeF31e0C3ec52169C61", "pausableHook": "0x2bD9aF503B9F608beAD63D4ACC328Abf9796b576", @@ -2610,7 +2610,7 @@ "interchainAccountIsm": "0x740bEd6E4eEc7c57a2818177Fba3f9E896D5DE1c", "interchainAccountRouter": "0xD5B70f7Da85F98A5197E55114A38f3eDcDCf020e", "interchainGasPaymaster": "0x919Af376D02751bFCaD9CBAD6bad0c3089dAE33f", - "interchainSecurityModule": "0xd2e0F1931b77d9973feE1182294B80104bbf2CCB", + "interchainSecurityModule": "0xaB85ee25c79a4B2e0Ae83c76B0856763F5b10eB6", "mailbox": "0x7d498740A4572f2B5c6b0A1Ba9d1d9DbE207e89E", "merkleTreeHook": "0x7d811da36c48cfDc7C445F14252F102bF06E3Fd7", "pausableHook": "0xCCC126d96efcc342BF2781A7d224D3AB1F25B19C", @@ -2670,7 +2670,7 @@ "interchainAccountIsm": "0x6cB503d97D1c900316583C8D55997A1f17b1ABd1", "interchainAccountRouter": "0x740bEd6E4eEc7c57a2818177Fba3f9E896D5DE1c", "interchainGasPaymaster": "0x48a53E3B176383BC98fcF4a24c9D470c19475164", - "interchainSecurityModule": "0xAd8A3F13043Ea6D60158Fe708a4c0Aa66654784b", + "interchainSecurityModule": "0x0d3D3Ef7310c36B1ADedd0BF6f623787d032085F", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", "pausableHook": "0xdb3338da7947dc9beDAB5f8685da721C293E0cbF", @@ -2732,7 +2732,7 @@ "interchainAccountIsm": "0x890eB21B76DCB165A1807cBE279f883716eA47D4", "interchainAccountRouter": "0xB7697612fbfb4ad02a11dCa16e9711eCB6Da4ceA", "interchainGasPaymaster": "0xA9425D5cBcD2c83EB2a5BF453EAA18968db3ef77", - "interchainSecurityModule": "0xbF88AC2A75E0f666A1fF5e2B9c0Ae3378E940e41", + "interchainSecurityModule": "0xE04dd1B83d27EAEbF59FD5cb89bb8572a378B1cb", "mailbox": "0x7FE7EA170cf08A25C2ff315814D96D93C311E692", "merkleTreeHook": "0x413c74F3D034dB54A1ecfFbd0Ad74Cb25E59f579", "pausableHook": "0x86abe4c3493A1eE0Aa42f0231b5594D42aBdA36e", @@ -2796,7 +2796,7 @@ "interchainAccountIsm": "0x6C3132f78260EdD1cE88Ea4FeEB8C2D6309ecc75", "interchainAccountRouter": "0x1681cc382e08a72d4b64A123080896e30f96B740", "interchainGasPaymaster": "0xB261C52241E133f957630AeeFEd48a82963AC33e", - "interchainSecurityModule": "0xe320c99D1c15be130C634ecB97c54dC02F2ab8A1", + "interchainSecurityModule": "0x8471463482Ba245D4Fd33f65EA83dDDCA4bDcd48", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xf83416bA0491C8BC80Dad259Fc7C007bC57Bd766", "pausableHook": "0x6cB503d97D1c900316583C8D55997A1f17b1ABd1", @@ -2864,7 +2864,7 @@ "interchainAccountIsm": "0x9667EfF1556A9D092fdbeC09244CB99b677E9D1E", "interchainAccountRouter": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C", "interchainGasPaymaster": "0xD5B70f7Da85F98A5197E55114A38f3eDcDCf020e", - "interchainSecurityModule": "0xf7E4168C13aAAB55e6d483091368606ed5D82EB9", + "interchainSecurityModule": "0x2186c92cf6Cae54b71405B7092967Ad37A8738ca", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x0b9A4A46f50f91f353B8Aa0F3Ca80E35E253bDd8", "pausableHook": "0x9450181a7719dAb93483d43a45473Ac2373E25B0", @@ -2946,6 +2946,129 @@ "http": "https://grpc-raw.kaon.kyve.network" } ] + }, + "auroratestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.testnet.aurora.dev/api", + "family": "blockscout", + "name": "auroratestnetscan", + "url": "https://explorer.testnet.aurora.dev" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 1313161555, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Aurora Testnet", + "domainId": 1313161555, + "isTestnet": true, + "name": "auroratestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "AETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://testnet.aurora.dev" + }, + { + "http": "https://endpoints.omniatech.io/v1/aurora/testnet/public" + }, + { + "http": "https://aurora-testnet.drpc.org" + } + ], + "technicalStack": "other", + "aggregationHook": "0x495A54e05CD28A4bC0Ea333031cA49457e80F4AC", + "domainRoutingIsm": "0xf6022b4f3f49755a21a57F12F182FdE76275Ed41", + "domainRoutingIsmFactory": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "fallbackRoutingHook": "0xE3D93F9296FA3dF262E1a54f0de02F71E845af6b", + "interchainGasPaymaster": "0xF61322936D80cd87B49df48F3DE24fD5c02dE9D1", + "interchainSecurityModule": "0x318FDE0ce35D5460919829F9d5dc1DD6988c71EB", + "mailbox": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "merkleTreeHook": "0x6d6a9bDDea1456673062633b7a4823dB13bDB9fb", + "pausableHook": "0x48c94311789194215Fe19002C2D33681A76d63dF", + "pausableIsm": "0xe0c5bDAfEe7F7065402337040E426A42b5C33650", + "protocolFee": "0x0De2F539569Fb1e2e3C1d233f7A63a18B9A17110", + "proxyAdmin": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841", + "staticAggregationHookFactory": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "staticAggregationIsm": "0x318FDE0ce35D5460919829F9d5dc1DD6988c71EB", + "staticAggregationIsmFactory": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "staticMerkleRootMultisigIsmFactory": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "staticMerkleRootWeightedMultisigIsmFactory": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", + "staticMessageIdMultisigIsmFactory": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "staticMessageIdWeightedMultisigIsmFactory": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", + "storageGasOracle": "0xDa5177080f7fC5d9255eB32cC64B9b4e5136A716", + "testRecipient": "0x2C6dD6768E669EDB7b53f26067C1C4534862c3de", + "validatorAnnounce": "0x711166cE892CBa0Fc01FdD74cFBE73b027678e15", + "index": { + "from": 194171128 + } + }, + "milkywaytestnet": { + "bech32Prefix": "milk", + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": 1 + }, + "canonicalAsset": "umilk", + "chainId": "ceers-2112", + "contractAddressBytes": 32, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Milkyway Testnet", + "domainId": 1162171030, + "gasPrice": { + "denom": "umilk", + "amount": "0.001" + }, + "grpcUrls": [ + { + "http": "https://grpc.testnet.milkyway.zone/" + } + ], + "index": { + "chunk": 10, + "from": 2449750 + }, + "isTestnet": true, + "name": "milkywaytestnet", + "nativeToken": { + "decimals": 6, + "denom": "umilk", + "name": "MILK", + "symbol": "MILK" + }, + "protocol": "cosmosnative", + "restUrls": [ + { + "http": "https://lcd.testnet.milkyway.zone/" + } + ], + "rpcUrls": [ + { + "http": "https://rpc.testnet.milkyway.zone/" + } + ], + "slip44": 118, + "technicalStack": "other", + "interchainGasPaymaster": "0x726f757465725f706f73745f6469737061746368000000040000000000000000", + "interchainSecurityModule": "0x726f757465725f69736d00000000000000000000000000040000000000000000", + "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", + "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000001", + "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000" } }, "defaultRpcConsensusType": "fallback" diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 9b2dbd0693a..37282764d77 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -51,6 +51,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< alfajores: true, arbitrumsepolia: true, arcadiatestnet2: true, + auroratestnet: true, basesepolia: true, bsctestnet: true, camptestnet: true, @@ -69,6 +70,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< infinityvmmonza: true, inksepolia: true, kyvetestnet: false, + milkywaytestnet: true, modetestnet: true, monadtestnet: true, odysseytestnet: true, @@ -95,6 +97,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< alfajores: true, arbitrumsepolia: true, arcadiatestnet2: true, + auroratestnet: true, basesepolia: true, bsctestnet: true, camptestnet: true, @@ -113,6 +116,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< infinityvmmonza: true, inksepolia: true, kyvetestnet: false, + milkywaytestnet: true, modetestnet: true, monadtestnet: true, odysseytestnet: true, @@ -139,6 +143,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< alfajores: true, arbitrumsepolia: true, arcadiatestnet2: false, + auroratestnet: true, basesepolia: true, bsctestnet: true, camptestnet: true, @@ -157,6 +162,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< infinityvmmonza: true, inksepolia: true, kyvetestnet: false, + milkywaytestnet: true, modetestnet: true, monadtestnet: true, odysseytestnet: true, @@ -194,6 +200,22 @@ const contextBase = { } as const; const gasPaymentEnforcement: GasPaymentEnforcement[] = [ + { + type: GasPaymentEnforcementPolicyType.Minimum, + payment: '1', + matchingList: [ + // Temporary workaround for testing milkywaytestnet->bsctestnet. + { + originDomain: getDomainId('milkywaytestnet'), + destinationDomain: getDomainId('bsctestnet'), + }, + // Temporary workaround for testing bsctestnet->milkywaytestnet. + { + originDomain: getDomainId('bsctestnet'), + destinationDomain: getDomainId('milkywaytestnet'), + }, + ], + }, { type: GasPaymentEnforcementPolicyType.None, matchingList: [ @@ -361,7 +383,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '385b307-20250418-150728', + tag: 'd9e0b4b-20250425-145730', }, chains: validatorChainConfig(Contexts.Hyperlane), resources: validatorResources, @@ -370,7 +392,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'da3978b-20250414-155929', + tag: 'd9e0b4b-20250425-145730', }, resources: scraperResources, }, @@ -385,7 +407,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '385b307-20250418-150728', + tag: 'd9e0b4b-20250425-145730', }, blacklist: relayBlacklist, gasPaymentEnforcement, diff --git a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json index 2866807361d..63f9efb6474 100644 --- a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json @@ -18,6 +18,9 @@ "arcadiatestnet2": { "validators": ["0xd39cd388ce3f616bc81be6dd3ec9348d7cdf4dff"] }, + "auroratestnet": { + "validators": ["0xab1a2c76bf4cced43fde7bc1b5b57b9be3e7f937"] + }, "basesepolia": { "validators": ["0x82e3b437a2944e3ff00258c93e72cd1ba5e0e921"] }, @@ -74,8 +77,8 @@ "inksepolia": { "validators": ["0xe61c846aee275070207fcbf43674eb254f06097a"] }, - "kyvetestnet": { - "validators": ["0x3c470ad2640bc0bcb6a790e8cf85e54d34ca92f5"] + "milkywaytestnet": { + "validators": ["0x65c7581e14efdf4d9c5320882170f022835bd742"] }, "modetestnet": { "validators": ["0x9a9de3e406ab3e4ff12aa03ca9b868b48dc40402"] diff --git a/typescript/infra/config/environments/testnet4/core/verification.json b/typescript/infra/config/environments/testnet4/core/verification.json index 0b273b99912..3a83cee6f20 100644 --- a/typescript/infra/config/environments/testnet4/core/verification.json +++ b/typescript/infra/config/environments/testnet4/core/verification.json @@ -3099,5 +3099,75 @@ "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", "isProxy": false } + ], + "auroratestnet": [ + { + "name": "ProxyAdmin", + "address": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", + "constructorArguments": "000000000000000000000000000000000000000000000000000000004e454153", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "00000000000000000000000011918dc33e067c5da83eef58e50f856398b8df4c000000000000000000000000ae7a78916ba4c507acb2f0e474ace545ff4bf84100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C" + }, + { + "name": "MerkleTreeHook", + "address": "0x6d6a9bDDea1456673062633b7a4823dB13bDB9fb", + "constructorArguments": "00000000000000000000000004438ef7622f5412f82915f59cad4f704c61ea48", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xE3D93F9296FA3dF262E1a54f0de02F71E845af6b", + "constructorArguments": "00000000000000000000000004438ef7622f5412f82915f59cad4f704c61ea48000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000006d6a9bddea1456673062633b7a4823db13bdb9fb", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x48c94311789194215Fe19002C2D33681A76d63dF", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xDa5177080f7fC5d9255eB32cC64B9b4e5136A716", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x23cD67A76c99578f77C73A306A29905D63D203b4", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xF61322936D80cd87B49df48F3DE24fD5c02dE9D1", + "constructorArguments": "00000000000000000000000023cd67a76c99578f77c73a306a29905d63d203b4000000000000000000000000ae7a78916ba4c507acb2f0e474ace545ff4bf84100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x23cD67A76c99578f77C73A306A29905D63D203b4" + }, + { + "name": "ProtocolFee", + "address": "0x0De2F539569Fb1e2e3C1d233f7A63a18B9A17110", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x711166cE892CBa0Fc01FdD74cFBE73b027678e15", + "constructorArguments": "00000000000000000000000004438ef7622f5412f82915f59cad4f704c61ea48", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 99d5d362a9e..77eed5e26e4 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -10,7 +10,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '4fd2990-20250414-150005', + tag: 'd9e0b4b-20250425-145730', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron @@ -32,6 +32,7 @@ export const keyFunderConfig: KeyFunderConfig< alfajores: '5', arbitrumsepolia: '0.1', arcadiatestnet2: '0.1', + auroratestnet: '0.05', basesepolia: '0.1', bsctestnet: '5', camptestnet: '0.1', @@ -51,6 +52,7 @@ export const keyFunderConfig: KeyFunderConfig< infinityvmmonza: '0', inksepolia: '0.1', kyvetestnet: '0', + milkywaytestnet: '0', modetestnet: '0.05', monadtestnet: '0.1', odysseytestnet: '0.1', diff --git a/typescript/infra/config/environments/testnet4/gasPrices.json b/typescript/infra/config/environments/testnet4/gasPrices.json index e3207126aca..c6e965c201c 100644 --- a/typescript/infra/config/environments/testnet4/gasPrices.json +++ b/typescript/infra/config/environments/testnet4/gasPrices.json @@ -19,8 +19,12 @@ "amount": "0.000000008", "decimals": 9 }, + "auroratestnet": { + "amount": "0.07", + "decimals": 9 + }, "basesepolia": { - "amount": "0.001000339", + "amount": "0.001001198", "decimals": 9 }, "bsctestnet": { @@ -91,6 +95,10 @@ "amount": "0.001", "decimals": 1 }, + "milkywaytestnet": { + "amount": "0.001", + "decimals": 1 + }, "modetestnet": { "amount": "0.001000252", "decimals": 9 @@ -100,11 +108,11 @@ "decimals": 9 }, "odysseytestnet": { - "amount": "1.000000252", + "amount": "1.034000276", "decimals": 9 }, "optimismsepolia": { - "amount": "0.001000336", + "amount": "0.001000345", "decimals": 9 }, "plumetestnet2": { @@ -112,15 +120,15 @@ "decimals": 9 }, "polygonamoy": { - "amount": "66.61600003", + "amount": "100.0", "decimals": 9 }, "scrollsepolia": { - "amount": "0.040772405", + "amount": "0.042895139", "decimals": 9 }, "sepolia": { - "amount": "9.024562066", + "amount": "21.954276416", "decimals": 9 }, "solanatestnet": { @@ -160,11 +168,11 @@ "decimals": 9 }, "unichaintestnet": { - "amount": "0.001000254", + "amount": "0.001000253", "decimals": 9 }, "weavevmtestnet": { - "amount": "1.000000007", + "amount": "1.2", "decimals": 9 } } diff --git a/typescript/infra/config/environments/testnet4/ism/verification.json b/typescript/infra/config/environments/testnet4/ism/verification.json index ff424d76a29..88851e693ac 100644 --- a/typescript/infra/config/environments/testnet4/ism/verification.json +++ b/typescript/infra/config/environments/testnet4/ism/verification.json @@ -3616,5 +3616,91 @@ "constructorArguments": "", "isProxy": true } + ], + "auroratestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x0033Aa0033189E05c33a9B82ccf857540d0b5C34", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x42aF5c65a035c4daFD15d8Ad5D86f9fCDc8A10F9", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x06115B4905a71dc18f9d9489B4e4B3e0A8c59426", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x8261624701dbAa452c1C874661F9719aE339E4DE", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xc2f12d971Bccd2cfAe792E22D06EA63ED6E8A302", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x5D71742EDc03161377eF9cD8F86fb9718f39b37a", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x5e42B15C8E06E24Cb71fC75943DEd7D869a2c2a4", + "constructorArguments": "", + "isProxy": true + } ] } diff --git a/typescript/infra/config/environments/testnet4/owners.ts b/typescript/infra/config/environments/testnet4/owners.ts index 162aaef6b31..45974a668b7 100644 --- a/typescript/infra/config/environments/testnet4/owners.ts +++ b/typescript/infra/config/environments/testnet4/owners.ts @@ -25,6 +25,9 @@ export const owners: ChainMap = { kyvetestnet: { owner: 'n/a - CSDK not supported here', }, + milkywaytestnet: { + owner: 'n/a - CSDK not supported here', + }, }; export const ethereumChainOwners: ChainMap = Object.fromEntries( diff --git a/typescript/infra/config/environments/testnet4/supportedChainNames.ts b/typescript/infra/config/environments/testnet4/supportedChainNames.ts index daa92765e01..0efc5c6e792 100644 --- a/typescript/infra/config/environments/testnet4/supportedChainNames.ts +++ b/typescript/infra/config/environments/testnet4/supportedChainNames.ts @@ -5,6 +5,7 @@ export const testnet4SupportedChainNames = [ 'alfajores', 'arbitrumsepolia', 'arcadiatestnet2', + 'auroratestnet', 'basesepolia', 'bsctestnet', 'camptestnet', @@ -23,6 +24,7 @@ export const testnet4SupportedChainNames = [ 'infinityvmmonza', 'inksepolia', 'kyvetestnet', + 'milkywaytestnet', 'modetestnet', 'monadtestnet', 'odysseytestnet', diff --git a/typescript/infra/config/environments/testnet4/tokenPrices.json b/typescript/infra/config/environments/testnet4/tokenPrices.json index 9ab9776dc84..0974b0496e3 100644 --- a/typescript/infra/config/environments/testnet4/tokenPrices.json +++ b/typescript/infra/config/environments/testnet4/tokenPrices.json @@ -4,6 +4,7 @@ "alfajores": "10", "arbitrumsepolia": "10", "arcadiatestnet2": "10", + "auroratestnet": "10", "basesepolia": "10", "bsctestnet": "10", "camptestnet": "10", @@ -22,6 +23,7 @@ "infinityvmmonza": "10", "inksepolia": "10", "kyvetestnet": "10", + "milkywaytestnet": "10", "modetestnet": "10", "monadtestnet": "10", "odysseytestnet": "10", @@ -33,7 +35,6 @@ "solanatestnet": "10", "somniatestnet": "10", "soneiumtestnet": "10", - "sonictestnet": "10", "sonicblaze": "10", "sonicsvmtestnet": "10", "suavetoliman": "10", diff --git a/typescript/infra/config/environments/testnet4/validators.ts b/typescript/infra/config/environments/testnet4/validators.ts index 9d73bb9d047..bf641a876d4 100644 --- a/typescript/infra/config/environments/testnet4/validators.ts +++ b/typescript/infra/config/environments/testnet4/validators.ts @@ -533,5 +533,26 @@ export const validatorChainConfig = ( 'kyvetestnet', ), }, + + auroratestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('auroratestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xab1a2c76bf4cced43fde7bc1b5b57b9be3e7f937'], + }, + 'auroratestnet', + ), + }, + milkywaytestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('milkywaytestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x65c7581e14efdf4d9c5320882170f022835bd742'], + }, + 'milkywaytestnet', + ), + }, }; }; diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index d7fd9e8b2e5..004c76ffbd0 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -270,6 +270,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + auroratestnet: { + threshold: 1, + validators: [ + { + address: '0xab1a2c76bf4cced43fde7bc1b5b57b9be3e7f937', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + avalanche: { threshold: 2, validators: [ @@ -1414,6 +1424,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + milkywaytestnet: { + threshold: 1, + validators: [ + { + address: '0x65c7581e14efdf4d9c5320882170f022835bd742', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + mint: { threshold: 2, validators: [ From eb54a9dc63fd3f8356a4b02cf93c98a60feaf96c Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Mon, 28 Apr 2025 14:35:25 +0100 Subject: [PATCH 080/223] fix: don't change signature order in metadata (#6046) ### Description Keep the ordering of metadata signatures the same for MerkleRootMultiSig ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: yjamin --- rust/main/chains/hyperlane-cosmos-native/src/ism.rs | 6 +----- rust/main/hyperlane-base/src/types/multisig.rs | 8 +------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/rust/main/chains/hyperlane-cosmos-native/src/ism.rs b/rust/main/chains/hyperlane-cosmos-native/src/ism.rs index ec0f33c885a..b892026c421 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/ism.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/ism.rs @@ -115,15 +115,11 @@ impl MultisigIsm for CosmosNativeIsm { t if t == MerkleRootMultisigIsm::type_url() => { let ism = MerkleRootMultisigIsm::decode(ism.value.as_slice()) .map_err(HyperlaneCosmosError::from)?; - let mut validators = ism + let validators = ism .validators .iter() .map(|v| H160::from_str(v).map(H256::from)) .collect::, _>>()?; - // on cosmos native, the ISM expects the checkpoints in the metadata to be sorted - // in ascending order of the validator address. So we sort the validators here, - // which will determine the order of the checkpoints in the metadata. - validators.sort(); Ok((validators, ism.threshold as u8)) } _ => Err(ChainCommunicationError::from_other_str(&format!( diff --git a/rust/main/hyperlane-base/src/types/multisig.rs b/rust/main/hyperlane-base/src/types/multisig.rs index 9cbbfb40117..59f593465e7 100644 --- a/rust/main/hyperlane-base/src/types/multisig.rs +++ b/rust/main/hyperlane-base/src/types/multisig.rs @@ -138,12 +138,6 @@ impl MultisigCheckpointSyncer { // the highest index for which we (supposedly) have (n+1) signed checkpoints latest_indices.sort_by(|a, b| b.1.cmp(&a.1)); - // create a slice with the sorted validators - let validators = latest_indices - .iter() - .map(|(address, _)| H256::from(*address)) - .collect::>(); - if let Some(&(_, highest_quorum_index)) = latest_indices.get(threshold - 1) { // The highest viable checkpoint index is the minimum of the highest index // we (supposedly) have a quorum for, and the maximum index for which we can @@ -156,7 +150,7 @@ impl MultisigCheckpointSyncer { for index in (minimum_index..=start_index).rev() { if let Ok(Some(checkpoint)) = - self.fetch_checkpoint(&validators, threshold, index).await + self.fetch_checkpoint(validators, threshold, index).await { return Ok(Some(checkpoint)); } From d2babb7ce8b860295830d1e593ad558f3f30921f Mon Sep 17 00:00:00 2001 From: xeno097 Date: Mon, 28 Apr 2025 10:01:58 -0400 Subject: [PATCH 081/223] feat: xERC20 extra lockboxes derivation updated logic (#6035) ### Description Updates the extra lockboxes logic to only rely on block explorers apis and skip derviation otherwise ### Drive-by changes - Removes rpc based logic to service lockboxes - Removes e2e tests for reading on chain lockboxes ### Related issues ### Backward compatibility ### Testing - Manual --- .changeset/lovely-melons-ring.md | 5 + .../cli/src/tests/warp/warp-check.e2e-test.ts | 114 +------------ .../sdk/src/block-explorer/etherscan.ts | 3 +- typescript/sdk/src/token/xerc20.ts | 161 ++++++++---------- 4 files changed, 75 insertions(+), 208 deletions(-) create mode 100644 .changeset/lovely-melons-ring.md diff --git a/.changeset/lovely-melons-ring.md b/.changeset/lovely-melons-ring.md new file mode 100644 index 00000000000..0cfa8335894 --- /dev/null +++ b/.changeset/lovely-melons-ring.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Remove fallback logic to derive extra lockboxes from rpc diff --git a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts index be9cc444d15..fd7ae117a0c 100644 --- a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts @@ -18,7 +18,7 @@ import { randomHookConfig, randomIsmConfig, } from '@hyperlane-xyz/sdk'; -import { Address, assert, deepCopy, randomInt } from '@hyperlane-xyz/utils'; +import { Address, assert, deepCopy } from '@hyperlane-xyz/utils'; import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; import { @@ -32,8 +32,6 @@ import { WARP_DEPLOY_OUTPUT_PATH, deployOrUseExistingCore, deployToken, - deployXERC20LockboxToken, - deployXERC20VSToken, getCombinedWarpRoutePath, handlePrompts, } from '../commands/helpers.js'; @@ -275,116 +273,6 @@ describe('hyperlane warp check e2e tests', async function () { expect(output.text()).to.includes(expectedDiffText); expect(output.text()).to.includes(expectedActualText); }); - - describe('check extra lockboxes', () => { - async function deployXERC20WarpRoute(): Promise< - [string, WarpRouteDeployConfig] - > { - const xERC20TokenSymbol = 'XERC20TOKEN'; - const xERC20Token = await deployXERC20VSToken( - ANVIL_KEY, - CHAIN_NAME_2, - undefined, - xERC20TokenSymbol, - ); - - const token = await deployToken( - ANVIL_KEY, - CHAIN_NAME_2, - undefined, - 'XERC20Collateral', - ); - const xERC20Lockbox = await deployXERC20LockboxToken( - ANVIL_KEY, - CHAIN_NAME_2, - token, - ); - - const tx = await xERC20Token.addBridge({ - bridge: xERC20Lockbox.address, - bufferCap: '1000', - rateLimitPerSecond: '1000', - }); - - await tx.wait(); - - const warpConfig: WarpRouteDeployConfig = { - [CHAIN_NAME_2]: { - type: TokenType.XERC20, - token: xERC20Token.address, - mailbox: chain2Addresses.mailbox, - owner: ownerAddress, - xERC20: { - warpRouteLimits: { - bufferCap: '0', - rateLimitPerSecond: '0', - }, - extraBridges: [ - { - limits: { - bufferCap: '1000', - rateLimitPerSecond: '1000', - }, - lockbox: xERC20Lockbox.address, - }, - ], - }, - }, - }; - - writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); - await hyperlaneWarpDeploy(WARP_DEPLOY_OUTPUT_PATH); - - return [xERC20TokenSymbol, warpConfig]; - } - - it(`should not find differences between the local limits and the on chain ones`, async function () { - const [xERC20TokenSymbol] = await deployXERC20WarpRoute(); - - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - xERC20TokenSymbol, - ).nothrow(); - - expect(output.exitCode).to.equal(0); - }); - - it(`should find differences between the local limits and the on chain ones`, async function () { - const [xERC20TokenSymbol, warpDeployConfig] = - await deployXERC20WarpRoute(); - - assert( - warpDeployConfig[CHAIN_NAME_2].type === TokenType.XERC20, - 'Deploy config should be for an XERC20 token', - ); - const currentExtraBridgesLimits = - warpDeployConfig[CHAIN_NAME_2].xERC20!.extraBridges![0]; - const wrongBufferCap = randomInt(100).toString(); - warpDeployConfig[CHAIN_NAME_2].xERC20!.extraBridges = [ - { - ...currentExtraBridgesLimits, - limits: { - bufferCap: wrongBufferCap, - rateLimitPerSecond: - currentExtraBridgesLimits.limits.rateLimitPerSecond, - }, - }, - ]; - - writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig); - const expectedDiffText = `EXPECTED: "${wrongBufferCap}"\n`; - const expectedActualText = `ACTUAL: "${currentExtraBridgesLimits.limits.rateLimitPerSecond}"\n`; - - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - xERC20TokenSymbol, - ).nothrow(); - - expect(output.exitCode).to.equal(1); - expect(output.text()).includes(expectedDiffText); - expect(output.text()).includes(expectedActualText); - }); - }); }); for (const hookType of MUTABLE_HOOK_TYPE) { diff --git a/typescript/sdk/src/block-explorer/etherscan.ts b/typescript/sdk/src/block-explorer/etherscan.ts index a78e2009080..957242bb12d 100644 --- a/typescript/sdk/src/block-explorer/etherscan.ts +++ b/typescript/sdk/src/block-explorer/etherscan.ts @@ -37,9 +37,10 @@ function formatExplorerUrl( async function handleEtherscanResponse(response: Response): Promise { const body = await response.json(); + const explorerUrl = new URL(response.url); if (body.status === '0') { throw new Error( - `Error while performing request to Etherscan like API: ${body.message} ${body.result}`, + `Error while performing request to Etherscan like API at ${explorerUrl.host}: ${body.message} ${body.result}`, ); } diff --git a/typescript/sdk/src/token/xerc20.ts b/typescript/sdk/src/token/xerc20.ts index b54735db602..cf45035bdab 100644 --- a/typescript/sdk/src/token/xerc20.ts +++ b/typescript/sdk/src/token/xerc20.ts @@ -3,14 +3,17 @@ import { Logger } from 'pino'; import { Log, getAbiItem, parseEventLogs, toEventSelector } from 'viem'; import { IXERC20Lockbox__factory } from '@hyperlane-xyz/core'; -import { Address, rootLogger, sleep } from '@hyperlane-xyz/utils'; +import { Address, rootLogger } from '@hyperlane-xyz/utils'; import { GetEventLogsResponse, getContractDeploymentTransaction, getLogsFromEtherscanLikeExplorerAPI, } from '../block-explorer/etherscan.js'; -import { ExplorerFamily } from '../metadata/chainMetadataTypes.js'; +import { + BlockExplorer, + ExplorerFamily, +} from '../metadata/chainMetadataTypes.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { ChainNameOrId } from '../types.js'; @@ -55,45 +58,72 @@ export type GetExtraLockboxesOptions = { chain: ChainNameOrId; xERC20Address: Address; multiProvider: MultiProvider; + explorerUrl: string; + apiKey?: string; logger?: Logger; }; +function isEvmBlockExplorerAndNotEtherscan( + blockExplorer: BlockExplorer, +): boolean { + if (!blockExplorer.family) { + return false; + } + + const byFamily: Record = { + [ExplorerFamily.Blockscout]: true, + [ExplorerFamily.Etherscan]: false, + [ExplorerFamily.Other]: false, + [ExplorerFamily.Routescan]: true, + [ExplorerFamily.Voyager]: false, + [ExplorerFamily.ZkSync]: true, + }; + + return byFamily[blockExplorer.family] ?? false; +} + export async function getExtraLockBoxConfigs({ xERC20Address, chain, multiProvider, logger = rootLogger, -}: GetExtraLockboxesOptions): Promise { - const { family, apiKey } = multiProvider.getExplorerApi(chain); - - // Fallback to use the rpc if the user has not provided an API key and the explorer family is Etherscan - // because the endpoint requires it or the explorer family is not known +}: Omit): Promise< + XERC20TokenExtraBridgesLimits[] +> { + const defaultExplorer = multiProvider.getExplorerApi(chain); + + const chainMetadata = multiProvider.getChainMetadata(chain); + const [fallBackExplorer] = + chainMetadata.blockExplorers?.filter((blockExplorer) => + isEvmBlockExplorerAndNotEtherscan(blockExplorer), + ) ?? []; + + // Fallback to use other block explorers if the default block explorer is etherscan and an API key is not + // configured const isExplorerConfiguredCorrectly = - family === ExplorerFamily.Etherscan ? !!apiKey : true; + defaultExplorer.family === ExplorerFamily.Etherscan + ? !!defaultExplorer.apiKey + : true; const canUseExplorerApi = - family !== ExplorerFamily.Other && isExplorerConfiguredCorrectly; + defaultExplorer.family !== ExplorerFamily.Other && + isExplorerConfiguredCorrectly; - const provider = multiProvider.getProvider(chain); - let logs: (ethers.providers.Log | GetEventLogsResponse)[]; - if (canUseExplorerApi) { - logs = await getConfigurationChangedLogsFromExplorerApi({ - chain, - multiProvider, - xERC20Address, - }); - } else { - logger.debug( - `Using rpc request to retrieve bridges on on lockbox contract ${xERC20Address} on chain ${chain}`, + const explorer = canUseExplorerApi ? defaultExplorer : fallBackExplorer; + if (!explorer) { + logger.warn( + `No block explorer was configured correctly, skipping lockbox derivation on chain ${chain}`, ); - - // Should be safe to use even with public RPCs as the total number of events in this topic should be low - logs = await getConfigurationChangedLogsFromRpc({ - chain, - multiProvider, - xERC20Address, - }); + return []; } + const logs = await getConfigurationChangedLogsFromExplorerApi({ + chain, + multiProvider, + xERC20Address, + explorerUrl: explorer.apiUrl, + apiKey: explorer.apiKey, + }); + const viemLogs = logs.map( (log) => ({ @@ -107,18 +137,23 @@ export async function getExtraLockBoxConfigs({ } as Log), ); - return getLockboxesFromLogs(viemLogs, provider, chain, logger); + return getLockboxesFromLogs( + viemLogs, + multiProvider.getProvider(chain), + chain, + logger, + ); } async function getConfigurationChangedLogsFromExplorerApi({ xERC20Address, chain, multiProvider, + explorerUrl, + apiKey, }: GetExtraLockboxesOptions): Promise> { - const { apiUrl, apiKey } = multiProvider.getExplorerApi(chain); - const contractDeploymentTx = await getContractDeploymentTransaction( - { apiUrl, apiKey }, + { apiUrl: explorerUrl, apiKey }, { contractAddress: xERC20Address }, ); @@ -129,7 +164,7 @@ async function getConfigurationChangedLogsFromExplorerApi({ ]); return getLogsFromEtherscanLikeExplorerAPI( - { apiUrl, apiKey }, + { apiUrl: explorerUrl, apiKey }, { address: xERC20Address, fromBlock: deploymentTransactionReceipt.blockNumber, @@ -139,68 +174,6 @@ async function getConfigurationChangedLogsFromExplorerApi({ ); } -async function getConfigurationChangedLogsFromRpc({ - xERC20Address, - chain, - multiProvider, -}: GetExtraLockboxesOptions): Promise> { - const provider = multiProvider.getProvider(chain); - - const endBlock = await provider.getBlockNumber(); - let currentStartBlock = await getContractCreationBlockFromRpc( - xERC20Address, - provider, - ); - - const logs = []; - const range = 5000; - while (currentStartBlock < endBlock) { - const currentLogs = await provider.getLogs({ - address: xERC20Address, - fromBlock: currentStartBlock, - toBlock: currentStartBlock + range, - topics: [CONFIGURATION_CHANGED_EVENT_SELECTOR], - }); - logs.push(...currentLogs); - currentStartBlock += range; - - // Sleep to avoid being rate limited with public RPCs - await sleep(350); - } - - return logs; -} - -// Retrieves the contract deployment number by performing a binary search -// calling getCode until the creation block is found -async function getContractCreationBlockFromRpc( - contractAddress: Address, - provider: ethers.providers.Provider, -): Promise { - const latestBlock = await provider.getBlockNumber(); - const latestCode = await provider.getCode(contractAddress, latestBlock); - if (latestCode === '0x') { - throw new Error('Provided address is not a contract'); - } - - let low = 0; - let high = latestBlock; - let creationBlock = latestBlock; - while (low <= high) { - const mid = Math.floor((low + high) / 2); - const code = await provider.getCode(contractAddress, mid); - - if (code !== '0x') { - creationBlock = mid; - high = mid - 1; - } else { - low = mid + 1; - } - } - - return creationBlock; -} - type ConfigurationChangedLog = Log< bigint, number, From ae9966b86536f841a509a8d379c9a5f963fcc17f Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:43:10 +0100 Subject: [PATCH 082/223] fix: milkyway `from` block (#6052) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/config/mainnet_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index dc33a7ce34e..5ede33376d2 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -9099,7 +9099,7 @@ ], "index": { "chunk": 10, - "from": 2454618 + "from": 2249100 }, "name": "milkyway", "nativeToken": { From 249ecc956cf7adef07498e9aa65dba913d8bb50e Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:16:08 -0400 Subject: [PATCH 083/223] feat: Add worldchain to EZETH (#6006) ### Description This PR adds worldchain to EZETH config ### Backward compatibility Yes ### Testing Manual - using checker as per Runbook --------- Co-authored-by: Le Co-authored-by: Le --- .../getRenzoEZETHSTAGEWarpConfig.ts | 2 ++ .../configGetters/getRenzoEZETHWarpConfig.ts | 16 ++++++++++++++-- .../config/environments/mainnet3/warp/warpIds.ts | 4 ++-- typescript/infra/config/warp.ts | 8 ++++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts index f7998b25550..1934d6f5bd4 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHSTAGEWarpConfig.ts @@ -26,6 +26,7 @@ const ezEthStagingAddresses: Record< swell: '0x585afea249031Ea4168A379F664e91dFc5F77E7D', unichain: '0x585afea249031Ea4168A379F664e91dFc5F77E7D', berachain: '0x585afea249031Ea4168A379F664e91dFc5F77E7D', + worldchain: '0xC33DdE0a44e3Bed87cc3Ff0325D3fcbA5279930E', }; export const ezEthStagingSafes: Record< @@ -47,6 +48,7 @@ export const ezEthStagingSafes: Record< swell: '0xf40b75fb85C3bEc70D75A1B45ef08FC48Db61115', unichain: '0x9D5FCF39FF17a67eB9CB4505f83920519EfEB01B', berachain: '0xf013c8Be28421b050cca5bD95cc57Af49568e8be', + worldchain: '0x3DA9AE6359Ad3eFFD33Ad334ae12bE55904BE4eA', }; const ezEthStagingLockbox = '0x74c8290836612e6251E49e8f3198fdD80C4DbEB8'; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts index c7eac5e935b..873168e0c7a 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts @@ -4,14 +4,12 @@ import { Mailbox__factory } from '@hyperlane-xyz/core'; import { ChainMap, ChainName, - ChainSubmissionStrategy, HookConfig, HookType, HypTokenRouterConfig, IsmType, MultisigConfig, TokenType, - TxSubmitterType, buildAggregationIsmConfigs, } from '@hyperlane-xyz/sdk'; import { Address, assert, symmetricDifference } from '@hyperlane-xyz/utils'; @@ -36,6 +34,7 @@ export const ezEthChainsToDeploy = [ 'swell', 'unichain', 'berachain', + 'worldchain', ]; export const MAX_PROTOCOL_FEE = parseEther('100').toString(); // Changing this will redeploy the PROTOCOL_FEE hook @@ -56,6 +55,7 @@ export const renzoTokenPrices: ChainMap = { swell: '3157.26', unichain: '2602.66', berachain: '10', + worldchain: '1599.53', }; export function getProtocolFee(chain: ChainName) { return (0.5 / Number(renzoTokenPrices[chain])).toFixed(10).toString(); // ~$0.50 USD @@ -101,6 +101,7 @@ const ezEthAddresses: Record<(typeof ezEthChainsToDeploy)[number], string> = { swell: '0x2416092f143378750bb29b79eD961ab195CcEea5', unichain: '0x2416092f143378750bb29b79eD961ab195CcEea5', berachain: '0x2416092f143378750bb29b79eD961ab195CcEea5', + worldchain: '0x2416092f143378750bb29b79eD961ab195CcEea5', }; export const ezEthValidators: ChainMap = { @@ -254,6 +255,16 @@ export const ezEthValidators: ChainMap = { { address: '0xae09cb3febc4cad59ef5a56c1df741df4eb1f4b6', alias: 'Renzo' }, ], }, + worldchain: { + threshold: 1, + validators: [ + { + address: '0x15c6aaf2d982651ea5ae5f080d0ddfe7d6545f19', + alias: 'Luganodes', + }, + { address: '0x650a1bcb489BE2079d82602c10837780ef6dADA8', alias: 'Renzo' }, + ], + }, }; export const ezEthSafes: Record<(typeof ezEthChainsToDeploy)[number], string> = @@ -273,6 +284,7 @@ export const ezEthSafes: Record<(typeof ezEthChainsToDeploy)[number], string> = swell: '0x435E8c9652Da151292F3981bbf663EBEB6668501', unichain: '0x70aF964829DA7F3f51973EE806AEeAB9225F2661', berachain: '0x865BA5789D82F2D4C5595a3968dad729A8C3daE6', + worldchain: '0x7Be36310285cA4e809C296526745DA983c8F8e0f', }; const existingProxyAdmins: ChainMap<{ address: string; owner: string }> = { diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index 7e7cc07ae18..d8d385be943 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -1,7 +1,7 @@ export enum WarpRouteIds { Ancient8EthereumUSDC = 'USDC/ancient8-ethereum', - ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETH = 'EZETH/arbitrum-base-berachain-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-swell-taiko-unichain-zircuit', - ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETHSTAGE = 'EZETHSTAGE/arbitrum-base-berachain-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-swell-taiko-unichain-zircuit', + ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainWorldchainZircuitEZETH = 'EZETH/arbitrum-base-berachain-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-swell-taiko-unichain-worldchain-zircuit', + ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainWorldchainZircuitEZETHSTAGE = 'EZETHSTAGE/arbitrum-base-berachain-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-swell-taiko-unichain-worldchain-zircuit', ArbitrumBaseEnduranceUSDC = 'USDC/arbitrum-base-endurance', ArbitrumEthereumZircuitAMPHRETH = 'AMPHRETH/arbitrum-ethereum-zircuit', ArbitrumNeutronEclip = 'ECLIP/arbitrum-neutron', diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index 2619afac7f4..4f3aeaa1f9c 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -86,9 +86,9 @@ export const warpConfigGetterMap: Record = { [WarpRouteIds.EthereumInevmUSDC]: getEthereumInevmUSDCWarpConfig, [WarpRouteIds.EthereumInevmUSDT]: getEthereumInevmUSDTWarpConfig, [WarpRouteIds.ArbitrumNeutronTIA]: getArbitrumNeutronTiaWarpConfig, - [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETH]: + [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainWorldchainZircuitEZETH]: getRenzoEZETHWarpConfig, - [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETHSTAGE]: + [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainWorldchainZircuitEZETHSTAGE]: getRenzoEZETHSTAGEWarpConfig, [WarpRouteIds.InevmInjectiveINJ]: getInevmInjectiveINJWarpConfig, [WarpRouteIds.ArbitrumAvalancheBaseFlowmainnetFormOptimismSolanamainnetWorldchainTRUMP]: @@ -144,9 +144,9 @@ type StrategyConfigGetter = () => ChainSubmissionStrategy; export const strategyConfigGetterMap: Record = { [WarpRouteIds.ArbitrumAvalancheBaseBscEthereumLumiaprismOptimismPolygonLUMIA]: getLUMIAGnosisSafeBuilderStrategyConfig, - [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETH]: + [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainWorldchainZircuitEZETH]: getEZETHGnosisSafeBuilderStrategyConfig, - [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainZircuitEZETHSTAGE]: + [WarpRouteIds.ArbitrumBaseBerachainBlastBscEthereumFraxtalLineaModeOptimismSeiSwellTaikoUnichainWorldchainZircuitEZETHSTAGE]: getEZETHSTAGEGnosisSafeBuilderStrategyConfig, [WarpRouteIds.ArbitrumBaseEthereumLumiaprismOptimismPolygonETH]: getArbitrumBaseEthereumLumiaprismOptimismPolygonETHGnosisSafeBuilderStrategyConfig, From cd8f7a30507e7a179ac92e538b01972f5aacbdff Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:37:29 +0200 Subject: [PATCH 084/223] chore: implemented review comments (#6042) ### Description This PR implements requested changes from the review of the following PRs: - https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/5888 - https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6022 ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual tests with UI if still everything works --- .../src/metadata/chainMetadataConversion.ts | 3 ++ .../adapters/CosmosModuleTokenAdapter.ts | 5 ++++ .../src/walletIntegrations/multiProtocol.tsx | 28 ++++++++----------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/typescript/sdk/src/metadata/chainMetadataConversion.ts b/typescript/sdk/src/metadata/chainMetadataConversion.ts index 08e6fe8d1f4..ad0a3daf0f8 100644 --- a/typescript/sdk/src/metadata/chainMetadataConversion.ts +++ b/typescript/sdk/src/metadata/chainMetadataConversion.ts @@ -70,6 +70,9 @@ export function chainMetadataToCosmosChain(metadata: ChainMetadata): { }, fees: { fee_tokens: [ + // if there is a gas price object available in the cosmos registry + // config we infer the gas denom and prices from it, if not we take + // the native token denom and omit the gas prices { denom: gasPrice?.denom ?? nativeToken.denom!, ...(gasPrice?.amount diff --git a/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts index c90b7803f6b..bb2a17a3add 100644 --- a/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmosModuleTokenAdapter.ts @@ -55,6 +55,11 @@ class CosmosModuleTokenAdapter const provider = await this.getProvider(); const denom = await this.getDenom(); + // if the address is a cosmos address we can simply read the account balance + // of that address. The address can also be an ETH address format indicating + // that the balance of a Hyp Token Contract should be returned. In this case + // we get the token by it's id and return the bridged supply which equals the + // balance the token has. if (isAddressCosmos(address)) { const coin = await provider.getBalance(address, denom); return BigInt(coin.amount); diff --git a/typescript/widgets/src/walletIntegrations/multiProtocol.tsx b/typescript/widgets/src/walletIntegrations/multiProtocol.tsx index 5bbff73a63b..b405751c00f 100644 --- a/typescript/widgets/src/walletIntegrations/multiProtocol.tsx +++ b/typescript/widgets/src/walletIntegrations/multiProtocol.tsx @@ -143,18 +143,15 @@ export function getAddressFromAccountAndChain( account?: AccountInfo, chainName?: ChainName, ) { - if (!account || !chainName) { + if (!account) { return 'Unknown'; } - // by default display the first address of the account - let address = account.addresses[0]?.address ?? 'Unknown'; - // only in cosmos there are multiple addresses per account, in this // case we display the cosmos hub address by default. If the user // selects a cosmos based origin chain in the swap form that cosmos // address is displayed instead - if (account?.protocol === ProtocolType.Cosmos) { + if (account.protocol === ProtocolType.Cosmos) { // chainName can be an EVM chain here, therefore if no // cosmos address was found we search for the cosmos hub // address below @@ -162,19 +159,18 @@ export function getAddressFromAccountAndChain( (a) => a.chainName === chainName, )?.address; - if (cosmosAddress) { - // set cosmos address if one has been found for the chain name - address = cosmosAddress; - } else { - // search for the cosmos hub address if no address has been found - // for the chain name - address = - account?.addresses?.find((a) => a.chainName === cosmoshub.name) - ?.address ?? 'Unknown'; - } + // if no cosmos address was found for the chain name we search + // for the cosmos hub address as fallback + return ( + cosmosAddress ?? + account?.addresses?.find((a) => a.chainName === cosmoshub.name) + ?.address ?? + 'Unknown' + ); } - return address; + // by default display the first address of the account + return account.addresses[0]?.address ?? 'Unknown'; } export function getAccountAddressAndPubKey( From 3a04631dadabed03f6619c8c27d70079b9a78744 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:53:08 +0100 Subject: [PATCH 085/223] chore: update .registryrc (#6055) ### Description chore: update .registryrc should've been updated alongside https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6052 ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .registryrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.registryrc b/.registryrc index bef059f9517..98d479046ce 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -ab67983b3c146f03284abd0268655520023c08e1 +7a0a79942383dac2fca1fa5e5a0e57f5d8b2d3f2 From 03145b36700ba6deff094d7f5236af2149201d48 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:09:25 +0100 Subject: [PATCH 086/223] feat: rearrange release workflow (#6040) ### Description feat: rearrange release workflow - before this change, we'd spend 10-20mins in CI waiting for the cli matrix to run before updating the release PR. - instead what is more beneficial is only running the full CLI test matrix when we detect that we may want to publish a release. ### Drive-by changes - passthrough `github` cache-provider to the install/build action so that we use the correct cache on depot runners. currently we are using the wrong cache. ### Related issues ### Backward compatibility ### Testing --- .github/actions/install-cli/action.yml | 5 +++ .github/workflows/release.yml | 57 ++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/.github/actions/install-cli/action.yml b/.github/actions/install-cli/action.yml index 81539a61a3e..c7a58c2b0ff 100644 --- a/.github/actions/install-cli/action.yml +++ b/.github/actions/install-cli/action.yml @@ -5,6 +5,10 @@ inputs: ref: description: "The Git ref to checkout" required: true + cache-provider: + description: "Choose cache provider: buildjet or github" + required: false + default: "buildjet" runs: using: "composite" @@ -13,6 +17,7 @@ runs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ inputs.ref }} + cache-provider: ${{ inputs.cache-provider }} - name: Pack the CLI shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34e95b91a13..ad42b21c8f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,55 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: + # This job prepares the release by creating or updating a release PR. + # Notice the omission of the `publish` flag in the changesets action. + prepare-release: + outputs: + hasChangesets: ${{ steps.changesets.outputs.hasChangesets }} + permissions: + id-token: write + contents: write + pull-requests: write + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + # check out full history + fetch-depth: 0 + submodules: recursive + + - name: Setup Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + + - name: Install Dependencies + run: yarn install --immutable + + - name: Create Release PR + id: changesets + uses: changesets/action@v1 + with: + version: yarn version:prepare + env: + NPM_CONFIG_PROVENANCE: true + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Set Changesets + run: echo "changesets=$(yarn changeset status --json)" >> $GITHUB_OUTPUT + + # The release PR removes individual changesets to prepare for a release. + # This means that once the release PR is merged, there are no changesets left. + # When there are no changesets left, we can run the cli-install-cross-platform-release-test + # workflow to verify that the CLI installs correctly on all platforms. + # In all other cases, we already have a barebones `cli-install` test on the default CI platform + # which will catch most issues before any offending PR is merged. cli-install-cross-platform-release-test: + needs: prepare-release + if: needs.prepare-release.outputs.hasChangesets == 'false' strategy: matrix: os: [depot-ubuntu-latest, depot-macos-latest, depot-windows-2022] @@ -28,13 +76,14 @@ jobs: uses: ./.github/actions/install-cli with: ref: ${{ github.sha }} + cache-provider: github - name: Test run the CLI run: hyperlane --version - release: - needs: - - cli-install-cross-platform-release-test + # This job publishes the release to NPM. + publish-release: + needs: cli-install-cross-platform-release-test permissions: id-token: write contents: write @@ -57,7 +106,7 @@ jobs: - name: Install Dependencies run: yarn install --immutable - - name: Create Release PR or Publish to NPM + - name: Publish Release to NPM id: changesets uses: changesets/action@v1 with: From eb0dfeeb3df132b791eaa9baf3feeaf42bc50341 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 14:26:25 -0400 Subject: [PATCH 087/223] Version Packages (#6053) 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 publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). 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/sdk@12.4.0 ### Minor Changes - d2babb7: Remove fallback logic to derive extra lockboxes from rpc ### Patch Changes - @hyperlane-xyz/cosmos-sdk@12.4.0 - @hyperlane-xyz/utils@12.4.0 - @hyperlane-xyz/core@7.1.3 ## @hyperlane-xyz/core@7.1.3 ### Patch Changes - @hyperlane-xyz/utils@12.4.0 ## @hyperlane-xyz/cosmos-sdk@12.4.0 ### Patch Changes - @hyperlane-xyz/cosmos-types@12.4.0 ## @hyperlane-xyz/helloworld@12.4.0 ### Patch Changes - Updated dependencies [d2babb7] - @hyperlane-xyz/sdk@12.4.0 - @hyperlane-xyz/core@7.1.3 ## @hyperlane-xyz/widgets@12.4.0 ### Patch Changes - Updated dependencies [d2babb7] - @hyperlane-xyz/sdk@12.4.0 - @hyperlane-xyz/cosmos-sdk@12.4.0 - @hyperlane-xyz/utils@12.4.0 ## @hyperlane-xyz/cli@12.4.0 ## @hyperlane-xyz/cosmos-types@12.4.0 ## @hyperlane-xyz/utils@12.4.0 ## @hyperlane-xyz/infra@12.4.0 ### Patch Changes - Updated dependencies [d2babb7] - @hyperlane-xyz/sdk@12.4.0 - @hyperlane-xyz/helloworld@12.4.0 - @hyperlane-xyz/utils@12.4.0 ## @hyperlane-xyz/ccip-server@12.4.0 ## @hyperlane-xyz/github-proxy@12.4.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Co-authored-by: Xaroz --- .changeset/lovely-melons-ring.md | 5 --- .github/workflows/test.yml | 4 +-- .prettierignore | 1 + solidity/CHANGELOG.md | 6 ++++ solidity/contracts/PackageVersioned.sol | 2 +- solidity/package.json | 4 +-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 2 ++ typescript/cli/package.json | 6 ++-- typescript/cli/src/version.ts | 2 +- typescript/cosmos-sdk/CHANGELOG.md | 6 ++++ typescript/cosmos-sdk/package.json | 4 +-- typescript/cosmos-types/CHANGELOG.md | 2 ++ typescript/cosmos-types/package.json | 2 +- typescript/github-proxy/CHANGELOG.md | 2 ++ typescript/github-proxy/package.json | 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 | 12 +++++++ typescript/sdk/package.json | 8 ++--- typescript/utils/CHANGELOG.md | 2 ++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 9 ++++++ typescript/widgets/package.json | 8 ++--- yarn.lock | 42 ++++++++++++------------- 28 files changed, 112 insertions(+), 56 deletions(-) delete mode 100644 .changeset/lovely-melons-ring.md diff --git a/.changeset/lovely-melons-ring.md b/.changeset/lovely-melons-ring.md deleted file mode 100644 index 0cfa8335894..00000000000 --- a/.changeset/lovely-melons-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Remove fallback logic to derive extra lockboxes from rpc diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index af2c47bb505..9c15899bee3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -91,12 +91,12 @@ jobs: if: github.event_name == 'pull_request' run: >- cat changed_files.txt | - xargs -r yarn exec prettier --check --ignore-unknown + xargs -r yarn exec prettier --check --ignore-unknown --no-error-on-unmatched-pattern - name: Run prettier (all files) if: github.event_name != 'pull_request' run: >- - yarn exec prettier --check --ignore-unknown . + yarn exec prettier --check --ignore-unknown --no-error-on-unmatched-pattern . continue-on-error: true - name: Clean up diff --git a/.prettierignore b/.prettierignore index f2f47dff36d..6f049111142 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,3 +15,4 @@ tools/ typescript/infra/helm/**/templates/ typescript/infra/src/infrastructure/external-secrets/helm/templates/ vectors/ +.changeset/ diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index d3e252ea475..b7c1735f2cf 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/core +## 7.1.3 + +### Patch Changes + +- @hyperlane-xyz/utils@12.4.0 + ## 7.1.2 ### Patch Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index d57ddf8b2ac..01263dcab62 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.1.2"; + string public constant PACKAGE_VERSION = "7.1.3"; } diff --git a/solidity/package.json b/solidity/package.json index a8ab079c144..c896bd53edd 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.1.2", + "version": "7.1.3", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.3.0", + "@hyperlane-xyz/utils": "12.4.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index e5a5d1596a4..d9ffbc6ba50 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 12.4.0 + ## 12.3.0 ## 12.2.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 6f441f0cfed..5d3ce1725a4 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.3.0", + "version": "12.4.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 a208b3996c5..bb240bbe1de 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cli +## 12.4.0 + ## 12.3.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 4f7f6aac59f..aaf805207ec 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.3.0", + "version": "12.4.0", "description": "A command-line utility for common Hyperlane operations", "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", @@ -9,8 +9,8 @@ "@ethersproject/abi": "*", "@ethersproject/providers": "*", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.3.0", - "@hyperlane-xyz/utils": "12.3.0", + "@hyperlane-xyz/sdk": "12.4.0", + "@hyperlane-xyz/utils": "12.4.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index f18f474793d..aa1cff221bd 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.3.0'; +export const VERSION = '12.4.0'; diff --git a/typescript/cosmos-sdk/CHANGELOG.md b/typescript/cosmos-sdk/CHANGELOG.md index 95f5fe51563..b1654ef41be 100644 --- a/typescript/cosmos-sdk/CHANGELOG.md +++ b/typescript/cosmos-sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/cosmos-sdk +## 12.4.0 + +### Patch Changes + +- @hyperlane-xyz/cosmos-types@12.4.0 + ## 12.3.0 ### Patch Changes diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 3e4e53febf9..82228dc642b 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-sdk", - "version": "12.3.0", + "version": "12.4.0", "description": "Hyperlane TypeScript SDK for the Cosmos Hyperlane SDK module", "type": "module", "exports": { @@ -46,6 +46,6 @@ }, "dependencies": { "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/cosmos-types": "12.3.0" + "@hyperlane-xyz/cosmos-types": "12.4.0" } } diff --git a/typescript/cosmos-types/CHANGELOG.md b/typescript/cosmos-types/CHANGELOG.md index dd0922cad4e..bd4f009b9b0 100644 --- a/typescript/cosmos-types/CHANGELOG.md +++ b/typescript/cosmos-types/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cosmos-types +## 12.4.0 + ## 12.3.0 ## 12.2.0 diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index f4e4a9b7c40..4ba6eeb8af1 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-types", - "version": "12.3.0", + "version": "12.4.0", "description": "Hyperlane TypeScript SDK types for the Cosmos Hyperlane SDK module", "type": "module", "exports": { diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index 6e3ece0b848..d42179fef8d 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 12.4.0 + ## 12.3.0 ## 12.2.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index 4f1c71fed3c..f60ebc39284 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.3.0", + "version": "12.4.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 253fe0ab870..3871e08cc4d 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/helloworld +## 12.4.0 + +### Patch Changes + +- Updated dependencies [d2babb7] + - @hyperlane-xyz/sdk@12.4.0 + - @hyperlane-xyz/core@7.1.3 + ## 12.3.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 0cff7eff8df..c2d7c8c7bb1 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": "12.3.0", + "version": "12.4.0", "dependencies": { - "@hyperlane-xyz/core": "7.1.2", + "@hyperlane-xyz/core": "7.1.3", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.3.0", + "@hyperlane-xyz/sdk": "12.4.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 6188612c182..b0404110e8d 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/infra +## 12.4.0 + +### Patch Changes + +- Updated dependencies [d2babb7] + - @hyperlane-xyz/sdk@12.4.0 + - @hyperlane-xyz/helloworld@12.4.0 + - @hyperlane-xyz/utils@12.4.0 + ## 12.3.0 ### Minor Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 40a34191f65..84a76ff62b2 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": "12.3.0", + "version": "12.4.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.3.0", + "@hyperlane-xyz/helloworld": "12.4.0", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.3.0", - "@hyperlane-xyz/utils": "12.3.0", + "@hyperlane-xyz/sdk": "12.4.0", + "@hyperlane-xyz/utils": "12.4.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index a357fc83c70..d03078f5e49 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,17 @@ # @hyperlane-xyz/sdk +## 12.4.0 + +### Minor Changes + +- d2babb7: Remove fallback logic to derive extra lockboxes from rpc + +### Patch Changes + +- @hyperlane-xyz/cosmos-sdk@12.4.0 +- @hyperlane-xyz/utils@12.4.0 +- @hyperlane-xyz/core@7.1.3 + ## 12.3.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 90370f614f0..b4ca79028ec 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,16 +1,16 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.3.0", + "version": "12.4.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.122", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.1.2", - "@hyperlane-xyz/cosmos-sdk": "12.3.0", - "@hyperlane-xyz/utils": "12.3.0", + "@hyperlane-xyz/core": "7.1.3", + "@hyperlane-xyz/cosmos-sdk": "12.4.0", + "@hyperlane-xyz/utils": "12.4.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index c1f963643a3..2da4f812a83 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/utils +## 12.4.0 + ## 12.3.0 ### Minor Changes diff --git a/typescript/utils/package.json b/typescript/utils/package.json index e35ad8b3df7..3f040634612 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": "12.3.0", + "version": "12.4.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index a39b520f8ed..d43991663be 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/widgets +## 12.4.0 + +### Patch Changes + +- Updated dependencies [d2babb7] + - @hyperlane-xyz/sdk@12.4.0 + - @hyperlane-xyz/cosmos-sdk@12.4.0 + - @hyperlane-xyz/utils@12.4.0 + ## 12.3.0 ### Minor Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index a6d3ec53a8c..58724266ade 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.3.0", + "version": "12.4.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -10,9 +10,9 @@ "@cosmjs/stargate": "^0.32.4", "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/cosmos-sdk": "12.3.0", - "@hyperlane-xyz/sdk": "12.3.0", - "@hyperlane-xyz/utils": "12.3.0", + "@hyperlane-xyz/cosmos-sdk": "12.4.0", + "@hyperlane-xyz/sdk": "12.4.0", + "@hyperlane-xyz/utils": "12.4.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index c0b9cf768f7..47f9d6f885f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7645,8 +7645,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.3.0" - "@hyperlane-xyz/utils": "npm:12.3.0" + "@hyperlane-xyz/sdk": "npm:12.4.0" + "@hyperlane-xyz/utils": "npm:12.4.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7686,14 +7686,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.1.2, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.3, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.3.0" + "@hyperlane-xyz/utils": "npm:12.4.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7733,13 +7733,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-sdk@npm:12.3.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": +"@hyperlane-xyz/cosmos-sdk@npm:12.4.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk" dependencies: "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/cosmos-types": "npm:12.3.0" + "@hyperlane-xyz/cosmos-types": "npm:12.4.0" "@types/mocha": "npm:^10.0.1" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" @@ -7755,7 +7755,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-types@npm:12.3.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": +"@hyperlane-xyz/cosmos-types@npm:12.4.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" dependencies: @@ -7789,14 +7789,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.3.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:12.4.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.2" + "@hyperlane-xyz/core": "npm:7.1.3" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.3.0" + "@hyperlane-xyz/sdk": "npm:12.4.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7845,10 +7845,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.3.0" + "@hyperlane-xyz/helloworld": "npm:12.4.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.3.0" - "@hyperlane-xyz/utils": "npm:12.3.0" + "@hyperlane-xyz/sdk": "npm:12.4.0" + "@hyperlane-xyz/utils": "npm:12.4.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7920,7 +7920,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.3.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:12.4.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7930,9 +7930,9 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.2" - "@hyperlane-xyz/cosmos-sdk": "npm:12.3.0" - "@hyperlane-xyz/utils": "npm:12.3.0" + "@hyperlane-xyz/core": "npm:7.1.3" + "@hyperlane-xyz/cosmos-sdk": "npm:12.4.0" + "@hyperlane-xyz/utils": "npm:12.4.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -7977,7 +7977,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.3.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:12.4.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8019,10 +8019,10 @@ __metadata: "@emotion/styled": "npm:^11.13.0" "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" - "@hyperlane-xyz/cosmos-sdk": "npm:12.3.0" + "@hyperlane-xyz/cosmos-sdk": "npm:12.4.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.3.0" - "@hyperlane-xyz/utils": "npm:12.3.0" + "@hyperlane-xyz/sdk": "npm:12.4.0" + "@hyperlane-xyz/utils": "npm:12.4.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From 1d368bf90cc7a0ddf4a4197abd184bf2d7c9e14d Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:29:48 +0100 Subject: [PATCH 088/223] fix: release workflow (#6063) ### Description remove the "set changesets" step that was failing. The step that fails is completely extraneous and from an old attempt at the flow - might bring back in future but not necessary atm ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .github/workflows/release.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad42b21c8f1..16db9b1ca75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,9 +47,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Set Changesets - run: echo "changesets=$(yarn changeset status --json)" >> $GITHUB_OUTPUT - # The release PR removes individual changesets to prepare for a release. # This means that once the release PR is merged, there are no changesets left. # When there are no changesets left, we can run the cli-install-cross-platform-release-test From 2f21320eac0c66eb7eee4ced43aa558a6366c28f Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:10:31 -0400 Subject: [PATCH 089/223] fix: update Checker checkChains() to convert remoteRouter domainId to name (#6064) ### Description This PR adds `this.multiProvider.getChainName(chn)` to convert the each domainId in remoteRouters to a chain name to allow Checker comparison. ### Backward compatibility Yes ### Testing Manual --------- Co-authored-by: Le --- typescript/sdk/src/token/checker.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/typescript/sdk/src/token/checker.ts b/typescript/sdk/src/token/checker.ts index 2a517e1268d..85ea557ba1b 100644 --- a/typescript/sdk/src/token/checker.ts +++ b/typescript/sdk/src/token/checker.ts @@ -41,9 +41,14 @@ export class HypERC20Checker extends ProxiedRouterChecker< expectedChains = Object.keys(this.configMap); const thisChainConfig = this.configMap[chain]; if (thisChainConfig?.remoteRouters) { - expectedChains = Object.keys(thisChainConfig.remoteRouters); + expectedChains = Object.keys(thisChainConfig.remoteRouters).map( + (remoteRouterChain) => + this.multiProvider.getChainName(remoteRouterChain), + ); } - expectedChains = expectedChains.filter((chn) => chn !== chain).sort(); + expectedChains = expectedChains + .filter((remoteRouterChain) => remoteRouterChain !== chain) + .sort(); await super.checkChain(chain, expectedChains); await this.checkToken(chain); From 43cdc6533de868c274283a391acc5e779dd787b4 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:39:40 -0400 Subject: [PATCH 090/223] chore: Update remoteRouter names to domainIds in Getters (#6066) ### Description This PR Update remoteRouter names to domainIds in Getters for zero USDC, USDT, and treasure Smol ### Related issues - Resolves https://linear.app/hyperlane-xyz/issue/ENG-1278/update-configs-that-have-violations ### Backward compatibility Yes ### Testing check warp deploy script Co-authored-by: Le --- ...ptimismPolygonZeroNetworkUSDCWarpConfig.ts | 20 +++++++++---------- ...ePolygonScrollZeroNetworkUSDTWarpConfig.ts | 12 +++++------ ...rumEthereumSolanaTreasureSMOLWarpConfig.ts | 4 ++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts index 86096b4f792..9491c9a2d8e 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts @@ -31,8 +31,8 @@ export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig token: tokens.arbitrum.USDC, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, - zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + 1135: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + 543210: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, }, }; @@ -47,8 +47,8 @@ export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig token: tokens.base.USDC, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, - zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + 1135: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + 543210: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, }, }; @@ -63,8 +63,8 @@ export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig token: tokens.optimism.USDC, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, - zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + 1135: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + 543210: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, }, }; @@ -79,8 +79,8 @@ export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig token: tokens.polygon.USDC, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, - zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + 1135: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + 543210: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, }, }; @@ -107,8 +107,8 @@ export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig token: tokens.ethereum.USDC, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - lisk: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, - zeronetwork: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, + 1135: { address: '0x0FC41a92F526A8CD22060A4052e156502D6B9db0' }, + 543210: { address: '0xbb967d98313EDF91751651C0E66ef8A8B7BeD9e1' }, }, }; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts index 883116bf51f..d85288e24a8 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts @@ -32,7 +32,7 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig token: tokens.arbitrum.USDT, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + 543210: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, }, }; @@ -47,7 +47,7 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig token: tokens.ethereum.USDT, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + 543210: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, }, }; @@ -62,7 +62,7 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig token: tokens.mantle.USDT, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + 543210: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, }, }; @@ -77,7 +77,7 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig token: tokens.mode.USDT, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + 543210: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, }, }; @@ -92,7 +92,7 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig token: tokens.polygon.USDT, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + 543210: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, }, }; @@ -107,7 +107,7 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig token: tokens.scroll.USDT, interchainSecurityModule: ISM_CONFIG, remoteRouters: { - zeronetwork: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, + 543210: { address: '0x36dcfe3A0C6e0b5425F298587159249d780AAfab' }, }, }; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts index fb9589f2135..0574aa279d0 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts @@ -56,6 +56,10 @@ export async function getArbitrumEthereumSolanaTreasureSMOLWarpConfig( symbol, decimals: 18, owner: evmOwner, + remoteRouters: { + 1: { address: '0x53cce6d10e43d1b3d11872ad22ec2acd8d2537b8' }, + 61166: { address: '0xb73e4f558F7d4436d77a18f56e4EE9d01764c641' }, + }, }, }; return tokenConfig; From efb558fd8c66de796832d120ad8a1e7a314cea95 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:47:09 -0400 Subject: [PATCH 091/223] feat: Add getRegistryWithFallback to warp-configs test (#5837) ### Description This PR attempts to solve the issue of updated Registry configs causing unrelated PRs config test to fail. This solution attempts to fetch the config on the `main` branch, and then failling back to `main~10` before finally failing. ### Backward compatibility Yes ### Testing Manual/Unit Tests --------- Co-authored-by: Le --- typescript/infra/test/warp-configs.test.ts | 34 +++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/typescript/infra/test/warp-configs.test.ts b/typescript/infra/test/warp-configs.test.ts index bb66ac3aea4..29fa1544a6a 100644 --- a/typescript/infra/test/warp-configs.test.ts +++ b/typescript/infra/test/warp-configs.test.ts @@ -3,8 +3,12 @@ import chaiAsPromised from 'chai-as-promised'; import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry'; import { getRegistry } from '@hyperlane-xyz/registry/fs'; -import { HypTokenRouterConfig, MultiProvider } from '@hyperlane-xyz/sdk'; -import { rootLogger } from '@hyperlane-xyz/utils'; +import { + HypTokenRouterConfig, + MultiProvider, + WarpRouteDeployConfig, +} from '@hyperlane-xyz/sdk'; +import { assert, rootLogger } from '@hyperlane-xyz/utils'; import { getWarpConfig, warpConfigGetterMap } from '../config/warp.js'; import { @@ -24,6 +28,14 @@ const warpIdsToSkip = [ 'USDT/base-celo-fraxtal-ink-lisk-mode-optimism-soneium-superseed-unichain-worldchain', ]; +async function getConfigsForBranch(branch: string) { + return getRegistry({ + registryUris: [DEFAULT_GITHUB_REGISTRY], + enableProxy: true, + logger: rootLogger, + branch, + }).getWarpDeployConfigs(); +} describe('Warp Configs', async function () { this.timeout(DEFAULT_TIMEOUT); const ENV = 'mainnet3'; @@ -32,15 +44,10 @@ describe('Warp Configs', async function () { ); let multiProvider: MultiProvider; - let configsFromGithub; - + let configsFromGithub: Record; before(async function () { multiProvider = (await getHyperlaneCore(ENV)).multiProvider; - configsFromGithub = await getRegistry({ - registryUris: [DEFAULT_GITHUB_REGISTRY], - enableProxy: true, - logger: rootLogger, - }).getWarpDeployConfigs(); + configsFromGithub = await getConfigsForBranch('main'); }); const envConfig = getEnvironmentConfig(ENV); @@ -56,7 +63,14 @@ describe('Warp Configs', async function () { delete warpConfig[key].mailbox; } } - const expectedConfig = configsFromGithub![warpRouteId]; + + // Attempt to read the config from main, but fallback to main~10 to decrease test CI failures for old PRs + // TODO: remove this when we have stable warp ids + const expectedConfig = + configsFromGithub[warpRouteId] ?? + (await getConfigsForBranch('main~10'))[warpRouteId]; + assert(expectedConfig, `Deploy config not found for ${warpRouteId}`); + for (const key in expectedConfig) { if (expectedConfig[key].mailbox) { delete expectedConfig[key].mailbox; From 802ae16d65a718c41da62bd1ad9d40e97b9b30b4 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:45:01 +0100 Subject: [PATCH 092/223] chore: update mainnet3 relayer image (#6061) ### Description chore: update mainnet3 relayer image using an image from `main` ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index aa7f186b046..d058fd2d0df 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -831,7 +831,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '24fe342-20250424-164437', + tag: '3a04631-20250428-170554', }, blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement: gasPaymentEnforcement, @@ -871,7 +871,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '62073e3-20250426-080512', + tag: '3a04631-20250428-170554', }, blacklist: [...blacklist, ...vanguardMatchingList], // We're temporarily (ab)using the RC relayer as a way to increase @@ -909,7 +909,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '24fe342-20250424-164437', + tag: '3a04631-20250428-170554', }, blacklist: [...blacklist, ...vanguardMatchingList], gasPaymentEnforcement, From e5c37918f67fc4076cf4d3c2f0ef2890948bc840 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Tue, 29 Apr 2025 11:55:06 +0100 Subject: [PATCH 093/223] chore: reformat cosmos-types code (#6058) ### Description This PR formats the protobuf-generated code in `cosmos-types` to comply with the updated Prettier rules. We also remove the formatter from the `yarn build` script because it doesn't have to run every time. ### Related issues Fixes ENG-1369. ### Backward compatibility Yes ### Testing Not needed for a formatting change. --- typescript/cosmos-types/package.json | 2 +- .../src/types/cosmos/app/v1alpha1/module.ts | 13 ++++++------- .../types/cosmos/base/query/v1beta1/pagination.ts | 13 ++++++------- .../src/types/cosmos/base/v1beta1/coin.ts | 13 ++++++------- .../cosmos-types/src/types/cosmos_proto/cosmos.ts | 13 ++++++------- .../cosmos-types/src/types/google/api/http.ts | 13 ++++++------- .../cosmos-types/src/types/google/protobuf/any.ts | 13 ++++++------- .../src/types/google/protobuf/descriptor.ts | 13 ++++++------- .../core/interchain_security/v1/genesis.ts | 13 ++++++------- .../hyperlane/core/interchain_security/v1/query.ts | 13 ++++++------- .../hyperlane/core/interchain_security/v1/tx.ts | 13 ++++++------- .../hyperlane/core/interchain_security/v1/types.ts | 13 ++++++------- .../src/types/hyperlane/core/module/v1/module.ts | 13 ++++++------- .../types/hyperlane/core/post_dispatch/v1/events.ts | 13 ++++++------- .../hyperlane/core/post_dispatch/v1/genesis.ts | 13 ++++++------- .../types/hyperlane/core/post_dispatch/v1/query.ts | 13 ++++++------- .../src/types/hyperlane/core/post_dispatch/v1/tx.ts | 13 ++++++------- .../types/hyperlane/core/post_dispatch/v1/types.ts | 13 ++++++------- .../src/types/hyperlane/core/v1/events.ts | 13 ++++++------- .../src/types/hyperlane/core/v1/genesis.ts | 13 ++++++------- .../src/types/hyperlane/core/v1/query.ts | 13 ++++++------- .../cosmos-types/src/types/hyperlane/core/v1/tx.ts | 13 ++++++------- .../src/types/hyperlane/core/v1/types.ts | 13 ++++++------- .../src/types/hyperlane/warp/module/v1/module.ts | 13 ++++++------- .../src/types/hyperlane/warp/v1/events.ts | 13 ++++++------- .../src/types/hyperlane/warp/v1/genesis.ts | 13 ++++++------- .../src/types/hyperlane/warp/v1/query.ts | 13 ++++++------- .../cosmos-types/src/types/hyperlane/warp/v1/tx.ts | 13 ++++++------- .../src/types/hyperlane/warp/v1/types.ts | 13 ++++++------- 29 files changed, 169 insertions(+), 197 deletions(-) diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index 4ba6eeb8af1..40c2e531da6 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -21,7 +21,7 @@ ], "license": "Apache-2.0", "scripts": { - "build": "rm -rf ./dist && tsc && prettier --write ./src --log-level silent", + "build": "rm -rf ./dist && tsc", "format": "prettier --write .", "lint": "eslint -c ./eslint.config.mjs", "prettier": "prettier --write ./src", diff --git a/typescript/cosmos-types/src/types/cosmos/app/v1alpha1/module.ts b/typescript/cosmos-types/src/types/cosmos/app/v1alpha1/module.ts index c11f6f719ae..0526934c152 100644 --- a/typescript/cosmos-types/src/types/cosmos/app/v1alpha1/module.ts +++ b/typescript/cosmos-types/src/types/cosmos/app/v1alpha1/module.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: cosmos/app/v1alpha1/module.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -366,12 +365,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/cosmos/base/query/v1beta1/pagination.ts b/typescript/cosmos-types/src/types/cosmos/base/query/v1beta1/pagination.ts index 970dcd14654..bf9d5f2490f 100644 --- a/typescript/cosmos-types/src/types/cosmos/base/query/v1beta1/pagination.ts +++ b/typescript/cosmos-types/src/types/cosmos/base/query/v1beta1/pagination.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: cosmos/base/query/v1beta1/pagination.proto - /* eslint-disable */ import Long from 'long'; import _m0 from 'protobufjs/minimal.js'; @@ -331,12 +330,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts b/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts index bc8db1b1153..19c271743fe 100644 --- a/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts +++ b/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: cosmos/base/v1beta1/coin.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -196,12 +195,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/cosmos_proto/cosmos.ts b/typescript/cosmos-types/src/types/cosmos_proto/cosmos.ts index fb8607956c4..082be09beb3 100644 --- a/typescript/cosmos-types/src/types/cosmos_proto/cosmos.ts +++ b/typescript/cosmos-types/src/types/cosmos_proto/cosmos.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: cosmos_proto/cosmos.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -322,12 +321,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/google/api/http.ts b/typescript/cosmos-types/src/types/google/api/http.ts index 2f32d31b51f..88ea13d1906 100644 --- a/typescript/cosmos-types/src/types/google/api/http.ts +++ b/typescript/cosmos-types/src/types/google/api/http.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: google/api/http.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -774,12 +773,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/google/protobuf/any.ts b/typescript/cosmos-types/src/types/google/protobuf/any.ts index 944dc32f39a..34505880ad7 100644 --- a/typescript/cosmos-types/src/types/google/protobuf/any.ts +++ b/typescript/cosmos-types/src/types/google/protobuf/any.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: google/protobuf/any.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -247,12 +246,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/google/protobuf/descriptor.ts b/typescript/cosmos-types/src/types/google/protobuf/descriptor.ts index 4d1a460ccf6..c100dae7b28 100644 --- a/typescript/cosmos-types/src/types/google/protobuf/descriptor.ts +++ b/typescript/cosmos-types/src/types/google/protobuf/descriptor.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: google/protobuf/descriptor.proto - /* eslint-disable */ import Long from 'long'; import _m0 from 'protobufjs/minimal.js'; @@ -6340,12 +6339,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/genesis.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/genesis.ts index 0a562db458f..6ea2400fc7c 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/genesis.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/genesis.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/interchain_security/v1/genesis.proto - /* eslint-disable */ import Long from 'long'; import _m0 from 'protobufjs/minimal.js'; @@ -273,12 +272,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/query.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/query.ts index a0c0069cb79..687d62e9d37 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/query.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/query.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/interchain_security/v1/query.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -790,12 +789,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts index ad41a1377db..73dd27dd984 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/interchain_security/v1/tx.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -853,12 +852,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts index e4c901bf456..29e8a944bad 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/interchain_security/v1/types.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -375,12 +374,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/module/v1/module.ts b/typescript/cosmos-types/src/types/hyperlane/core/module/v1/module.ts index f5e410847f4..a80224dfbea 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/module/v1/module.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/module/v1/module.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/module/v1/module.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -98,12 +97,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts index ce63db80910..0e9a114f107 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/post_dispatch/v1/events.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -484,12 +483,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/genesis.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/genesis.ts index ab88d77b36e..6a4314ec8fd 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/genesis.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/genesis.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/post_dispatch/v1/genesis.proto - /* eslint-disable */ import Long from 'long'; import _m0 from 'protobufjs/minimal.js'; @@ -324,12 +323,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/query.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/query.ts index 95334d5ef64..8693850a082 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/query.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/query.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/post_dispatch/v1/query.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -1849,12 +1848,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts index 483d265d096..59008794b13 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/post_dispatch/v1/tx.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -1337,12 +1336,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/types.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/types.ts index 7927450bf8a..a09acbffa55 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/types.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/types.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/post_dispatch/v1/types.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -690,12 +689,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts index 0ee9e5538be..dccda5b49ec 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/v1/events.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -337,12 +336,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/genesis.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/genesis.ts index d945c4edeef..20030b67b0f 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/genesis.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/genesis.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/v1/genesis.proto - /* eslint-disable */ import Long from 'long'; import _m0 from 'protobufjs/minimal.js'; @@ -347,12 +346,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts index 8ff29c2dba5..0c0868a9400 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/v1/query.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -960,12 +959,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts index cadcb16801a..6a705b8f4e0 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/v1/tx.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -723,12 +722,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/types.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/types.ts index a04ea88a028..39e5e063dec 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/types.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/types.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/core/v1/types.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -229,12 +228,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/module/v1/module.ts b/typescript/cosmos-types/src/types/hyperlane/warp/module/v1/module.ts index 9ed9a30a382..3fd97fd6f17 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/module/v1/module.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/module/v1/module.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/warp/module/v1/module.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -128,12 +127,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts index 65fc9969424..24a727f745d 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/warp/v1/events.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -113,12 +112,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/genesis.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/genesis.ts index 0d18e0a6e17..a33b6383f18 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/genesis.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/genesis.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/warp/v1/genesis.proto - /* eslint-disable */ import Long from 'long'; import _m0 from 'protobufjs/minimal.js'; @@ -245,12 +244,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/query.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/query.ts index e2c2a48b1a3..aa59ab3937c 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/query.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/query.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/warp/v1/query.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -1142,12 +1141,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts index f0d4e02604e..b2d5b74b974 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/warp/v1/tx.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -1318,12 +1317,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/types.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/types.ts index 5b6d7699eb3..28067e605fb 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/types.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/types.ts @@ -3,7 +3,6 @@ // protoc-gen-ts_proto v1.181.2 // protoc unknown // source: hyperlane/warp/v1/types.proto - /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; @@ -414,12 +413,12 @@ type Builtin = export type DeepPartial = T extends Builtin ? T : T extends globalThis.Array - ? globalThis.Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin From 16d14fe782ecac501b4cd54f5cacb90e1a657547 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:03:30 +0100 Subject: [PATCH 094/223] fix: make env-test script more robust (#6073) ### Description This PR prevents the exit trap handler in `fork.sh` from failing, which causes the whole test to fail sometimes. ### Backward compatibility Yes ### Testing env-test matrix jobs in CI --- typescript/infra/fork.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/infra/fork.sh b/typescript/infra/fork.sh index 0a947258a4a..97f146b54aa 100755 --- a/typescript/infra/fork.sh +++ b/typescript/infra/fork.sh @@ -11,7 +11,7 @@ if [ -z "$ENVIRONMENT" ] || [ -z "$MODULE" ] || [ -z "$CHAIN" ]; then fi # kill all child processes on exit -trap 'jobs -p | xargs -r kill' EXIT +trap 'jobs -p | xargs -r kill 2>/dev/null || true' EXIT # exit 1 on any subsequent failures set -e From f87a8ab4194e9eb7180d4988d1b70ab60a341535 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:23:17 +0100 Subject: [PATCH 095/223] fix: metadata building error message (#6074) ### Description the actual error message is not being printed ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/agents/relayer/src/msg/metadata/base.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/base.rs b/rust/main/agents/relayer/src/msg/metadata/base.rs index 3fe4284a1db..ff72e12de36 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base.rs @@ -22,7 +22,7 @@ use crate::settings::matching_list::MatchingList; #[derive(Clone, Debug, PartialEq, thiserror::Error)] pub enum MetadataBuildError { - #[error("Some external error causing the build to fail")] + #[error("An external error causes the build to fail ({0})")] FailedToBuild(String), /// While building metadata, encountered something that should /// prohibit all metadata for the message from being built. From d78d89b3a3b065dc720fab5aaac91da8fb6e2081 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 29 Apr 2025 13:03:29 -0400 Subject: [PATCH 096/223] fix: tracing unit test (#6078) ### Description - remove trace subscriber and use #[traced_test] decorator if you want to see logs, use `-- --nocapture` ### Related issues - https://linear.app/hyperlane-xyz/issue/BACK-179/cargo-test-fails-unless-run-on-a-single-thread ### Backward compatibility Yes --- rust/main/Cargo.lock | 1 + rust/main/agents/relayer/src/msg/op_batch.rs | 9 ++------- rust/main/submitter/Cargo.toml | 1 + .../submitter/src/payload_dispatcher/tests.rs | 15 +++------------ 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 50d08130ccc..2a6a04feea9 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -10307,6 +10307,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "tracing-test", "uuid 1.11.0", ] diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index aeec6050c7d..f1cf4440c03 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -253,11 +253,9 @@ mod tests { )); } + #[tracing_test::traced_test] #[tokio::test] async fn test_handle_batch_succeeds_eventually() { - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); let mut mock_mailbox = MockMailboxContract::new(); let dummy_domain: HyperlaneDomain = KnownHyperlaneDomain::Alfajores.into(); @@ -311,13 +309,10 @@ mod tests { ) } + #[tracing_test::traced_test] #[tokio::test] #[ignore] async fn benchmarking_with_real_rpcs() { - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); - let arb_chain_conf = ChainConf { domain: HyperlaneDomain::Known(hyperlane_core::KnownHyperlaneDomain::Arbitrum), // TODO diff --git a/rust/main/submitter/Cargo.toml b/rust/main/submitter/Cargo.toml index 5a96475d2c0..85d7bc83802 100644 --- a/rust/main/submitter/Cargo.toml +++ b/rust/main/submitter/Cargo.toml @@ -30,5 +30,6 @@ uuid = { workspace = true, features = ["v4", "serde"] } [dev-dependencies] tracing-subscriber.workspace = true +tracing-test.workspace = true mockall.workspace = true tempfile.workspace = true diff --git a/rust/main/submitter/src/payload_dispatcher/tests.rs b/rust/main/submitter/src/payload_dispatcher/tests.rs index ac82f2ca8ac..44968f1e8e0 100644 --- a/rust/main/submitter/src/payload_dispatcher/tests.rs +++ b/rust/main/submitter/src/payload_dispatcher/tests.rs @@ -58,12 +58,9 @@ async fn test_entrypoint_send_is_detected_by_loader() { ); } +#[tracing_test::traced_test] #[tokio::test] async fn test_entrypoint_send_is_finalized_by_dispatcher() { - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); - let payload = FullPayload::random(); let adapter = MockAdapter::new(); @@ -103,12 +100,9 @@ async fn test_entrypoint_send_is_finalized_by_dispatcher() { assert_metrics(metrics, metrics_assertion); } +#[tracing_test::traced_test] #[tokio::test] async fn test_entrypoint_send_is_dropped_by_dispatcher() { - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); - let payload = FullPayload::random(); let mut adapter = MockAdapter::new(); @@ -160,12 +154,9 @@ async fn test_entrypoint_send_is_dropped_by_dispatcher() { assert_metrics(metrics, metrics_assertion); } +#[tracing_test::traced_test] #[tokio::test] async fn test_entrypoint_payload_fails_simulation() { - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); - let payload = FullPayload::random(); let mut adapter = MockAdapter::new(); From c3adc68d4a4fb62401ff10ccc5d8b0fd50b5e487 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Tue, 29 Apr 2025 14:00:34 -0400 Subject: [PATCH 097/223] ci: only run infra tests when infra changes (#6067) ### Description Only runs infra (registry and grafana) tests when there are relevant changes to the config that governs the services these tests depend on --- .github/workflows/test.yml | 35 +++++++++++++++++++++++++++++++++-- typescript/infra/package.json | 8 +++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c15899bee3..54bbba6da64 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,8 +106,6 @@ jobs: yarn-test: runs-on: ubuntu-latest - env: - GRAFANA_SERVICE_ACCOUNT_TOKEN: ${{ secrets.GRAFANA_SERVICE_ACCOUNT_TOKEN }} needs: [yarn-install] timeout-minutes: 10 steps: @@ -131,6 +129,39 @@ jobs: - name: Unit Tests run: yarn test:ci + infra-test: + runs-on: ubuntu-latest + needs: [yarn-install] + env: + GRAFANA_SERVICE_ACCOUNT_TOKEN: ${{ secrets.GRAFANA_SERVICE_ACCOUNT_TOKEN }} + PR_BASE: ${{ github.base_ref }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + submodules: recursive + fetch-depth: 0 + + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Checkout registry + uses: ./.github/actions/checkout-registry + + - name: Balance Tests + run: | + if git diff $PR_BASE...HEAD --name-only | grep -qE '^typescript/infra/config/environments/mainnet3/balances|^.registryrc$'; then + yarn --cwd typescript/infra test:balance + fi + + - name: Warp Config Tests + run: | + if git diff $PR_BASE...HEAD --name-only | grep -qE '^typescript/infra/|^.registryrc$'; then + yarn --cwd typescript/infra test:warp + fi + cli-install-test: runs-on: ubuntu-latest steps: diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 84a76ff62b2..127efbfd308 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -76,10 +76,12 @@ "hardhat-esm": "NODE_OPTIONS='--experimental-loader ts-node/esm/transpile-only --no-warnings=ExperimentalWarning' hardhat --config hardhat.config.cts", "kathy": "yarn tsx ./scripts/send-test-messages.ts", "prettier": "prettier --write ./src ./config ./scripts ./test", - "test": "yarn test:unit && yarn test:hardhat", - "test:unit": "mocha --config ../sdk/.mocharc.json test/**/*.test.ts", + "test:warp": "mocha --config ../sdk/.mocharc.json test/warp-configs.test.ts", + "test:balance": "mocha --config ../sdk/.mocharc.json test/balance-alerts.test.ts", + "test:unit": "mocha --config ../sdk/.mocharc.json \"test/**/*.test.ts\" --ignore \"test/{warp-configs,balance-alerts}.test.ts\"", "test:hardhat": "yarn hardhat-esm test test/govern.hardhat-test.ts", - "test:ci": "yarn test", + "test": "yarn test:unit && yarn test:hardhat && yarn test:warp && yarn test:balance", + "test:ci": "yarn test:unit && yarn test:hardhat", "update-agent-config:mainnet3": "tsx scripts/agents/update-agent-config.ts -e mainnet3", "update-agent-config:test": "tsx scripts/agents/update-agent-config.ts -e test", "update-agent-config:testnet4": "tsx scripts/agents/update-agent-config.ts -e testnet4" From d9fe2070f2f9b046dc3d702f91ebbd486bbe1511 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:47:33 -0400 Subject: [PATCH 098/223] chore: Remove DEFAULT_GITHUB_REGISTRY from check-warp-deploy (#6079) ### Description Removing `DEFAULT_GITHUB_REGISTRY` from check-warp-deploy because we will update the cronjob to clone main instead. This reduces github api rate limiting ### Backward compatibility Yes ### Testing Manual - will test by running `kubectl create job --from=cronjob/check-warp-deploy check-warp-deploy-manual-2025-04-29` Co-authored-by: Le --- typescript/infra/scripts/check/check-warp-deploy.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/typescript/infra/scripts/check/check-warp-deploy.ts b/typescript/infra/scripts/check/check-warp-deploy.ts index c0f40bbbf63..9b9b4f2f44e 100644 --- a/typescript/infra/scripts/check/check-warp-deploy.ts +++ b/typescript/infra/scripts/check/check-warp-deploy.ts @@ -35,10 +35,9 @@ async function main() { WarpRouteIds.ArbitrumBaseBlastBscEthereumGnosisLiskMantleModeOptimismPolygonScrollZeroNetworkZoraMainnet, ]; - const registries = [DEFAULT_GITHUB_REGISTRY, DEFAULT_REGISTRY_URI]; - const warpCoreConfigMap = await getWarpConfigMapFromMergedRegistry( - registries, - ); + const registries = [DEFAULT_REGISTRY_URI]; + const warpCoreConfigMap = + await getWarpConfigMapFromMergedRegistry(registries); console.log(chalk.yellow('Skipping the following warp routes:')); routesToSkip.forEach((route) => console.log(chalk.yellow(`- ${route}`))); From 644c0a257ace135890a2a5f1833e758cc3f2315e Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Tue, 29 Apr 2025 22:54:35 +0100 Subject: [PATCH 099/223] feat: support registry override for check warp deploy job (#5905) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing Co-authored-by: Le Co-authored-by: Le --- .../config/environments/mainnet3/warp/checkWarpDeploy.ts | 1 + .../infra/helm/check-warp-deploy/templates/cron-job.yaml | 4 ++++ typescript/infra/helm/check-warp-deploy/values.yaml | 1 + typescript/infra/scripts/check/deploy-check-warp-deploy.ts | 1 + typescript/infra/src/config/funding.ts | 4 +++- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts b/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts index 866de41f977..f364392b0c0 100644 --- a/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts +++ b/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts @@ -10,4 +10,5 @@ export const checkWarpDeployConfig: CheckWarpDeployConfig = { cronSchedule: '0 15 * * *', // set to 3pm utc every day prometheusPushGateway: 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', + registryCommit: 'main', // This will always use the latest version from the main branch }; diff --git a/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml b/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml index 06339a6b751..545889f5aa9 100644 --- a/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml +++ b/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml @@ -27,6 +27,10 @@ spec: env: - name: PROMETHEUS_PUSH_GATEWAY value: {{ .Values.infra.prometheusPushGateway }} + {{- if .Values.hyperlane.registryCommit }} + - name: REGISTRY_COMMIT + value: {{ .Values.hyperlane.registryCommit }} + {{- end }} envFrom: - secretRef: name: check-warp-deploy-env-var-secret diff --git a/typescript/infra/helm/check-warp-deploy/values.yaml b/typescript/infra/helm/check-warp-deploy/values.yaml index db61386e9cd..df3ab71e788 100644 --- a/typescript/infra/helm/check-warp-deploy/values.yaml +++ b/typescript/infra/helm/check-warp-deploy/values.yaml @@ -4,6 +4,7 @@ image: hyperlane: runEnv: mainnet3 chains: [] + registryCommit: '' # Registry commit to use cronjob: schedule: '0 15 * * *' successfulJobsHistoryLimit: 1 diff --git a/typescript/infra/scripts/check/deploy-check-warp-deploy.ts b/typescript/infra/scripts/check/deploy-check-warp-deploy.ts index bb7faa4487c..7f6d1dde58b 100644 --- a/typescript/infra/scripts/check/deploy-check-warp-deploy.ts +++ b/typescript/infra/scripts/check/deploy-check-warp-deploy.ts @@ -72,6 +72,7 @@ function getCheckWarpDeployHelmValues( hyperlane: { runEnv: agentConfig.runEnv, chains: agentConfig.environmentChainNames, + registryCommit: config.registryCommit, }, infra: { prometheusPushGateway: config.prometheusPushGateway, diff --git a/typescript/infra/src/config/funding.ts b/typescript/infra/src/config/funding.ts index d656ac724f9..f92d7c95ad4 100644 --- a/typescript/infra/src/config/funding.ts +++ b/typescript/infra/src/config/funding.ts @@ -30,4 +30,6 @@ export interface KeyFunderConfig chainsToSkip: ChainName[]; } -export interface CheckWarpDeployConfig extends CronJobConfig {} +export interface CheckWarpDeployConfig extends CronJobConfig { + registryCommit?: string; +} From 95215c88975583030e991d2263c45a74b51c7ce8 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 30 Apr 2025 10:59:52 +0100 Subject: [PATCH 100/223] feat(submitter): measure tx submissions (#6077) ### Description Adds a metric that is incremented each time a transaction is submitted ### Drive-by changes ### Related issues ### Backward compatibility ### Testing Unit tests and a new sealevel E2E invariant --- .../src/payload_dispatcher/metrics.rs | 17 ++++++++++++++++ .../stages/inclusion_stage.rs | 3 +++ .../submitter/src/payload_dispatcher/tests.rs | 15 ++++++++++++++ .../src/sealevel/termination_invariants.rs | 20 +++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/rust/main/submitter/src/payload_dispatcher/metrics.rs b/rust/main/submitter/src/payload_dispatcher/metrics.rs index a5f889381bd..a663c1ace98 100644 --- a/rust/main/submitter/src/payload_dispatcher/metrics.rs +++ b/rust/main/submitter/src/payload_dispatcher/metrics.rs @@ -32,6 +32,8 @@ pub struct DispatcherMetrics { pub dropped_payloads: IntCounterVec, pub dropped_transactions: IntCounterVec, + pub transaction_submissions: IntCounterVec, + pub finalized_transactions: IntCounterVec, // includes a label for the error causing the retry, and a label for the type of call @@ -91,6 +93,14 @@ impl DispatcherMetrics { &["destination", "reason",], registry.clone() )?; + let transaction_submissions = register_int_counter_vec_with_registry!( + opts!( + namespaced("transaction_submissions"), + "The number of times transactions were resubmitted", + ), + &["destination",], + registry.clone() + )?; let finalized_transactions = register_int_counter_vec_with_registry!( opts!( namespaced("finalized_transactions"), @@ -123,6 +133,7 @@ impl DispatcherMetrics { finality_stage_pool_length, dropped_payloads, dropped_transactions, + transaction_submissions, finalized_transactions, call_retries, in_flight_transaction_time, @@ -168,6 +179,12 @@ impl DispatcherMetrics { .inc(); } + pub fn update_transaction_submissions_metric(&self, domain: &str) { + self.transaction_submissions + .with_label_values(&[domain]) + .inc(); + } + pub fn update_finalized_transactions_metric(&self, domain: &str) { self.finalized_transactions .with_label_values(&[domain]) diff --git a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs index ecf1163ee52..907ab10ff3e 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs @@ -217,6 +217,9 @@ impl InclusionStage { // update tx submission attempts tx.submission_attempts += 1; + state + .metrics + .update_transaction_submissions_metric(&state.domain); // update tx status in db update_tx_status(state, &mut tx, TransactionStatus::Mempool).await?; diff --git a/rust/main/submitter/src/payload_dispatcher/tests.rs b/rust/main/submitter/src/payload_dispatcher/tests.rs index 44968f1e8e0..34810cabfaf 100644 --- a/rust/main/submitter/src/payload_dispatcher/tests.rs +++ b/rust/main/submitter/src/payload_dispatcher/tests.rs @@ -96,6 +96,9 @@ async fn test_entrypoint_send_is_finalized_by_dispatcher() { dropped_transactions: 0, dropped_payload_reason: "".to_string(), dropped_transaction_reason: "".to_string(), + // in `mock_adapter_methods`, the tx_status method is mocked to return `PendingInclusion` for the first 2 calls, + // which causes the tx to be resubmitted each time + transaction_submissions: 2, }; assert_metrics(metrics, metrics_assertion); } @@ -150,6 +153,7 @@ async fn test_entrypoint_send_is_dropped_by_dispatcher() { dropped_transactions: 1, dropped_payload_reason: "DroppedInTransaction(FailedSimulation)".to_string(), dropped_transaction_reason: "FailedSimulation".to_string(), + transaction_submissions: 0, }; assert_metrics(metrics, metrics_assertion); } @@ -189,6 +193,7 @@ async fn test_entrypoint_payload_fails_simulation() { dropped_transactions: 0, dropped_payload_reason: "FailedSimulation".to_string(), dropped_transaction_reason: "".to_string(), + transaction_submissions: 0, }; assert_metrics(metrics, metrics_assertion); } @@ -291,6 +296,7 @@ struct MetricsAssertion { dropped_transactions: u64, dropped_payload_reason: String, dropped_transaction_reason: String, + transaction_submissions: u64, } fn assert_metrics(metrics: DispatcherMetrics, assertion: MetricsAssertion) { @@ -350,4 +356,13 @@ fn assert_metrics(metrics: DispatcherMetrics, assertion: MetricsAssertion) { "Dropped transactions metric is incorrect for domain {}", assertion.domain ); + let transaction_submissions = metrics + .transaction_submissions + .with_label_values(&[&assertion.domain]) + .get(); + assert_eq!( + transaction_submissions, assertion.transaction_submissions, + "Transaction submissions metric is incorrect for domain {}", + assertion.domain + ); } diff --git a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs index 8682ae26d66..73318bf9750 100644 --- a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs @@ -148,6 +148,14 @@ fn submitter_metrics_invariants_met( .iter() .sum::(); + let transaction_submissions = fetch_metric( + relayer_port, + "hyperlane_lander_transaction_submissions", + filter_hashmap, + )? + .iter() + .sum::(); + if finalized_transactions < params.total_messages_expected { log!( "hyperlane_lander_finalized_transactions {} count, expected {}", @@ -197,6 +205,18 @@ fn submitter_metrics_invariants_met( return Ok(false); } + // resubmissions are possible because it takes a while for the local + // solana validator to report a tx hash as included once broadcast + // but no more than 2 submissions are expected per message + if transaction_submissions > 2 * params.total_messages_expected { + log!( + "hyperlane_lander_transaction_submissions {} count, expected {}", + transaction_submissions, + params.total_messages_expected + ); + return Ok(false); + } + Ok(true) } From 667bb782ec4a9f97ce724ffe499a1db63269d172 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 30 Apr 2025 11:00:50 +0100 Subject: [PATCH 101/223] fix: Add script to run relayer locally (#5981) ### Description Add script to run relayer locally ### Backward compatibility Yes ### Testing Manual --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/docker-compose.yaml | 20 ++++++++++++++++++++ rust/start.sh | 11 +++++++++++ 2 files changed, 31 insertions(+) create mode 100644 rust/docker-compose.yaml create mode 100755 rust/start.sh diff --git a/rust/docker-compose.yaml b/rust/docker-compose.yaml new file mode 100644 index 00000000000..a4d26b527b5 --- /dev/null +++ b/rust/docker-compose.yaml @@ -0,0 +1,20 @@ +services: + relayer-service: + image: gcr.io/abacus-labs-dev/hyperlane-agent:8117739-20250420-201559 + command: > + /bin/sh -c "./relayer + --db /relayer/database + --relayChains arbitrum,base,bsc,ethereum,optimism + --defaultSigner.key $DEFAULT_SIGNER_KEY + > /relayer/logs/relayer-$(date +%Y-%m-%d-%H-%M-%S).log 2>&1 && tail -f /dev/null" + ports: + - "9090:9090" + volumes: + - /tmp/relayer:/relayer + environment: + - HYP_CHAINS_ARBITRUM_CUSTOMRPCURLS=${HYP_CHAINS_ARBITRUM_CUSTOMRPCURLS} + - HYP_CHAINS_BASE_CUSTOMRPCURLS=${HYP_CHAINS_BASE_CUSTOMRPCURLS} + - HYP_CHAINS_BSC_CUSTOMRPCURLS=${HYP_CHAINS_BSC_CUSTOMRPCURLS} + - HYP_CHAINS_ETHEREUM_CUSTOMRPCURLS=${HYP_CHAINS_ETHEREUM_CUSTOMRPCURLS} + - HYP_CHAINS_OPTIMISM_CUSTOMRPCURLS=${HYP_CHAINS_OPTIMISM_CUSTOMRPCURLS} + - DEFAULT_SIGNER_KEY=${DEFAULT_SIGNER_KEY} diff --git a/rust/start.sh b/rust/start.sh new file mode 100755 index 00000000000..ca0f1111485 --- /dev/null +++ b/rust/start.sh @@ -0,0 +1,11 @@ +#!/bin/zsh + +source ~/.zshrc + +export HYP_CHAINS_ARBITRUM_CUSTOMRPCURLS=$(rpcs mainnet3 arbitrum) +export HYP_CHAINS_BASE_CUSTOMRPCURLS=$(rpcs mainnet3 base) +export HYP_CHAINS_BSC_CUSTOMRPCURLS=$(rpcs mainnet3 bsc) +export HYP_CHAINS_ETHEREUM_CUSTOMRPCURLS=$(rpcs mainnet3 ethereum) +export HYP_CHAINS_OPTIMISM_CUSTOMRPCURLS=$(rpcs mainnet3 optimism) + +docker compose up -d From 1209c6b2370ff5c2b330e48a2e365ff9ccc7f356 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:02:39 +0100 Subject: [PATCH 102/223] chore: fetch latest branch in docker-entrypoint (#6081) ### Description chore: fetch latest branch in docker-entrypoint ### Drive-by changes add docker-entrypoint as one of the conditions for running monorepo image builds ### Related issues ### Backward compatibility ### Testing ``` $ kubectl logs check-warp-deploy-pbio-zq7lk REGISTRY_COMMIT: main REGISTRY_URI: /hyperlane-registry Current commit: 7a0a79942383dac2fca1fa5e5a0e57f5d8b2d3f2 Updating Hyperlane registry to: main Previous HEAD position was 7a0a799 Update chain metadata and address files Switched to branch 'main' Your branch is up to date with 'origin/main'. HEAD is now at af29ded Update chain metadata and address files ``` --- .github/workflows/monorepo-docker.yml | 1 + docker-entrypoint.sh | 14 ++++++++++++-- .../helm/check-warp-deploy/templates/cron-job.yaml | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/monorepo-docker.yml b/.github/workflows/monorepo-docker.yml index fa3ed62ce87..1eae7ae8ce5 100644 --- a/.github/workflows/monorepo-docker.yml +++ b/.github/workflows/monorepo-docker.yml @@ -10,6 +10,7 @@ on: - 'typescript/infra/**' - '.registryrc' - 'Dockerfile' + - 'docker-entrypoint.sh' - '.dockerignore' - '.github/workflows/monorepo-docker.yml' diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 719396d5bdd..801b0c035e8 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -4,13 +4,23 @@ set -e # Set default registry URI, same as Dockerfile REGISTRY_URI="/hyperlane-registry" +echo "REGISTRY_COMMIT: $REGISTRY_COMMIT" +echo "REGISTRY_URI: $REGISTRY_URI" +echo "Current commit: $(git -C "$REGISTRY_URI" rev-parse HEAD)" + # Only update registry if REGISTRY_COMMIT is set if [ -n "$REGISTRY_COMMIT" ]; then - echo "Updating Hyperlane registry to commit: ${REGISTRY_COMMIT}" + echo "Updating Hyperlane registry to: $REGISTRY_COMMIT" OLDPWD=$(pwd) cd "$REGISTRY_URI" - git fetch origin "$REGISTRY_COMMIT" + git fetch origin git checkout "$REGISTRY_COMMIT" + + # Only reset if it's a branch + if git show-ref --verify --quiet "refs/remotes/origin/$REGISTRY_COMMIT"; then + git reset --hard "origin/$REGISTRY_COMMIT" + fi + cd "$OLDPWD" fi diff --git a/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml b/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml index 545889f5aa9..0af8333ef8e 100644 --- a/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml +++ b/typescript/infra/helm/check-warp-deploy/templates/cron-job.yaml @@ -18,7 +18,7 @@ spec: - name: check-warp-deploy image: {{ .Values.image.repository }}:{{ .Values.image.tag }} imagePullPolicy: Always - command: + args: - ./node_modules/.bin/tsx - ./typescript/infra/scripts/check/check-warp-deploy.ts - -e From 673f7df81c8ff4cec85bc0a069ed9664d7428b55 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 30 Apr 2025 11:21:00 +0100 Subject: [PATCH 103/223] feat: Send operations to confirm queue if they have non-dropped payloads (#6072) ### Description Send operations to confirm queue if they have non-dropped payloads ### Drive-by changes - Fix of reprepare reason for database operations with payload ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../agents/relayer/src/msg/op_submitter.rs | 314 +++++++++++++----- .../src/traits/pending_operation.rs | 10 +- 2 files changed, 242 insertions(+), 82 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index f66f459e917..0ebd8f4cb14 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -171,6 +171,11 @@ impl SerialSubmitter { let entrypoint = self.payload_dispatcher_entrypoint.take().map(Arc::new); + let prepare_task = match &entrypoint { + None => self.create_classic_prepare_task(), + Some(entrypoint) => self.create_lander_prepare_task(entrypoint.clone()), + }; + let submit_task = match &entrypoint { None => self.create_classic_submit_task(), Some(entrypoint) => self.create_lander_submit_task(entrypoint.clone()), @@ -183,7 +188,7 @@ impl SerialSubmitter { let tasks = [ self.create_receive_task(rx_prepare), - self.create_prepare_task(), + prepare_task, submit_task, confirm_task, ]; @@ -211,13 +216,13 @@ impl SerialSubmitter { .expect("spawning tokio task from Builder is infallible") } - fn create_prepare_task(&self) -> JoinHandle<()> { - let name = Self::task_name("prepare::", &self.domain); + fn create_classic_prepare_task(&self) -> JoinHandle<()> { + let name = Self::task_name("prepare_classic::", &self.domain); tokio::task::Builder::new() .name(&name) .spawn(TaskMonitor::instrument( &self.task_monitor, - prepare_task( + prepare_classic_task( self.domain.clone(), self.prepare_queue.clone(), self.submit_queue.clone(), @@ -265,6 +270,30 @@ impl SerialSubmitter { .expect("spawning tokio task from Builder is infallible") } + fn create_lander_prepare_task( + &self, + entrypoint: Arc, + ) -> JoinHandle<()> { + let name = Self::task_name("prepare_lander::", &self.domain); + tokio::task::Builder::new() + .name(&name) + .spawn(TaskMonitor::instrument( + &self.task_monitor, + prepare_lander_task( + entrypoint, + self.domain.clone(), + self.prepare_queue.clone(), + self.submit_queue.clone(), + self.confirm_queue.clone(), + self.max_batch_size, + self.max_submit_queue_len, + self.metrics.clone(), + self.db.clone(), + ), + )) + .expect("spawning tokio task from Builder is infallible") + } + fn create_lander_submit_task( &self, entrypoint: Arc, @@ -333,7 +362,7 @@ async fn receive_task( } #[instrument(skip_all, fields(%domain))] -async fn prepare_task( +async fn prepare_classic_task( domain: HyperlaneDomain, mut prepare_queue: OpQueue, submit_queue: OpQueue, @@ -342,84 +371,215 @@ async fn prepare_task( max_submit_queue_len: Option, metrics: SerialSubmitterMetrics, ) { - // Prepare at most `max_batch_size` ops at a time to avoid getting rate-limited - let ops_to_prepare = max_batch_size as usize; loop { - // Apply backpressure to the prepare queue if the submit queue is too long. - if let Some(max_len) = max_submit_queue_len { - let submit_queue_len = submit_queue.len().await as u32; - if submit_queue_len >= max_len { - debug!( - %submit_queue_len, - max_submit_queue_len=%max_len, - "Submit queue is too long, waiting to prepare more ops" - ); - // The submit queue is too long, so give some time before checking again - sleep(Duration::from_millis(150)).await; - continue; - } + if apply_backpressure(&submit_queue, &max_submit_queue_len).await { + // The submit queue is too long, so give some time before checking again + sleep(Duration::from_millis(150)).await; + continue; } - // Pop messages here according to the configured batch. - let mut batch = prepare_queue.pop_many(ops_to_prepare).await; - if batch.is_empty() { - // queue is empty so give some time before checking again to prevent burning CPU - sleep(Duration::from_millis(100)).await; + + let Some(batch) = get_batch_or_wait(&mut prepare_queue, max_batch_size).await else { + continue; + }; + + process_batch( + domain.clone(), + batch, + &mut prepare_queue, + &submit_queue, + &confirm_queue, + &metrics, + ) + .await; + } +} + +#[instrument(skip_all, fields(%domain))] +#[allow(clippy::too_many_arguments)] +async fn prepare_lander_task( + entrypoint: Arc, + domain: HyperlaneDomain, + mut prepare_queue: OpQueue, + submit_queue: OpQueue, + confirm_queue: OpQueue, + max_batch_size: u32, + max_submit_queue_len: Option, + metrics: SerialSubmitterMetrics, + db: Arc, +) { + loop { + if apply_backpressure(&submit_queue, &max_submit_queue_len).await { + // The submit queue is too long, so give some time before checking again + sleep(Duration::from_millis(150)).await; continue; } - let mut task_prep_futures = vec![]; - let op_refs = batch.iter_mut().map(|op| op.as_mut()).collect::>(); - for op in op_refs { - trace!(?op, "Preparing operation"); - debug_assert_eq!(*op.destination_domain(), domain); - task_prep_futures.push(op.prepare()); + + let Some(batch) = get_batch_or_wait(&mut prepare_queue, max_batch_size).await else { + continue; + }; + + let batch_to_process = confirm_already_submitted_operations( + entrypoint.clone(), + &confirm_queue, + db.clone(), + batch, + ) + .await; + + process_batch( + domain.clone(), + batch_to_process, + &mut prepare_queue, + &submit_queue, + &confirm_queue, + &metrics, + ) + .await; + } +} + +/// This function checks the status of the payloads associated with the operations in the batch. +/// If the payload is not dropped, the operation is pushed to the confirmation queue. +/// If the payload is dropped, does not exist or there is issue in retrieving payload or its status, the operation will go through prepare logic. +async fn confirm_already_submitted_operations( + entrypoint: Arc, + confirm_queue: &OpQueue, + db: Arc, + batch: Vec, +) -> Vec { + use ConfirmReason::AlreadySubmitted; + use PendingOperationStatus::Confirm; + + let mut ops_to_prepare = vec![]; + for op in batch.into_iter() { + if has_operation_been_submitted(entrypoint.clone(), db.clone(), &op).await { + let status = Some(Confirm(AlreadySubmitted)); + confirm_queue.push(op, status).await; + } else { + ops_to_prepare.push(op); } - let res = join_all(task_prep_futures).await; - let not_ready_count = res - .iter() - .filter(|r| { - matches!( - r, - PendingOperationResult::NotReady | PendingOperationResult::Reprepare(_) - ) - }) - .count(); - let batch_len = batch.len(); - for (op, prepare_result) in batch.into_iter().zip(res.into_iter()) { - match prepare_result { - PendingOperationResult::Success => { - debug!(?op, "Operation prepared"); - metrics.ops_prepared.inc(); - // TODO: push multiple messages at once - submit_queue - .push(op, Some(PendingOperationStatus::ReadyToSubmit)) - .await; - } - PendingOperationResult::NotReady => { - prepare_queue.push(op, None).await; - } - PendingOperationResult::Reprepare(reason) => { - metrics.ops_failed.inc(); - prepare_queue - .push(op, Some(PendingOperationStatus::Retry(reason))) - .await; - } - PendingOperationResult::Drop => { - metrics.ops_dropped.inc(); - op.decrement_metric_if_exists(); - } - PendingOperationResult::Confirm(reason) => { - debug!(?op, "Pushing operation to confirm queue"); - confirm_queue - .push(op, Some(PendingOperationStatus::Confirm(reason))) - .await; - } - } + } + ops_to_prepare +} + +async fn has_operation_been_submitted( + entrypoint: Arc, + db: Arc, + op: &QueueOperation, +) -> bool { + let id = op.id(); + + let payload_ids = match db.retrieve_payload_ids_by_message_id(&id) { + Ok(ids) => ids, + Err(_) => return false, + }; + + let payload_ids = match payload_ids { + None => return false, + Some(ids) if ids.is_empty() => return false, + Some(ids) => ids, + }; + + // TODO checking only the first payload id since we support a single payload per message at this point + let payload_id = payload_ids[0].clone(); + let status = entrypoint.payload_status(payload_id).await; + + match status { + Ok(PayloadStatus::Dropped(_)) => false, + Ok(_) => true, + Err(_) => false, + } +} + +/// Applies backpressure to the prepare queue if the submit queue is too long. +async fn apply_backpressure(submit_queue: &OpQueue, max_len: &Option) -> bool { + if let Some(max_len) = max_len { + let submit_queue_len = submit_queue.len().await as u32; + if submit_queue_len >= *max_len { + debug!( + %submit_queue_len, + max_submit_queue_len=%max_len, + "Submit queue is too long, waiting to prepare more ops" + ); + return true; } - if not_ready_count == batch_len { - // none of the operations are ready yet, so wait for a little bit - sleep(Duration::from_millis(500)).await; + } + false +} + +/// Helper method to get a batch from the queue or wait if the queue is empty. +async fn get_batch_or_wait(queue: &mut OpQueue, batch_size: u32) -> Option> { + let batch = queue.pop_many(batch_size as usize).await; + if batch.is_empty() { + // Queue is empty, wait before retrying to prevent burning CPU. + sleep(Duration::from_millis(100)).await; + None + } else { + Some(batch) + } +} + +async fn process_batch( + domain: HyperlaneDomain, + mut batch: Vec, + prepare_queue: &mut OpQueue, + submit_queue: &OpQueue, + confirm_queue: &OpQueue, + metrics: &SerialSubmitterMetrics, +) { + let mut task_prep_futures = vec![]; + let op_refs = batch.iter_mut().map(|op| op.as_mut()).collect::>(); + for op in op_refs { + trace!(?op, "Preparing operation"); + debug_assert_eq!(*op.destination_domain(), domain); + task_prep_futures.push(op.prepare()); + } + let res = join_all(task_prep_futures).await; + let not_ready_count = res + .iter() + .filter(|r| { + matches!( + r, + PendingOperationResult::NotReady | PendingOperationResult::Reprepare(_) + ) + }) + .count(); + let batch_len = batch.len(); + for (op, prepare_result) in batch.into_iter().zip(res.into_iter()) { + match prepare_result { + PendingOperationResult::Success => { + debug!(?op, "Operation prepared"); + metrics.ops_prepared.inc(); + // TODO: push multiple messages at once + submit_queue + .push(op, Some(PendingOperationStatus::ReadyToSubmit)) + .await; + } + PendingOperationResult::NotReady => { + prepare_queue.push(op, None).await; + } + PendingOperationResult::Reprepare(reason) => { + metrics.ops_failed.inc(); + prepare_queue + .push(op, Some(PendingOperationStatus::Retry(reason))) + .await; + } + PendingOperationResult::Drop => { + metrics.ops_dropped.inc(); + op.decrement_metric_if_exists(); + } + PendingOperationResult::Confirm(reason) => { + debug!(?op, "Pushing operation to confirm queue"); + confirm_queue + .push(op, Some(PendingOperationStatus::Confirm(reason))) + .await; + } } } + if not_ready_count == batch_len { + // none of the operations are ready yet, so wait for a little bit + sleep(Duration::from_millis(500)).await; + } } #[instrument(skip_all, fields(%domain))] @@ -701,7 +861,7 @@ async fn confirm_lander_task( op, prepare_queue.clone(), &metrics, - Some(&ReprepareReason::ErrorRetrievingPayloadId), + Some(&ReprepareReason::ErrorRetrievingPayloadIds), ) .await; None @@ -729,7 +889,7 @@ async fn confirm_lander_task( op, prepare_queue.clone(), &metrics, - Some(&ReprepareReason::ErrorRetrievingPayloadId), + Some(&ReprepareReason::ErrorRetrievingPayloadStatus), ) .await; return; diff --git a/rust/main/hyperlane-core/src/traits/pending_operation.rs b/rust/main/hyperlane-core/src/traits/pending_operation.rs index 5101c20a083..ebb20e16089 100644 --- a/rust/main/hyperlane-core/src/traits/pending_operation.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation.rs @@ -279,11 +279,11 @@ pub enum ReprepareReason { #[strum(to_string = "Failed to store payload id by message id")] /// Failed to store payload id by message id ErrorStoringPayloadIdsByMessageId, - #[strum(to_string = "Failed to retrieve payload id by message id")] - /// Failed to retrieve payload id by message id - ErrorRetrievingPayloadId, - #[strum(to_string = "Failed to retrieve payload id by message id")] - /// Failed to retrieve payload id by message id + #[strum(to_string = "Failed to retrieve payload ids by message id")] + /// Failed to retrieve payload ids by message id + ErrorRetrievingPayloadIds, + #[strum(to_string = "Failed to retrieve payload id status by message id")] + /// Failed to retrieve payload id status by message id ErrorRetrievingPayloadStatus, } From 608a50756e3a3ef6fbf27320e011f8b988dcbc1f Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:26:33 +0100 Subject: [PATCH 104/223] feat(submitter): track all historic hashes in `Transaction` (#6068) ### Description - instead of tracking a single tx hash, the `Transaction` struct now tracks all historic hashes this logical transaction has had. This is because after resubmitting the tx with a new gas price, it may still be a previous hash that gets included in the end - the status of a transaction is now found by querying all of its historic hashes. If at least one hash is finalized, the tx is finalized. If at least one hash is included, the tx is included. If every hash query returns an error, the status default to `PendingInclusion`, to avoid dropping txs when RPCs go down (this happens even in e2e) - adds two new fields to `Transaction`: `creation_timestamp` and `last_submission_attempt`. Both of these are timestamps, and are used to enforce a minimum time interval before resubmitting a Transaction. They are useful because in the local solana validator it takes 10-30s for the hash of a submitted tx to be queried without returning an error. Without them, the transaction would get continuously resubmitted. - adds a new `tx_ready_for_resubmission` method to the `AdaptsChains` trait. It defaults to `true` everywhere, but on solana it is overridden to use `creation_timestamp`, `last_submission_attempt` and `TX_RESUBMISSION_MIN_DELAY_SECS` (15s). ### Drive-by changes - reduces verbosity of `Transaction` logs by silencing the `instruction` property of the `vm_specific_data` for Sealevel. I kept the tx cost data because that will likely be useful. - renames the db key for mapping a message to multiple payloads to `PAYLOAD_IDS_BY_MESSAGE_ID` from `PAYLOAD_ID_BY_MESSAGE_ID` ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5893 ### Testing E2E and some unit tests --- .../src/db/rocks/hyperlane_db.rs | 6 +- .../submitter/src/chain_tx_adapter/adapter.rs | 6 + .../chains/sealevel/adapter.rs | 131 +++++++++------ .../chains/sealevel/precursor.rs | 12 +- .../chains/sealevel/transaction/factory.rs | 4 +- .../chains/sealevel/transaction/update.rs | 3 +- .../stages/inclusion_stage.rs | 4 + .../src/payload_dispatcher/test_utils.rs | 4 +- rust/main/submitter/src/transaction/types.rs | 158 +++++++++++++++++- 9 files changed, 266 insertions(+), 62 deletions(-) diff --git a/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs index 70bb2979655..a9c6a842f21 100644 --- a/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -37,7 +37,7 @@ const MERKLE_LEAF_INDEX_BY_MESSAGE_ID: &str = "merkle_leaf_index_by_message_id_" 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"; -const PAYLOAD_ID_BY_MESSAGE_ID: &str = "payload_id_by_message_id_"; +const PAYLOAD_IDS_BY_MESSAGE_ID: &str = "payload_ids_by_message_id_"; /// Rocks DB result type pub type DbResult = std::result::Result; @@ -664,14 +664,14 @@ impl HyperlaneDb for HyperlaneRocksDB { message_id: &H256, payload_ids: Vec, ) -> DbResult<()> { - self.store_value_by_key(PAYLOAD_ID_BY_MESSAGE_ID, message_id, &payload_ids) + self.store_value_by_key(PAYLOAD_IDS_BY_MESSAGE_ID, message_id, &payload_ids) } fn retrieve_payload_ids_by_message_id( &self, message_id: &H256, ) -> DbResult>> { - self.retrieve_value_by_key(PAYLOAD_ID_BY_MESSAGE_ID, message_id) + self.retrieve_value_by_key(PAYLOAD_IDS_BY_MESSAGE_ID, message_id) } } diff --git a/rust/main/submitter/src/chain_tx_adapter/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/adapter.rs index a2ed0e12bf3..79cccd70b5e 100644 --- a/rust/main/submitter/src/chain_tx_adapter/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/adapter.rs @@ -50,6 +50,12 @@ pub trait AdaptsChain: Send + Sync { /// Queries the chain by txhash to get the tx status. Called in the Inclusion Stage and Finality Stage of the PayloadDispatcher async fn tx_status(&self, tx: &Transaction) -> Result; + /// Return true if the transaction can be resubmitted (such as by escalating the gas price). Called in the Inclusion Stage (PayloadDispatcher). + /// Defaults to true, since most chains don't have special rules for tx resubmission. + async fn tx_ready_for_resubmission(&self, _tx: &Transaction) -> bool { + true + } + /// uses BatchManager, returns any reverted Payload IDs sent in a Transaction. Called in the Finality Stage (PayloadDispatcher) async fn reverted_payloads( &self, diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index ae61e572ead..0a80a06d94f 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -1,8 +1,10 @@ -use std::sync::Arc; use std::time::Duration; +use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; -use eyre::{bail, eyre, ContextCompat, Report, Result}; +use chrono::Utc; +use eyre::{bail, eyre, ContextCompat, Report}; +use futures_util::future::join_all; use serde_json::json; use solana_client::rpc_response::{Response, RpcSimulateTransactionResult}; use solana_sdk::{ @@ -14,7 +16,7 @@ use solana_sdk::{ signature::{Signature, Signer}, transaction::Transaction as SealevelTransaction, }; -use tracing::{info, warn}; +use tracing::{info, instrument, warn}; use uuid::Uuid; use hyperlane_base::{ @@ -27,7 +29,7 @@ use hyperlane_base::{ }, CoreMetrics, }; -use hyperlane_core::{ChainResult, ReorgPeriod, H256}; +use hyperlane_core::{ChainResult, ReorgPeriod, H256, H512}; use hyperlane_sealevel::fallback::{SealevelFallbackRpcClient, SubmitSealevelRpc}; use hyperlane_sealevel::{ PriorityFeeOracleConfig, SealevelProvider, SealevelProviderForSubmitter, SealevelTxCostEstimate, @@ -38,6 +40,7 @@ use crate::payload::FullPayload; use crate::transaction::{ SignerAddress, Transaction, TransactionId, TransactionStatus, VmSpecificTxData, }; +use crate::TransactionDropReason; use crate::{ chain_tx_adapter::chains::sealevel::transaction::{Precursor, TransactionFactory, Update}, error::SubmitterError, @@ -51,6 +54,8 @@ use crate::{ error, }; +const TX_RESUBMISSION_MIN_DELAY_SECS: u64 = 15; + pub struct SealevelTxAdapter { estimated_block_time: Duration, max_batch_size: u32, @@ -63,7 +68,11 @@ pub struct SealevelTxAdapter { } impl SealevelTxAdapter { - pub fn new(conf: ChainConf, raw_conf: RawChainConf, metrics: &CoreMetrics) -> Result { + pub fn new( + conf: ChainConf, + raw_conf: RawChainConf, + metrics: &CoreMetrics, + ) -> eyre::Result { let connection_conf = get_connection_conf(&conf); let urls = &connection_conf.urls; let chain_info = conf.metrics_conf().chain; @@ -109,7 +118,7 @@ impl SealevelTxAdapter { provider: Box, oracle: Box, submitter: Box, - ) -> Result { + ) -> eyre::Result { let estimated_block_time = conf.estimated_block_time; let reorg_period = conf.reorg_period.clone(); let max_batch_size = Self::batch_size(&conf)?; @@ -147,7 +156,7 @@ impl SealevelTxAdapter { } } - fn batch_size(conf: &ChainConf) -> Result { + fn batch_size(conf: &ChainConf) -> eyre::Result { Ok(conf .connection .operation_submission_config() @@ -206,6 +215,51 @@ impl SealevelTxAdapter { ) .await } + + #[instrument(skip(self))] + async fn get_tx_hash_status(&self, tx_hash: H512) -> Result { + let signature = Signature::new(tx_hash.as_ref()); + let transaction_search_result = self.client.get_transaction(signature).await; + + let transaction = match transaction_search_result { + Ok(transaction) => transaction, + Err(err) => { + warn!(?err, "Failed to get transaction status by hash"); + return Err(SubmitterError::TxSubmissionError( + "Transaction hash not found".to_string(), + )); + } + }; + + // slot at which transaction was included into blockchain + let inclusion_slot = transaction.slot; + + info!(slot = ?inclusion_slot, "found transaction"); + + // if block with this slot is added to the chain, transaction is considered to be confirmed + let confirming_slot = inclusion_slot + self.reorg_period.as_blocks()? as u64; + + let confirming_block = self.client.get_block(confirming_slot).await; + + if confirming_block.is_ok() { + info!("finalized transaction"); + return Ok(TransactionStatus::Finalized); + } + + // block which includes transaction into blockchain + let including_block = self.client.get_block(inclusion_slot).await; + + match including_block { + Ok(_) => { + info!("included transaction"); + Ok(TransactionStatus::Included) + } + Err(_) => { + info!("pending transaction"); + Ok(TransactionStatus::PendingInclusion) + } + } + } } #[async_trait] @@ -259,9 +313,6 @@ impl AdaptsChain for SealevelTxAdapter { } async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { - if tx.hash.is_some() { - return Ok(()); - } info!(?tx, "submitting transaction"); let not_estimated = tx.precursor(); // TODO: the `estimate` call shouldn't happen here - the `Transaction` argument should already contain the precursor, @@ -287,53 +338,24 @@ impl AdaptsChain for SealevelTxAdapter { Ok(()) } + #[instrument(skip(self))] async fn tx_status(&self, tx: &Transaction) -> Result { info!(?tx, "checking status of transaction"); - let Some(h512) = tx.hash else { + if tx.tx_hashes.is_empty() { return Ok(TransactionStatus::PendingInclusion); - }; - let signature = Signature::new(h512.as_ref()); - let transaction_search_result = self.client.get_transaction(signature).await; - - let transaction = match transaction_search_result { - Ok(transaction) => transaction, - Err(err) => { - warn!(?tx, ?err, "Failed to get transaction status by hash"); - return Err(SubmitterError::TxSubmissionError( - "Transaction hash not found".to_string(), - )); - } - }; - - // slot at which transaction was included into blockchain - let inclusion_slot = transaction.slot; - - info!(?tx, slot = ?inclusion_slot, "found transaction"); - - // if block with this slot is added to the chain, transaction is considered to be confirmed - let confirming_slot = inclusion_slot + self.reorg_period.as_blocks()? as u64; - - let confirming_block = self.client.get_block(confirming_slot).await; - - if confirming_block.is_ok() { - info!(?tx, "finalized transaction"); - return Ok(TransactionStatus::Finalized); } - // block which includes transaction into blockchain - let including_block = self.client.get_block(inclusion_slot).await; - - match including_block { - Ok(_) => { - info!(?tx, "included transaction"); - Ok(TransactionStatus::Included) - } - Err(_) => { - info!(?tx, "pending transaction"); - Ok(TransactionStatus::PendingInclusion) - } - } + let hash_status_futures = tx + .tx_hashes + .iter() + .map(|tx_hash| self.get_tx_hash_status(*tx_hash)) + .collect::>(); + // this may lead to rate limiting if too many hashes build up. Consider querying from most recent to oldest + let hash_status_results = join_all(hash_status_futures).await; + Ok(TransactionStatus::classify_tx_status_from_hash_statuses( + hash_status_results, + )) } async fn reverted_payloads( @@ -351,6 +373,13 @@ impl AdaptsChain for SealevelTxAdapter { fn max_batch_size(&self) -> u32 { self.max_batch_size } + + async fn tx_ready_for_resubmission(&self, tx: &Transaction) -> bool { + let last_submission_time = tx.last_submission_attempt.unwrap_or(tx.creation_timestamp); + let seconds_since_last_submission = + (Utc::now() - last_submission_time).num_seconds() as u64; + seconds_since_last_submission >= TX_RESUBMISSION_MIN_DELAY_SECS + } } #[cfg(test)] diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/precursor.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/precursor.rs index c981743a698..d1192aaab63 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/precursor.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/precursor.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + use solana_sdk::instruction::Instruction as SealevelInstruction; use hyperlane_sealevel::SealevelTxCostEstimate; @@ -6,12 +8,20 @@ use crate::chain_tx_adapter::chains::sealevel::payload; use crate::chain_tx_adapter::chains::sealevel::payload::Instruction; use crate::payload::FullPayload; -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq)] +#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq)] pub struct SealevelTxPrecursor { pub instruction: SealevelInstruction, pub estimate: SealevelTxCostEstimate, } +impl Debug for SealevelTxPrecursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SealevelTxPrecursor") + .field("cost_estimate", &self.estimate) + .finish() + } +} + impl SealevelTxPrecursor { pub fn new(instruction: SealevelInstruction, estimate: SealevelTxCostEstimate) -> Self { Self { diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/factory.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/factory.rs index 1143117ab97..44820577f1a 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/factory.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/factory.rs @@ -10,11 +10,13 @@ impl TransactionFactory { pub fn build(payload: &FullPayload, precursor: SealevelTxPrecursor) -> Transaction { Transaction { id: TransactionId::new(Uuid::new_v4()), - hash: None, + tx_hashes: vec![], vm_specific_data: VmSpecificTxData::Svm(precursor), payload_details: vec![payload.details.clone()], status: TransactionStatus::PendingInclusion, submission_attempts: 0, + creation_timestamp: chrono::Utc::now(), + last_submission_attempt: None, } } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs index a373a845e53..d76572d44e3 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs @@ -9,7 +9,8 @@ pub trait Update { impl Update for Transaction { fn update_after_submission(&mut self, hash: H512, precursor: SealevelTxPrecursor) -> &mut Self { - self.hash = Some(hash); + self.tx_hashes.push(hash); + self.last_submission_attempt = Some(chrono::Utc::now()); self.submission_attempts += 1; // Data is updated since transaction is re-estimated before submission diff --git a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs index 907ab10ff3e..1e0d69bb79d 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs @@ -154,6 +154,10 @@ impl InclusionStage { match tx_status { TransactionStatus::PendingInclusion | TransactionStatus::Mempool => { info!(tx_id = ?tx.id, ?tx_status, "Transaction is pending inclusion"); + if !state.adapter.tx_ready_for_resubmission(&tx).await { + info!(?tx, "Transaction is not ready for resubmission"); + return Ok(()); + } return Self::process_pending_tx(tx, state, pool).await; } TransactionStatus::Included | TransactionStatus::Finalized => { diff --git a/rust/main/submitter/src/payload_dispatcher/test_utils.rs b/rust/main/submitter/src/payload_dispatcher/test_utils.rs index 4da718d0cdd..945d44968da 100644 --- a/rust/main/submitter/src/payload_dispatcher/test_utils.rs +++ b/rust/main/submitter/src/payload_dispatcher/test_utils.rs @@ -52,11 +52,13 @@ pub(crate) fn dummy_tx(payloads: Vec, status: TransactionStatus) -> .collect(); Transaction { id: UniqueIdentifier::random(), - hash: None, + tx_hashes: vec![], vm_specific_data: VmSpecificTxData::Evm, payload_details: details.clone(), status, submission_attempts: 0, + creation_timestamp: chrono::Utc::now(), + last_submission_attempt: None, } } diff --git a/rust/main/submitter/src/transaction/types.rs b/rust/main/submitter/src/transaction/types.rs index 825f19ff4f9..f0f48b3e85a 100644 --- a/rust/main/submitter/src/transaction/types.rs +++ b/rust/main/submitter/src/transaction/types.rs @@ -1,14 +1,17 @@ // TODO: re-enable clippy warnings #![allow(dead_code)] +use std::collections::HashMap; use std::ops::Deref; +use chrono::{DateTime, Utc}; use uuid::Uuid; use hyperlane_core::{identifiers::UniqueIdentifier, H256, H512}; use crate::chain_tx_adapter::SealevelTxPrecursor; use crate::payload::{FullPayload, PayloadDetails, PayloadId}; +use crate::SubmitterError; pub type TransactionId = UniqueIdentifier; pub type SignerAddress = H256; @@ -18,8 +21,10 @@ pub type SignerAddress = H256; pub struct Transaction { /// unique tx identifier. Used as primary key in the db. pub id: TransactionId, - /// tx identifier obtained by hashing its contents. This may change when gas price is escalated - pub hash: Option, + /// all historic tx identifiers this transaction has had, obtained by hashing its contents. + /// a `Transaction` may have had more than one hash because this changes + /// when gas price is escalated + pub tx_hashes: Vec, /// may include nonce, gas price, etc pub vm_specific_data: VmSpecificTxData, /// this is a vec to accommodate batching @@ -27,9 +32,13 @@ pub struct Transaction { pub status: TransactionStatus, /// incremented on submission / gas escalation pub submission_attempts: u32, + /// the date and time the transaction was created in-memory by the submitter + pub creation_timestamp: DateTime, + /// the date and time the transaction was last submitted + pub last_submission_attempt: Option>, } -#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq)] +#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq, Hash)] pub enum TransactionStatus { /// default state. If the tx appears dropped from the mempool, it goes back to this state #[default] @@ -44,7 +53,47 @@ pub enum TransactionStatus { Dropped(DropReason), } -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq)] +impl TransactionStatus { + pub fn classify_tx_status_from_hash_statuses( + statuses: Vec>, + ) -> TransactionStatus { + let mut status_counts = HashMap::::new(); + + // count the occurrences of each successfully queried hash status + for status in statuses.iter().flatten() { + *status_counts.entry(status.clone()).or_insert(0) += 1; + } + + let finalized_count = status_counts + .get(&TransactionStatus::Finalized) + .unwrap_or(&0); + let included_count = status_counts + .get(&TransactionStatus::Included) + .unwrap_or(&0); + let pending_count = status_counts + .get(&TransactionStatus::PendingInclusion) + .unwrap_or(&0); + let mempool_count = status_counts.get(&TransactionStatus::Mempool).unwrap_or(&0); + if *finalized_count > 0 { + return TransactionStatus::Finalized; + } else if *included_count > 0 { + return TransactionStatus::Included; + } else if *pending_count > 0 { + return TransactionStatus::PendingInclusion; + } else if *mempool_count > 0 { + return TransactionStatus::Mempool; + } else if !status_counts.is_empty() { + // if the hashmap is not empty, it must mean that the hashes were dropped, + // because the hashmap is populated only if the status query was successful + return TransactionStatus::Dropped(DropReason::DroppedByChain); + } + + // otherwise, return `PendingInclusion`, assuming the rpc is down temporarily and returns errors + TransactionStatus::PendingInclusion + } +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq, Hash)] pub enum DropReason { /// currently only assigned when a reorg is detected DroppedByChain, @@ -59,3 +108,104 @@ pub enum VmSpecificTxData { Svm(SealevelTxPrecursor), CosmWasm, } + +#[cfg(test)] +mod tests { + #[test] + fn test_transaction_status_classification_finalized() { + use super::*; + use crate::SubmitterError; + + let statuses = vec![ + Ok(TransactionStatus::Included), + Ok(TransactionStatus::Dropped(DropReason::DroppedByChain)), + Ok(TransactionStatus::PendingInclusion), + Err(SubmitterError::NetworkError("Network error".to_string())), + Ok(TransactionStatus::Finalized), + ]; + + let classified_status = TransactionStatus::classify_tx_status_from_hash_statuses(statuses); + assert_eq!(classified_status, TransactionStatus::Finalized); + } + + #[test] + fn test_transaction_status_classification_included() { + use super::*; + use crate::SubmitterError; + + let statuses = vec![ + Ok(TransactionStatus::Dropped(DropReason::DroppedByChain)), + Ok(TransactionStatus::Included), + Ok(TransactionStatus::PendingInclusion), + Err(SubmitterError::NetworkError("Network error".to_string())), + ]; + + let classified_status = TransactionStatus::classify_tx_status_from_hash_statuses(statuses); + assert_eq!(classified_status, TransactionStatus::Included); + } + + #[test] + fn test_transaction_status_classification_errors() { + use super::*; + use crate::SubmitterError; + + let statuses = vec![ + Err(SubmitterError::NetworkError("Network error".to_string())), + Err(SubmitterError::NetworkError("Network error".to_string())), + Err(SubmitterError::NetworkError("Network error".to_string())), + Err(SubmitterError::NetworkError("Network error".to_string())), + ]; + + let classified_status = TransactionStatus::classify_tx_status_from_hash_statuses(statuses); + assert_eq!(classified_status, TransactionStatus::PendingInclusion); + } + + #[test] + fn test_transaction_status_classification_dropped() { + use super::*; + use crate::SubmitterError; + + let statuses = vec![ + Err(SubmitterError::NetworkError("Network error".to_string())), + Ok(TransactionStatus::Dropped(DropReason::DroppedByChain)), + Err(SubmitterError::NetworkError("Network error".to_string())), + ]; + + let classified_status = TransactionStatus::classify_tx_status_from_hash_statuses(statuses); + assert_eq!( + classified_status, + TransactionStatus::Dropped(DropReason::DroppedByChain) + ); + } + + #[test] + fn test_transaction_status_classification_pending() { + use super::*; + use crate::SubmitterError; + + let statuses = vec![ + Ok(TransactionStatus::Dropped(DropReason::DroppedByChain)), + Ok(TransactionStatus::Mempool), + Ok(TransactionStatus::PendingInclusion), + Err(SubmitterError::NetworkError("Network error".to_string())), + ]; + + let classified_status = TransactionStatus::classify_tx_status_from_hash_statuses(statuses); + assert_eq!(classified_status, TransactionStatus::PendingInclusion); + } + + #[test] + fn test_transaction_status_classification_mempool() { + use super::*; + use crate::SubmitterError; + + let statuses = vec![ + Err(SubmitterError::NetworkError("Network error".to_string())), + Ok(TransactionStatus::Mempool), + Ok(TransactionStatus::Dropped(DropReason::DroppedByChain)), + ]; + + let classified_status = TransactionStatus::classify_tx_status_from_hash_statuses(statuses); + assert_eq!(classified_status, TransactionStatus::Mempool); + } +} From b105a862544124ae010aa1eee4d79e39c571848d Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 30 Apr 2025 11:49:45 +0100 Subject: [PATCH 105/223] chore: update seaorm to latest again after previously it was reverted (#5041) ### Description Upgrade SeaORM to 1.1.10 ### Related issues Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4793 ### Backward compatibility Yes ### Testing E2E tests to check no regressions --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/Dockerfile | 2 +- rust/main/Cargo.lock | 947 ++++++++++++------ rust/main/Cargo.toml | 7 +- .../migration/bin/generate_entities.rs | 2 +- .../agents/scraper/migration/bin/init_db.rs | 2 +- .../scraper/migration/bin/recreate_db.rs | 2 +- .../scraper/migration/src/l20230309_types.rs | 4 +- ...0230309_000003_create_table_transaction.rs | 7 +- .../agents/scraper/src/db/generated/block.rs | 4 +- .../agents/scraper/src/db/generated/cursor.rs | 2 +- .../src/db/generated/delivered_message.rs | 6 +- .../agents/scraper/src/db/generated/domain.rs | 10 +- .../scraper/src/db/generated/gas_payment.rs | 24 +- .../scraper/src/db/generated/message.rs | 12 +- .../agents/scraper/src/db/generated/mod.rs | 2 +- .../scraper/src/db/generated/prelude.rs | 2 +- .../scraper/src/db/generated/transaction.rs | 12 +- rust/main/rust-toolchain | 2 +- rust/sealevel/Cargo.toml | 12 - 19 files changed, 705 insertions(+), 356 deletions(-) diff --git a/rust/Dockerfile b/rust/Dockerfile index 1bde4548612..5b7ab65bcc5 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -5,7 +5,7 @@ # -------- Base Image with Tools -------- # Base image containing all necessary build tools and dependencies -FROM rust:1.80.1 AS base +FROM rust:1.81.0 AS base RUN apt-get update && \ apt-get install -y musl-tools clang && \ rustup target add x86_64-unknown-linux-musl && \ diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 2a6a04feea9..ea0ea2a23a0 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -179,6 +179,56 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -336,7 +386,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -459,9 +509,9 @@ dependencies = [ [[package]] name = "atoi" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" dependencies = [ "num-traits", ] @@ -1086,19 +1136,6 @@ dependencies = [ "derive-new", ] -[[package]] -name = "bae" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2 1.0.93", - "quote 1.0.37", - "syn 1.0.109", -] - [[package]] name = "base16ct" version = "0.1.1" @@ -1185,17 +1222,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" -[[package]] -name = "bigdecimal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" -dependencies = [ - "num-bigint 0.4.6", - "num-integer", - "num-traits", -] - [[package]] name = "bigdecimal" version = "0.4.5" @@ -1207,6 +1233,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", + "serde", ] [[package]] @@ -1594,7 +1621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1617,17 +1644,18 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] name = "cc" -version = "1.0.94" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -1710,8 +1738,7 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive", - "clap_lex", + "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", "strsim 0.10.0", @@ -1719,17 +1746,38 @@ dependencies = [ "textwrap 0.16.1", ] +[[package]] +name = "clap" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +dependencies = [ + "anstream", + "anstyle", + "clap_lex 0.7.4", + "strsim 0.11.1", +] + [[package]] name = "clap_derive" -version = "3.2.25" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ - "heck 0.4.1", - "proc-macro-error", + "heck 0.5.0", "proc-macro2 1.0.93", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -1741,6 +1789,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "cloudabi" version = "0.0.3" @@ -1772,7 +1826,7 @@ dependencies = [ "lazy_static", "serde", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1788,7 +1842,7 @@ dependencies = [ "k256 0.13.4", "serde", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1805,7 +1859,7 @@ dependencies = [ "pbkdf2 0.11.0", "rand 0.8.5", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1821,7 +1875,7 @@ dependencies = [ "pbkdf2 0.12.2", "rand 0.8.5", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1842,7 +1896,7 @@ dependencies = [ "serde_derive", "sha2 0.10.8", "sha3 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1862,7 +1916,7 @@ dependencies = [ "serde_derive", "sha2 0.10.8", "sha3 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1892,6 +1946,12 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "combine" version = "3.8.1" @@ -2118,7 +2178,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-rpc", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -2138,7 +2198,7 @@ dependencies = [ "ed25519-zebra 3.1.0", "k256 0.13.4", "rand_core 0.6.4", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2161,7 +2221,7 @@ dependencies = [ "rand_core 0.6.4", "rayon", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2194,7 +2254,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2207,7 +2267,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2251,7 +2311,7 @@ dependencies = [ "serde-json-wasm 0.5.2", "sha2 0.10.8", "static_assertions 1.1.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2274,7 +2334,7 @@ dependencies = [ "serde-json-wasm 1.0.1", "sha2 0.10.8", "static_assertions 1.1.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2305,6 +2365,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32c" version = "0.6.8" @@ -2385,7 +2460,7 @@ dependencies = [ "elliptic-curve 0.13.8", "hex 0.4.3", "k256 0.13.4", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2538,7 +2613,7 @@ dependencies = [ "schemars", "semver", "serde", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2553,7 +2628,7 @@ dependencies = [ "schemars", "semver", "serde", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2583,7 +2658,7 @@ dependencies = [ "schemars", "semver", "serde", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2597,7 +2672,7 @@ dependencies = [ "serde", "serde_json", "static_assertions 1.1.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2776,6 +2851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid 0.9.6", + "pem-rfc7468", "zeroize", ] @@ -2960,15 +3036,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -2979,17 +3046,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -3110,7 +3166,7 @@ dependencies = [ "getrandom 0.2.15", "hyperlane-core", "solana-program", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3218,6 +3274,9 @@ name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] [[package]] name = "elliptic-curve" @@ -3376,6 +3435,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "eth-keystore" version = "0.5.0" @@ -3394,7 +3464,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "sha3 0.10.8", - "thiserror", + "thiserror 1.0.63", "uuid 0.8.2", ] @@ -3411,7 +3481,7 @@ dependencies = [ "serde", "serde_json", "sha3 0.10.8", - "thiserror", + "thiserror 1.0.63", "uint 0.9.5", ] @@ -3522,7 +3592,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3588,7 +3658,7 @@ dependencies = [ "serde_json", "strum 0.24.1", "syn 1.0.109", - "thiserror", + "thiserror 1.0.63", "tiny-keccak 2.0.2", "unicode-xid 0.2.5", ] @@ -3605,7 +3675,7 @@ dependencies = [ "serde", "serde-aux", "serde_json", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -3628,7 +3698,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "tracing-futures", @@ -3683,7 +3753,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-tungstenite 0.17.2", "tracing", @@ -3713,7 +3783,7 @@ dependencies = [ "rusoto_kms", "sha2 0.10.8", "spki 0.6.0", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -3885,6 +3955,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -3953,7 +4034,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.98", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -4008,7 +4089,7 @@ dependencies = [ "serde", "serde_json", "tai64", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -4094,7 +4175,7 @@ dependencies = [ "secrecy", "serde", "tai64", - "thiserror", + "thiserror 1.0.63", "zeroize", ] @@ -4257,7 +4338,7 @@ dependencies = [ "rand 0.8.5", "semver", "tai64", - "thiserror", + "thiserror 1.0.63", "tokio", "zeroize", ] @@ -4302,7 +4383,7 @@ dependencies = [ "postcard", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "uint 0.9.5", ] @@ -4411,13 +4492,13 @@ dependencies = [ [[package]] name = "futures-intrusive" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.11.2", + "parking_lot 0.12.3", ] [[package]] @@ -4549,6 +4630,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + [[package]] name = "gimli" version = "0.28.1" @@ -4568,7 +4661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474" dependencies = [ "combine", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -4700,11 +4793,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -4767,23 +4860,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] [[package]] name = "heck" @@ -5147,7 +5228,7 @@ dependencies = [ "solana-sdk", "static_assertions 1.1.0", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-metrics", "tracing", @@ -5169,7 +5250,7 @@ dependencies = [ "async-rwlock", "async-trait", "auto_impl 1.2.0", - "bigdecimal 0.4.5", + "bigdecimal", "borsh 0.9.3", "bs58 0.5.1", "bytes", @@ -5198,7 +5279,7 @@ dependencies = [ "solana-sdk", "strum 0.26.3", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.63", "tiny-keccak 2.0.2", "tokio", "tracing", @@ -5242,7 +5323,7 @@ dependencies = [ "sha256", "tendermint", "tendermint-rpc", - "thiserror", + "thiserror 1.0.63", "time", "tokio", "tonic 0.12.3", @@ -5282,7 +5363,7 @@ dependencies = [ "sha256", "tendermint", "tendermint-rpc", - "thiserror", + "thiserror 1.0.63", "time", "tokio", "tonic 0.12.3", @@ -5324,7 +5405,7 @@ dependencies = [ "serde", "sha2 0.10.8", "sha3 0.10.8", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5353,7 +5434,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "tracing-futures", @@ -5371,7 +5452,7 @@ dependencies = [ "futures", "hyperlane-core", "serde", - "thiserror", + "thiserror 1.0.63", "tracing", "tracing-futures", "url", @@ -5398,7 +5479,7 @@ dependencies = [ "async-trait", "hyperlane-application", "hyperlane-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5439,7 +5520,7 @@ dependencies = [ "solana-program", "solana-sdk", "solana-transaction-status", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "tracing-futures", @@ -5459,7 +5540,7 @@ dependencies = [ "num-traits", "serializable-account-meta", "solana-program", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5489,7 +5570,7 @@ dependencies = [ "serializable-account-meta", "solana-program", "spl-noop", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5519,7 +5600,7 @@ dependencies = [ "num-traits", "serializable-account-meta", "solana-program", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5533,7 +5614,7 @@ dependencies = [ "hyperlane-sealevel-mailbox", "serializable-account-meta", "solana-program", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5803,6 +5884,17 @@ dependencies = [ "serde", ] +[[package]] +name = "inherent" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c38228f24186d9cc68c729accb4d413be9eaed6ad07ff79e0270d9e56f3de13" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.37", + "syn 2.0.98", +] + [[package]] name = "injective-protobuf" version = "0.2.2" @@ -5881,6 +5973,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -5907,10 +6005,11 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -5990,6 +6089,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] [[package]] name = "lazycell" @@ -5999,9 +6101,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" @@ -6103,6 +6205,16 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.16" @@ -6372,7 +6484,7 @@ dependencies = [ "rustc_version", "smallvec", "tagptr", - "thiserror", + "thiserror 1.0.63", "triomphe", "uuid 1.11.0", ] @@ -6404,7 +6516,7 @@ dependencies = [ "hyperlane-core", "solana-program", "spl-type-length-value", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6535,6 +6647,23 @@ dependencies = [ "serde", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -6634,6 +6763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -6829,6 +6959,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.4.3" @@ -6847,25 +6986,26 @@ checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "ouroboros" -version = "0.15.6" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" dependencies = [ "aliasable", "ouroboros_macro", + "static_assertions 1.1.0", ] [[package]] name = "ouroboros_macro" -version = "0.15.6" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" dependencies = [ - "Inflector", - "proc-macro-error", + "heck 0.4.1", "proc-macro2 1.0.93", + "proc-macro2-diagnostics", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -7085,6 +7225,15 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -7107,7 +7256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.63", "ucd-trie", ] @@ -7145,6 +7294,15 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "pgvector" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e8871b6d7ca78348c6cd29b911b94851f3429f0cd403130ca17f26c1fb91a6" +dependencies = [ + "serde", +] + [[package]] name = "pharos" version = "0.5.3" @@ -7187,6 +7345,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der 0.7.9", + "pkcs8 0.10.2", + "spki 0.7.3", +] + [[package]] name = "pkcs8" version = "0.8.0" @@ -7351,7 +7520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ "once_cell", - "thiserror", + "thiserror 1.0.63", "toml 0.5.11", ] @@ -7388,6 +7557,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.37", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2 1.0.93", + "quote 1.0.37", + "syn 2.0.98", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -7406,6 +7597,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.37", + "syn 2.0.98", + "version_check", + "yansi", +] + [[package]] name = "prometheus" version = "0.13.4" @@ -7418,7 +7622,7 @@ dependencies = [ "memchr", "parking_lot 0.12.3", "protobuf", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -7609,7 +7813,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustls 0.20.9", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "webpki", @@ -7629,7 +7833,7 @@ dependencies = [ "rustls-native-certs 0.6.3", "rustls-pemfile 0.2.1", "slab", - "thiserror", + "thiserror 1.0.63", "tinyvec", "tracing", "webpki", @@ -7667,6 +7871,12 @@ dependencies = [ "proc-macro2 1.0.93", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.3.0" @@ -7854,7 +8064,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -7954,7 +8164,7 @@ dependencies = [ "strum 0.26.3", "submitter", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-metrics", "tokio-test", @@ -8176,7 +8386,27 @@ dependencies = [ ] [[package]] -name = "rstest" +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.2.0", + "spki 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "rstest" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" @@ -8683,7 +8913,7 @@ dependencies = [ "sea-orm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "time", "tokio", "tokio-test", @@ -8714,28 +8944,42 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sea-bae" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25" +dependencies = [ + "heck 0.4.1", + "proc-macro-error2", + "proc-macro2 1.0.93", + "quote 1.0.37", + "syn 2.0.98", +] + [[package]] name = "sea-orm" -version = "0.11.3" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fade86e8d41fd1a4721f84cb834f4ca2783f973cc30e6212b7fafc134f169214" +checksum = "21e61af841881c137d4bc8e0d8411cee9168548b404f9e4788e8af7e8f94bd4e" dependencies = [ "async-stream", "async-trait", - "bigdecimal 0.3.1", + "bigdecimal", "chrono", - "futures", + "futures-util", "log", "ouroboros", + "pgvector", "rust_decimal", "sea-orm-macros", "sea-query", "sea-query-binder", - "sea-strum", "serde", "serde_json", "sqlx", - "thiserror", + "strum 0.26.3", + "thiserror 2.0.12", "time", "tracing", "url", @@ -8744,13 +8988,14 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "0.11.3" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbf34a2caf70c2e3be9bb1e674e9540f6dfd7c8f40f6f05daf3b9740e476005" +checksum = "0f3d39b35e43a05998228f6c4abaa50b7fcaf001dea94fbdc04188a8be82a016" dependencies = [ "chrono", - "clap 3.2.25", + "clap 4.5.37", "dotenvy", + "glob", "regex", "sea-schema", "tracing", @@ -8760,27 +9005,27 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "0.11.3" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" +checksum = "d6b86e3e77b548e6c6c1f612a1ca024d557dffdb81b838bf482ad3222140c77b" dependencies = [ - "bae", - "heck 0.3.3", + "heck 0.4.1", "proc-macro2 1.0.93", "quote 1.0.37", - "syn 1.0.109", + "sea-bae", + "syn 2.0.98", + "unicode-ident", ] [[package]] name = "sea-orm-migration" -version = "0.11.3" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278d3adfd0832b6ffc17d3cfbc574d3695a5c1b38814e0bc8ac238d33f3d87cf" +checksum = "4ae8b2af58a56c8f850e8d8a18a462255eb36a571f085f3405f6b83d9cf23e0f" dependencies = [ "async-trait", - "clap 3.2.25", + "clap 4.5.37", "dotenvy", - "futures", "sea-orm", "sea-orm-cli", "sea-schema", @@ -8790,12 +9035,14 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.28.5" +version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbab99b8cd878ab7786157b7eb8df96333a6807cc6e45e8888c85b51534b401a" +checksum = "d99447c24da0cded00089e2021e1624af90878c65f7534319448d01da3df869d" dependencies = [ - "bigdecimal 0.3.1", + "bigdecimal", "chrono", + "inherent", + "ordered-float", "rust_decimal", "sea-query-derive", "serde_json", @@ -8805,11 +9052,11 @@ dependencies = [ [[package]] name = "sea-query-binder" -version = "0.3.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cea85029985b40dfbf18318d85fe985c04db7c1b4e5e8e0a0a0cdff5f1e30f9" +checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" dependencies = [ - "bigdecimal 0.3.1", + "bigdecimal", "chrono", "rust_decimal", "sea-query", @@ -8821,22 +9068,23 @@ dependencies = [ [[package]] name = "sea-query-derive" -version = "0.3.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" +checksum = "bae0cbad6ab996955664982739354128c58d16e126114fe88c2a493642502aab" dependencies = [ + "darling 0.20.10", "heck 0.4.1", "proc-macro2 1.0.93", "quote 1.0.37", - "syn 1.0.109", - "thiserror", + "syn 2.0.98", + "thiserror 2.0.12", ] [[package]] name = "sea-schema" -version = "0.11.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb2940bb5a10bc6cd05b450ce6cd3993e27fddd7eface2becb97fc5af3a040e" +checksum = "0ef5dd7848c993f3789d09a2616484c72c9330cae2b048df59d8c9b8c0343e95" dependencies = [ "futures", "sea-query", @@ -8845,36 +9093,14 @@ dependencies = [ [[package]] name = "sea-schema-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" -dependencies = [ - "heck 0.3.3", - "proc-macro2 1.0.93", - "quote 1.0.37", - "syn 1.0.109", -] - -[[package]] -name = "sea-strum" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391d06a6007842cfe79ac6f7f53911b76dfd69fc9a6769f1cf6569d12ce20e1b" -dependencies = [ - "sea-strum_macros", -] - -[[package]] -name = "sea-strum_macros" -version = "0.23.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" +checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" dependencies = [ - "heck 0.3.3", + "heck 0.4.1", "proc-macro2 1.0.93", "quote 1.0.37", - "rustversion", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -9344,6 +9570,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -9385,7 +9614,7 @@ dependencies = [ "solana-vote-program", "spl-token", "spl-token-2022", - "thiserror", + "thiserror 1.0.63", "zstd", ] @@ -9406,7 +9635,7 @@ dependencies = [ "solana-program", "solana-program-runtime", "solana-sdk", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9420,7 +9649,7 @@ dependencies = [ "solana-perf", "solana-remote-wallet", "solana-sdk", - "thiserror", + "thiserror 1.0.63", "tiny-bip39", "uriparse", "url", @@ -9486,7 +9715,7 @@ dependencies = [ "solana-version", "solana-vote-program", "spl-token-2022", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-stream", "tokio-tungstenite 0.17.2", @@ -9526,7 +9755,7 @@ dependencies = [ "solana-sdk", "solana-version", "spl-memo 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -9560,7 +9789,7 @@ dependencies = [ "sha2 0.10.8", "solana-frozen-abi-macro", "subtle", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9695,7 +9924,7 @@ dependencies = [ "solana-frozen-abi", "solana-frozen-abi-macro", "solana-sdk-macro", - "thiserror", + "thiserror 1.0.63", "tiny-bip39", "wasm-bindgen", "zeroize", @@ -9724,7 +9953,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-sdk", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9750,7 +9979,7 @@ dependencies = [ "qstring", "semver", "solana-sdk", - "thiserror", + "thiserror 1.0.63", "uriparse", ] @@ -9799,7 +10028,7 @@ dependencies = [ "solana-logger", "solana-program", "solana-sdk-macro", - "thiserror", + "thiserror 1.0.63", "uriparse", "wasm-bindgen", ] @@ -9839,7 +10068,7 @@ dependencies = [ "solana-metrics", "solana-perf", "solana-sdk", - "thiserror", + "thiserror 1.0.63", "tokio", "x509-parser", ] @@ -9869,7 +10098,7 @@ dependencies = [ "spl-memo 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "spl-token", "spl-token-2022", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9904,7 +10133,7 @@ dependencies = [ "solana-metrics", "solana-program-runtime", "solana-sdk", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9933,7 +10162,7 @@ dependencies = [ "solana-program", "solana-sdk", "subtle", - "thiserror", + "thiserror 1.0.63", "zeroize", ] @@ -9994,7 +10223,7 @@ dependencies = [ "solana-program", "spl-token", "spl-token-2022", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -10033,7 +10262,7 @@ dependencies = [ "num-traits", "num_enum 0.5.11", "solana-program", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -10050,7 +10279,7 @@ dependencies = [ "solana-zk-token-sdk", "spl-memo 3.0.1 (git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane)", "spl-token", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -10064,113 +10293,216 @@ dependencies = [ "num-traits", "num_enum 0.6.1", "solana-program", - "thiserror", + "thiserror 1.0.63", ] [[package]] -name = "sqlformat" -version = "0.2.4" +name = "sqlx" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e" dependencies = [ - "nom", - "unicode_categories", + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", ] [[package]] -name = "sqlx" -version = "0.6.3" +name = "sqlx-core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" +checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3" dependencies = [ + "base64 0.22.1", + "bigdecimal", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.3.1", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap 2.5.0", + "log", + "memchr", + "native-tls", + "once_cell", + "percent-encoding", + "rust_decimal", + "serde", + "serde_json", + "sha2 0.10.8", + "smallvec", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid 1.11.0", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.37", "sqlx-core", - "sqlx-macros", + "sqlx-macros-core", + "syn 2.0.98", ] [[package]] -name = "sqlx-core" -version = "0.6.3" +name = "sqlx-macros-core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" +checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex 0.4.3", + "once_cell", + "proc-macro2 1.0.93", + "quote 1.0.37", + "serde", + "serde_json", + "sha2 0.10.8", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.98", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7" dependencies = [ - "ahash 0.7.8", "atoi", - "base64 0.13.1", - "bigdecimal 0.3.1", - "bitflags 1.3.2", + "base64 0.22.1", + "bigdecimal", + "bitflags 2.6.0", "byteorder", "bytes", "chrono", - "crossbeam-queue", - "dirs", + "crc", + "digest 0.10.7", "dotenvy", "either", - "event-listener 2.5.3", "futures-channel", "futures-core", - "futures-intrusive", + "futures-io", "futures-util", - "hashlink", + "generic-array 0.14.7", "hex 0.4.3", "hkdf", "hmac 0.12.1", - "indexmap 1.9.3", "itoa", - "libc", "log", "md-5 0.10.6", "memchr", - "num-bigint 0.4.6", "once_cell", - "paste", "percent-encoding", "rand 0.8.5", + "rsa", "rust_decimal", "serde", - "serde_json", "sha1", "sha2 0.10.8", "smallvec", - "sqlformat", - "sqlx-rt", + "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.12", "time", - "tokio-stream", - "url", + "tracing", "uuid 1.11.0", "whoami", ] [[package]] -name = "sqlx-macros" -version = "0.6.3" +name = "sqlx-postgres" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" +checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6" dependencies = [ + "atoi", + "base64 0.22.1", + "bigdecimal", + "bitflags 2.6.0", + "byteorder", + "chrono", + "crc", "dotenvy", - "either", - "heck 0.4.1", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex 0.4.3", + "hkdf", + "hmac 0.12.1", + "home", + "itoa", + "log", + "md-5 0.10.6", + "memchr", + "num-bigint 0.4.6", "once_cell", - "proc-macro2 1.0.93", - "quote 1.0.37", + "rand 0.8.5", + "rust_decimal", + "serde", "serde_json", + "sha2 0.10.8", + "smallvec", "sqlx-core", - "sqlx-rt", - "syn 1.0.109", - "url", + "stringprep", + "thiserror 2.0.12", + "time", + "tracing", + "uuid 1.11.0", + "whoami", ] [[package]] -name = "sqlx-rt" -version = "0.6.3" +name = "sqlx-sqlite" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" dependencies = [ - "native-tls", - "once_cell", - "tokio", - "tokio-native-tls", + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.12", + "time", + "tracing", + "url", + "uuid 1.11.0", ] [[package]] @@ -10303,7 +10635,7 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "tracing-subscriber", @@ -10462,7 +10794,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "thiserror", + "thiserror 1.0.63", "url", ] @@ -10568,7 +10900,7 @@ dependencies = [ "tendermint", "tendermint-config", "tendermint-proto", - "thiserror", + "thiserror 1.0.63", "time", "tokio", "tracing", @@ -10613,7 +10945,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -10627,6 +10968,17 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.37", + "syn 2.0.98", +] + [[package]] name = "thousands" version = "0.2.0" @@ -10689,7 +11041,7 @@ dependencies = [ "rand 0.7.3", "rustc-hash", "sha2 0.9.9", - "thiserror", + "thiserror 1.0.63", "unicode-normalization", "wasm-bindgen", "zeroize", @@ -11193,7 +11545,7 @@ dependencies = [ "rand 0.8.5", "rustls 0.20.9", "sha-1", - "thiserror", + "thiserror 1.0.63", "url", "utf-8", "webpki", @@ -11214,7 +11566,7 @@ dependencies = [ "log", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.63", "url", "utf-8", ] @@ -11345,12 +11697,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "universal-hash" version = "0.5.1" @@ -11427,6 +11773,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "0.8.2" @@ -11468,7 +11820,7 @@ dependencies = [ "hyperlane-cosmos", "hyperlane-ethereum", "hyperlane-test", - "itertools 0.10.5", + "itertools 0.12.1", "k256 0.13.4", "mockall", "prometheus", @@ -11476,7 +11828,7 @@ dependencies = [ "rusoto_core", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-test", "tracing", @@ -11591,6 +11943,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -11746,7 +12107,6 @@ checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ "redox_syscall 0.5.3", "wasite", - "web-sys", ] [[package]] @@ -11971,6 +12331,15 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -11984,7 +12353,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror", + "thiserror 1.0.63", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -12013,7 +12382,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -12047,7 +12416,7 @@ dependencies = [ "rustls-native-certs 0.6.3", "serde", "tame-gcs", - "thiserror", + "thiserror 1.0.63", "tokio", "tonic 0.10.2", "tower 0.4.13", @@ -12064,6 +12433,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 397092ae260..951d2725c94 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -47,6 +47,7 @@ aws-sdk-ssooidc = "=1.50.0" aws-sdk-sts = "=1.50.0" axum = "0.6.1" backtrace = "0.3" +cc = "1.2.2" base64 = "0.21.2" bigdecimal = "0.4.2" bincode = "1.3" @@ -96,7 +97,7 @@ injective-protobuf = "0.2.2" moka = "0.12.8" injective-std = "1.13.6" itertools = "*" -jobserver = "=0.1.26" +jobserver = "0.1.32" jsonrpc-core = "18.0" k256 = { version = "0.13.4", features = ["arithmetic", "std", "ecdsa"] } lazy_static = "1.5.0" @@ -124,14 +125,14 @@ ripemd = "0.1.3" rlp = "=0.5.2" rocksdb = "0.21.0" rstest = "0.25.0" -sea-orm = { version = "0.11.1", features = [ +sea-orm = { version = "1.1.10", features = [ "sqlx-postgres", "runtime-tokio-native-tls", "with-bigdecimal", "with-time", "macros", ] } -sea-orm-migration = { version = "0.11.1", features = [ +sea-orm-migration = { version = "1.1.10", features = [ "sqlx-postgres", "runtime-tokio-native-tls", ] } diff --git a/rust/main/agents/scraper/migration/bin/generate_entities.rs b/rust/main/agents/scraper/migration/bin/generate_entities.rs index 9481ece179b..b8276e659d6 100644 --- a/rust/main/agents/scraper/migration/bin/generate_entities.rs +++ b/rust/main/agents/scraper/migration/bin/generate_entities.rs @@ -7,7 +7,7 @@ mod common; const RAW_DB_PATH: &str = "./agents/scraper/src/db/generated"; const DOCKER_NAME: &str = "scraper-entity-generator"; -const CLI_VERSION: &str = "0.12.3"; +const CLI_VERSION: &str = "1.1.1"; struct PostgresDockerContainer; diff --git a/rust/main/agents/scraper/migration/bin/init_db.rs b/rust/main/agents/scraper/migration/bin/init_db.rs index 661080336fb..2d51a7c83fb 100644 --- a/rust/main/agents/scraper/migration/bin/init_db.rs +++ b/rust/main/agents/scraper/migration/bin/init_db.rs @@ -4,7 +4,7 @@ use common::*; mod common; -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), DbErr> { let db = init().await?; diff --git a/rust/main/agents/scraper/migration/bin/recreate_db.rs b/rust/main/agents/scraper/migration/bin/recreate_db.rs index ed6b0ac48f2..1bcbed04dd8 100644 --- a/rust/main/agents/scraper/migration/bin/recreate_db.rs +++ b/rust/main/agents/scraper/migration/bin/recreate_db.rs @@ -3,7 +3,7 @@ use common::*; mod common; -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), DbErr> { let db = init().await?; diff --git a/rust/main/agents/scraper/migration/src/l20230309_types.rs b/rust/main/agents/scraper/migration/src/l20230309_types.rs index 06c22bf9138..39cf8111856 100644 --- a/rust/main/agents/scraper/migration/src/l20230309_types.rs +++ b/rust/main/agents/scraper/migration/src/l20230309_types.rs @@ -2,10 +2,10 @@ use sea_orm_migration::prelude::*; /// Hashes are to be stored as binary. #[allow(non_upper_case_globals)] -pub const Hash: ColumnType = ColumnType::Binary(BlobSize::Tiny); +pub const Hash: ColumnType = ColumnType::Blob; /// Addresses are to be stored as binary. #[allow(non_upper_case_globals)] -pub const Address: ColumnType = ColumnType::Binary(BlobSize::Tiny); +pub const Address: ColumnType = ColumnType::Blob; /// 256-bit integer as base-10 digits: ceil(log_10(2^256)) const SIGNIFICANT_DIGITS_IN_256_BIT_INTEGER: u32 = 78; diff --git a/rust/main/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs b/rust/main/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs index 283968c6d69..b797c31118f 100644 --- a/rust/main/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs +++ b/rust/main/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs @@ -53,11 +53,8 @@ impl MigrationTrait for Migration { .col(ColumnDef::new_with_type(Transaction::GasUsed, Wei).not_null()) .col(ColumnDef::new_with_type(Transaction::CumulativeGasUsed, Wei).not_null()) .col( - ColumnDef::new_with_type( - Transaction::RawInputData, - ColumnType::Binary(BlobSize::Blob(None)), - ) - .borrow_mut(), + ColumnDef::new_with_type(Transaction::RawInputData, ColumnType::Blob) + .borrow_mut(), ) .foreign_key( ForeignKey::create() diff --git a/rust/main/agents/scraper/src/db/generated/block.rs b/rust/main/agents/scraper/src/db/generated/block.rs index 098cb839c2c..772cf645690 100644 --- a/rust/main/agents/scraper/src/db/generated/block.rs +++ b/rust/main/agents/scraper/src/db/generated/block.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; @@ -56,7 +56,7 @@ impl ColumnTrait for Column { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), Self::Domain => ColumnType::Integer.def(), - Self::Hash => ColumnType::Binary(BlobSize::Blob(None)).def().unique(), + Self::Hash => ColumnType::VarBinary(StringLen::None).def().unique(), Self::Height => ColumnType::BigInteger.def(), Self::Timestamp => ColumnType::DateTime.def(), } diff --git a/rust/main/agents/scraper/src/db/generated/cursor.rs b/rust/main/agents/scraper/src/db/generated/cursor.rs index f04b47428c4..0db99f422ff 100644 --- a/rust/main/agents/scraper/src/db/generated/cursor.rs +++ b/rust/main/agents/scraper/src/db/generated/cursor.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; diff --git a/rust/main/agents/scraper/src/db/generated/delivered_message.rs b/rust/main/agents/scraper/src/db/generated/delivered_message.rs index a3da2fe6de6..0de9bde96a0 100644 --- a/rust/main/agents/scraper/src/db/generated/delivered_message.rs +++ b/rust/main/agents/scraper/src/db/generated/delivered_message.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; @@ -57,9 +57,9 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), - Self::MsgId => ColumnType::Binary(BlobSize::Blob(None)).def().unique(), + Self::MsgId => ColumnType::VarBinary(StringLen::None).def().unique(), Self::Domain => ColumnType::Integer.def(), - Self::DestinationMailbox => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::DestinationMailbox => ColumnType::VarBinary(StringLen::None).def(), Self::DestinationTxId => ColumnType::BigInteger.def(), Self::Sequence => ColumnType::BigInteger.def().null(), } diff --git a/rust/main/agents/scraper/src/db/generated/domain.rs b/rust/main/agents/scraper/src/db/generated/domain.rs index cf1aaa2a7c5..3015d997504 100644 --- a/rust/main/agents/scraper/src/db/generated/domain.rs +++ b/rust/main/agents/scraper/src/db/generated/domain.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; @@ -52,7 +52,6 @@ pub enum Relation { Block, Cursor, DeliveredMessage, - GasPayment, Message, } @@ -78,7 +77,6 @@ impl RelationTrait for Relation { Self::Block => Entity::has_many(super::block::Entity).into(), Self::Cursor => Entity::has_many(super::cursor::Entity).into(), Self::DeliveredMessage => Entity::has_many(super::delivered_message::Entity).into(), - Self::GasPayment => Entity::has_many(super::gas_payment::Entity).into(), Self::Message => Entity::has_many(super::message::Entity).into(), } } @@ -102,12 +100,6 @@ impl Related for Entity { } } -impl Related for Entity { - fn to() -> RelationDef { - Relation::GasPayment.def() - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Message.def() diff --git a/rust/main/agents/scraper/src/db/generated/gas_payment.rs b/rust/main/agents/scraper/src/db/generated/gas_payment.rs index 5df74b084ab..859bbb2df2f 100644 --- a/rust/main/agents/scraper/src/db/generated/gas_payment.rs +++ b/rust/main/agents/scraper/src/db/generated/gas_payment.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; @@ -11,6 +11,8 @@ impl EntityName for Entity { } } +/// @NOTE: Replaced all occurrences of `Decimal` with `BigDecimal` +/// due to the following issue: https://github.com/SeaQL/sea-orm/issues/1530 #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] pub struct Model { pub id: i64, @@ -57,8 +59,8 @@ impl PrimaryKeyTrait for PrimaryKey { #[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation { - Domain, - Origin, + Domain2, + Domain1, Transaction, } @@ -69,16 +71,14 @@ impl ColumnTrait for Column { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), Self::Domain => ColumnType::Integer.def(), - Self::MsgId => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::MsgId => ColumnType::VarBinary(StringLen::None).def(), Self::Payment => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::GasAmount => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::TxId => ColumnType::BigInteger.def(), Self::LogIndex => ColumnType::BigInteger.def(), Self::Origin => ColumnType::Integer.def(), Self::Destination => ColumnType::Integer.def(), - Self::InterchainGasPaymaster => { - ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def() - } + Self::InterchainGasPaymaster => ColumnType::VarBinary(StringLen::None).def(), Self::Sequence => ColumnType::BigInteger.def().null(), } } @@ -87,11 +87,11 @@ impl ColumnTrait for Column { impl RelationTrait for Relation { fn def(&self) -> RelationDef { match self { - Self::Domain => Entity::belongs_to(super::domain::Entity) + Self::Domain2 => Entity::belongs_to(super::domain::Entity) .from(Column::Domain) .to(super::domain::Column::Id) .into(), - Self::Origin => Entity::belongs_to(super::domain::Entity) + Self::Domain1 => Entity::belongs_to(super::domain::Entity) .from(Column::Origin) .to(super::domain::Column::Id) .into(), @@ -103,12 +103,6 @@ impl RelationTrait for Relation { } } -impl Related for Entity { - fn to() -> RelationDef { - Relation::Domain.def() - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Transaction.def() diff --git a/rust/main/agents/scraper/src/db/generated/message.rs b/rust/main/agents/scraper/src/db/generated/message.rs index e06ea5de15a..dbb11661fb2 100644 --- a/rust/main/agents/scraper/src/db/generated/message.rs +++ b/rust/main/agents/scraper/src/db/generated/message.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; @@ -65,14 +65,14 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), - Self::MsgId => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::MsgId => ColumnType::VarBinary(StringLen::None).def(), Self::Origin => ColumnType::Integer.def(), Self::Destination => ColumnType::Integer.def(), Self::Nonce => ColumnType::Integer.def(), - Self::Sender => ColumnType::Binary(BlobSize::Blob(None)).def(), - Self::Recipient => ColumnType::Binary(BlobSize::Blob(None)).def(), - Self::MsgBody => ColumnType::Binary(BlobSize::Blob(None)).def().null(), - Self::OriginMailbox => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::Sender => ColumnType::VarBinary(StringLen::None).def(), + Self::Recipient => ColumnType::VarBinary(StringLen::None).def(), + Self::MsgBody => ColumnType::VarBinary(StringLen::None).def().null(), + Self::OriginMailbox => ColumnType::VarBinary(StringLen::None).def(), Self::OriginTxId => ColumnType::BigInteger.def(), } } diff --git a/rust/main/agents/scraper/src/db/generated/mod.rs b/rust/main/agents/scraper/src/db/generated/mod.rs index 929adca9d68..8617f9e8c5c 100644 --- a/rust/main/agents/scraper/src/db/generated/mod.rs +++ b/rust/main/agents/scraper/src/db/generated/mod.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 pub mod prelude; diff --git a/rust/main/agents/scraper/src/db/generated/prelude.rs b/rust/main/agents/scraper/src/db/generated/prelude.rs index 1280bf70da6..db1f04aa1fd 100644 --- a/rust/main/agents/scraper/src/db/generated/prelude.rs +++ b/rust/main/agents/scraper/src/db/generated/prelude.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 #[allow(unused_imports)] pub use super::{ block::Entity as Block, cursor::Entity as Cursor, diff --git a/rust/main/agents/scraper/src/db/generated/transaction.rs b/rust/main/agents/scraper/src/db/generated/transaction.rs index 4f1139c8c97..fd5ad5e529c 100644 --- a/rust/main/agents/scraper/src/db/generated/transaction.rs +++ b/rust/main/agents/scraper/src/db/generated/transaction.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 use sea_orm::entity::prelude::*; @@ -11,6 +11,8 @@ impl EntityName for Entity { } } +/// @NOTE: Replaced all occurrences of `Decimal` with `BigDecimal` +/// due to the following issue: https://github.com/SeaQL/sea-orm/issues/1530 #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] pub struct Model { pub id: i64, @@ -75,7 +77,7 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), - Self::Hash => ColumnType::Binary(BlobSize::Blob(None)).def().unique(), + Self::Hash => ColumnType::VarBinary(StringLen::None).def().unique(), Self::BlockId => ColumnType::BigInteger.def(), Self::GasLimit => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::MaxPriorityFeePerGas => ColumnType::Decimal(Some((78u32, 0u32))).def().null(), @@ -83,11 +85,11 @@ impl ColumnTrait for Column { Self::GasPrice => ColumnType::Decimal(Some((78u32, 0u32))).def().null(), Self::EffectiveGasPrice => ColumnType::Decimal(Some((78u32, 0u32))).def().null(), Self::Nonce => ColumnType::BigInteger.def(), - Self::Sender => ColumnType::Binary(BlobSize::Blob(None)).def(), - Self::Recipient => ColumnType::Binary(BlobSize::Blob(None)).def().null(), + Self::Sender => ColumnType::VarBinary(StringLen::None).def(), + Self::Recipient => ColumnType::VarBinary(StringLen::None).def().null(), Self::GasUsed => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::CumulativeGasUsed => ColumnType::Decimal(Some((78u32, 0u32))).def(), - Self::RawInputData => ColumnType::Binary(BlobSize::Blob(None)).def().null(), + Self::RawInputData => ColumnType::VarBinary(StringLen::None).def().null(), } } } diff --git a/rust/main/rust-toolchain b/rust/main/rust-toolchain index 7f466bd2dfc..bbf217f21b9 100644 --- a/rust/main/rust-toolchain +++ b/rust/main/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "1.80.1" +channel = "1.81.0" profile = "default" diff --git a/rust/sealevel/Cargo.toml b/rust/sealevel/Cargo.toml index 411f7a974aa..ffc287a2165 100644 --- a/rust/sealevel/Cargo.toml +++ b/rust/sealevel/Cargo.toml @@ -48,7 +48,6 @@ borsh = "0.9" bs58 = "0.5.0" bytes = "1" clap = "4" -color-eyre = "0.6" config = "0.13.3" console-subscriber = "0.2.0" convert_case = "0.6" @@ -94,17 +93,6 @@ reqwest = "0.11" ripemd = "0.1.3" rlp = "=0.5.2" rocksdb = "0.21.0" -sea-orm = { version = "0.11.1", features = [ - "sqlx-postgres", - "runtime-tokio-native-tls", - "with-bigdecimal", - "with-time", - "macros", -] } -sea-orm-migration = { version = "0.11.1", features = [ - "sqlx-postgres", - "runtime-tokio-native-tls", -] } semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" From 6d17bd0584b20f0ebd34e4599adf12953c143b30 Mon Sep 17 00:00:00 2001 From: Jamin <57451149+yjamin@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:49:36 +0200 Subject: [PATCH 106/223] fix: broadcast channel send only unique tx (#6009) ### Description Only send unique TX Ids via the channel when hook indexing. ### Drive-by changes None ### Related issues [Linear](https://linear.app/hyperlane-xyz/issue/BACK-71/contractsyncs-broadcast-channel-should-only-send-unique-tx-ids-for-the) Closes #5618 ### Backward compatibility Yes ### Testing Local E2E testing --- .../hyperlane-base/src/contract_sync/mod.rs | 9 +++++-- .../src/ethereum/termination_invariants.rs | 1 + .../src/invariants/termination_invariants.rs | 27 +++++++++++++------ .../src/sealevel/termination_invariants.rs | 2 ++ 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/rust/main/hyperlane-base/src/contract_sync/mod.rs b/rust/main/hyperlane-base/src/contract_sync/mod.rs index fb95433bdf5..652e0fca316 100644 --- a/rust/main/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/main/hyperlane-base/src/contract_sync/mod.rs @@ -222,8 +222,13 @@ where ); if let Some(tx) = self.broadcast_sender.as_ref() { - for (_, meta) in &logs { - if let Err(err) = tx.send(meta.transaction_id).await { + // If multiple logs occur in the same transaction they'll have the same transaction_id. + // Deduplicate their txids to avoid doing wasteful queries in txid indexer + let unique_txids: HashSet<_> = + logs.iter().map(|(_, meta)| meta.transaction_id).collect(); + + for tx_id in unique_txids { + if let Err(err) = tx.send(tx_id).await { trace!(?err, "Error sending txid to receiver"); } } diff --git a/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs b/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs index 61bedc0aafe..5ff0ef39663 100644 --- a/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs @@ -39,6 +39,7 @@ pub fn termination_invariants_met( + FAILED_MESSAGE_COUNT, non_matching_igp_message_count: 0, double_insertion_message_count: (config.kathy_messages as u32 / 4) * 2, + sealevel_tx_id_indexing: false, }; if !relayer_termination_invariants_met(params)? { return Ok(false); diff --git a/rust/main/utils/run-locally/src/invariants/termination_invariants.rs b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs index 56593578df1..68af608213f 100644 --- a/rust/main/utils/run-locally/src/invariants/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs @@ -22,6 +22,7 @@ pub struct RelayerTerminationInvariantParams<'a> { pub submitter_queue_length_expected: u32, pub non_matching_igp_message_count: u32, pub double_insertion_message_count: u32, + pub sealevel_tx_id_indexing: bool, } /// returns false if invariants are not met @@ -40,6 +41,7 @@ pub fn relayer_termination_invariants_met( submitter_queue_length_expected, non_matching_igp_message_count, double_insertion_message_count, + sealevel_tx_id_indexing, } = params; log!("Checking relayer termination invariants"); @@ -127,18 +129,27 @@ pub fn relayer_termination_invariants_met( let total_tx_id_log_count = *log_counts .get(&tx_id_indexing_line_filter) .expect("Failed to get tx id indexing log count"); + + // Sealevel relayer does not require tx id indexing. + // It performs sequenced indexing, that's why we don't expect any tx_id_logs + let expected_tx_id_logs = if sealevel_tx_id_indexing { + 0 + } else { + config.kathy_messages + }; + // there are 3 txid-indexed events: + // - relayer: merkle insertion and gas payment + // - scraper: gas payment + // some logs are emitted for multiple events, so requiring there to be at least + // `config.kathy_messages` logs is a reasonable approximation, since all three of these events + // are expected to be logged for each message. assert!( - // there are 3 txid-indexed events: - // - relayer: merkle insertion and gas payment - // - scraper: gas payment - // some logs are emitted for multiple events, so requiring there to be at least - // `config.kathy_messages` logs is a reasonable approximation, since all three of these events - // are expected to be logged for each message. - total_tx_id_log_count as u64 >= config.kathy_messages, + total_tx_id_log_count as u64 >= expected_tx_id_logs, "Didn't find as many tx id logs as expected. Found {} and expected {}", total_tx_id_log_count, - config.kathy_messages + expected_tx_id_logs ); + assert!( !log_counts.contains_key(&hyper_incoming_body_line_filter), "Verbose logs not expected at the log level set in e2e" diff --git a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs index 73318bf9750..583b9d76cac 100644 --- a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs @@ -51,6 +51,7 @@ pub fn termination_invariants_met( submitter_queue_length_expected: sol_messages_with_non_matching_igp, non_matching_igp_message_count: 0, double_insertion_message_count: sol_messages_with_non_matching_igp, + sealevel_tx_id_indexing: true, }; if !relayer_termination_invariants_met(relayer_invariant_params.clone())? { log!("Relayer termination invariants not met"); @@ -242,6 +243,7 @@ mod tests { submitter_queue_length_expected: 0, non_matching_igp_message_count: 0, double_insertion_message_count: 0, + sealevel_tx_id_indexing: true, }; assert_eq!( super::submitter_metrics_invariants_met( From 01bb4e2fa17fa8366b3b06901fbce8169856d2d5 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:28:15 +0100 Subject: [PATCH 107/223] fix: foundry error messages after foundry@1.1.0 upgrade (#6090) ### Description We change the ERC20 errors messages coming back from foundry tests. The difference is caused by foundry@1.1.0 release. ### Backward compatibility No, as this only works with foundry@1.1.0. --- .../src/providers/SmartProvider/SmartProvider.foundry-test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/sdk/src/providers/SmartProvider/SmartProvider.foundry-test.ts b/typescript/sdk/src/providers/SmartProvider/SmartProvider.foundry-test.ts index d0470de815d..86a59f4fa60 100644 --- a/typescript/sdk/src/providers/SmartProvider/SmartProvider.foundry-test.ts +++ b/typescript/sdk/src/providers/SmartProvider/SmartProvider.foundry-test.ts @@ -157,7 +157,7 @@ describe('SmartProvider', async () => { await token.transfer(constants.AddressZero, 1000000); } catch (e: any) { expect(e.error.message).to.equal( - 'execution reverted: revert: ERC20: transfer to the zero address', + 'execution reverted: ERC20: transfer to the zero address', ); } }); @@ -174,7 +174,7 @@ describe('SmartProvider', async () => { await token.transfer(signer.address, 1000000); } catch (e: any) { expect(e.error.message).to.equal( - 'execution reverted: revert: ERC20: transfer amount exceeds balance', + 'execution reverted: ERC20: transfer amount exceeds balance', ); } }); From b9e474f562716862388cc07140712cd236e2a0c0 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:27:37 +0100 Subject: [PATCH 108/223] chore: update to JSON import attributes syntax (#6045) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description This PR updates all JSON imports in the codebase from the older import assertions syntax to the new import attributes syntax, as recommended by Node.js and V8. Using the updated syntax prevents deprecation warnings when running scripts. For reference, see [V8: Import Attributes](https://v8.dev/features/import-attributes) ### Backward compatibility The change is compatible with Node starting with v20.10. As v20 is our current version, it would be good but we need to check our CI and Docker configurations are all upgraded to Node v20. There are no functional changes; only import syntax has been updated. ### Testing - Verify CI test tasks pass - Test an infra script like `check-warp-deploy` in a Docker image --- solidity/test/merkle.test.ts | 9 ++++++--- solidity/test/message.test.ts | 6 +++--- typescript/infra/config/environments/mainnet3/funding.ts | 2 +- typescript/infra/scripts/agents/update-agent-config.ts | 4 ++-- typescript/infra/scripts/print-gas-prices.ts | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/solidity/test/merkle.test.ts b/solidity/test/merkle.test.ts index 5546ac86bf6..317f6b38f92 100644 --- a/solidity/test/merkle.test.ts +++ b/solidity/test/merkle.test.ts @@ -1,10 +1,13 @@ import { expect } from 'chai'; import { utils } from 'ethers'; -import merkleTestCases from '../../vectors/merkle.json' assert { type: 'json' }; -import { TestMerkle, TestMerkle__factory } from '../core-utils/typechain'; +import merkleTestCases from '../../vectors/merkle.json' with { type: 'json' }; +import { + TestMerkle, + TestMerkle__factory, +} from '../core-utils/typechain/index.js'; -import { getSigner } from './signer'; +import { getSigner } from './signer.js'; describe('Merkle', async () => { for (const testCase of merkleTestCases) { diff --git a/solidity/test/message.test.ts b/solidity/test/message.test.ts index 996bb31e7b7..e11ee1421d1 100644 --- a/solidity/test/message.test.ts +++ b/solidity/test/message.test.ts @@ -7,14 +7,14 @@ import { messageId, } from '@hyperlane-xyz/utils'; -import testCases from '../../vectors/message.json' assert { type: 'json' }; +import testCases from '../../vectors/message.json' with { type: 'json' }; import { Mailbox__factory, TestMessage, TestMessage__factory, -} from '../core-utils/typechain'; +} from '../core-utils/typechain/index.js'; -import { getSigner, getSigners } from './signer'; +import { getSigner, getSigners } from './signer.js'; const remoteDomain = 1000; const localDomain = 2000; diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index d89c7e3c8d5..dbaafcd17db 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -2,7 +2,7 @@ import { KeyFunderConfig } from '../../../src/config/funding.js'; import { Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; -import desiredRelayerBalances from './balances/desiredRelayerBalances.json' assert { type: 'json' }; +import desiredRelayerBalances from './balances/desiredRelayerBalances.json' with { type: 'json' }; import { environment } from './chains.js'; import { mainnet3SupportedChainNames } from './supportedChainNames.js'; diff --git a/typescript/infra/scripts/agents/update-agent-config.ts b/typescript/infra/scripts/agents/update-agent-config.ts index 2d991881e7b..d6dc57feef3 100644 --- a/typescript/infra/scripts/agents/update-agent-config.ts +++ b/typescript/infra/scripts/agents/update-agent-config.ts @@ -25,8 +25,8 @@ import { } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; -import mainnet3GasPrices from '../../config/environments/mainnet3/gasPrices.json' assert { type: 'json' }; -import testnet4GasPrices from '../../config/environments/testnet4/gasPrices.json' assert { type: 'json' }; +import mainnet3GasPrices from '../../config/environments/mainnet3/gasPrices.json' with { type: 'json' }; +import testnet4GasPrices from '../../config/environments/testnet4/gasPrices.json' with { type: 'json' }; import { getCombinedChainsToScrape } from '../../src/config/agent/scraper.js'; import { DeployEnvironment, diff --git a/typescript/infra/scripts/print-gas-prices.ts b/typescript/infra/scripts/print-gas-prices.ts index 0c63b3b1929..3d80097d981 100644 --- a/typescript/infra/scripts/print-gas-prices.ts +++ b/typescript/infra/scripts/print-gas-prices.ts @@ -12,10 +12,10 @@ import { ProtocolType } from '@hyperlane-xyz/utils'; // Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')` // to avoid circular dependencies. import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js'; -import mainnet3GasPrices from '../config/environments/mainnet3/gasPrices.json' assert { type: 'json' }; +import mainnet3GasPrices from '../config/environments/mainnet3/gasPrices.json' with { type: 'json' }; import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; import { getRegistry as getTestnet4Registry } from '../config/environments/testnet4/chains.js'; -import testnet4GasPrices from '../config/environments/testnet4/gasPrices.json' assert { type: 'json' }; +import testnet4GasPrices from '../config/environments/testnet4/gasPrices.json' with { type: 'json' }; import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/supportedChainNames.js'; import { getArgs } from './agent-utils.js'; From 0c8b90daf8731bbfca2b8f33aaab3c21dca17ae8 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:16:38 +0100 Subject: [PATCH 109/223] chore: update formatting after Prettier upgrade (#6065) ### Description This PR updates source code formatting after the recent Prettier upgrade. We also add more root-level Prettier ignores as we currently run it recursively from root. ### Related issues Fixes ENG-1388. ### Backward compatibility Yes ### Testing CI should catch any accidental breakage. --- .github/actions/install-cli/action.yml | 12 ++--- .../actions/yarn-build-with-cache/action.yml | 2 +- .github/dependabot.yml | 6 +-- .github/workflows/static-analysis.yml | 6 +-- .lintstagedrc | 4 +- .prettierignore | 13 ++++- .prettierrc | 5 +- .yarnrc.yml | 2 +- codecov.yml | 10 ++-- funding.json | 6 +-- solidity/.solhint.json | 4 +- solidity/.vscode/settings.json | 8 ++-- tsconfig.json | 13 +++-- typescript/.vscode/extensions.json | 5 +- typescript/.vscode/settings.json | 8 ++-- typescript/ccip-server/eslint.config.mjs | 4 +- .../ccip-server/src/services/ProofsService.ts | 5 +- typescript/cli/src/avs/check.ts | 9 ++-- .../cli/src/tests/warp/warp-bridge-utils.ts | 5 +- .../dry-run/chains/alfajores/metadata.yaml | 2 +- .../dry-run/chains/anvil/metadata.yaml | 2 +- .../dry-run/chains/fuji/metadata.yaml | 2 +- .../fork/chains/anvil1/metadata.yaml | 2 +- .../fork/chains/ethereum/metadata.yaml | 2 +- typescript/cli/tsconfig.json | 9 +++- typescript/helloworld/.prettierrc | 5 +- typescript/helloworld/.solhint.json | 2 +- typescript/helloworld/eslint.config.mjs | 4 +- typescript/helloworld/tsconfig.json | 12 +++-- typescript/infra/scripts/check/check-utils.ts | 47 +++++++++--------- .../scripts/check/check-validator-rpcs.ts | 5 +- .../scripts/check/check-validator-version.ts | 5 +- .../funding/calculate-relayer-daily-burn.ts | 5 +- .../funding/fund-keys-from-deployer.ts | 16 ++++--- typescript/infra/scripts/funding/vanguard.ts | 27 ++++++----- .../infra/scripts/funding/write-alert.ts | 4 +- typescript/infra/scripts/print-balances.ts | 13 +++-- .../safes/governance/check-safe-signers.ts | 13 +++-- .../sealevel-helpers/print-gas-oracles.ts | 31 ++++++------ .../infra/scripts/validators/find-reorg.ts | 10 ++-- .../validators/print-latest-checkpoints.ts | 5 +- .../scripts/validators/verify-validators.ts | 5 +- .../scripts/warp-routes/monitor/status.ts | 5 +- typescript/infra/src/agents/aws/validator.ts | 10 ++-- typescript/infra/src/utils/safe.ts | 13 +++-- typescript/infra/supertoken.yaml | 35 +++++++------- typescript/infra/tsconfig.json | 2 +- typescript/sdk/src/aws/validator.ts | 10 ++-- .../sdk/src/block-explorer/etherscan.ts | 7 +-- .../core/adapters/CosmNativeCoreAdapter.ts | 5 +- .../core/testHyperlaneDeploy.hardhat-test.ts | 10 ++-- .../sdk/src/deploy/HyperlaneDeployer.ts | 10 ++-- typescript/sdk/src/deploy/verify/types.ts | 42 ++++++++-------- .../sdk/src/gas/HyperlaneIgpDeployer.ts | 5 +- typescript/sdk/src/gcp/validator.ts | 10 ++-- .../src/hook/EvmHookModule.hardhat-test.ts | 28 +++++------ typescript/sdk/src/hook/EvmHookReader.test.ts | 5 +- typescript/sdk/src/hook/EvmHookReader.ts | 5 +- .../sdk/src/hook/HyperlaneHookDeployer.ts | 5 +- .../sdk/src/ism/EvmIsmModule.hardhat-test.ts | 48 +++++++++---------- .../ism/metadata/arbL2ToL1.hardhat-test.ts | 18 +++---- typescript/sdk/src/ism/utils.ts | 10 ++-- .../liquidity-layer/LiquidityLayerApp.ts | 5 +- .../ethersV5/EV5GnosisSafeTxBuilder.ts | 4 +- .../ethersV5/EV5GnosisSafeTxSubmitter.ts | 5 +- .../src/router/adapters/EvmRouterAdapter.ts | 10 ++-- typescript/sdk/src/test/testUtils.ts | 4 +- .../sdk/src/token/EvmERC20WarpRouteReader.ts | 5 +- typescript/sdk/src/token/TokenAmount.ts | 5 +- .../sdk/src/token/adapters/EvmTokenAdapter.ts | 2 +- .../token/adapters/SealevelTokenAdapter.ts | 15 +++--- typescript/sdk/src/token/xerc20.ts | 25 +++++----- typescript/sdk/src/utils/gnosisSafe.js | 5 +- typescript/sdk/src/warp/WarpCore.ts | 5 +- typescript/sdk/src/zksync/ZKSyncDeployer.ts | 5 +- typescript/sdk/tsconfig.json | 2 +- ...no-restricted-imports-from-exports.test.ts | 26 +++++----- typescript/utils/src/objects.ts | 4 +- typescript/widgets/eslint.config.mjs | 8 ++-- typescript/widgets/postcss.config.cjs | 2 +- .../widgets/src/messages/useMessageStage.ts | 4 +- .../widgets/src/walletIntegrations/cosmos.ts | 2 +- typescript/widgets/tailwind.config.cjs | 6 +-- typescript/widgets/tsconfig.json | 2 +- 84 files changed, 401 insertions(+), 388 deletions(-) diff --git a/.github/actions/install-cli/action.yml b/.github/actions/install-cli/action.yml index c7a58c2b0ff..819bbae7ac8 100644 --- a/.github/actions/install-cli/action.yml +++ b/.github/actions/install-cli/action.yml @@ -1,17 +1,17 @@ -name: "Install Hyperlane CLI" -description: "Install the Hyperlane CLI by packaging from a local build" +name: 'Install Hyperlane CLI' +description: 'Install the Hyperlane CLI by packaging from a local build' inputs: ref: - description: "The Git ref to checkout" + description: 'The Git ref to checkout' required: true cache-provider: - description: "Choose cache provider: buildjet or github" + description: 'Choose cache provider: buildjet or github' required: false - default: "buildjet" + default: 'buildjet' runs: - using: "composite" + using: 'composite' steps: - name: yarn-build uses: ./.github/actions/yarn-build-with-cache diff --git a/.github/actions/yarn-build-with-cache/action.yml b/.github/actions/yarn-build-with-cache/action.yml index 77aabed082b..0fb6dd1e863 100644 --- a/.github/actions/yarn-build-with-cache/action.yml +++ b/.github/actions/yarn-build-with-cache/action.yml @@ -11,7 +11,7 @@ inputs: default: 'buildjet' runs: - using: "composite" + using: 'composite' steps: # Depot transparently redirect anything bound for GitHub's Actions cache to their own for extra speed. # But this is not available to access for non-depot runners. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e6e4f566cfe..16c9d7a914b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,8 +5,8 @@ version: 2 updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: 'npm' # See documentation for possible values + directory: '/' # Location of package manifests schedule: - interval: "daily" + interval: 'daily' open-pull-requests-limit: 0 # Only allow security update PRs diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index f45e83ec1a1..14ac496bf76 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -2,7 +2,7 @@ name: static-analysis on: push: - # Triggers the workflow on push to main + # Triggers the workflow on push to main branches: [main] paths: - 'solidity/**' @@ -56,7 +56,7 @@ jobs: uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.slither.outputs.sarif }} - category: "slither" + category: 'slither' - name: Olympix Integrated Security uses: olympix/integrated-security@main @@ -70,4 +70,4 @@ jobs: uses: github/codeql-action/upload-sarif@v3 with: sarif_file: olympix.sarif - category: "olympix" + category: 'olympix' diff --git a/.lintstagedrc b/.lintstagedrc index df50b8c4ebc..1edf348a217 100644 --- a/.lintstagedrc +++ b/.lintstagedrc @@ -1,6 +1,6 @@ { "*.js": "prettier --write", - "*.ts": "prettier --write", + "*.ts": "prettier --write", "*.md": "prettier --write", "*.sol": "prettier --write" -} \ No newline at end of file +} diff --git a/.prettierignore b/.prettierignore index 6f049111142..270b3c76b69 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,11 +8,20 @@ *.Dockerfile Dockerfile -typescript/*/dist/ +**/dist/ +.changeset/ +.yarn/ rust/ +solidity/artifacts/ +solidity/cache/ +solidity/core-utils/typechain/ +solidity/lib/ tools/ +typescript/cli/configs/ +typescript/helloworld/artifacts/ +typescript/helloworld/cache/ +typescript/helloworld/src/types/ typescript/infra/helm/**/templates/ typescript/infra/src/infrastructure/external-secrets/helm/templates/ vectors/ -.changeset/ diff --git a/.prettierrc b/.prettierrc index 9d7657f82a8..d3c7c17c5d8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -18,5 +18,8 @@ "importOrderSeparation": true, "importOrderSortSpecifiers": true, "importOrderParserPlugins": ["importAssertions", "typescript", "jsx"], - "plugins": ["prettier-plugin-solidity", "@trivago/prettier-plugin-sort-imports"] + "plugins": [ + "prettier-plugin-solidity", + "@trivago/prettier-plugin-sort-imports" + ] } diff --git a/.yarnrc.yml b/.yarnrc.yml index 951f7562aa5..683cec9d068 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -8,6 +8,6 @@ nodeLinker: node-modules plugins: - path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs - spec: "https://mskelton.dev/yarn-outdated/v3" + spec: 'https://mskelton.dev/yarn-outdated/v3' yarnPath: .yarn/releases/yarn-4.5.1.cjs diff --git a/codecov.yml b/codecov.yml index f748f30dc59..2d890dad7d4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,19 +2,19 @@ coverage: status: project: default: - target: auto # strictly increasing coverage + target: auto # strictly increasing coverage threshold: 3% # buffer for coverage drop comment: - layout: "header, diff, flags, components" # show component info in the PR comment - + layout: 'header, diff, flags, components' # show component info in the PR comment + component_management: - default_rules: # default rules that will be inherited by all components + default_rules: # default rules that will be inherited by all components statuses: - type: project # in this case every component that does not have a status defined will have a project type one target: auto branches: - - "!main" + - '!main' individual_components: - component_id: module_core name: core diff --git a/funding.json b/funding.json index 7eca8c5782c..ce2f103f7f0 100644 --- a/funding.json +++ b/funding.json @@ -1,5 +1,5 @@ { - "opRetro": { - "projectId": "0xa47182d330bd0c5c69b1418462f3f742099138f09bff057189cdd19676a6acd1" - } + "opRetro": { + "projectId": "0xa47182d330bd0c5c69b1418462f3f742099138f09bff057189cdd19676a6acd1" + } } diff --git a/solidity/.solhint.json b/solidity/.solhint.json index 177b098d2f3..ad83381ac39 100644 --- a/solidity/.solhint.json +++ b/solidity/.solhint.json @@ -2,14 +2,14 @@ "extends": "solhint:recommended", "rules": { "compiler-version": ["error", ">=0.6.11"], - "func-visibility": ["warn", {"ignoreConstructors":true}], + "func-visibility": ["warn", { "ignoreConstructors": true }], "not-rely-on-time": "off", "avoid-low-level-calls": "off", "no-inline-assembly": "off", "code-complexity": ["warn", 4], "var-name-mixedcase": "off", "func-name-mixedcase": "off", - "reason-string": ["warn",{"maxLength":64}], + "reason-string": ["warn", { "maxLength": 64 }], "prettier/prettier": "error", "gas-custom-errors": "off" }, diff --git a/solidity/.vscode/settings.json b/solidity/.vscode/settings.json index 36df1b15eea..6ba20e916c8 100644 --- a/solidity/.vscode/settings.json +++ b/solidity/.vscode/settings.json @@ -1,11 +1,11 @@ { "files.exclude": { "**/node_modules": true, - "**/*.js": {"when": "$(basename).ts"}, - "**/*.map": {"when": "$(basename).map"}, + "**/*.js": { "when": "$(basename).ts" }, + "**/*.map": { "when": "$(basename).map" }, "**/artifacts": true, "**/cache": true, "**/dist": true, - "**/types": true, - }, + "**/types": true + } } diff --git a/tsconfig.json b/tsconfig.json index 61731967d2b..041f5d7864d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,8 +6,15 @@ "forceConsistentCasingInFileNames": true, "incremental": false, "lib": [ - "ES2015", "ES2016", "ES2017", "ES2018", - "ES2019", "ES2020","ES2021", "ES2022", "DOM" + "ES2015", + "ES2016", + "ES2017", + "ES2018", + "ES2019", + "ES2020", + "ES2021", + "ES2022", + "DOM" ], "module": "nodenext", "moduleResolution": "nodenext", @@ -23,7 +30,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "target": "es2022", + "target": "es2022" }, "ts-node": { "experimentalSpecifierResolution": "node", diff --git a/typescript/.vscode/extensions.json b/typescript/.vscode/extensions.json index 325f1d3c5b3..4108e99003b 100644 --- a/typescript/.vscode/extensions.json +++ b/typescript/.vscode/extensions.json @@ -3,10 +3,7 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. - "recommendations": [ - "dbaeumer.vscode-eslint", - "tintinweb.vscode-ethover", - ], + "recommendations": ["dbaeumer.vscode-eslint", "tintinweb.vscode-ethover"], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] } diff --git a/typescript/.vscode/settings.json b/typescript/.vscode/settings.json index bae8aeaf068..fb446c2cd71 100644 --- a/typescript/.vscode/settings.json +++ b/typescript/.vscode/settings.json @@ -1,8 +1,8 @@ { "files.exclude": { "**/node_modules": true, - "**/*.js": {"when": "$(basename).ts"}, - "**/*.map": {"when": "$(basename).map"}, - "**/dist": true, - }, + "**/*.js": { "when": "$(basename).ts" }, + "**/*.map": { "when": "$(basename).map" }, + "**/dist": true + } } diff --git a/typescript/ccip-server/eslint.config.mjs b/typescript/ccip-server/eslint.config.mjs index 17cd27a740d..aae6eb5cfe2 100644 --- a/typescript/ccip-server/eslint.config.mjs +++ b/typescript/ccip-server/eslint.config.mjs @@ -12,6 +12,6 @@ export default [ }, }, { - ignores: ['**/__mocks__/*','**/tests/*',] - } + ignores: ['**/__mocks__/*', '**/tests/*'], + }, ]; diff --git a/typescript/ccip-server/src/services/ProofsService.ts b/typescript/ccip-server/src/services/ProofsService.ts index 5db40c56486..6c341ae1eb5 100644 --- a/typescript/ccip-server/src/services/ProofsService.ts +++ b/typescript/ccip-server/src/services/ProofsService.ts @@ -94,9 +94,8 @@ class ProofsService { * @returns the proofId */ async requestProofFromSuccinct(messageId: string) { - const { timestamp } = await this.hyperlaneService.getOriginBlockByMessageId( - messageId, - ); + const { timestamp } = + await this.hyperlaneService.getOriginBlockByMessageId(messageId); const slot = await this.lightClientService.calculateSlot(BigInt(timestamp)); const syncCommitteePoseidon = ''; // TODO get from LC return this.lightClientService.requestProof(syncCommitteePoseidon, slot); diff --git a/typescript/cli/src/avs/check.ts b/typescript/cli/src/avs/check.ts index b2af3f7b73d..7c65e7e2caf 100644 --- a/typescript/cli/src/avs/check.ts +++ b/typescript/cli/src/avs/check.ts @@ -116,9 +116,8 @@ const getAvsOperators = async ( if (operatorKey) { // If operator key is provided, only fetch the operator's validator info - const signingKey = await ecdsaStakeRegistry.getLastestOperatorSigningKey( - operatorKey, - ); + const signingKey = + await ecdsaStakeRegistry.getLastestOperatorSigningKey(operatorKey); avsOperators[signingKey] = { operatorAddress: operatorKey, chains: {}, @@ -434,9 +433,7 @@ const getEcdsaStakeRegistryAddress = ( try { return avsAddresses[chain]['ecdsaStakeRegistry']; } catch { - topLevelErrors.push( - `❗️ EcdsaStakeRegistry address not found for ${chain}`, - ); + topLevelErrors.push(`❗️ EcdsaStakeRegistry address not found for ${chain}`); return undefined; } }; diff --git a/typescript/cli/src/tests/warp/warp-bridge-utils.ts b/typescript/cli/src/tests/warp/warp-bridge-utils.ts index 8ac62e8790b..c636865759a 100644 --- a/typescript/cli/src/tests/warp/warp-bridge-utils.ts +++ b/typescript/cli/src/tests/warp/warp-bridge-utils.ts @@ -250,9 +250,8 @@ export async function collateralizeWarpTokens( !warpDeployConfig[chainName].type.match(/.*synthetic/i) && warpDeployConfig[chainName].type.match(/.*collateral/i) ) { - const decimals = await walletAndCollateralByChain[ - chainName - ].collateral.decimals(); + const decimals = + await walletAndCollateralByChain[chainName].collateral.decimals(); const tx = await walletAndCollateralByChain[ chainName ].collateral.transfer( diff --git a/typescript/cli/test-configs/dry-run/chains/alfajores/metadata.yaml b/typescript/cli/test-configs/dry-run/chains/alfajores/metadata.yaml index a66f02c82fb..edcd99183a1 100644 --- a/typescript/cli/test-configs/dry-run/chains/alfajores/metadata.yaml +++ b/typescript/cli/test-configs/dry-run/chains/alfajores/metadata.yaml @@ -10,4 +10,4 @@ rpcUrls: - http: https://alfajores-forno.celo-testnet.org blocks: confirmations: 1 - estimateBlockTime: 1 \ No newline at end of file + estimateBlockTime: 1 diff --git a/typescript/cli/test-configs/dry-run/chains/anvil/metadata.yaml b/typescript/cli/test-configs/dry-run/chains/anvil/metadata.yaml index 2836513cc62..655cb549017 100644 --- a/typescript/cli/test-configs/dry-run/chains/anvil/metadata.yaml +++ b/typescript/cli/test-configs/dry-run/chains/anvil/metadata.yaml @@ -7,4 +7,4 @@ rpcUrls: nativeToken: name: Ether symbol: ETH - decimals: 18 \ No newline at end of file + decimals: 18 diff --git a/typescript/cli/test-configs/dry-run/chains/fuji/metadata.yaml b/typescript/cli/test-configs/dry-run/chains/fuji/metadata.yaml index c19460ae28d..d2a4dbbc969 100644 --- a/typescript/cli/test-configs/dry-run/chains/fuji/metadata.yaml +++ b/typescript/cli/test-configs/dry-run/chains/fuji/metadata.yaml @@ -3,4 +3,4 @@ domainId: 43113 name: fuji protocol: ethereum rpcUrls: - - http: https://api.avax-test.network/ext/bc/C/rpc \ No newline at end of file + - http: https://api.avax-test.network/ext/bc/C/rpc diff --git a/typescript/cli/test-configs/fork/chains/anvil1/metadata.yaml b/typescript/cli/test-configs/fork/chains/anvil1/metadata.yaml index 197c0b783fe..01444182a6e 100644 --- a/typescript/cli/test-configs/fork/chains/anvil1/metadata.yaml +++ b/typescript/cli/test-configs/fork/chains/anvil1/metadata.yaml @@ -8,4 +8,4 @@ rpcUrls: nativeToken: name: Ether symbol: ETH - decimals: 18 \ No newline at end of file + decimals: 18 diff --git a/typescript/cli/test-configs/fork/chains/ethereum/metadata.yaml b/typescript/cli/test-configs/fork/chains/ethereum/metadata.yaml index d7155bfc03f..6035cc9ea72 100644 --- a/typescript/cli/test-configs/fork/chains/ethereum/metadata.yaml +++ b/typescript/cli/test-configs/fork/chains/ethereum/metadata.yaml @@ -6,4 +6,4 @@ rpcUrls: - http: http://127.0.0.1:8555 blocks: confirmations: 1 - estimateBlockTime: 1 \ No newline at end of file + estimateBlockTime: 1 diff --git a/typescript/cli/tsconfig.json b/typescript/cli/tsconfig.json index 77ae051fee6..15c45f83c92 100644 --- a/typescript/cli/tsconfig.json +++ b/typescript/cli/tsconfig.json @@ -4,5 +4,12 @@ "outDir": "./dist/", "rootDir": "." }, - "include": ["./cli.ts", "src/logger.ts", "env.ts", "./src/**/*.ts", "./src/*.d.ts", "./examples/**/*.ts",], + "include": [ + "./cli.ts", + "src/logger.ts", + "env.ts", + "./src/**/*.ts", + "./src/*.d.ts", + "./examples/**/*.ts" + ] } diff --git a/typescript/helloworld/.prettierrc b/typescript/helloworld/.prettierrc index fda694a678a..d7662e1351d 100644 --- a/typescript/helloworld/.prettierrc +++ b/typescript/helloworld/.prettierrc @@ -17,5 +17,8 @@ "importOrder": ["^@hyperlane-xyz/(.*)$", "^../(.*)$", "^./(.*)$"], "importOrderSeparation": true, "importOrderSortSpecifiers": true, - "plugins": ["prettier-plugin-solidity", "@trivago/prettier-plugin-sort-imports"] + "plugins": [ + "prettier-plugin-solidity", + "@trivago/prettier-plugin-sort-imports" + ] } diff --git a/typescript/helloworld/.solhint.json b/typescript/helloworld/.solhint.json index 0a512e65b03..1c3979b992c 100644 --- a/typescript/helloworld/.solhint.json +++ b/typescript/helloworld/.solhint.json @@ -2,7 +2,7 @@ "extends": "solhint:recommended", "rules": { "compiler-version": ["error", ">=0.6.0"], - "func-visibility": ["warn", {"ignoreConstructors":true}], + "func-visibility": ["warn", { "ignoreConstructors": true }], "not-rely-on-time": "off" } } diff --git a/typescript/helloworld/eslint.config.mjs b/typescript/helloworld/eslint.config.mjs index f88d2081577..38723aa1e5a 100644 --- a/typescript/helloworld/eslint.config.mjs +++ b/typescript/helloworld/eslint.config.mjs @@ -6,7 +6,7 @@ export default [ files: ['./src/**/*.ts'], }, { - ignores: ["**/src/types/*"], + ignores: ['**/src/types/*'], }, { ignores: ['./src/scripts'], @@ -14,4 +14,4 @@ export default [ 'no-console': ['off'], }, }, -]; \ No newline at end of file +]; diff --git a/typescript/helloworld/tsconfig.json b/typescript/helloworld/tsconfig.json index 0d5e2b5c695..c52ba2db69d 100644 --- a/typescript/helloworld/tsconfig.json +++ b/typescript/helloworld/tsconfig.json @@ -6,8 +6,14 @@ "forceConsistentCasingInFileNames": true, "incremental": false, "lib": [ - "ES2015", "ES2016", "ES2017", "ES2018", - "ES2019", "ES2020","ES2021", "DOM" + "ES2015", + "ES2016", + "ES2017", + "ES2018", + "ES2019", + "ES2020", + "ES2021", + "DOM" ], "module": "nodenext", "moduleResolution": "nodenext", @@ -25,7 +31,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "target": "es2022", + "target": "es2022" }, "exclude": ["./node_modules/", "./dist/", "./src/types/hardhat.d.ts"], "include": ["./src/"], diff --git a/typescript/infra/scripts/check/check-utils.ts b/typescript/infra/scripts/check/check-utils.ts index 85691679cbd..ac966f7dca4 100644 --- a/typescript/infra/scripts/check/check-utils.ts +++ b/typescript/infra/scripts/check/check-utils.ts @@ -208,29 +208,32 @@ export async function getGovernor( const filteredAddresses = Object.keys(warpAddresses) // filter out changes not in config .filter((key) => key in config) - .reduce((obj, key) => { - obj[key] = { - ...warpAddresses[key], - }; + .reduce( + (obj, key) => { + obj[key] = { + ...warpAddresses[key], + }; - // Use the specified proxyAdmin if it is set in the config - let proxyAdmin = config[key].proxyAdmin?.address; - // If the owner in the config is an AW account and there is no proxyAdmin in the config, - // set the proxyAdmin to the AW singleton proxyAdmin. - // This will ensure that the checker will check that any proxies are owned by the singleton proxyAdmin. - if ( - !proxyAdmin && - eqAddress(config[key].owner, envConfig.owners[key]?.owner) - ) { - proxyAdmin = chainAddresses[key]?.proxyAdmin; - } + // Use the specified proxyAdmin if it is set in the config + let proxyAdmin = config[key].proxyAdmin?.address; + // If the owner in the config is an AW account and there is no proxyAdmin in the config, + // set the proxyAdmin to the AW singleton proxyAdmin. + // This will ensure that the checker will check that any proxies are owned by the singleton proxyAdmin. + if ( + !proxyAdmin && + eqAddress(config[key].owner, envConfig.owners[key]?.owner) + ) { + proxyAdmin = chainAddresses[key]?.proxyAdmin; + } - if (proxyAdmin) { - obj[key].proxyAdmin = proxyAdmin; - } + if (proxyAdmin) { + obj[key].proxyAdmin = proxyAdmin; + } - return obj; - }, {} as typeof warpAddresses); + return obj; + }, + {} as typeof warpAddresses, + ); const { contractsMap, foreignDeployments } = attachContractsMapAndGetForeignDeployments( @@ -243,8 +246,8 @@ export async function getGovernor( const nonEvmChains = chains ? chains.filter((c) => foreignDeployments[c]) : fork && foreignDeployments[fork] - ? [fork] - : []; + ? [fork] + : []; if (nonEvmChains.length > 0) { const chainList = nonEvmChains.join(', '); diff --git a/typescript/infra/scripts/check/check-validator-rpcs.ts b/typescript/infra/scripts/check/check-validator-rpcs.ts index b9a96e0c8f9..b580401da74 100644 --- a/typescript/infra/scripts/check/check-validator-rpcs.ts +++ b/typescript/infra/scripts/check/check-validator-rpcs.ts @@ -70,9 +70,8 @@ async function main() { const location = storageLocations[i][storageLocations[i].length - 1]; try { - const validatorInstance = await getValidatorFromStorageLocation( - location, - ); + const validatorInstance = + await getValidatorFromStorageLocation(location); const metadata = await validatorInstance.getMetadata(); const matchCount = publicRpcs.filter((rpc) => diff --git a/typescript/infra/scripts/check/check-validator-version.ts b/typescript/infra/scripts/check/check-validator-version.ts index f87f1d32fd9..78dead97f4a 100644 --- a/typescript/infra/scripts/check/check-validator-version.ts +++ b/typescript/infra/scripts/check/check-validator-version.ts @@ -149,9 +149,8 @@ async function main() { // Get metadata from each storage location try { - const validatorInstance = await getValidatorFromStorageLocation( - location, - ); + const validatorInstance = + await getValidatorFromStorageLocation(location); const metadata = await validatorInstance.getMetadata(); const gitSha = metadata?.git_sha; diff --git a/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts b/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts index 625d0d02d49..af32de95330 100644 --- a/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts +++ b/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts @@ -287,9 +287,8 @@ async function calculateDailyRelayerBurn( if (lowProposedDailyBurn.length > 0) { console.table(lowProposedDailyBurn); - const userAdjustments = await handleLowProposedDailyBurn( - lowProposedDailyBurn, - ); + const userAdjustments = + await handleLowProposedDailyBurn(lowProposedDailyBurn); updatedBurnData = { ...updatedBurnData, ...userAdjustments }; } diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index 03537689af6..62956777a2f 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -341,10 +341,13 @@ class ContextFunder { // Indexed by the identifier for quicker lookup const idsAndAddresses: Record = - allIdsAndAddresses.reduce((agg, idAndAddress) => { - agg[idAndAddress.identifier] = idAndAddress; - return agg; - }, {} as Record); + allIdsAndAddresses.reduce( + (agg, idAndAddress) => { + agg[idAndAddress.identifier] = idAndAddress; + return agg; + }, + {} as Record, + ); const agentConfig = getAgentConfig(context, environment); // Unfetched keys per chain and role, so we know which keys @@ -868,9 +871,8 @@ class ContextFunder { L1MessageQueue.abi, l1ChainSigner, ); - const gasQuote = await l1MessageQueue.estimateCrossDomainMessageFee( - l2GasLimit, - ); + const gasQuote = + await l1MessageQueue.estimateCrossDomainMessageFee(l2GasLimit); const totalAmount = amount.add(gasQuote); return l1EthGateway['depositETH(address,uint256,uint256)']( to, diff --git a/typescript/infra/scripts/funding/vanguard.ts b/typescript/infra/scripts/funding/vanguard.ts index 0f8a65484c9..90e55bdede3 100644 --- a/typescript/infra/scripts/funding/vanguard.ts +++ b/typescript/infra/scripts/funding/vanguard.ts @@ -172,18 +172,21 @@ async function fundVanguards() { rootLogger.info('\nTop ups needed for the following:'); // eslint-disable-next-line no-console console.table( - Object.entries(topUpsNeeded).reduce((acc, [chain, topUps]) => { - const chainEntries: Record = {} as Record< - VanguardName, - string - >; - VANGUARDS.forEach((vanguard) => { - const match = topUps.find((t) => t.vanguard === vanguard); - chainEntries[vanguard] = match ? match.balance : '-'; - }); - acc[chain] = chainEntries; - return acc; - }, {} as Record>), + Object.entries(topUpsNeeded).reduce( + (acc, [chain, topUps]) => { + const chainEntries: Record = {} as Record< + VanguardName, + string + >; + VANGUARDS.forEach((vanguard) => { + const match = topUps.find((t) => t.vanguard === vanguard); + chainEntries[vanguard] = match ? match.balance : '-'; + }); + acc[chain] = chainEntries; + return acc; + }, + {} as Record>, + ), ); if (fund) { diff --git a/typescript/infra/scripts/funding/write-alert.ts b/typescript/infra/scripts/funding/write-alert.ts index cc09ae64f6a..d934ab3d282 100644 --- a/typescript/infra/scripts/funding/write-alert.ts +++ b/typescript/infra/scripts/funding/write-alert.ts @@ -244,8 +244,8 @@ function generateDiffTable( currentThreshold === undefined ? 'new' : currentThreshold < newThreshold - ? 'increase' - : 'decrease', + ? 'increase' + : 'decrease', }); } } diff --git a/typescript/infra/scripts/print-balances.ts b/typescript/infra/scripts/print-balances.ts index a8a36ec07b6..89dd35e4c27 100644 --- a/typescript/infra/scripts/print-balances.ts +++ b/typescript/infra/scripts/print-balances.ts @@ -104,11 +104,14 @@ async function main() { }), ); - const formattedBalances = balancesObject.reduce((acc, chainData) => { - const { chain, symbol, ...roleBalances } = chainData; - acc[chain] = { symbol, ...roleBalances }; - return acc; - }, {} as Record); + const formattedBalances = balancesObject.reduce( + (acc, chainData) => { + const { chain, symbol, ...roleBalances } = chainData; + acc[chain] = { symbol, ...roleBalances }; + return acc; + }, + {} as Record, + ); console.table(formattedBalances); process.exit(0); diff --git a/typescript/infra/scripts/safes/governance/check-safe-signers.ts b/typescript/infra/scripts/safes/governance/check-safe-signers.ts index d0fdb2630cb..4ca03f85dc6 100644 --- a/typescript/infra/scripts/safes/governance/check-safe-signers.ts +++ b/typescript/infra/scripts/safes/governance/check-safe-signers.ts @@ -112,11 +112,14 @@ async function main() { // Group other violations by chain const violationsByChain = violations .filter((v) => v.type !== SafeConfigViolationType.thresholdMismatch) - .reduce((acc, v) => { - if (!acc[v.chain]) acc[v.chain] = []; - acc[v.chain].push(v); - return acc; - }, {} as Record); + .reduce( + (acc, v) => { + if (!acc[v.chain]) acc[v.chain] = []; + acc[v.chain].push(v); + return acc; + }, + {} as Record, + ); // Display chain-specific violations as bulleted lists for (const [chain, chainViolations] of Object.entries(violationsByChain)) { diff --git a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts index 6c8468d2b43..8128f556e37 100644 --- a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts +++ b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts @@ -119,21 +119,24 @@ function getChainConnections( throw new Error(`Unknown environment: ${environment}`); } - return connectedChains.reduce((agg, chains) => { - // Make sure each chain is connected to every other chain - chains.forEach((chainA) => { - chains.forEach((chainB) => { - if (chainA === chainB) { - return; - } - if (agg[chainA] === undefined) { - agg[chainA] = new Set(); - } - agg[chainA].add(chainB as ChainName); + return connectedChains.reduce( + (agg, chains) => { + // Make sure each chain is connected to every other chain + chains.forEach((chainA) => { + chains.forEach((chainB) => { + if (chainA === chainB) { + return; + } + if (agg[chainA] === undefined) { + agg[chainA] = new Set(); + } + agg[chainA].add(chainB as ChainName); + }); }); - }); - return agg; - }, {} as ChainMap>); + return agg; + }, + {} as ChainMap>, + ); } main().catch((err) => { diff --git a/typescript/infra/scripts/validators/find-reorg.ts b/typescript/infra/scripts/validators/find-reorg.ts index 85fccc04a8c..332dae9a83d 100644 --- a/typescript/infra/scripts/validators/find-reorg.ts +++ b/typescript/infra/scripts/validators/find-reorg.ts @@ -26,9 +26,8 @@ async function main() { .demandOption('validator') .alias('v', 'validator').argv; - const { core, multiProvider, chainAddresses } = await getHyperlaneCore( - environment, - ); + const { core, multiProvider, chainAddresses } = + await getHyperlaneCore(environment); const provider = multiProvider.getProvider(chain); const validatorAnnounce = core.getContracts(chain).validatorAnnounce; @@ -41,9 +40,8 @@ async function main() { [validator], ); const storageLocation = storageLocations[0][0]; - const validatorInstance = await getValidatorFromStorageLocation( - storageLocation, - ); + const validatorInstance = + await getValidatorFromStorageLocation(storageLocation); const latestCheckpointIndex = await validatorInstance.getLatestCheckpointIndex(); diff --git a/typescript/infra/scripts/validators/print-latest-checkpoints.ts b/typescript/infra/scripts/validators/print-latest-checkpoints.ts index 3368f3bde87..e9d06f1f38a 100644 --- a/typescript/infra/scripts/validators/print-latest-checkpoints.ts +++ b/typescript/infra/scripts/validators/print-latest-checkpoints.ts @@ -97,9 +97,8 @@ async function main() { // Get metadata from each storage location try { - const validatorInstance = await getValidatorFromStorageLocation( - location, - ); + const validatorInstance = + await getValidatorFromStorageLocation(location); const latestCheckpoint = await validatorInstance.getLatestCheckpointIndex(); diff --git a/typescript/infra/scripts/validators/verify-validators.ts b/typescript/infra/scripts/validators/verify-validators.ts index 16fce980c2c..6c67e04e2d0 100644 --- a/typescript/infra/scripts/validators/verify-validators.ts +++ b/typescript/infra/scripts/validators/verify-validators.ts @@ -38,9 +38,8 @@ async function main() { const address = prospectiveValidator.address; const bucket = prospectiveValidator.s3Bucket; try { - const metrics = await prospectiveValidator.compare( - controlValidator, - ); + const metrics = + await prospectiveValidator.compare(controlValidator); console.log( `${chain} ${bucket} validators against control ${controlValidator.s3Bucket}`, ); diff --git a/typescript/infra/scripts/warp-routes/monitor/status.ts b/typescript/infra/scripts/warp-routes/monitor/status.ts index a6908a1fe09..a306f834711 100644 --- a/typescript/infra/scripts/warp-routes/monitor/status.ts +++ b/typescript/infra/scripts/warp-routes/monitor/status.ts @@ -23,9 +23,8 @@ const LOG_AMOUNT = 5; async function main() { configureRootLogger(LogFormat.Pretty, LogLevel.Info); - const { environment, warpRouteId } = await withWarpRouteIdRequired( - getArgs(), - ).parse(); + const { environment, warpRouteId } = + await withWarpRouteIdRequired(getArgs()).parse(); const config = getEnvironmentConfig(environment); await assertCorrectKubeContext(config); diff --git a/typescript/infra/src/agents/aws/validator.ts b/typescript/infra/src/agents/aws/validator.ts index 398190a9c11..92a01ef8a48 100644 --- a/typescript/infra/src/agents/aws/validator.ts +++ b/typescript/infra/src/agents/aws/validator.ts @@ -49,12 +49,10 @@ export class InfraS3Validator extends S3Validator { other: InfraS3Validator, count = 5, ): Promise { - const latestCheckpointIndex = await this.s3Bucket.getS3Obj( - LATEST_KEY, - ); - const otherLatestCheckpointIndex = await other.s3Bucket.getS3Obj( - LATEST_KEY, - ); + const latestCheckpointIndex = + await this.s3Bucket.getS3Obj(LATEST_KEY); + const otherLatestCheckpointIndex = + await other.s3Bucket.getS3Obj(LATEST_KEY); if (!otherLatestCheckpointIndex || !latestCheckpointIndex) { throw new Error('Failed to get latest checkpoints'); diff --git a/typescript/infra/src/utils/safe.ts b/typescript/infra/src/utils/safe.ts index f3fb37a35e1..bb859cd65f8 100644 --- a/typescript/infra/src/utils/safe.ts +++ b/typescript/infra/src/utils/safe.ts @@ -385,9 +385,8 @@ export async function updateSafeOwner({ `Threshold change ${currentThreshold} => ${newThreshold}`, ), ); - const { data: thresholdTxData } = await safeSdk.createChangeThresholdTx( - newThreshold, - ); + const { data: thresholdTxData } = + await safeSdk.createChangeThresholdTx(newThreshold); transactions.push({ to: thresholdTxData.to, data: thresholdTxData.data, @@ -473,10 +472,10 @@ export async function getPendingTxsForChains( confs >= threshold ? SafeTxStatus.READY_TO_EXECUTE : confs === 0 - ? SafeTxStatus.NO_CONFIRMATIONS - : threshold - confs - ? SafeTxStatus.ONE_AWAY - : SafeTxStatus.PENDING; + ? SafeTxStatus.NO_CONFIRMATIONS + : threshold - confs + ? SafeTxStatus.ONE_AWAY + : SafeTxStatus.PENDING; txs.push({ chain, diff --git a/typescript/infra/supertoken.yaml b/typescript/infra/supertoken.yaml index e4f50c02971..51107ce6a7d 100644 --- a/typescript/infra/supertoken.yaml +++ b/typescript/infra/supertoken.yaml @@ -14,25 +14,25 @@ base: type: aggregationHook fallback: type: defaultHook - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' type: fallbackRoutingHook interchainSecurityModule: domains: celo: lowerIsm: domains: {} - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' type: defaultFallbackRoutingIsm threshold: 5 type: amountRoutingIsm upperIsm: originChain: celo type: ccipIsm - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' type: defaultFallbackRoutingIsm - mailbox: "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" - token: "0xbe963b68e5ddeeb8ebd156ed795ae560615b3c0c" + mailbox: '0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D' + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' + token: '0xbe963b68e5ddeeb8ebd156ed795ae560615b3c0c' type: xERC20 celo: hook: @@ -50,31 +50,30 @@ celo: type: aggregationHook fallback: type: defaultHook - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' type: fallbackRoutingHook interchainSecurityModule: domains: base: lowerIsm: domains: {} - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' type: defaultFallbackRoutingIsm threshold: 5 type: amountRoutingIsm upperIsm: originChain: base type: ccipIsm - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' type: defaultFallbackRoutingIsm - mailbox: "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb" - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" - token: "0x9f18a8f72aa28c8c89f1c4086ab491a60c383017" + mailbox: '0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb' + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' + token: '0x9f18a8f72aa28c8c89f1c4086ab491a60c383017' type: xERC20Lockbox optimism: - hook: "0x0000000000000000000000000000000000000000" - interchainSecurityModule: "0x0000000000000000000000000000000000000000" - mailbox: "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D" - owner: "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" - token: "0xbe963b68e5ddeeb8ebd156ed795ae560615b3c0c" + hook: '0x0000000000000000000000000000000000000000' + interchainSecurityModule: '0x0000000000000000000000000000000000000000' + mailbox: '0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D' + owner: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba' + token: '0xbe963b68e5ddeeb8ebd156ed795ae560615b3c0c' type: xERC20 - diff --git a/typescript/infra/tsconfig.json b/typescript/infra/tsconfig.json index 20cf059b11d..5faf1675820 100644 --- a/typescript/infra/tsconfig.json +++ b/typescript/infra/tsconfig.json @@ -7,7 +7,7 @@ "rootDir": "./", "noUnusedLocals": false, "module": "nodenext", - "moduleResolution": "nodenext", + "moduleResolution": "nodenext" }, "exclude": ["./node_modules/", "./dist/", "./tmp.ts"], "include": [ diff --git a/typescript/sdk/src/aws/validator.ts b/typescript/sdk/src/aws/validator.ts index 008584c3e55..58134d400ff 100644 --- a/typescript/sdk/src/aws/validator.ts +++ b/typescript/sdk/src/aws/validator.ts @@ -45,9 +45,8 @@ export class S3Validator extends BaseValidator { caching: true, }; const s3Bucket = new S3Wrapper(s3Config); - const announcement = await s3Bucket.getS3Obj( - ANNOUNCEMENT_KEY, - ); + const announcement = + await s3Bucket.getS3Obj(ANNOUNCEMENT_KEY); if (!announcement) { throw new Error('No announcement found'); } @@ -102,9 +101,8 @@ export class S3Validator extends BaseValidator { } async getLatestCheckpointIndex(): Promise { - const latestCheckpointIndex = await this.s3Bucket.getS3Obj( - LATEST_KEY, - ); + const latestCheckpointIndex = + await this.s3Bucket.getS3Obj(LATEST_KEY); if (!latestCheckpointIndex) return -1; diff --git a/typescript/sdk/src/block-explorer/etherscan.ts b/typescript/sdk/src/block-explorer/etherscan.ts index 957242bb12d..574805b7762 100644 --- a/typescript/sdk/src/block-explorer/etherscan.ts +++ b/typescript/sdk/src/block-explorer/etherscan.ts @@ -71,9 +71,10 @@ export async function tryGetContractDeploymentTransaction( const requestUrl = formatExplorerUrl(explorerOptions, options); const response = await fetch(requestUrl); - const [deploymentTx] = await handleEtherscanResponse< - Array - >(response); + const [deploymentTx] = + await handleEtherscanResponse< + Array + >(response); return deploymentTx; } diff --git a/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts b/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts index 2991764f143..0b8415d6644 100644 --- a/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts +++ b/typescript/sdk/src/core/adapters/CosmNativeCoreAdapter.ts @@ -68,9 +68,8 @@ export class CosmNativeCoreAdapter delayMs?: number, maxAttempts?: number, ): Promise { - const provider = await this.multiProvider.getCosmJsNativeProvider( - destination, - ); + const provider = + await this.multiProvider.getCosmJsNativeProvider(destination); await pollAsync( async () => { diff --git a/typescript/sdk/src/core/testHyperlaneDeploy.hardhat-test.ts b/typescript/sdk/src/core/testHyperlaneDeploy.hardhat-test.ts index 3ba603c15c1..515ff3a7c51 100644 --- a/typescript/sdk/src/core/testHyperlaneDeploy.hardhat-test.ts +++ b/typescript/sdk/src/core/testHyperlaneDeploy.hardhat-test.ts @@ -77,13 +77,11 @@ describe('TestCoreDeployer', async () => { }); it('processes outbound messages for two domains', async () => { - const localResponses = await testCoreApp.processOutboundMessages( - localChain, - ); + const localResponses = + await testCoreApp.processOutboundMessages(localChain); expect(localResponses.get(remoteChain)!.length).to.equal(1); - const remoteResponses = await testCoreApp.processOutboundMessages( - remoteChain, - ); + const remoteResponses = + await testCoreApp.processOutboundMessages(remoteChain); expect(remoteResponses.get(localChain)!.length).to.equal(1); }); diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index 6c95122aa2f..afd7a97b6c6 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -139,9 +139,8 @@ export abstract class HyperlaneDeployer< const failedChains: ChainName[] = []; const deployChain = async (chain: ChainName) => { - const signerUrl = await this.multiProvider.tryGetExplorerAddressUrl( - chain, - ); + const signerUrl = + await this.multiProvider.tryGetExplorerAddressUrl(chain); const signerAddress = await this.multiProvider.getSignerAddress(chain); const fromString = signerUrl || signerAddress; this.logger.info(`Deploying to ${chain} from ${fromString}`); @@ -782,9 +781,8 @@ export abstract class HyperlaneDeployer< this.logger.debug( `Transferring ownership of ${contractName} to ${owner} on ${chain}`, ); - const estimatedGas = await ownable.estimateGas.transferOwnership( - owner, - ); + const estimatedGas = + await ownable.estimateGas.transferOwnership(owner); return this.multiProvider.handleTx( chain, ownable.transferOwnership(owner, { diff --git a/typescript/sdk/src/deploy/verify/types.ts b/typescript/sdk/src/deploy/verify/types.ts index 3ef1dd9cd36..469b44463cd 100644 --- a/typescript/sdk/src/deploy/verify/types.ts +++ b/typescript/sdk/src/deploy/verify/types.ts @@ -84,24 +84,24 @@ export type FormOptions = address: string; } : Action extends ExplorerApiActions.VERIFY_IMPLEMENTATION - ? CompilerOptions & { - contractaddress: string; - sourceCode: string; - contractname: string; - /* TYPO IS ENFORCED BY API */ - constructorArguements?: string; - } - : Action extends ExplorerApiActions.VERIFY_PROXY - ? { - address: string; - expectedimplementation: string; - } - : Action extends ExplorerApiActions.CHECK_IMPLEMENTATION_STATUS - ? { - guid: string; - } - : Action extends ExplorerApiActions.CHECK_PROXY_STATUS - ? { - guid: string; - } - : never; + ? CompilerOptions & { + contractaddress: string; + sourceCode: string; + contractname: string; + /* TYPO IS ENFORCED BY API */ + constructorArguements?: string; + } + : Action extends ExplorerApiActions.VERIFY_PROXY + ? { + address: string; + expectedimplementation: string; + } + : Action extends ExplorerApiActions.CHECK_IMPLEMENTATION_STATUS + ? { + guid: string; + } + : Action extends ExplorerApiActions.CHECK_PROXY_STATUS + ? { + guid: string; + } + : never; diff --git a/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts b/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts index c889551d99b..0081bc520a4 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts @@ -88,9 +88,8 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer< if (gasParamsToSet.length > 0) { await this.runIfOwner(chain, igp, async () => { - const estimatedGas = await igp.estimateGas.setDestinationGasConfigs( - gasParamsToSet, - ); + const estimatedGas = + await igp.estimateGas.setDestinationGasConfigs(gasParamsToSet); return this.multiProvider.handleTx( chain, igp.setDestinationGasConfigs(gasParamsToSet, { diff --git a/typescript/sdk/src/gcp/validator.ts b/typescript/sdk/src/gcp/validator.ts index e335ca27060..f86efba25fb 100644 --- a/typescript/sdk/src/gcp/validator.ts +++ b/typescript/sdk/src/gcp/validator.ts @@ -54,9 +54,8 @@ export class GcpValidator extends BaseValidator { caching: true, }; const storage = new GcpStorageWrapper(storageConfig); - const announcement = await storage.getObject( - ANNOUNCEMENT_KEY, - ); + const announcement = + await storage.getObject(ANNOUNCEMENT_KEY); if (!announcement) { throw new Error('No announcement found'); } @@ -111,9 +110,8 @@ export class GcpValidator extends BaseValidator { } async getLatestCheckpointIndex(): Promise { - const latestCheckpointIndex = await this.storage.getObject( - LATEST_KEY, - ); + const latestCheckpointIndex = + await this.storage.getObject(LATEST_KEY); if (!latestCheckpointIndex) return -1; diff --git a/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts b/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts index ecb18a07df7..65b31eb3c9f 100644 --- a/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts +++ b/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts @@ -64,11 +64,14 @@ describe('EvmHookModule', async () => { // get addresses of factories for the chain factoryContracts = contractsMap[chain]; - proxyFactoryAddresses = Object.keys(factoryContracts).reduce((acc, key) => { - acc[key] = - contractsMap[chain][key as keyof ProxyFactoryFactories].address; - return acc; - }, {} as Record) as HyperlaneAddresses; + proxyFactoryAddresses = Object.keys(factoryContracts).reduce( + (acc, key) => { + acc[key] = + contractsMap[chain][key as keyof ProxyFactoryFactories].address; + return acc; + }, + {} as Record, + ) as HyperlaneAddresses; // legacy HyperlaneIsmFactory is required to do a core deploy const legacyIsmFactory = new HyperlaneIsmFactory( @@ -509,9 +512,8 @@ describe('EvmHookModule', async () => { it(`no changes to an existing ${type} means no redeployment or updates`, async () => { // create a new hook - const { hook, initialHookAddress } = await createHook( - exampleRoutingConfig, - ); + const { hook, initialHookAddress } = + await createHook(exampleRoutingConfig); // expect 0 updates await expectTxsAndUpdate(hook, exampleRoutingConfig, 0); @@ -534,9 +536,8 @@ describe('EvmHookModule', async () => { }; // create a new hook - const { hook, initialHookAddress } = await createHook( - exampleRoutingConfig, - ); + const { hook, initialHookAddress } = + await createHook(exampleRoutingConfig); // add a new domain exampleRoutingConfig.domains[TestChainName.test2] = { @@ -564,9 +565,8 @@ describe('EvmHookModule', async () => { }; // create a new hook - const { hook, initialHookAddress } = await createHook( - exampleRoutingConfig, - ); + const { hook, initialHookAddress } = + await createHook(exampleRoutingConfig); // add multiple new domains exampleRoutingConfig.domains[TestChainName.test2] = { diff --git a/typescript/sdk/src/hook/EvmHookReader.test.ts b/typescript/sdk/src/hook/EvmHookReader.test.ts index e0bf029e8a9..ad51171ce66 100644 --- a/typescript/sdk/src/hook/EvmHookReader.test.ts +++ b/typescript/sdk/src/hook/EvmHookReader.test.ts @@ -180,9 +180,8 @@ describe('EvmHookReader', () => { expect(hookConfig).to.deep.equal(expectedConfig); // should get same result if we call the specific method for the hook type - const config = await evmHookReader.deriveMailboxDefaultHookConfig( - mockAddress, - ); + const config = + await evmHookReader.deriveMailboxDefaultHookConfig(mockAddress); expect(config).to.deep.equal(hookConfig); }); diff --git a/typescript/sdk/src/hook/EvmHookReader.ts b/typescript/sdk/src/hook/EvmHookReader.ts index 91f983c8e57..97b22edaa90 100644 --- a/typescript/sdk/src/hook/EvmHookReader.ts +++ b/typescript/sdk/src/hook/EvmHookReader.ts @@ -168,9 +168,8 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { derivedHookConfig = await this.deriveAmountRoutingHookConfig(address); break; case OnchainHookType.MAILBOX_DEFAULT_HOOK: - derivedHookConfig = await this.deriveMailboxDefaultHookConfig( - address, - ); + derivedHookConfig = + await this.deriveMailboxDefaultHookConfig(address); break; default: throw new Error( diff --git a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts index 9aa3bf15696..2afd02283d7 100644 --- a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts +++ b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts @@ -393,9 +393,8 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< }, 'Setting routing hooks', ); - const estimatedGas = await routingHook.estimateGas.setHooks( - routingConfigs, - ); + const estimatedGas = + await routingHook.estimateGas.setHooks(routingConfigs); return this.multiProvider.handleTx( chain, routingHook.setHooks(routingConfigs, { diff --git a/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts b/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts index 8dbd1a742df..73a183d8d77 100644 --- a/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts +++ b/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts @@ -51,11 +51,14 @@ describe('EvmIsmModule', async () => { // get addresses of factories for the chain factoryContracts = contractsMap[chain]; - factoryAddresses = Object.keys(factoryContracts).reduce((acc, key) => { - acc[key] = - contractsMap[chain][key as keyof ProxyFactoryFactories].address; - return acc; - }, {} as Record) as HyperlaneAddresses; + factoryAddresses = Object.keys(factoryContracts).reduce( + (acc, key) => { + acc[key] = + contractsMap[chain][key as keyof ProxyFactoryFactories].address; + return acc; + }, + {} as Record, + ) as HyperlaneAddresses; // legacy HyperlaneIsmFactory is required to do a core deploy const legacyIsmFactory = new HyperlaneIsmFactory( @@ -216,9 +219,8 @@ describe('EvmIsmModule', async () => { it(`update route in an existing ${type}`, async () => { // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // changing the type of a domain should enroll the domain ( @@ -235,9 +237,8 @@ describe('EvmIsmModule', async () => { it(`deletes route in an existing ${type}`, async () => { // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // deleting the domain should unenroll the domain delete exampleRoutingConfig.domains[TestChainName.test3]; @@ -278,9 +279,8 @@ describe('EvmIsmModule', async () => { it(`updates owner in an existing ${type}`, async () => { // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // change the config owner exampleRoutingConfig.owner = randomAddress(); @@ -295,9 +295,8 @@ describe('EvmIsmModule', async () => { it(`no changes to an existing ${type} means no redeployment or updates`, async () => { // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // expect 0 updates await expectTxsAndUpdate(ism, exampleRoutingConfig, 0); @@ -381,9 +380,8 @@ describe('EvmIsmModule', async () => { const originalOwner = exampleRoutingConfig.owner; // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // update the config owner and impersonate the original owner exampleRoutingConfig.owner = randomAddress(); @@ -399,9 +397,8 @@ describe('EvmIsmModule', async () => { it(`update validators in an existing ${type}`, async () => { // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // update the validators for a domain exampleRoutingConfig.domains[TestChainName.test2] = { @@ -420,9 +417,8 @@ describe('EvmIsmModule', async () => { it(`update threshold in an existing ${type}`, async () => { // create a new ISM - const { ism, initialIsmAddress } = await createIsm( - exampleRoutingConfig, - ); + const { ism, initialIsmAddress } = + await createIsm(exampleRoutingConfig); // update the threshold for a domain ( diff --git a/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts b/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts index e186da5c9a0..1728378e65a 100644 --- a/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts +++ b/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts @@ -120,11 +120,14 @@ describe('ArbL2ToL1MetadataBuilder', () => { }; factoryContracts = contractsMap.test4; - proxyFactoryAddresses = Object.keys(factoryContracts).reduce((acc, key) => { - acc[key] = - contractsMap[origin][key as keyof ProxyFactoryFactories].address; - return acc; - }, {} as Record) as HyperlaneAddresses; + proxyFactoryAddresses = Object.keys(factoryContracts).reduce( + (acc, key) => { + acc[key] = + contractsMap[origin][key as keyof ProxyFactoryFactories].address; + return acc; + }, + {} as Record, + ) as HyperlaneAddresses; arbBridge = await multiProvider.handleDeploy( origin, new MockArbBridge__factory(), @@ -234,9 +237,8 @@ describe('ArbL2ToL1MetadataBuilder', () => { it(`should build valid metadata if already preverified by 3rd party relayer`, async () => { setArbitrumBridgeStatus(ChildToParentMessageStatus.CONFIRMED); - const calldata = await metadataBuilder.buildArbitrumBridgeCalldata( - context, - ); + const calldata = + await metadataBuilder.buildArbitrumBridgeCalldata(context); metadata = ArbL2ToL1MetadataBuilder.encodeArbL2ToL1Metadata(calldata); await arbBridge.executeTransaction( calldata.proof, diff --git a/typescript/sdk/src/ism/utils.ts b/typescript/sdk/src/ism/utils.ts index b93db8858a8..7f1486a3654 100644 --- a/typescript/sdk/src/ism/utils.ts +++ b/typescript/sdk/src/ism/utils.ts @@ -128,9 +128,8 @@ export async function moduleCanCertainlyVerify( provider, ); - const [, threshold] = await multisigModule.validatorsAndThreshold( - message, - ); + const [, threshold] = + await multisigModule.validatorsAndThreshold(message); return threshold > 0; } else if (moduleType === ModuleType.ROUTING) { const routingIsm = IRoutingIsm__factory.connect(destModule, provider); @@ -341,9 +340,8 @@ export async function moduleMatchesConfig( moduleAddress, provider, ); - const [subModules, threshold] = await aggregationIsm.modulesAndThreshold( - '0x', - ); + const [subModules, threshold] = + await aggregationIsm.modulesAndThreshold('0x'); matches &&= threshold === config.threshold; matches &&= subModules.length === config.modules.length; diff --git a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts index 6e26989348c..9fbcc838f77 100644 --- a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts +++ b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts @@ -198,8 +198,9 @@ export class LiquidityLayerApp extends HyperlaneApp< async attemptPortalTransferCompletion( message: PortalBridgeMessage, ): Promise { - const destinationPortalAdapter = this.getContracts(message.destination) - .portalAdapter!; + const destinationPortalAdapter = this.getContracts( + message.destination, + ).portalAdapter!; const transferId = await destinationPortalAdapter.transferId( this.multiProvider.getDomainId(message.origin), diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts index bae3ed269a8..3d80a7b9355 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts @@ -63,9 +63,7 @@ export class EV5GnosisSafeTxBuilder extends EV5GnosisSafeTxSubmitter { const transactions: SafeTransactionData[] = await Promise.all( txs.map( async (tx: AnnotatedEV5Transaction) => - ( - await this.createSafeTransaction(tx) - ).data, + (await this.createSafeTransaction(tx)).data, ), ); return { diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts index 549bfa80865..19d0f135b94 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts @@ -101,9 +101,8 @@ export class EV5GnosisSafeTxSubmitter implements EV5TxSubmitterInterface { private async proposeSafeTransaction( safeTransaction: SafeTransaction, ): Promise { - const safeTxHash: string = await this.safe.getTransactionHash( - safeTransaction, - ); + const safeTxHash: string = + await this.safe.getTransactionHash(safeTransaction); const senderAddress: Address = await this.multiProvider.getSignerAddress( this.props.chain, ); diff --git a/typescript/sdk/src/router/adapters/EvmRouterAdapter.ts b/typescript/sdk/src/router/adapters/EvmRouterAdapter.ts index a47bd277632..fef60bed4dd 100644 --- a/typescript/sdk/src/router/adapters/EvmRouterAdapter.ts +++ b/typescript/sdk/src/router/adapters/EvmRouterAdapter.ts @@ -34,9 +34,8 @@ export class EvmRouterAdapter extends BaseEvmAdapter implements IRouterAdapter { } async remoteRouter(remoteDomain: Domain): Promise
{ - const routerAddressesAsBytes32 = await this.getConnectedContract().routers( - remoteDomain, - ); + const routerAddressesAsBytes32 = + await this.getConnectedContract().routers(remoteDomain); return bytes32ToAddress(routerAddressesAsBytes32); } @@ -59,9 +58,8 @@ export class EvmGasRouterAdapter { async quoteGasPayment(destination: ChainName): Promise { const destDomain = this.multiProvider.getDomainId(destination); - const amount = await this.getConnectedContract().quoteGasPayment( - destDomain, - ); + const amount = + await this.getConnectedContract().quoteGasPayment(destDomain); return amount.toString(); } diff --git a/typescript/sdk/src/test/testUtils.ts b/typescript/sdk/src/test/testUtils.ts index b426a6911a1..fb21f6d5da8 100644 --- a/typescript/sdk/src/test/testUtils.ts +++ b/typescript/sdk/src/test/testUtils.ts @@ -295,8 +295,8 @@ export const randomIsmConfig = ( const moduleType = providedIsmType ? ismTypeToModuleType(providedIsmType) : depth === maxDepth - ? randomNonNestedModuleType() - : randomModuleType(); + ? randomNonNestedModuleType() + : randomModuleType(); switch (moduleType) { case ModuleType.MERKLE_ROOT_MULTISIG: { diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 7a3b136f451..66775184398 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -71,9 +71,8 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { ): Promise { // Derive the config type const type = await this.deriveTokenType(warpRouteAddress); - const mailboxClientConfig = await this.fetchMailboxClientConfig( - warpRouteAddress, - ); + const mailboxClientConfig = + await this.fetchMailboxClientConfig(warpRouteAddress); const tokenConfig = await this.fetchTokenConfig(type, warpRouteAddress); const remoteRouters = await this.fetchRemoteRouters(warpRouteAddress); const proxyAdmin = await this.fetchProxyAdminConfig(warpRouteAddress); diff --git a/typescript/sdk/src/token/TokenAmount.ts b/typescript/sdk/src/token/TokenAmount.ts index 76536dffabc..5660b2cd518 100644 --- a/typescript/sdk/src/token/TokenAmount.ts +++ b/typescript/sdk/src/token/TokenAmount.ts @@ -5,7 +5,10 @@ import type { IToken } from './IToken.js'; export class TokenAmount { public readonly amount: bigint; - constructor(_amount: Numberish, public readonly token: IToken) { + constructor( + _amount: Numberish, + public readonly token: IToken, + ) { this.amount = BigInt(_amount); } diff --git a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts index 34bf24801b7..e8157387990 100644 --- a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts @@ -394,7 +394,7 @@ export class EvmHypSyntheticRebaseAdapter extends EvmHypSyntheticAdapter implements IHypTokenAdapter { - public declare contract: HypERC4626; + declare public contract: HypERC4626; constructor( public readonly chainName: ChainName, diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index d0f11bef727..7c05489a9ad 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -172,9 +172,8 @@ export class SealevelTokenAdapter new PublicKey(owner), ); try { - const response = await this.getProvider().getTokenAccountBalance( - tokenPubKey, - ); + const response = + await this.getProvider().getTokenAccountBalance(tokenPubKey); return BigInt(response.value.amount); } catch (error: any) { if (error.message?.includes(NON_EXISTENT_ACCOUNT_ERROR)) return 0n; @@ -747,9 +746,8 @@ export class SealevelHypCollateralAdapter extends SealevelHypTokenAdapter { // the escrow account. if (eqAddress(owner, this.addresses.warpRouter)) { const collateralAccount = this.deriveEscrowAccount(); - const response = await this.getProvider().getTokenAccountBalance( - collateralAccount, - ); + const response = + await this.getProvider().getTokenAccountBalance(collateralAccount); return BigInt(response.value.amount); } @@ -821,9 +819,8 @@ export class SealevelHypSyntheticAdapter extends SealevelHypTokenAdapter { new PublicKey(owner), ); try { - const response = await this.getProvider().getTokenAccountBalance( - tokenPubKey, - ); + const response = + await this.getProvider().getTokenAccountBalance(tokenPubKey); return BigInt(response.value.amount); } catch (error: any) { if (error.message?.includes(NON_EXISTENT_ACCOUNT_ERROR)) return 0n; diff --git a/typescript/sdk/src/token/xerc20.ts b/typescript/sdk/src/token/xerc20.ts index cf45035bdab..978cbde9533 100644 --- a/typescript/sdk/src/token/xerc20.ts +++ b/typescript/sdk/src/token/xerc20.ts @@ -134,7 +134,7 @@ export async function getExtraLockBoxConfigs({ logIndex: Number(log.logIndex), transactionIndex: Number(log.transactionIndex), topics: log.topics, - } as Log), + }) as Log, ); return getLockboxesFromLogs( @@ -199,17 +199,20 @@ async function getLockboxesFromLogs( // A bridge might appear more than once in the event logs, we are only // interested in the most recent one for each bridge so we deduplicate // entries here - const dedupedBridges = parsedLogs.reduce((acc, log) => { - const bridgeAddress = log.args.bridge; - const isMostRecentLogForBridge = - log.blockNumber > (acc[bridgeAddress]?.blockNumber ?? 0n); - - if (isMostRecentLogForBridge) { - acc[bridgeAddress] = log; - } + const dedupedBridges = parsedLogs.reduce( + (acc, log) => { + const bridgeAddress = log.args.bridge; + const isMostRecentLogForBridge = + log.blockNumber > (acc[bridgeAddress]?.blockNumber ?? 0n); + + if (isMostRecentLogForBridge) { + acc[bridgeAddress] = log; + } - return acc; - }, {} as Record); + return acc; + }, + {} as Record, + ); const lockboxPromises = Object.values(dedupedBridges) // Removing bridges where the limits are set to 0 because it is equivalent of being deactivated diff --git a/typescript/sdk/src/utils/gnosisSafe.js b/typescript/sdk/src/utils/gnosisSafe.js index 77894075b5f..b2d43636949 100644 --- a/typescript/sdk/src/utils/gnosisSafe.js +++ b/typescript/sdk/src/utils/gnosisSafe.js @@ -68,9 +68,8 @@ export async function getSafe(chain, multiProvider, safeAddress) { // Get the safe version const safeService = getSafeService(chain, multiProvider); - const { version: rawSafeVersion } = await safeService.getSafeInfo( - safeAddress, - ); + const { version: rawSafeVersion } = + await safeService.getSafeInfo(safeAddress); // Remove any build metadata from the version e.g. 1.3.0+L2 --> 1.3.0 const safeVersion = rawSafeVersion.split(' ')[0].split('+')[0].split('-')[0]; diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index 28e94115f4e..6617e6d89a2 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -614,9 +614,8 @@ export class WarpCore { ); if (destinationCollateralError) return destinationCollateralError; - const originCollateralError = await this.validateOriginCollateral( - originTokenAmount, - ); + const originCollateralError = + await this.validateOriginCollateral(originTokenAmount); if (originCollateralError) return originCollateralError; const balancesError = await this.validateTokenBalances( diff --git a/typescript/sdk/src/zksync/ZKSyncDeployer.ts b/typescript/sdk/src/zksync/ZKSyncDeployer.ts index 92069263115..087cb2190ce 100644 --- a/typescript/sdk/src/zksync/ZKSyncDeployer.ts +++ b/typescript/sdk/src/zksync/ZKSyncDeployer.ts @@ -166,9 +166,8 @@ export class ZKSyncDeployer { ) { const dependencyContract = artifact.factoryDeps[dependencyHash]; if (!visited.has(dependencyContract)) { - const dependencyArtifact = await this.loadArtifact( - dependencyContract, - ); + const dependencyArtifact = + await this.loadArtifact(dependencyContract); factoryDeps.push(dependencyArtifact.bytecode); visited.add(dependencyContract); const transitiveDeps = await this.extractFactoryDepsRecursive( diff --git a/typescript/sdk/tsconfig.json b/typescript/sdk/tsconfig.json index 8b20eb5f5bd..5bbbb14dbc1 100644 --- a/typescript/sdk/tsconfig.json +++ b/typescript/sdk/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "./dist/", - "rootDir": "./src/", + "rootDir": "./src/" }, "include": ["./src/**/*.ts", "./src/*.d.ts"] } diff --git a/typescript/utils/src/eslint-rules/no-restricted-imports-from-exports.test.ts b/typescript/utils/src/eslint-rules/no-restricted-imports-from-exports.test.ts index da861d5c53d..7a20bff0bc9 100644 --- a/typescript/utils/src/eslint-rules/no-restricted-imports-from-exports.test.ts +++ b/typescript/utils/src/eslint-rules/no-restricted-imports-from-exports.test.ts @@ -54,23 +54,23 @@ describe('no-restricted-imports-from-exports rule', () => { ); // Stub path.resolve to simplify path resolution in tests - sinon - .stub(path, 'resolve') - .callsFake(function (...args: unknown[]): string { - const lastArg = args[args.length - 1]; + sinon.stub(path, 'resolve').callsFake(function ( + ...args: unknown[] + ): string { + const lastArg = args[args.length - 1]; - if (typeof lastArg !== 'string') { - return String(lastArg); - } + if (typeof lastArg !== 'string') { + return String(lastArg); + } - for (const [pattern, mapping] of Object.entries(pathMappings)) { - if (lastArg.includes(pattern)) { - return mapping; - } + for (const [pattern, mapping] of Object.entries(pathMappings)) { + if (lastArg.includes(pattern)) { + return mapping; } + } - return lastArg; - }); + return lastArg; + }); sinon .stub(path, 'dirname') diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index f4586ab10a0..2e081acf8b6 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -72,8 +72,8 @@ export function deepFind( const entries = isObject(obj) ? Object.values(obj) : Array.isArray(obj) - ? obj - : []; + ? obj + : []; return entries.map((e) => deepFind(e as any, func, depth - 1)).find((v) => v); } diff --git a/typescript/widgets/eslint.config.mjs b/typescript/widgets/eslint.config.mjs index e9f4b3ff393..3aa59b3f54f 100644 --- a/typescript/widgets/eslint.config.mjs +++ b/typescript/widgets/eslint.config.mjs @@ -1,11 +1,14 @@ import react from 'eslint-plugin-react'; import reactHooks from 'eslint-plugin-react-hooks'; -import MonorepoDefaults, {compat} from '../../eslint.config.mjs'; +import MonorepoDefaults, { compat } from '../../eslint.config.mjs'; export default [ ...MonorepoDefaults, - ...compat.extends("plugin:react/recommended", "plugin:react-hooks/recommended"), + ...compat.extends( + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + ), { settings: { react: { @@ -21,7 +24,6 @@ export default [ 'react-hooks': reactHooks, }, - rules: { 'react/react-in-jsx-scope': 'off', 'react/prop-types': 'off', diff --git a/typescript/widgets/postcss.config.cjs b/typescript/widgets/postcss.config.cjs index 08a01d4d167..ee5f90b3090 100644 --- a/typescript/widgets/postcss.config.cjs +++ b/typescript/widgets/postcss.config.cjs @@ -2,4 +2,4 @@ module.exports = { plugins: { tailwindcss: {}, }, -} +}; diff --git a/typescript/widgets/src/messages/useMessageStage.ts b/typescript/widgets/src/messages/useMessageStage.ts index 26dd1678b4e..9f2b86b06cf 100644 --- a/typescript/widgets/src/messages/useMessageStage.ts +++ b/typescript/widgets/src/messages/useMessageStage.ts @@ -78,8 +78,8 @@ export function useMessageStage({ stage: data?.stage ? data.stage : isValidMessage(message) - ? Stage.Sent - : Stage.Preparing, + ? Stage.Sent + : Stage.Preparing, timings: data?.timings ? data.timings : defaultTiming, isLoading, error, diff --git a/typescript/widgets/src/walletIntegrations/cosmos.ts b/typescript/widgets/src/walletIntegrations/cosmos.ts index 5ce233d31fb..27619b78c4d 100644 --- a/typescript/widgets/src/walletIntegrations/cosmos.ts +++ b/typescript/widgets/src/walletIntegrations/cosmos.ts @@ -97,7 +97,7 @@ export function useCosmosActiveChain( _multiProvider: MultiProtocolProvider, ): ActiveChainInfo { // Cosmoskit doesn't have the concept of an active chain - return useMemo(() => ({} as ActiveChainInfo), []); + return useMemo(() => ({}) as ActiveChainInfo, []); } export function useCosmosTransactionFns( diff --git a/typescript/widgets/tailwind.config.cjs b/typescript/widgets/tailwind.config.cjs index 48dedefff00..ebcc13f7f9f 100644 --- a/typescript/widgets/tailwind.config.cjs +++ b/typescript/widgets/tailwind.config.cjs @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ -const defaultTheme = require('tailwindcss/defaultTheme') +const defaultTheme = require('tailwindcss/defaultTheme'); module.exports = { content: ['./src/**/*.{js,ts,jsx,tsx}'], @@ -77,7 +77,7 @@ module.exports = { 700: '#952281', 800: '#6B185C', 900: '#400E37', - } + }, }, fontSize: { md: '0.95rem', @@ -102,7 +102,7 @@ module.exports = { }, animation: { 'pulse-slow': 'pulse 3s infinite cubic-bezier(.4,0,.6,1)', - } + }, }, }, plugins: [], diff --git a/typescript/widgets/tsconfig.json b/typescript/widgets/tsconfig.json index 99d9a0d4d04..c6021f38c0c 100644 --- a/typescript/widgets/tsconfig.json +++ b/typescript/widgets/tsconfig.json @@ -9,5 +9,5 @@ "outDir": "./dist" }, "include": ["./src/**/*"], - "exclude": ["node_modules", "dist", "**/*.stories.tsx"], + "exclude": ["node_modules", "dist", "**/*.stories.tsx"] } From b9f482f2198525992b62c5af7de199e26cb7f552 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:54:34 -0400 Subject: [PATCH 110/223] chore: Add latest proxyAdmin owners to Getters (#6093) ### Description This PR addresses alert related to proxyAdmin violations: https://hyperlaneworkspace.slack.com/archives/C08GHFP3214/p1746025724641399 ### Backward compatibility Yes ### Testing `yarn tsx check-warp-deploy.ts` Co-authored-by: Le --- .../configGetters/getAncient8EthereumUSDCWarpConfig.ts | 8 ++++++++ .../getEclipseEthereumSolanaUSDTWarpConfig.ts | 6 +++++- .../configGetters/getEclipseEthereumWBTCWarpConfig.ts | 6 +++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.ts index d4b38bfb4f4..cd68be6351f 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.ts @@ -13,6 +13,8 @@ import { RouterConfigWithoutOwner, tokens, } from '../../../../../src/config/warp.js'; +import { regularIcas } from '../../governance/ica/regular.js'; +import { regularSafes } from '../../governance/safe/regular.js'; export const getAncient8EthereumUSDCWarpConfig = async ( routerConfig: ChainMap, @@ -34,6 +36,9 @@ export const getAncient8EthereumUSDCWarpConfig = async ( // for the hook module. The hook configuration is the Ethereum // default hook for the Ancient8 remote (no routing). hook: '0x19b2cF952b70b217c90FC408714Fbc1acD29A6A8', + proxyAdmin: { + owner: regularSafes.ethereum, + }, }; const ancient8: HypTokenRouterConfig = { @@ -42,6 +47,9 @@ export const getAncient8EthereumUSDCWarpConfig = async ( type: TokenType.synthetic, // Uses the default ISM interchainSecurityModule: ethers.constants.AddressZero, + proxyAdmin: { + owner: regularIcas.ancient8, + }, }; return { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumSolanaUSDTWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumSolanaUSDTWarpConfig.ts index c6576138b56..6e90895e9fa 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumSolanaUSDTWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumSolanaUSDTWarpConfig.ts @@ -11,6 +11,7 @@ import { RouterConfigWithoutOwner, tokens, } from '../../../../../src/config/warp.js'; +import { regularSafes } from '../../governance/safe/regular.js'; import { SEALEVEL_WARP_ROUTE_HANDLER_GAS_AMOUNT } from '../consts.js'; export const getEclipseEthereumSolanaUSDTWarpConfig = async ( @@ -25,12 +26,15 @@ export const getEclipseEthereumSolanaUSDTWarpConfig = async ( gas: SEALEVEL_WARP_ROUTE_HANDLER_GAS_AMOUNT, interchainSecurityModule: ethers.constants.AddressZero, }; - let ethereum: HypTokenRouterConfig = { + const ethereum: HypTokenRouterConfig = { ...routerConfig.ethereum, ...abacusWorksEnvOwnerConfig.ethereum, type: TokenType.collateral, token: tokens.ethereum.USDT, interchainSecurityModule: ethers.constants.AddressZero, + proxyAdmin: { + owner: regularSafes.ethereum, + }, }; // Intentionally don't enroll Solana to avoid transferring diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumWBTCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumWBTCWarpConfig.ts index 011d151ba8a..dad5a1e1465 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumWBTCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumWBTCWarpConfig.ts @@ -11,6 +11,7 @@ import { RouterConfigWithoutOwner, tokens, } from '../../../../../src/config/warp.js'; +import { regularSafes } from '../../governance/safe/regular.js'; import { SEALEVEL_WARP_ROUTE_HANDLER_GAS_AMOUNT } from '../consts.js'; export const getEclipseEthereumWBTCWarpConfig = async ( @@ -26,12 +27,15 @@ export const getEclipseEthereumWBTCWarpConfig = async ( interchainSecurityModule: ethers.constants.AddressZero, }; - let ethereum: HypTokenRouterConfig = { + const ethereum: HypTokenRouterConfig = { ...routerConfig.ethereum, ...abacusWorksEnvOwnerConfig.ethereum, type: TokenType.collateral, token: tokens.ethereum.WBTC, interchainSecurityModule: ethers.constants.AddressZero, + proxyAdmin: { + owner: regularSafes.ethereum, + }, }; return { From 1a56053333ca3d53b9301a49557d83f743d29ca4 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 1 May 2025 02:26:39 +0100 Subject: [PATCH 111/223] fix: operational fixes (#6086) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .../agents/relayer/src/msg/op_submitter.rs | 6 +- .../chains/sealevel/adapter.rs | 20 ++++--- rust/main/submitter/src/error.rs | 8 ++- rust/main/submitter/src/payload/types.rs | 16 +++++- .../src/payload_dispatcher/db/loader.rs | 30 +++++++--- .../payload_dispatcher/db/payload/loader.rs | 4 +- .../db/payload/payload_db.rs | 13 ++++- .../db/transaction/loader.rs | 4 +- .../src/payload_dispatcher/entrypoint.rs | 2 +- .../stages/building_stage.rs | 41 -------------- .../stages/inclusion_stage.rs | 56 ++++++++++++------- .../submitter/src/payload_dispatcher/tests.rs | 3 + 12 files changed, 113 insertions(+), 90 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index 0ebd8f4cb14..a4db53a5633 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -852,11 +852,7 @@ async fn confirm_lander_task( Some((op, op_results)) } Ok(Some(_)) | Ok(None) | Err(_) => { - error!( - ?op, - %message_id, - "Error retrieving payload id by message id", - ); + debug!(?op, ?message_id, "No payload id found for message id",); send_back_on_failed_submission( op, prepare_queue.clone(), diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index 0a80a06d94f..2835cf2b719 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -284,9 +284,13 @@ impl AdaptsChain for SealevelTxAdapter { let mut transactions = Vec::new(); for (not_estimated, payload) in payloads_and_precursors.into_iter() { - let Ok(estimated) = self.estimate(not_estimated).await else { - transactions.push(TxBuildingResult::new(vec![payload.details.clone()], None)); - continue; + let estimated = match self.estimate(not_estimated).await { + Ok(estimated) => estimated, + Err(err) => { + warn!(?err, ?payload, "failed to estimate payload"); + transactions.push(TxBuildingResult::new(vec![payload.details.clone()], None)); + continue; + } }; let transaction = TransactionFactory::build(payload, estimated); transactions.push(TxBuildingResult::new( @@ -375,10 +379,12 @@ impl AdaptsChain for SealevelTxAdapter { } async fn tx_ready_for_resubmission(&self, tx: &Transaction) -> bool { - let last_submission_time = tx.last_submission_attempt.unwrap_or(tx.creation_timestamp); - let seconds_since_last_submission = - (Utc::now() - last_submission_time).num_seconds() as u64; - seconds_since_last_submission >= TX_RESUBMISSION_MIN_DELAY_SECS + if let Some(ref last_submission_time) = tx.last_submission_attempt { + let seconds_since_last_submission = + (Utc::now() - last_submission_time).num_seconds() as u64; + return seconds_since_last_submission >= TX_RESUBMISSION_MIN_DELAY_SECS; + } + true } } diff --git a/rust/main/submitter/src/error.rs b/rust/main/submitter/src/error.rs index 9ad20e95c9f..0c4c66d0b08 100644 --- a/rust/main/submitter/src/error.rs +++ b/rust/main/submitter/src/error.rs @@ -63,7 +63,13 @@ impl IsRetryable for SubmitterError { SubmitterError::NetworkError(_) => true, SubmitterError::TxSubmissionError(_) => true, SubmitterError::ChannelSendFailure(_) => true, - SubmitterError::ChainCommunicationError(_) => true, + SubmitterError::ChainCommunicationError(err) => { + // on SVM, reverted simulations will return an error containing this + if err.to_string().contains("simulation result") { + return false; + } + true + } SubmitterError::EyreError(_) => true, SubmitterError::NonRetryableError(_) => false, SubmitterError::TxReverted => false, diff --git a/rust/main/submitter/src/payload/types.rs b/rust/main/submitter/src/payload/types.rs index 7431fecb00c..bc1914614fa 100644 --- a/rust/main/submitter/src/payload/types.rs +++ b/rust/main/submitter/src/payload/types.rs @@ -1,6 +1,7 @@ // TODO: re-enable clippy warnings #![allow(dead_code)] +use std::fmt::Debug; use std::ops::Deref; use chrono::{DateTime, Utc}; @@ -38,7 +39,7 @@ impl PayloadDetails { } /// Full details about a payload. This is instantiated by the caller of PayloadDispatcher -#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, PartialEq, Eq)] +#[derive(Clone, Default, serde::Deserialize, serde::Serialize, PartialEq, Eq)] pub struct FullPayload { /// reference to payload used by other components pub details: PayloadDetails, @@ -56,6 +57,19 @@ pub struct FullPayload { pub inclusion_soft_deadline: Option>, } +impl Debug for FullPayload { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FullPayload") + .field("id", &self.details.id) + .field("metadata", &self.details.metadata) + .field("to", &self.to) + .field("status", &self.status) + .field("value", &self.value) + .field("inclusion_soft_deadline", &self.inclusion_soft_deadline) + .finish() + } +} + impl FullPayload { pub fn new(id: PayloadId, metadata: impl Into, data: Vec, to: Address) -> Self { Self { diff --git a/rust/main/submitter/src/payload_dispatcher/db/loader.rs b/rust/main/submitter/src/payload_dispatcher/db/loader.rs index deb33e1ae82..7009f70f591 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/loader.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/loader.rs @@ -88,19 +88,32 @@ impl DbIterator { // Always prioritize advancing the the high nonce iterator, as // we have a preference for higher nonces if let Some(high_index_iter) = &mut self.high_index_iter { - if let Some(LoadingOutcome::Loaded) = high_index_iter.try_load_item().await? { - // If we have a high nonce item, we can process it - high_index_iter.iterate(); - return Ok(LoadingOutcome::Loaded); + match high_index_iter.try_load_item().await? { + Some(LoadingOutcome::Loaded) => { + high_index_iter.iterate(); + // If we have a high nonce item, we can process it + return Ok(LoadingOutcome::Loaded); + } + Some(LoadingOutcome::Skipped) => { + high_index_iter.iterate(); + } + None => {} } } // Low nonce messages are only processed if the high nonce iterator // can't make any progress - if let Some(LoadingOutcome::Loaded) = self.low_index_iter.try_load_item().await? { - // If we have a low nonce item, we can process it - self.low_index_iter.iterate(); - return Ok(LoadingOutcome::Loaded); + match self.low_index_iter.try_load_item().await? { + Some(LoadingOutcome::Loaded) => { + // If we have a low nonce item, we can process it + self.low_index_iter.iterate(); + return Ok(LoadingOutcome::Loaded); + } + Some(LoadingOutcome::Skipped) => { + // If we don't have any items, we can skip + self.low_index_iter.iterate(); + } + None => {} } Ok(LoadingOutcome::Skipped) } @@ -117,6 +130,7 @@ impl DbIterator { // If we are only loading backward, we have processed all items return Ok(()); } + debug!(?self, "No items to process, sleeping for a bit"); // sleep to wait for new items to be added sleep(Duration::from_millis(100)).await; } else { diff --git a/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs b/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs index 2b1e297ada0..73d91513ffe 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/payload/loader.rs @@ -5,7 +5,7 @@ use std::{ use async_trait::async_trait; use derive_new::new; -use tracing::trace; +use tracing::{debug, trace}; use crate::{ error::SubmitterError, @@ -54,7 +54,7 @@ impl LoadableFromDb for PayloadDbLoader { Ok(LoadingOutcome::Loaded) } PayloadStatus::Dropped(_) | PayloadStatus::InTransaction(_) => { - trace!(?item, "Payload already processed"); + debug!(?item, "Payload already processed"); Ok(LoadingOutcome::Skipped) } } diff --git a/rust/main/submitter/src/payload_dispatcher/db/payload/payload_db.rs b/rust/main/submitter/src/payload_dispatcher/db/payload/payload_db.rs index 3911fdfe195..ab95f9120cc 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/payload/payload_db.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/payload/payload_db.rs @@ -7,9 +7,10 @@ use async_trait::async_trait; use eyre::eyre; use hyperlane_base::db::{DbError, DbResult, HyperlaneRocksDB}; use hyperlane_core::{identifiers::UniqueIdentifier, Decode, Encode, HyperlaneProtocolError}; +use tracing::debug; use crate::{ - payload::{FullPayload, PayloadId, PayloadStatus}, + payload::{self, FullPayload, PayloadId, PayloadStatus}, transaction::TransactionId, }; @@ -104,6 +105,16 @@ impl PayloadDb for HyperlaneRocksDB { .await?; self.store_payload_id_by_index(payload_index, payload.id()) .await?; + debug!( + ?payload, + index = payload_index, + "Updated highest index for incoming payload" + ); + } else { + debug!( + payload_id = ?payload.id(), + "Payload with ID already exists, not updating index", + ); } self.store_value_by_key(PAYLOAD_BY_ID_STORAGE_PREFIX, payload.id(), payload) } diff --git a/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs b/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs index 8bcf98fd7ff..66b7f3d012c 100644 --- a/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs +++ b/rust/main/submitter/src/payload_dispatcher/db/transaction/loader.rs @@ -6,7 +6,7 @@ use std::{ use async_trait::async_trait; use derive_new::new; use tokio::sync::mpsc::Sender; -use tracing::trace; +use tracing::{debug, trace}; use crate::{ error::SubmitterError, @@ -59,7 +59,7 @@ impl LoadableFromDb for TransactionDbLoader { Ok(LoadingOutcome::Loaded) } TransactionStatus::Finalized | TransactionStatus::Dropped(_) => { - trace!(?item, "Transaction already processed"); + debug!(?item, "Transaction already processed"); Ok(LoadingOutcome::Skipped) } } diff --git a/rust/main/submitter/src/payload_dispatcher/entrypoint.rs b/rust/main/submitter/src/payload_dispatcher/entrypoint.rs index a558af0b5bf..79be279fc99 100644 --- a/rust/main/submitter/src/payload_dispatcher/entrypoint.rs +++ b/rust/main/submitter/src/payload_dispatcher/entrypoint.rs @@ -45,8 +45,8 @@ impl PayloadDispatcherEntrypoint { #[async_trait] impl Entrypoint for PayloadDispatcherEntrypoint { async fn send_payload(&self, payload: &FullPayload) -> Result<(), SubmitterError> { - info!(payload=?payload.details, "Sending payload to dispatcher"); self.inner.payload_db.store_payload_by_id(payload).await?; + info!(payload=?payload.details, "Sent payload to dispatcher"); Ok(()) } diff --git a/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs index 7609dc560f1..fba3210ec96 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/building_stage.rs @@ -96,22 +96,6 @@ impl BuildingStage { return Ok(()); }; info!(?tx, "Transaction built successfully"); - let simulation_success = call_until_success_or_nonretryable_error( - || self.state.adapter.simulate_tx(&tx), - "Simulating transaction", - &self.state, - ) - .await - .unwrap_or(false); - if !simulation_success { - warn!( - ?tx, - payload_details = ?tx.payload_details, - "Transaction simulation failed. Dropping transaction" - ); - self.drop_tx(&tx, DropReason::FailedSimulation).await; - return Ok(()); - }; call_until_success_or_nonretryable_error( || self.send_tx_to_inclusion_stage(tx.clone()), "Sending transaction to inclusion stage", @@ -270,31 +254,6 @@ mod tests { assert_eq!(queue.lock().await.len(), 0); } - #[tokio::test] - async fn test_txs_failed_simulation() { - const PAYLOADS_TO_SEND: usize = 3; - let succesful_build = true; - let successful_simulation = false; - let (building_stage, mut receiver, queue) = - test_setup(PAYLOADS_TO_SEND, succesful_build, successful_simulation); - - for _ in 0..PAYLOADS_TO_SEND { - let payload_to_send = FullPayload::random(); - initialize_payload_db(&building_stage.state.payload_db, &payload_to_send).await; - queue.lock().await.push_back(payload_to_send.clone()); - let payload_details_received = - run_building_stage(1, &building_stage, &mut receiver).await; - assert_eq!(payload_details_received, vec![]); - assert_db_status_for_payloads( - &building_stage.state, - &payload_details_received, - PayloadStatus::Dropped(DropReason::FailedSimulation), - ) - .await; - } - assert_eq!(queue.lock().await.len(), 0); - } - async fn run_building_stage( sent_payload_count: usize, building_stage: &BuildingStage, diff --git a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs index 1e0d69bb79d..2199752d49d 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs @@ -91,8 +91,14 @@ impl InclusionStage { .metrics .update_liveness_metric(format!("{}::receive_txs", STAGE_NAME).as_str(), &domain); if let Some(tx) = building_stage_receiver.recv().await { - pool.lock().await.insert(tx.id.clone(), tx.clone()); + // the lock is held until the metric is updated, to prevent race conditions + let mut pool_lock = pool.lock().await; + let pool_len = pool_lock.len(); + pool_lock.insert(tx.id.clone(), tx.clone()); info!(?tx, "Received transaction"); + state + .metrics + .update_queue_length_metric(STAGE_NAME, pool_len as u64, &domain); } else { error!("Building stage channel closed"); return Err(SubmitterError::ChannelClosed); @@ -114,18 +120,22 @@ impl InclusionStage { // evaluate the pool every block sleep(*estimated_block_time).await; - let pool_snapshot = pool.lock().await.clone(); - state.metrics.update_queue_length_metric( - STAGE_NAME, - pool_snapshot.len() as u64, - &domain, - ); + let pool_snapshot = { + let pool_snapshot = pool.lock().await.clone(); + state.metrics.update_queue_length_metric( + STAGE_NAME, + pool_snapshot.len() as u64, + &domain, + ); + pool_snapshot + }; info!(pool_size=?pool_snapshot.len() , "Processing transactions in inclusion pool"); - for (_, tx) in pool_snapshot { + for (_, mut tx) in pool_snapshot { if let Err(err) = Self::try_process_tx(tx.clone(), &finality_stage_sender, &state, &pool).await { - error!(?err, ?tx, "Error processing transaction. Skipping for now"); + error!(?err, ?tx, "Error processing transaction. Dropping it"); + Self::drop_tx(&state, &mut tx, TxDropReason::FailedSimulation, &pool).await?; } } } @@ -187,18 +197,21 @@ impl InclusionStage { pool: &InclusionStagePool, ) -> Result<()> { info!(?tx, "Processing pending transaction"); - let simulation_success = call_until_success_or_nonretryable_error( - || state.adapter.simulate_tx(&tx), - "Simulating transaction", - state, - ) - .await?; - if !simulation_success { - warn!(?tx, "Transaction simulation failed"); - Self::drop_tx(state, &mut tx, TxDropReason::FailedSimulation, pool).await?; - return Err(eyre!("Transaction simulation failed")); - } - info!(?tx, "Transaction simulation succeeded"); + // TODO: simulating the transaction is commented out for now, because + // on SVM the tx is simulated in the `submit` call. + // let simulation_success = call_until_success_or_nonretryable_error( + // || state.adapter.simulate_tx(&tx), + // "Simulating transaction", + // state, + // ) + // .await + // // if simulation fails or hits a non-retryable error, drop the tx + // .unwrap_or(false); + // if !simulation_success { + // warn!(?tx, "Transaction simulation failed"); + // return Err(eyre!("Transaction simulation failed")); + // } + // info!(?tx, "Transaction simulation succeeded"); // successively calling `submit` will result in escalating gas price until the tx is accepted // by the node. @@ -324,6 +337,7 @@ mod tests { } #[tokio::test] + #[ignore] async fn test_failed_simulation() { const TXS_TO_PROCESS: usize = 3; diff --git a/rust/main/submitter/src/payload_dispatcher/tests.rs b/rust/main/submitter/src/payload_dispatcher/tests.rs index 34810cabfaf..5bdd01f6788 100644 --- a/rust/main/submitter/src/payload_dispatcher/tests.rs +++ b/rust/main/submitter/src/payload_dispatcher/tests.rs @@ -105,6 +105,7 @@ async fn test_entrypoint_send_is_finalized_by_dispatcher() { #[tracing_test::traced_test] #[tokio::test] +#[ignore] async fn test_entrypoint_send_is_dropped_by_dispatcher() { let payload = FullPayload::random(); @@ -134,6 +135,7 @@ async fn test_entrypoint_send_is_dropped_by_dispatcher() { entrypoint.inner.payload_db.clone(), payload.id(), |payload_status| { + println!("Payload status: {:?}", payload_status); matches!( payload_status, PayloadStatus::InTransaction(TransactionStatus::Dropped(_)) @@ -160,6 +162,7 @@ async fn test_entrypoint_send_is_dropped_by_dispatcher() { #[tracing_test::traced_test] #[tokio::test] +#[ignore] async fn test_entrypoint_payload_fails_simulation() { let payload = FullPayload::random(); From 45c53df883352e1dde047eae8491711a72b78f68 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 1 May 2025 11:12:29 +0100 Subject: [PATCH 112/223] chore(submitter): default to considering errors non-retryable (#6098) ### Description - lean on higher level retries from the MessageProcessor to avoid getting the submitter blocked on errors that are actually non-retryable - we can look for increases in the `dropped_transactions` metric to find errors that we can classify as retryable ### Drive-by changes ### Related issues Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6101 ### Backward compatibility ### Testing --- rust/main/submitter/src/error.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/rust/main/submitter/src/error.rs b/rust/main/submitter/src/error.rs index 0c4c66d0b08..64d04458c6b 100644 --- a/rust/main/submitter/src/error.rs +++ b/rust/main/submitter/src/error.rs @@ -60,17 +60,21 @@ pub trait IsRetryable { impl IsRetryable for SubmitterError { fn is_retryable(&self) -> bool { match self { - SubmitterError::NetworkError(_) => true, SubmitterError::TxSubmissionError(_) => true, - SubmitterError::ChannelSendFailure(_) => true, - SubmitterError::ChainCommunicationError(err) => { - // on SVM, reverted simulations will return an error containing this - if err.to_string().contains("simulation result") { - return false; - } - true + SubmitterError::NetworkError(_) => { + // TODO: add logic to classify based on the error message + false } - SubmitterError::EyreError(_) => true, + // tx submission errors are not retryable so gas gets escalated + SubmitterError::ChainCommunicationError(_) => { + // TODO: add logic to classify based on the error message + false + } + SubmitterError::EyreError(_) => { + // TODO: add logic to classify based on the error message + false + } + SubmitterError::ChannelSendFailure(_) => false, SubmitterError::NonRetryableError(_) => false, SubmitterError::TxReverted => false, SubmitterError::SimulationFailed => false, From 54a94535a25d7bfc0c718e7b100b844b3d6ac601 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Thu, 1 May 2025 11:17:08 +0100 Subject: [PATCH 113/223] chore: sync Node version with CI and Docker (#6048) ### Description This PR puts Node versions we use in CI and Docker in sync with `.nvmrc`. ### Drive-by changes Removed the obsolete job names from `release.yml` ### Related issues Fixes ENG-1365. ### Backward compatibility This is a Node.js upgrade that could cause issues. The risk is mitigated by the following: - the engineering team uses Node 20 in development for almost a year now - our CI test suite ### Testing - Check all affected CI jobs pass - Check running infra scripts from a Docker image --- .github/workflows/release.yml | 6 ++---- .github/workflows/storage-analysis.yml | 2 +- .github/workflows/test.yml | 16 ++++++++-------- Dockerfile | 2 +- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16db9b1ca75..02db7267c43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,6 @@ jobs: id-token: write contents: write pull-requests: write - name: Release runs-on: ubuntu-latest steps: - name: Checkout Repo @@ -29,10 +28,10 @@ jobs: fetch-depth: 0 submodules: recursive - - name: Setup Node.js 18.x + - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 18.x + node-version-file: .nvmrc - name: Install Dependencies run: yarn install --immutable @@ -85,7 +84,6 @@ jobs: id-token: write contents: write pull-requests: write - name: Release runs-on: ubuntu-latest steps: - name: Checkout Repo diff --git a/.github/workflows/storage-analysis.yml b/.github/workflows/storage-analysis.yml index f5d8ed47479..df6b21d9dbb 100644 --- a/.github/workflows/storage-analysis.yml +++ b/.github/workflows/storage-analysis.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 18 + node-version-file: .nvmrc - name: yarn-cache uses: buildjet/cache@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 54bbba6da64..a8914684b7a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,15 +26,15 @@ jobs: yarn-install: runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v4 - with: - node-version: 18 - - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + - name: yarn-cache uses: buildjet/cache@v4 with: @@ -298,16 +298,16 @@ jobs: matrix: e2e-type: [cosmwasm, cosmosnative, evm, sealevel] steps: - - uses: actions/setup-node@v4 - with: - node-version: 18 - - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + # Set rust_changes to true if: # - on the main branch # - PR with changes to ./rust diff --git a/Dockerfile b/Dockerfile index 573fe3a160e..a12642cefc3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine +FROM node:20-alpine WORKDIR /hyperlane-monorepo From eb79a3c28be25304cd5ef4c2ae1568d54a6bbe43 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Thu, 1 May 2025 13:53:32 +0100 Subject: [PATCH 114/223] feat: ousdt extension (#5899) ### Description feat: ousdt extension - create new staging config - update current prod config - add the CLI strategy for building the safe tx submission jsons on existing ousdt safes ### Drive-by changes - create new AW safes where required - add new+missing AW safes to config - paralellise check-safe-signers script ### Related issues - https://github.com/hyperlane-xyz/hyperlane-registry/pull/749 ### Backward compatibility ### Testing --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 14 +- .../mainnet3/governance/safe/aw.ts | 13 ++ .../configGetters/getSuperTokenWarpConfig.ts | 177 +++++++++++++++--- .../warp/strategies/ousdtSafeTxBuilder.yaml | 55 ++++++ .../environments/mainnet3/warp/warpIds.ts | 4 +- .../safes/governance/check-safe-signers.ts | 101 +++++----- .../warp-routes/export-warp-configs.ts | 4 +- 8 files changed, 286 insertions(+), 84 deletions(-) create mode 100644 typescript/infra/config/environments/mainnet3/warp/strategies/ousdtSafeTxBuilder.yaml diff --git a/.registryrc b/.registryrc index 98d479046ce..9ed0ce751b9 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -7a0a79942383dac2fca1fa5e5a0e57f5d8b2d3f2 +fad1ad9000a5795ed5c617f6ab677d330f52a3b3 diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 5ede33376d2..8ae6b56f6af 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -3636,7 +3636,8 @@ "interchainAccountIsm": "0xa97ec3E58cBd60199dcFDd6396431BE85c2E363e", "interchainAccountRouter": "0xA0a44cB8Bc0f7EDe788b0Cd29524A5b14fED7b45", "timelockController": "0x0000000000000000000000000000000000000000", - "technicalStack": "other" + "technicalStack": "other", + "gnosisSafeTransactionServiceUrl": "https://multisign.bitlayer.org/txs/" }, "coredao": { "blockExplorers": [ @@ -5410,7 +5411,8 @@ "validatorAnnounce": "0xc6835e52C1b976F1ebC71Bc8919738E02849FdA9", "index": { "from": 9688791 - } + }, + "gnosisSafeTransactionServiceUrl": "https://txs.safe.metall2.com/" }, "polynomialfi": { "blockExplorers": [ @@ -7123,7 +7125,7 @@ "index": { "from": 612005 }, - "gnosisSafeTransactionServiceUrl": "https://trx-soneium-stg.safe.protofire.io" + "gnosisSafeTransactionServiceUrl": "https://trx-soneium.safe.protofire.io" }, "sonic": { "blockExplorers": [ @@ -7187,7 +7189,8 @@ "validatorAnnounce": "0x84444cE490233CFa76E3F1029bc166aa8c266907", "index": { "from": 482156 - } + }, + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-sonic.safe.global/" }, "telos": { "blockExplorers": [ @@ -8312,7 +8315,8 @@ "validatorAnnounce": "0xB2b0A80b2fa3fC9aB1564A4FaF013d4D6084B325", "index": { "from": 42656210 - } + }, + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-ronin.safe.onchainden.com" }, "sophon": { "blockExplorers": [ diff --git a/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts b/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts index f5f96e90736..0acf039e84f 100644 --- a/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts +++ b/typescript/infra/config/environments/mainnet3/governance/safe/aw.ts @@ -48,4 +48,17 @@ export const awSafes: ChainMap
= { zklink: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', treasure: '0xCB21F61A3c8139F18e635d45aD1e62A4A61d2c3D', sophon: '0x3D1baf8cA4935f416671640B1Aa9E17E005986eE', + + // ousdt extension + worldchain: '0x95b1634566663117322999ce42cDEaEF18c089Be', + unichain: '0x028C71E99e23fD393DE4207486D1aF7FA2b26b33', + ink: '0x8DEe31BF7da558ee3224D22E224e172783CA8d70', + soneium: '0xD97F1bc0d49f994137Acf36baE2aEd9b2E4F239a', + superseed: '0x2915Ff7B025bc65bBFfD1621F6B3d4E4295dB4F6', + lisk: '0x831d0b06DF466263c06FFcD467cf91c6FA57c62C', + sonic: '0x7f56412491D8E77331Ff0300d3C8E42A6D233FdC', + bitlayer: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', + ronin: '0x5F7771EA40546e2932754C263455Cb0023a55ca7', + metis: '0xf6B817Cf8b4440F38951851cf1160969039966A2', + metal: '0x41A4e3425c7FeE8711D1C1b2c2acc1879F849b45', }; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getSuperTokenWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getSuperTokenWarpConfig.ts index 70d5f089ce0..5fa848a343b 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getSuperTokenWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getSuperTokenWarpConfig.ts @@ -13,9 +13,12 @@ import { import { Address } from '@hyperlane-xyz/utils'; import { RouterConfigWithoutOwner } from '../../../../../src/config/warp.js'; +import { awSafes } from '../../governance/safe/aw.js'; +import { DEPLOYER } from '../../owners.js'; // Environment-independent configuration const deploymentChains = [ + 'ethereum', 'celo', 'optimism', 'base', @@ -27,9 +30,16 @@ const deploymentChains = [ 'superseed', 'lisk', 'worldchain', + 'sonic', + 'bitlayer', + 'ronin', + 'mantle', + 'metis', + 'linea', + 'metal', ] as const; const supportedCCIPChains = ['base', 'mode', 'optimism']; -const xERC20LockboxChain: SuperTokenChainName = 'celo'; +const xERC20LockboxChains: SuperTokenChainName[] = ['celo', 'ethereum']; type SuperTokenChainName = (typeof deploymentChains)[number]; type TypedSuperTokenChainMap = { @@ -40,8 +50,10 @@ type TypedSuperTokenChainMap = { // Production const upperBufferCap = '20000000000000'; // 20M = 20 * 10^6 ^ 10^6 -const lowerBufferCap = '2000000000000'; // 2M = 10 * 10^6 ^ 10^6 +const middleBufferCap = '8000000000000'; // 8M = 8 * 10^6 ^ 10^6 +const lowerBufferCap = '2000000000000'; // 2M = 2 * 10^6 ^ 10^6 const productionBufferCapByChain: TypedSuperTokenChainMap = { + ethereum: upperBufferCap, celo: upperBufferCap, optimism: upperBufferCap, base: upperBufferCap, @@ -53,10 +65,19 @@ const productionBufferCapByChain: TypedSuperTokenChainMap = { superseed: lowerBufferCap, lisk: lowerBufferCap, worldchain: '0', + sonic: middleBufferCap, + bitlayer: lowerBufferCap, + ronin: lowerBufferCap, + mantle: middleBufferCap, + metis: lowerBufferCap, + linea: lowerBufferCap, + metal: lowerBufferCap, }; const productionDefaultRateLimitPerSecond = '5000000000'; // 5k/s = 5 * 10^3 ^ 10^6 +const middleRateLimitPerSecond = '2000000000'; // 2k/s = 2 * 10^3 ^ 10^6 const lowerRateLimitPerSecond = '500000000'; // 0.5k/s = 0.5 * 10^3 ^ 10^6 const productionRateLimitByChain: TypedSuperTokenChainMap = { + ethereum: productionDefaultRateLimitPerSecond, celo: productionDefaultRateLimitPerSecond, optimism: productionDefaultRateLimitPerSecond, base: productionDefaultRateLimitPerSecond, @@ -68,9 +89,17 @@ const productionRateLimitByChain: TypedSuperTokenChainMap = { superseed: lowerRateLimitPerSecond, lisk: lowerRateLimitPerSecond, worldchain: '0', + sonic: middleRateLimitPerSecond, + bitlayer: lowerRateLimitPerSecond, + ronin: lowerRateLimitPerSecond, + mantle: middleRateLimitPerSecond, + metis: lowerRateLimitPerSecond, + linea: lowerRateLimitPerSecond, + metal: lowerRateLimitPerSecond, }; const productionOwnerByChain: TypedSuperTokenChainMap = { + ethereum: awSafes['ethereum'], celo: '0xf1b3fc934bB46c459253fb38555A400b94909800', optimism: '0x8E3340E241880F80359AA95Ae20Dc498d3f62503', base: '0x125d1b64dfd7898DD06ac3E060A432691b8Fa676', @@ -82,11 +111,22 @@ const productionOwnerByChain: TypedSuperTokenChainMap = { superseed: '0x0731a8e0DC88Df79d9643BD6C1f26cfe6fa53382', lisk: '0x6F0A0038FcDB2F1655219f1b92f7E9aD4b78Aa49', worldchain: '0x998238aF5A2DDC7ae08Dbe4B60b82EF63A1538cd', + sonic: awSafes['sonic'], + bitlayer: awSafes['bitlayer'], + ronin: awSafes['ronin'], + mantle: awSafes['mantle'], + metis: awSafes['metis'], + linea: awSafes['linea'], + metal: awSafes['metal'], }; const productionOwnerOverridesByChain: ChainMap< Record<'collateralToken' | 'collateralProxyAdmin', string> > = { + ethereum: { + collateralToken: productionOwnerByChain.ethereum, + collateralProxyAdmin: productionOwnerByChain.ethereum, + }, celo: { collateralToken: productionOwnerByChain.celo, collateralProxyAdmin: productionOwnerByChain.celo, @@ -131,25 +171,59 @@ const productionOwnerOverridesByChain: ChainMap< collateralToken: productionOwnerByChain.worldchain, collateralProxyAdmin: productionOwnerByChain.worldchain, }, + sonic: { + collateralToken: productionOwnerByChain.sonic, + collateralProxyAdmin: productionOwnerByChain.sonic, + }, + bitlayer: { + collateralToken: productionOwnerByChain.bitlayer, + collateralProxyAdmin: productionOwnerByChain.bitlayer, + }, + ronin: { + collateralToken: productionOwnerByChain.ronin, + collateralProxyAdmin: productionOwnerByChain.ronin, + }, + mantle: { + collateralToken: productionOwnerByChain.mantle, + collateralProxyAdmin: productionOwnerByChain.mantle, + }, + metis: { + collateralToken: productionOwnerByChain.metis, + collateralProxyAdmin: productionOwnerByChain.metis, + }, + linea: { + collateralToken: productionOwnerByChain.linea, + collateralProxyAdmin: productionOwnerByChain.linea, + }, + metal: { + collateralToken: productionOwnerByChain.metal, + collateralProxyAdmin: productionOwnerByChain.metal, + }, }; const productionAmountRoutingThreshold = 250000000000; // 250k = 250 * 10^3 ^ 10^6 -const productionXERC20LockboxAddress = +const productionEthereumXERC20LockboxAddress = + '0x6D265C7dD8d76F25155F1a7687C693FDC1220D12'; +const productionCeloXERC20LockboxAddress = '0x5e5F4d6B03db16E7f00dE7C9AFAA53b92C8d1D42'; const productionXERC20TokenAddress = '0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189'; -const productionExtraLockboxLimits: XERC20LimitConfig = { - bufferCap: upperBufferCap, - rateLimitPerSecond: productionDefaultRateLimitPerSecond, -}; - const zeroLimits: XERC20LimitConfig = { bufferCap: '0', rateLimitPerSecond: '0', }; const productionExtraLockboxes = { + ethereum: [ + { + lockbox: productionEthereumXERC20LockboxAddress, + limits: { + bufferCap: productionBufferCapByChain.ethereum, + rateLimitPerSecond: productionRateLimitByChain.ethereum, + }, + }, + ], base: [ { // usdt @@ -159,7 +233,7 @@ const productionExtraLockboxes = { { // usdc lockbox: '0xe92e51d99ae33114c60d9621fb2e1ec0acea7e30', - limits: productionExtraLockboxLimits, + limits: zeroLimits, }, ], optimism: [ @@ -171,13 +245,14 @@ const productionExtraLockboxes = { { // usdt lockbox: '0x18c4cdc2d774c047eac8375bb09853c4d6d6df36', - limits: productionExtraLockboxLimits, + limits: zeroLimits, }, ], }; const productionXERC20AddressesByChain: TypedSuperTokenChainMap
= { - celo: productionXERC20LockboxAddress, + ethereum: productionEthereumXERC20LockboxAddress, + celo: productionCeloXERC20LockboxAddress, optimism: productionXERC20TokenAddress, base: productionXERC20TokenAddress, unichain: productionXERC20TokenAddress, @@ -188,11 +263,19 @@ const productionXERC20AddressesByChain: TypedSuperTokenChainMap
= { superseed: productionXERC20TokenAddress, lisk: productionXERC20TokenAddress, worldchain: productionXERC20TokenAddress, + sonic: productionXERC20TokenAddress, + bitlayer: productionXERC20TokenAddress, + ronin: productionXERC20TokenAddress, + mantle: productionXERC20TokenAddress, + metis: productionXERC20TokenAddress, + linea: productionXERC20TokenAddress, + metal: productionXERC20TokenAddress, }; // Staging const stagingDefaultBufferCap = '25000000000'; const stagingBufferCapByChain: TypedSuperTokenChainMap = { + ethereum: stagingDefaultBufferCap, celo: stagingDefaultBufferCap, optimism: stagingDefaultBufferCap, base: stagingDefaultBufferCap, @@ -204,9 +287,17 @@ const stagingBufferCapByChain: TypedSuperTokenChainMap = { superseed: stagingDefaultBufferCap, lisk: stagingDefaultBufferCap, worldchain: stagingDefaultBufferCap, + sonic: stagingDefaultBufferCap, + bitlayer: stagingDefaultBufferCap, + ronin: stagingDefaultBufferCap, + mantle: stagingDefaultBufferCap, + metis: stagingDefaultBufferCap, + linea: stagingDefaultBufferCap, + metal: stagingDefaultBufferCap, }; const stagingDefaultRateLimitPerSecond = '120000000'; const stagingRateLimitByChain: TypedSuperTokenChainMap = { + ethereum: stagingDefaultRateLimitPerSecond, celo: stagingDefaultRateLimitPerSecond, optimism: stagingDefaultRateLimitPerSecond, base: stagingDefaultRateLimitPerSecond, @@ -218,10 +309,18 @@ const stagingRateLimitByChain: TypedSuperTokenChainMap = { superseed: stagingDefaultRateLimitPerSecond, lisk: stagingDefaultRateLimitPerSecond, worldchain: stagingDefaultRateLimitPerSecond, + sonic: stagingDefaultRateLimitPerSecond, + bitlayer: stagingDefaultRateLimitPerSecond, + ronin: stagingDefaultRateLimitPerSecond, + mantle: stagingDefaultRateLimitPerSecond, + metis: stagingDefaultRateLimitPerSecond, + linea: stagingDefaultRateLimitPerSecond, + metal: stagingDefaultRateLimitPerSecond, }; -const ownerAddress = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; +const ownerAddress = DEPLOYER; const stagingOwnerByChain: TypedSuperTokenChainMap = { + ethereum: ownerAddress, celo: ownerAddress, optimism: ownerAddress, base: ownerAddress, @@ -233,13 +332,23 @@ const stagingOwnerByChain: TypedSuperTokenChainMap = { superseed: ownerAddress, lisk: ownerAddress, worldchain: ownerAddress, + sonic: ownerAddress, + bitlayer: ownerAddress, + ronin: ownerAddress, + mantle: ownerAddress, + metis: ownerAddress, + linea: ownerAddress, + metal: ownerAddress, }; const stagingAmountRoutingThreshold = 5; -const stagingXERC20LockboxAddress = - '0x44eca3a9B45e80F30cAb25bA16a5bF36591c7D29'; -const stagingXERC20TokenAddress = '0xb0eb0856DD9A2DadBF170637A59f9eE2ca4A3f4a'; +const stagingEthereumXERC20LockboxAddress = + '0x935EAaAb78B491Cd9281f438E413767893913983'; +const stagingCeloXERC20LockboxAddress = + '0x9a3D8d7E931679374448FB2B661F664D42d05057'; +const stagingXERC20TokenAddress = '0x0290B74980C051EB46b84b1236645444e77da0E9'; const stagingXERC20AddressesByChain: TypedSuperTokenChainMap
= { - celo: stagingXERC20LockboxAddress, + ethereum: stagingEthereumXERC20LockboxAddress, + celo: stagingCeloXERC20LockboxAddress, optimism: stagingXERC20TokenAddress, base: stagingXERC20TokenAddress, unichain: stagingXERC20TokenAddress, @@ -250,36 +359,47 @@ const stagingXERC20AddressesByChain: TypedSuperTokenChainMap
= { superseed: stagingXERC20TokenAddress, lisk: stagingXERC20TokenAddress, worldchain: stagingXERC20TokenAddress, -}; - -const stagingExtraLockboxLimits: XERC20LimitConfig = { - bufferCap: stagingDefaultBufferCap, - rateLimitPerSecond: stagingDefaultRateLimitPerSecond, + sonic: stagingXERC20TokenAddress, + bitlayer: stagingXERC20TokenAddress, + ronin: stagingXERC20TokenAddress, + mantle: stagingXERC20TokenAddress, + metis: stagingXERC20TokenAddress, + linea: stagingXERC20TokenAddress, + metal: stagingXERC20TokenAddress, }; const stagingExtraLockboxes = { + ethereum: [ + { + lockbox: stagingEthereumXERC20LockboxAddress, + limits: { + bufferCap: stagingBufferCapByChain.ethereum, + rateLimitPerSecond: stagingRateLimitByChain.ethereum, + }, + }, + ], base: [ { // usdt lockbox: '0xd28ca33022d41758bed4f1a31a99dde8fc4d89b3', - limits: stagingExtraLockboxLimits, + limits: zeroLimits, }, { // usdc lockbox: '0x50df545016d26735daacbbf5afda56dc17d8748b', - limits: stagingExtraLockboxLimits, + limits: zeroLimits, }, ], optimism: [ { // usdc lockbox: '0x18c4cdc2d774c047eac8375bb09853c4d6d6df36', - limits: stagingExtraLockboxLimits, + limits: zeroLimits, }, { // usdt lockbox: '0x07e437d73e9e43ceece6ea14085b26159e3f7f31', - limits: stagingExtraLockboxLimits, + limits: zeroLimits, }, ], }; @@ -395,10 +515,9 @@ function generateSuperTokenConfig( { ...routerConfig[chain], owner: ownerByChain[chain], - type: - chain === xERC20LockboxChain - ? TokenType.XERC20Lockbox - : TokenType.XERC20, + type: xERC20LockboxChains.includes(chain) + ? TokenType.XERC20Lockbox + : TokenType.XERC20, token: xERC20AddressesByChain[chain], xERC20: { warpRouteLimits: { diff --git a/typescript/infra/config/environments/mainnet3/warp/strategies/ousdtSafeTxBuilder.yaml b/typescript/infra/config/environments/mainnet3/warp/strategies/ousdtSafeTxBuilder.yaml new file mode 100644 index 00000000000..911f8dbcc74 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/strategies/ousdtSafeTxBuilder.yaml @@ -0,0 +1,55 @@ +celo: + submitter: + chain: celo + type: gnosisSafeTxBuilder + safeAddress: '0xf1b3fc934bB46c459253fb38555A400b94909800' +optimism: + submitter: + chain: optimism + type: gnosisSafeTxBuilder + safeAddress: '0x8E3340E241880F80359AA95Ae20Dc498d3f62503' +base: + submitter: + chain: base + type: gnosisSafeTxBuilder + safeAddress: '0x125d1b64dfd7898DD06ac3E060A432691b8Fa676' +unichain: + submitter: + chain: unichain + type: gnosisSafeTxBuilder + safeAddress: '0xf306ad5bF95960188c67A30f5546D193760ca3D0' +ink: + submitter: + chain: ink + type: gnosisSafeTxBuilder + safeAddress: '0x1BBf2CE75A77b8A10dA7e73dC1F76456008010bD' +soneium: + submitter: + chain: soneium + type: gnosisSafeTxBuilder + safeAddress: '0x31Bf112F33556A0F1dc76881cfA8A36Bc2134A57' +mode: + submitter: + chain: mode + type: gnosisSafeTxBuilder + safeAddress: '0xD4c01B4753575899AD81aAca0bb2DB7796E9F7C0' +fraxtal: + submitter: + chain: fraxtal + type: gnosisSafeTxBuilder + safeAddress: '0x21C0CA5be5aC9BC6161Bf1cfE281A18Fe2190079' +superseed: + submitter: + chain: superseed + type: gnosisSafeTxBuilder + safeAddress: '0x0731a8e0DC88Df79d9643BD6C1f26cfe6fa53382' +lisk: + submitter: + chain: lisk + type: gnosisSafeTxBuilder + safeAddress: '0x6F0A0038FcDB2F1655219f1b92f7E9aD4b78Aa49' +worldchain: + submitter: + chain: worldchain + type: gnosisSafeTxBuilder + safeAddress: '0x998238aF5A2DDC7ae08Dbe4B60b82EF63A1538cd' diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index d8d385be943..9f3f05c18d7 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -80,8 +80,8 @@ export enum WarpRouteIds { ArbitrumSolanaLOGX = 'LOGX/arbitrum-solanamainnet', // TODO: uncomment after merging the staging route to registry // this has been commented out as it leads to check-warp-deploy cron job failing - // SuperTokenStaging = 'USDTSTAGING/base-celo-fraxtal-ink-lisk-mode-optimism-soneium-superseed-unichain-worldchain', - SuperUSDT = 'USDT/base-celo-fraxtal-ink-lisk-mode-optimism-soneium-superseed-unichain-worldchain', + // SuperTokenStaging = 'USDTSTAGING/base-bitlayer-celo-ethereum-fraxtal-ink-linea-lisk-mantle-metal-metis-mode-optimism-ronin-soneium-sonic-superseed-unichain-worldchain', + SuperUSDT = 'USDT/base-bitlayer-celo-ethereum-fraxtal-ink-linea-lisk-mantle-metal-metis-mode-optimism-ronin-soneium-sonic-superseed-unichain-worldchain', HyperevmSolanaSOL = 'SOL/hyperevm-solanamainnet', MintSolanaMINT = 'MINT/mint-solanamainnet', EthereumUnichainPumpBTC = 'pumpBTCuni/ethereum-unichain', diff --git a/typescript/infra/scripts/safes/governance/check-safe-signers.ts b/typescript/infra/scripts/safes/governance/check-safe-signers.ts index 4ca03f85dc6..6bfe14c67b9 100644 --- a/typescript/infra/scripts/safes/governance/check-safe-signers.ts +++ b/typescript/infra/scripts/safes/governance/check-safe-signers.ts @@ -45,55 +45,65 @@ async function main() { Object.keys(safes), ); - for (const [chain, safeAddress] of Object.entries(safes)) { - let safeSdk: Safe.default; - try { - ({ safeSdk } = await getSafeAndService( - chain, - multiProvider, - safeAddress, - )); - } catch (error) { - rootLogger.error(`[${chain}] could not get safe: ${error}`); - continue; - } + const chainViolations = await Promise.all( + Object.entries(safes).map(async ([chain, safeAddress]) => { + let safeSdk: Safe.default; + try { + ({ safeSdk } = await getSafeAndService( + chain, + multiProvider, + safeAddress, + )); + } catch (error) { + rootLogger.error(`[${chain}] could not get safe: ${error}`); + return []; + } - const currentOwners = await safeSdk.getOwners(); - const currentThreshold = await safeSdk.getThreshold(); - const expectedOwners = signers; - const { ownersToRemove, ownersToAdd } = await getOwnerChanges( - currentOwners, - expectedOwners, - ); + const [currentOwners, currentThreshold] = await Promise.all([ + safeSdk.getOwners(), + safeSdk.getThreshold(), + ]); + const expectedOwners = signers; + const { ownersToRemove, ownersToAdd } = await getOwnerChanges( + currentOwners, + expectedOwners, + ); - if (ownersToRemove.length > 0) { - violations.push({ - type: SafeConfigViolationType.unexpectedOwners, - chain, - safeAddress, - owners: ownersToRemove, - }); - } + const chainViolations: SafeConfigViolation[] = []; - if (ownersToAdd.length > 0) { - violations.push({ - type: SafeConfigViolationType.missingOwners, - chain, - safeAddress, - owners: ownersToAdd, - }); - } + if (ownersToRemove.length > 0) { + chainViolations.push({ + type: SafeConfigViolationType.unexpectedOwners, + chain, + safeAddress, + owners: ownersToRemove, + }); + } - if (threshold !== currentThreshold) { - violations.push({ - type: SafeConfigViolationType.thresholdMismatch, - chain, - safeAddress, - expected: threshold.toString(), - actual: currentThreshold.toString(), - }); - } - } + if (ownersToAdd.length > 0) { + chainViolations.push({ + type: SafeConfigViolationType.missingOwners, + chain, + safeAddress, + owners: ownersToAdd, + }); + } + + if (threshold !== currentThreshold) { + chainViolations.push({ + type: SafeConfigViolationType.thresholdMismatch, + chain, + safeAddress, + expected: threshold.toString(), + actual: currentThreshold.toString(), + }); + } + + return chainViolations; + }), + ); + + violations.push(...chainViolations.flat()); if (violations.length > 0) { // Display threshold mismatches in a table @@ -101,6 +111,7 @@ async function main() { (v) => v.type === SafeConfigViolationType.thresholdMismatch, ); if (thresholdViolations.length > 0) { + // eslint-disable-next-line no-console console.table(thresholdViolations, [ 'chain', 'safeAddress', diff --git a/typescript/infra/scripts/warp-routes/export-warp-configs.ts b/typescript/infra/scripts/warp-routes/export-warp-configs.ts index 139f5a1971e..642790bd2a4 100644 --- a/typescript/infra/scripts/warp-routes/export-warp-configs.ts +++ b/typescript/infra/scripts/warp-routes/export-warp-configs.ts @@ -3,12 +3,12 @@ import { objMap } from '@hyperlane-xyz/utils'; import { getRegistry } from '../../config/registry.js'; import { getWarpConfig, warpConfigGetterMap } from '../../config/warp.js'; -import { getArgs, withOutputFile } from '../agent-utils.js'; +import { getArgs } from '../agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; // Writes the warp configs into the Registry async function main() { - const { environment } = await withOutputFile(getArgs()).argv; + const { environment } = await getArgs().argv; const { multiProvider } = await getHyperlaneCore(environment); const envConfig = getEnvironmentConfig(environment); const registry = getRegistry(); From c8ace887c116f1fc2b65945c64a68b59fb5d741b Mon Sep 17 00:00:00 2001 From: Jason Guo <33064781+Xaroz@users.noreply.github.com> Date: Thu, 1 May 2025 11:20:18 -0400 Subject: [PATCH 115/223] chore: export HypTokenRouterConfigMailboxOptional types (#6104) ### Description Export HypTokenRouterConfigMailboxOptionalSchema and HypTokenRouterConfigMailboxOptional ### Drive-by changes ### Related issues ### Backward compatibility Yes ### Testing --- .changeset/three-schools-happen.md | 5 +++++ typescript/sdk/src/index.ts | 2 ++ typescript/sdk/src/token/types.ts | 11 ++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 .changeset/three-schools-happen.md diff --git a/.changeset/three-schools-happen.md b/.changeset/three-schools-happen.md new file mode 100644 index 00000000000..923048f1aea --- /dev/null +++ b/.changeset/three-schools-happen.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': patch +--- + +Export HypTokenRouterConfigMailboxOptionalSchema and HypTokenRouterConfigMailboxOptional diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 37e567bfd45..0a2b1a6d400 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -610,6 +610,8 @@ export { HypTokenConfigSchema, HypTokenRouterConfig, HypTokenRouterConfigSchema, + HypTokenRouterConfigMailboxOptional, + HypTokenRouterConfigMailboxOptionalSchema, isCollateralRebaseTokenConfig, isCollateralTokenConfig, isXERC20TokenConfig, diff --git a/typescript/sdk/src/token/types.ts b/typescript/sdk/src/token/types.ts index e661cdf0e25..e8c89dd321b 100644 --- a/typescript/sdk/src/token/types.ts +++ b/typescript/sdk/src/token/types.ts @@ -149,11 +149,12 @@ export function derivedIsmAddress(config: DerivedTokenRouterConfig) { : config.interchainSecurityModule.address; } -const HypTokenRouterConfigMailboxOptionalSchema = HypTokenConfigSchema.and( - GasRouterConfigSchema.extend({ - mailbox: z.string().optional(), - }), -); +export const HypTokenRouterConfigMailboxOptionalSchema = + HypTokenConfigSchema.and( + GasRouterConfigSchema.extend({ + mailbox: z.string().optional(), + }), + ); export type HypTokenRouterConfigMailboxOptional = z.infer< typeof HypTokenRouterConfigMailboxOptionalSchema From 862f14f83ff350b38f395614a59c6dd28cdc59cf Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Thu, 1 May 2025 17:45:17 +0200 Subject: [PATCH 116/223] feat: Add warp route ID lookup and validation to `warp check` command (#5395) ### Description This PR introduces `--warpRouteId` flag to `hl warp check` command that allows users to directly specify a warp route ID to check. When `--warpRouteId` is not specified, a user is prompted to search for a route ID from the registry. 1. Added a new option `warpRouteIdCommandOption` to specify warp routes by ID, making it easier to reference specific routes without needing full configuration files. 2. Modified the `check` command to accept either a warp route ID, symbol, or configuration file or , making it more flexible for different use cases, also to support running without any parameters, providing an interactive search interface that displays all available warp routes from the registry. 3. Removed the requirement for the `config` option to be mandatory in the `check` command, allowing users to use other identification methods. 4. Added `WarpRouteDeployConfigMailboxRequired` type import from SDK to ensure proper type safety when working with warp route configurations. ### Drive-by changes 1. Refactored the `runWarpRouteRead` function to separate concerns by: - Moving XERC20 limit checking to a standalone `logXerc20Limits` function - Adding input validation with clear error messages - Creating a helper function `deriveWarpRouteConfigs` to handle the common logic 6. Created a new function `getWarpRouteConfigsByCore` to specifically get warp route configurations from a core config, enabling registry-based lookup. 7. Added `validateEvmCompatibility` to centralize chain validation logic, improving code maintainability. 8. Removed `ChainSelectionMode.WARP_READ` and related resolution method from `MultiChainResolver`, simplifying the chain resolution process. [As we do not need signer for warp read anymore and as in new version of `runWarpRouteRead` we do not need context state to get warp core. 9. Removed the `warpCoreConfig` property from `CommandContext` interface, suggesting a move toward more explicit parameter passing rather than state in context. 10. Added utility function `getWarpConfigs` that provides a unified way to retrieve warp configurations from multiple sources (registry ID lookup, file paths, symbol-based lookup, or interactive search), eliminating redundant code and simplifying the configuration retrieval process. 11. Added utility function `filterWarpConfigsToMatchingChains` that ensures compatibility between configurations by comparing chain sets, highlighting mismatches with clear visual indicators, and filtering to only include chains that exist in both configs, preventing errors when configs have different chain coverage. 12. Modify `hyperlaneWarpCheckRaw` to support additional parameters: `warpCoreConfigPath` and `warpRouteId`. ### Related issues https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5320 Resolves ENG-1032 ### Backward compatibility Yes ### Testing Updated cli e2e tests --------- Co-authored-by: Morteza Shojaei --- .changeset/grumpy-cats-call.md | 5 + typescript/cli/src/commands/options.ts | 6 + typescript/cli/src/commands/warp.ts | 46 +++-- typescript/cli/src/config/warp.ts | 2 +- .../strategies/chain/MultiChainResolver.ts | 30 ---- typescript/cli/src/context/types.ts | 2 - typescript/cli/src/read/warp.ts | 170 +++++++++++------- typescript/cli/src/tests/commands/warp.ts | 10 +- .../cli/src/tests/warp/warp-check.e2e-test.ts | 152 +++++++++------- .../cli/src/tests/warp/warp-read.e2e-test.ts | 4 +- typescript/cli/src/utils/warp.ts | 165 ++++++++++++++++- 11 files changed, 403 insertions(+), 189 deletions(-) create mode 100644 .changeset/grumpy-cats-call.md diff --git a/.changeset/grumpy-cats-call.md b/.changeset/grumpy-cats-call.md new file mode 100644 index 00000000000..6348dc906ab --- /dev/null +++ b/.changeset/grumpy-cats-call.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +feat(cli): Add registry-based warp route lookup and config validation diff --git a/typescript/cli/src/commands/options.ts b/typescript/cli/src/commands/options.ts index 130006265d9..e2e67139165 100644 --- a/typescript/cli/src/commands/options.ts +++ b/typescript/cli/src/commands/options.ts @@ -265,3 +265,9 @@ export const avsChainCommandOption: Options = { demandOption: true, choices: ['holesky', 'ethereum'], }; + +export const warpRouteIdCommandOption: Options = { + type: 'string', + description: 'Warp route ID to specify the warp route', + alias: 'id', +}; diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index a01d9712c1e..9092726d757 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -21,7 +21,7 @@ import { import { evaluateIfDryRunFailure } from '../deploy/dry-run.js'; import { runWarpRouteApply, runWarpRouteDeploy } from '../deploy/warp.js'; import { log, logBlue, logCommandHeader, logGreen } from '../logger.js'; -import { runWarpRouteRead } from '../read/warp.js'; +import { getWarpRouteConfigsByCore, runWarpRouteRead } from '../read/warp.js'; import { sendTestTransfer } from '../send/transfer.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { @@ -31,7 +31,11 @@ import { writeYamlOrJson, } from '../utils/files.js'; import { selectRegistryWarpRoute } from '../utils/tokens.js'; -import { getWarpCoreConfigOrExit } from '../utils/warp.js'; +import { + filterWarpConfigsToMatchingChains, + getWarpConfigs, + getWarpCoreConfigOrExit, +} from '../utils/warp.js'; import { runVerifyWarpRoute } from '../verify/warp.js'; import { @@ -46,6 +50,7 @@ import { symbolCommandOption, warpCoreConfigCommandOption, warpDeploymentConfigCommandOption, + warpRouteIdCommandOption, } from './options.js'; import { MessageOptionsArgTypes, messageSendOptions } from './send.js'; @@ -341,9 +346,10 @@ const send: CommandModuleWithWriteContext< }; export const check: CommandModuleWithContext<{ - config: string; + config?: string; symbol?: string; warp?: string; + warpRouteId?: string; }> = { command: 'check', describe: @@ -358,35 +364,37 @@ export const check: CommandModuleWithContext<{ demandOption: false, }, config: inputFileCommandOption({ - defaultPath: DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, description: 'The path to a warp route deployment configuration file', + demandOption: false, + alias: 'wd', }), + warpRouteId: warpRouteIdCommandOption, }, - handler: async ({ context, config, symbol, warp }) => { + handler: async ({ context, symbol, warp, warpRouteId, config }) => { logCommandHeader('Hyperlane Warp Check'); - const warpRouteConfig = await readWarpRouteDeployConfig(config, context); - const onChainWarpConfig = await runWarpRouteRead({ + let { warpCoreConfig, warpDeployConfig } = await getWarpConfigs({ context, - warp, + warpRouteId, symbol, + warpDeployConfigPath: config, + warpCoreConfigPath: warp, }); - const warpCoreConfig = - context.warpCoreConfig ?? - (await getWarpCoreConfigOrExit({ - context, - warp, - symbol, - })); + ({ warpCoreConfig, warpDeployConfig } = filterWarpConfigsToMatchingChains( + warpDeployConfig, + warpCoreConfig, + )); - if (!warpCoreConfig) { - throw new Error('No warp core config found'); - } + // Get on-chain config + const onChainWarpConfig = await getWarpRouteConfigsByCore({ + context, + warpCoreConfig, + }); const expandedWarpDeployConfig = await expandWarpDeployConfig( context.multiProvider, - warpRouteConfig, + warpDeployConfig, getRouterAddressesFromWarpCoreConfig(warpCoreConfig), ); diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 25a6b32cbaf..e61ae89fc58 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -61,7 +61,7 @@ const TYPE_CHOICES = Object.values(TokenType).map((type) => ({ description: TYPE_DESCRIPTIONS[type], })); -async function fillDefaults( +export async function fillDefaults( context: CommandContext, config: ChainMap>, ): Promise> { diff --git a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts index b7caa5455da..aac6581cb4b 100644 --- a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts +++ b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts @@ -22,14 +22,12 @@ import { readYamlOrJson, runFileSelectionStep, } from '../../../utils/files.js'; -import { getWarpCoreConfigOrExit } from '../../../utils/warp.js'; import { ChainResolver } from './types.js'; enum ChainSelectionMode { AGENT_KURTOSIS, WARP_CONFIG, - WARP_READ, STRATEGY, CORE_APPLY, DEFAULT, @@ -48,8 +46,6 @@ export class MultiChainResolver implements ChainResolver { switch (this.mode) { case ChainSelectionMode.WARP_CONFIG: return this.resolveWarpRouteConfigChains(argv); - case ChainSelectionMode.WARP_READ: - return this.resolveWarpCoreConfigChains(argv); case ChainSelectionMode.AGENT_KURTOSIS: return this.resolveAgentChains(argv); case ChainSelectionMode.STRATEGY: @@ -73,28 +69,6 @@ export class MultiChainResolver implements ChainResolver { return argv.context.chains; } - private async resolveWarpCoreConfigChains( - argv: Record, - ): Promise { - if (argv.symbol || argv.warp) { - const warpCoreConfig = await getWarpCoreConfigOrExit({ - context: argv.context, - warp: argv.warp, - symbol: argv.symbol, - }); - - argv.context.warpCoreConfig = warpCoreConfig; - const chains = extractChainsFromObj(warpCoreConfig); - return chains; - } else if (argv.chain) { - return [argv.chain]; - } else { - throw new Error( - `Please specify either a symbol, chain and address or warp file`, - ); - } - } - private async resolveAgentChains( argv: Record, ): Promise { @@ -244,10 +218,6 @@ export class MultiChainResolver implements ChainResolver { return new MultiChainResolver(ChainSelectionMode.WARP_CONFIG); } - static forWarpCoreConfig(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.WARP_READ); - } - static forCoreApply(): MultiChainResolver { return new MultiChainResolver(ChainSelectionMode.CORE_APPLY); } diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index 35466b95707..a99a08802a6 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -6,7 +6,6 @@ import type { ChainMap, ChainMetadata, MultiProvider, - WarpCoreConfig, } from '@hyperlane-xyz/sdk'; export interface ContextSettings { @@ -28,7 +27,6 @@ export interface CommandContext { key?: string; // just for evm chains backward compatibility signerAddress?: string; - warpCoreConfig?: WarpCoreConfig; strategyPath?: string; } diff --git a/typescript/cli/src/read/warp.ts b/typescript/cli/src/read/warp.ts index 61778aa7994..c6bccd142fb 100644 --- a/typescript/cli/src/read/warp.ts +++ b/typescript/cli/src/read/warp.ts @@ -10,7 +10,9 @@ import { ChainName, EvmERC20WarpRouteReader, HypTokenRouterConfig, + MultiProvider, TokenStandard, + WarpCoreConfig, } from '@hyperlane-xyz/sdk'; import { isAddressEvm, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; @@ -22,82 +24,83 @@ export async function runWarpRouteRead({ context, chain, address, - warp, symbol, }: { context: CommandContext; chain?: ChainName; - warp?: string; address?: string; symbol?: string; -}): Promise> { - const { multiProvider } = context; +}): Promise> { + const hasTokenSymbol = Boolean(symbol); + const hasChainAddress = Boolean(chain && address); + + if (!hasTokenSymbol && !hasChainAddress) { + logRed( + 'Invalid input parameters. Please provide either a token symbol or both chain name and token address', + ); + process.exit(1); + } - let addresses: ChainMap; - if (symbol || warp) { - const warpCoreConfig = - context.warpCoreConfig ?? // this case is be handled by MultiChainHandler.forWarpCoreConfig() interceptor - (await getWarpCoreConfigOrExit({ + const warpCoreConfig = hasTokenSymbol + ? await getWarpCoreConfigOrExit({ context, - warp, symbol, - })); - - // TODO: merge with XERC20TokenAdapter and WarpRouteReader - const xerc20Limits = await Promise.all( - warpCoreConfig.tokens - .filter( - (t) => - t.standard === TokenStandard.EvmHypXERC20 || - t.standard === TokenStandard.EvmHypXERC20Lockbox, - ) - .map(async (t) => { - const provider = multiProvider.getProvider(t.chainName); - const router = t.addressOrDenom!; - const xerc20Address = - t.standard === TokenStandard.EvmHypXERC20Lockbox - ? await HypXERC20Lockbox__factory.connect( - router, - provider, - ).xERC20() - : await HypXERC20__factory.connect( - router, - provider, - ).wrappedToken(); - - const xerc20 = IXERC20__factory.connect(xerc20Address, provider); - const mint = await xerc20.mintingCurrentLimitOf(router); - const burn = await xerc20.burningCurrentLimitOf(router); - - const formattedLimits = objMap({ mint, burn }, (_, v) => - ethers.utils.formatUnits(v, t.decimals), - ); - - return [t.chainName, formattedLimits]; - }), - ); + }) + : undefined; - if (xerc20Limits.length > 0) { - logGray('xERC20 Limits:'); - logTable(Object.fromEntries(xerc20Limits)); - } + const addresses = warpCoreConfig + ? Object.fromEntries( + warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom!]), + ) + : { [chain!]: address! }; - addresses = Object.fromEntries( - warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom!]), - ); - } else if (chain && address) { - addresses = { - [chain]: address, - }; - } else { - logRed(`Please specify either a symbol, chain and address or warp file`); - process.exit(1); + return deriveWarpRouteConfigs(context, addresses, warpCoreConfig); +} + +export async function getWarpRouteConfigsByCore({ + context, + warpCoreConfig, +}: { + context: CommandContext; + warpCoreConfig: WarpCoreConfig; +}): Promise> { + const addresses = Object.fromEntries( + warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom!]), + ); + + return deriveWarpRouteConfigs(context, addresses, warpCoreConfig); +} + +async function deriveWarpRouteConfigs( + context: CommandContext, + addresses: ChainMap, + warpCoreConfig?: WarpCoreConfig, +): Promise> { + const { multiProvider } = context; + + validateEvmCompatibility(addresses); + + // Get XERC20 limits if warpCoreConfig is available + if (warpCoreConfig) { + await logXerc20Limits(warpCoreConfig, multiProvider); } - // Check if there any non-EVM chains in the config and exit + // Derive and return warp route config + return promiseObjAll( + objMap(addresses, async (chain, address) => + new EvmERC20WarpRouteReader(multiProvider, chain).deriveWarpRouteConfig( + address, + ), + ), + ); +} + +// Validate that all chains are EVM compatible +function validateEvmCompatibility(addresses: ChainMap): void { const nonEvmChains = Object.entries(addresses) .filter(([_, address]) => !isAddressEvm(address)) .map(([chain]) => chain); + if (nonEvmChains.length > 0) { const chainList = nonEvmChains.join(', '); logRed( @@ -107,14 +110,47 @@ export async function runWarpRouteRead({ ); process.exit(1); } +} - const config = await promiseObjAll( - objMap(addresses, async (chain, address) => - new EvmERC20WarpRouteReader(multiProvider, chain).deriveWarpRouteConfig( - address, - ), - ), +/** + * Logs XERC20 token limits for the given warp core config + */ +export async function logXerc20Limits( + warpCoreConfig: WarpCoreConfig, + multiProvider: MultiProvider, +): Promise { + const xerc20Tokens = warpCoreConfig.tokens.filter( + (t) => + t.standard === TokenStandard.EvmHypXERC20 || + t.standard === TokenStandard.EvmHypXERC20Lockbox, + ); + + if (xerc20Tokens.length === 0) { + return; + } + + // TODO: merge with XERC20TokenAdapter and WarpRouteReader + const xerc20Limits = await Promise.all( + xerc20Tokens.map(async (t) => { + const provider = multiProvider.getProvider(t.chainName); + const router = t.addressOrDenom!; + const xerc20Address = + t.standard === TokenStandard.EvmHypXERC20Lockbox + ? await HypXERC20Lockbox__factory.connect(router, provider).xERC20() + : await HypXERC20__factory.connect(router, provider).wrappedToken(); + + const xerc20 = IXERC20__factory.connect(xerc20Address, provider); + const mint = await xerc20.mintingCurrentLimitOf(router); + const burn = await xerc20.burningCurrentLimitOf(router); + + const formattedLimits = objMap({ mint, burn }, (_, v) => + ethers.utils.formatUnits(v, t.decimals), + ); + + return [t.chainName, formattedLimits]; + }), ); - return config; + logGray('xERC20 Limits:'); + logTable(Object.fromEntries(xerc20Limits)); } diff --git a/typescript/cli/src/tests/commands/warp.ts b/typescript/cli/src/tests/commands/warp.ts index ca59297128e..fb9db6a28bd 100644 --- a/typescript/cli/src/tests/commands/warp.ts +++ b/typescript/cli/src/tests/commands/warp.ts @@ -121,24 +121,32 @@ export function hyperlaneWarpRead( export function hyperlaneWarpCheckRaw({ warpDeployPath, symbol, + warpCoreConfigPath, + warpRouteId, }: { symbol?: string; warpDeployPath?: string; + warpCoreConfigPath?: string; + warpRouteId?: string; }): ProcessPromise { return $`${localTestRunCmdPrefix()} hyperlane warp check \ --registry ${REGISTRY_PATH} \ ${symbol ? ['--symbol', symbol] : ''} \ --verbosity debug \ - ${warpDeployPath ? ['--config', warpDeployPath] : ''}`; + ${warpDeployPath ? ['--config', warpDeployPath] : ''} \ + ${warpCoreConfigPath ? ['--warp', warpCoreConfigPath] : ''} \ + ${warpRouteId ? ['--warpRouteId', warpRouteId] : ''}`; } export function hyperlaneWarpCheck( warpDeployPath: string, symbol: string, + warpCoreConfigPath?: string, ): ProcessPromise { return hyperlaneWarpCheckRaw({ warpDeployPath, symbol, + warpCoreConfigPath, }); } diff --git a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts index fd7ae117a0c..57203378e43 100644 --- a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts @@ -3,7 +3,10 @@ import { Wallet } from 'ethers'; import { zeroAddress } from 'viem'; import { ERC20Test } from '@hyperlane-xyz/core'; -import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { + ChainAddresses, + createWarpRouteConfigId, +} from '@hyperlane-xyz/registry'; import { HookConfig, HookType, @@ -27,17 +30,13 @@ import { CHAIN_NAME_3, CORE_CONFIG_PATH, DEFAULT_E2E_TEST_TIMEOUT, - KeyBoardKeys, - TestPromptAction, WARP_DEPLOY_OUTPUT_PATH, deployOrUseExistingCore, deployToken, getCombinedWarpRoutePath, - handlePrompts, } from '../commands/helpers.js'; import { hyperlaneWarpApply, - hyperlaneWarpCheck, hyperlaneWarpCheckRaw, hyperlaneWarpDeploy, } from '../commands/warp.js'; @@ -50,6 +49,7 @@ describe('hyperlane warp check e2e tests', async function () { let token: ERC20Test; let tokenSymbol: string; let ownerAddress: Address; + let combinedWarpCoreConfigPath: string; let warpConfig: WarpRouteDeployConfig; before(async function () { @@ -60,10 +60,22 @@ describe('hyperlane warp check e2e tests', async function () { token = await deployToken(ANVIL_KEY, CHAIN_NAME_2); tokenSymbol = await token.symbol(); + + combinedWarpCoreConfigPath = getCombinedWarpRoutePath(tokenSymbol, [ + CHAIN_NAME_2, + CHAIN_NAME_3, + ]); }); async function deployAndExportWarpRoute(): Promise { writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); + // currently warp deploy is not writing the deploy config to the registry + // should remove this once the deploy config is written to the registry + writeYamlOrJson( + combinedWarpCoreConfigPath.replace('-config.yaml', '-deploy.yaml'), + warpConfig, + ); + await hyperlaneWarpDeploy(WARP_DEPLOY_OUTPUT_PATH); return warpConfig; @@ -87,62 +99,73 @@ describe('hyperlane warp check e2e tests', async function () { }; }); - describe('hyperlane warp check --config ...', () => { - it(`should exit early if no symbol, chain or warp file have been provided`, async function () { + describe('hyperlane warp check --config ... and hyperlane warp check --warp ...', () => { + const expectedError = + 'Both --config/-wd and --warp/-wc must be provided together when using individual file paths'; + it(`should require both warp core & warp deploy config paths to be provided together`, async function () { await deployAndExportWarpRoute(); - const output = await hyperlaneWarpCheckRaw({ + const output1 = await hyperlaneWarpCheckRaw({ warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, }) .stdio('pipe') .nothrow(); - expect(output.exitCode).to.equal(1); - expect(output.text()).to.include( - 'Please specify either a symbol, chain and address or warp file', - ); + const output2 = await hyperlaneWarpCheckRaw({ + warpCoreConfigPath: combinedWarpCoreConfigPath, + }) + .stdio('pipe') + .nothrow(); + + expect(output1.exitCode).to.equal(1); + expect(output1.text()).to.include(expectedError); + expect(output2.exitCode).to.equal(1); + expect(output2.text()).to.include(expectedError); }); }); - describe('hyperlane warp check --symbol ... --config ...', () => { + describe('hyperlane warp check --symbol ...', () => { it(`should not find any differences between the on chain config and the local one`, async function () { await deployAndExportWarpRoute(); - const steps: TestPromptAction[] = [ - { - check: (currentOutput) => - currentOutput.includes('Please enter the private key for chain'), - input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, - }, - { - check: (currentOutput) => - currentOutput.includes('Please enter the private key for chain'), - input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, - }, - ]; - - const output = hyperlaneWarpCheckRaw({ + // only one route exists for this token so no need to interact with prompts + const output = await hyperlaneWarpCheckRaw({ symbol: tokenSymbol, - warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, }) .stdio('pipe') .nothrow(); - const finalOutput = await handlePrompts(output, steps); + expect(output.exitCode).to.equal(0); + expect(output.text()).to.include('No violations found'); + }); + }); + + describe('hyperlane warp check --warpRouteId ...', () => { + it(`should not find any differences between the on chain config and the local one`, async function () { + await deployAndExportWarpRoute(); + + const output = await hyperlaneWarpCheckRaw({ + warpRouteId: createWarpRouteConfigId(tokenSymbol, [ + CHAIN_NAME_2, + CHAIN_NAME_3, + ]), + }) + .stdio('pipe') + .nothrow(); - expect(finalOutput.exitCode).to.equal(0); - expect(finalOutput.text()).to.include('No violations found'); + expect(output.exitCode).to.equal(0); + expect(output.text()).to.include('No violations found'); }); }); - describe('hyperlane warp check --symbol ... --config ... --key ...', () => { + describe('hyperlane warp check --config ... --warp ...', () => { it(`should not find any differences between the on chain config and the local one`, async function () { await deployAndExportWarpRoute(); - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ); + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }); expect(output.exitCode).to.equal(0); expect(output.text()).to.includes('No violations found'); @@ -159,10 +182,10 @@ describe('hyperlane warp check e2e tests', async function () { it(`should not find any differences between the on chain config and the local one`, async function () { await deployAndExportWarpRoute(); - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ); + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }); expect(output.exitCode).to.equal(0); expect(output.text()).to.includes('No violations found'); @@ -183,10 +206,10 @@ describe('hyperlane warp check e2e tests', async function () { it(`should not find any differences between the on chain config and the local one`, async function () { await deployAndExportWarpRoute(); - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ); + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }); expect(output.exitCode).to.equal(0); expect(output.text()).to.includes('No violations found'); @@ -203,10 +226,10 @@ describe('hyperlane warp check e2e tests', async function () { const expectedActualText = `ACTUAL: "${zeroAddress.toLowerCase()}"\n`; writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig); - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ) + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }) .stdio('pipe') .nothrow(); @@ -226,10 +249,10 @@ describe('hyperlane warp check e2e tests', async function () { writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig); - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ).nothrow(); + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }).nothrow(); expect(output.exitCode).to.equal(1); expect(output.text().includes(expectedDiffText)).to.be.true; @@ -264,10 +287,10 @@ describe('hyperlane warp check e2e tests', async function () { const expectedDiffText = ` EXPECTED: address: "${warpCore.tokens[0].addressOrDenom!.toLowerCase()}"`; - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ).nothrow(); + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }).nothrow(); expect(output.exitCode).to.equal(1); expect(output.text()).to.includes(expectedDiffText); @@ -295,11 +318,10 @@ describe('hyperlane warp check e2e tests', async function () { const expectedDiffText = `EXPECTED: "${wrongOwner.toLowerCase()}"\n`; const expectedActualText = `ACTUAL: "${actualOwner.toLowerCase()}"\n`; - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ).nothrow(); - + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }).nothrow(); expect(output.exitCode).to.equal(1); expect(output.text().includes(expectedDiffText)).to.be.true; expect(output.text().includes(expectedActualText)).to.be.true; @@ -334,10 +356,10 @@ describe('hyperlane warp check e2e tests', async function () { const expectedDiffText = `EXPECTED: "${wrongOwner.toLowerCase()}"\n`; const expectedActualText = `ACTUAL: "${actualOwner.toLowerCase()}"\n`; - const output = await hyperlaneWarpCheck( - WARP_DEPLOY_OUTPUT_PATH, - tokenSymbol, - ).nothrow(); + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }).nothrow(); expect(output.exitCode).to.equal(1); expect(output.text().includes(expectedDiffText)).to.be.true; diff --git a/typescript/cli/src/tests/warp/warp-read.e2e-test.ts b/typescript/cli/src/tests/warp/warp-read.e2e-test.ts index 8d0066f2e90..0b0c82fd5ee 100644 --- a/typescript/cli/src/tests/warp/warp-read.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-read.e2e-test.ts @@ -58,7 +58,7 @@ describe('hyperlane warp read e2e tests', async function () { }); describe('hyperlane warp read --config ...', () => { - it('should exit early if no symbol, chain or warp file have been provided', async () => { + it('should exit early if no symbol or no chain and address', async () => { await hyperlaneWarpDeploy(WARP_CONFIG_PATH_2); const output = await hyperlaneWarpReadRaw({ @@ -67,7 +67,7 @@ describe('hyperlane warp read e2e tests', async function () { expect(output.exitCode).to.equal(1); expect(output.text()).to.include( - 'Please specify either a symbol, chain and address or warp file', + 'Invalid input parameters. Please provide either a token symbol or both chain name and token address', ); }); }); diff --git a/typescript/cli/src/utils/warp.ts b/typescript/cli/src/utils/warp.ts index 08888628e5d..310e96c633d 100644 --- a/typescript/cli/src/utils/warp.ts +++ b/typescript/cli/src/utils/warp.ts @@ -1,6 +1,23 @@ -import { WarpCoreConfig } from '@hyperlane-xyz/sdk'; +import search from '@inquirer/search'; -import { readWarpCoreConfig } from '../config/warp.js'; +import { filterWarpRoutesIds } from '@hyperlane-xyz/registry'; +import { + WarpCoreConfig, + WarpRouteDeployConfigMailboxRequired, + WarpRouteDeployConfigMailboxRequiredSchema, +} from '@hyperlane-xyz/sdk'; +import { + assert, + intersection, + objFilter, + setEquality, +} from '@hyperlane-xyz/utils'; + +import { + fillDefaults, + readWarpCoreConfig, + readWarpRouteDeployConfig, +} from '../config/warp.js'; import { CommandContext } from '../context/types.js'; import { logRed } from '../logger.js'; @@ -33,3 +50,147 @@ export async function getWarpCoreConfigOrExit({ return warpCoreConfig; } + +/** + * Gets both warp configs based on the provided inputs. Handles all cases: + * - warpRouteId: gets configs directly from registry + * - warpDeployConfigPath & warpCoreConfigPath: reads from files + * - symbol: prompts user to select from matching routes + * - no inputs: prompts user to search and select from all routes + */ +export async function getWarpConfigs({ + context, + warpRouteId, + warpDeployConfigPath, + warpCoreConfigPath, + symbol, +}: { + context: CommandContext; + warpRouteId?: string; + warpDeployConfigPath?: string; + warpCoreConfigPath?: string; + symbol?: string; +}): Promise<{ + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; + warpCoreConfig: WarpCoreConfig; +}> { + if (warpDeployConfigPath || warpCoreConfigPath) { + if (!warpDeployConfigPath || !warpCoreConfigPath) { + throw new Error( + 'Both --config/-wd and --warp/-wc must be provided together when using individual file paths', + ); + } + const warpDeployConfig = await readWarpRouteDeployConfig( + warpDeployConfigPath, + context, + ); + const warpCoreConfig = readWarpCoreConfig(warpCoreConfigPath); + return { warpDeployConfig, warpCoreConfig }; + } + + let selectedId = warpRouteId; + if (!selectedId) { + const { ids: routeIds } = filterWarpRoutesIds( + (await context.registry.listRegistryContent()).deployments.warpRoutes, + symbol ? { symbol } : undefined, + ); + + assert(routeIds.length !== 0, 'No valid warp routes found in registry'); + + selectedId = + routeIds.length === 1 + ? routeIds[0] + : ((await search({ + message: 'Select a warp route:', + source: (term) => { + return routeIds.filter((id) => + id.toLowerCase().includes(term?.toLowerCase() || ''), + ); + }, + pageSize: 20, + })) as string); + } + + const warpCoreConfig = await context.registry.getWarpRoute(selectedId); + assert(warpCoreConfig, `Missing warp config for warp route ${selectedId}.`); + const warpDeployConfig = + await context.registry.getWarpDeployConfig(selectedId); + assert( + warpDeployConfig, + `Missing warp deploy config for warp route ${selectedId}.`, + ); + + const filledConfig = await fillDefaults(context, warpDeployConfig); + const validatedConfig = + WarpRouteDeployConfigMailboxRequiredSchema.parse(filledConfig); + + return { + warpDeployConfig: validatedConfig, + warpCoreConfig, + }; +} + +/** + * Compares chains between warp deploy and core configs, filters them to only include matching chains, + * and logs warnings if there are mismatches. + * @param warpDeployConfig The warp deployment configuration + * @param warpCoreConfig The warp core configuration + * @returns The filtered warp deploy and core configs containing only matching chains + */ +export function filterWarpConfigsToMatchingChains( + warpDeployConfig: WarpRouteDeployConfigMailboxRequired, + warpCoreConfig: WarpCoreConfig, +): { + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; + warpCoreConfig: WarpCoreConfig; +} { + const deployConfigChains = Object.keys(warpDeployConfig); + const coreConfigChains = warpCoreConfig.tokens.map( + (t: { chainName: string }) => t.chainName, + ); + + const deploySet = new Set(deployConfigChains); + const coreSet = new Set(coreConfigChains); + + if (!setEquality(deploySet, coreSet)) { + logRed( + 'Warning: Chain mismatch between warp core config and warp deploy config:', + ); + logRed('──────────────────────'); + logRed('Deploy config chains:'); + deployConfigChains.forEach((chain: string) => logRed(` - ${chain}`)); + logRed('Core config chains:'); + coreConfigChains.forEach((chain: string) => logRed(` - ${chain}`)); + + const matchingChains = intersection(deploySet, coreSet); + if (matchingChains.size === 0) { + logRed('Error: No matching chains found between configs'); + process.exit(1); + } + + logRed( + `Continuing with check for matching chains: ${Array.from( + matchingChains, + ).join(', ')}\n`, + ); + + // Filter configs to only include matching chains + const filteredWarpDeployConfig = objFilter( + warpDeployConfig, + (chain: string, _v): _v is any => matchingChains.has(chain), + ); + const filteredWarpCoreConfig = { + ...warpCoreConfig, + tokens: warpCoreConfig.tokens.filter((token: { chainName: string }) => + matchingChains.has(token.chainName), + ), + }; + + return { + warpDeployConfig: filteredWarpDeployConfig, + warpCoreConfig: filteredWarpCoreConfig, + }; + } + + return { warpDeployConfig, warpCoreConfig }; +} From f692b35be7bf9d7083877bfe259e18e8bf124ab6 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Thu, 1 May 2025 16:51:49 +0100 Subject: [PATCH 117/223] feat: skip typescript tests for rust-only PRs (#6084) ### Description feat: skip typescript tests for rust-only PRs - detect if the PR has _only_ rust changes - if only rust changes, skip: - yarn-test - cli-install-test - cli-e2e matrix - cosmos-sdk-e2e - coverage - env-test matrix context: https://hyperlaneworkspace.slack.com/archives/C08H0T2FL2V/p1746007815024329 A second order benefit is the reduction in `ubuntu-latest` usage, which will hopefully reduce the waiting times for these runners in general ### Drive-by changes fix the current infra test diffing ### Related issues ### Backward compatibility ### Testing https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/runs/14753714224?pr=6084 ![image](https://github.com/user-attachments/assets/85c2fff3-1c8e-4d9c-adb8-8846937eb475) ![image](https://github.com/user-attachments/assets/c01f9c24-2ae7-4df0-a7dd-948bdb6e7ee4) --- .github/actions/check-job-status/action.yml | 21 +++ .github/workflows/test.yml | 186 +++++++++++++++++--- 2 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 .github/actions/check-job-status/action.yml diff --git a/.github/actions/check-job-status/action.yml b/.github/actions/check-job-status/action.yml new file mode 100644 index 00000000000..a4c89660a28 --- /dev/null +++ b/.github/actions/check-job-status/action.yml @@ -0,0 +1,21 @@ +name: 'Check Job Status' +description: 'Check the result of a job and print a message' +inputs: + job_name: + description: 'The name of the job' + required: true + result: + description: 'The result of the job' + required: true +runs: + using: 'composite' + steps: + - name: Check job status + shell: bash + run: | + if [[ "${{ inputs.result }}" == "skipped" ]]; then + echo "${{ inputs.job_name }} was skipped!" + elif [[ "${{ inputs.result }}" != "success" ]]; then + echo "${{ inputs.job_name }} failed" + exit 1 + fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8914684b7a..4e4a396b315 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -104,10 +104,75 @@ jobs: run: | rm -f changed_files.txt - yarn-test: + set-base-sha: + runs-on: ubuntu-latest + outputs: + base_sha: ${{ steps.determine-base-sha.outputs.base_sha }} + current_sha: ${{ steps.determine-base-sha.outputs.current_sha }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Determine BASE_SHA + id: determine-base-sha + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo "base_sha=${{ github.event.pull_request.base.sha }}" >> $GITHUB_OUTPUT + echo "current_sha=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" == "merge_group" ]; then + echo "base_sha=${{ github.event.merge_group.base_sha }}" >> $GITHUB_OUTPUT + echo "current_sha=${{ github.event.merge_group.head_sha }}" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" == "push" ]; then + echo "base_sha=$${{ github.event.push.before }}" >> $GITHUB_OUTPUT + echo "current_sha=${{ github.event.push.after }}" >> $GITHUB_OUTPUT + else + echo "base_sha=$(git rev-parse HEAD^)" >> $GITHUB_OUTPUT + echo "current_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + fi + + rust-only: + needs: [set-base-sha] + outputs: + only_rust: ${{ steps.check-rust-only.outputs.only_rust }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 + + # Check if changes are ONLY rust + - name: Check if changes are ONLY rust + id: check-rust-only + run: | + # Check for Rust changes + if git diff ${{ needs.set-base-sha.outputs.base_sha }}..${{ needs.set-base-sha.outputs.current_sha }} --name-only | grep -qE '^rust/'; then + # Check if there are only Rust changes + if ! git diff ${{ needs.set-base-sha.outputs.base_sha }}..${{ needs.set-base-sha.outputs.current_sha }} --name-only | grep -qvE '^rust/'; then + echo "only_rust=true" >> $GITHUB_OUTPUT + echo "Only Rust changes detected." + else + echo "only_rust=false" >> $GITHUB_OUTPUT + fi + else + echo "only_rust=false" >> $GITHUB_OUTPUT + fi + + # If the push event is to main, fallback to rust_only as false. + - name: Check if push event to main + id: check-push-main + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + echo "only_rust=false" >> $GITHUB_OUTPUT + echo "Push event to main detected. Setting only_rust to false." + + yarn-test-run: runs-on: ubuntu-latest - needs: [yarn-install] + needs: [rust-only] timeout-minutes: 10 + if: needs.rust-only.outputs.only_rust == 'false' steps: - uses: actions/checkout@v4 with: @@ -129,12 +194,25 @@ jobs: - name: Unit Tests run: yarn test:ci + yarn-test: + runs-on: ubuntu-latest + needs: [yarn-test-run] + if: always() + steps: + - uses: actions/checkout@v4 + - name: Check yarn-test status + uses: ./.github/actions/check-job-status + with: + job_name: 'Yarn Test' + result: ${{ needs.yarn-test-run.result }} + infra-test: runs-on: ubuntu-latest - needs: [yarn-install] + needs: [yarn-install, set-base-sha] env: GRAFANA_SERVICE_ACCOUNT_TOKEN: ${{ secrets.GRAFANA_SERVICE_ACCOUNT_TOKEN }} - PR_BASE: ${{ github.base_ref }} + BALANCE_CHANGES: false + WARP_CONFIG_CHANGES: false steps: - uses: actions/checkout@v4 with: @@ -142,28 +220,37 @@ jobs: submodules: recursive fetch-depth: 0 + - name: Check for balance/config changes + run: | + if git diff ${{ needs.set-base-sha.outputs.base_sha }}..${{ needs.set-base-sha.outputs.current_sha }} --name-only | grep -qE '^typescript/infra/config/environments/mainnet3/balances|^.registryrc$'; then + echo "BALANCE_CHANGES=true" >> $GITHUB_ENV + fi + if git diff ${{ needs.set-base-sha.outputs.base_sha }}..${{ needs.set-base-sha.outputs.current_sha }} --name-only | grep -qE '^typescript/infra/|^.registryrc$'; then + echo "WARP_CONFIG_CHANGES=true" >> $GITHUB_ENV + fi + - name: yarn-build uses: ./.github/actions/yarn-build-with-cache + if: env.BALANCE_CHANGES == 'true' || env.WARP_CONFIG_CHANGES == 'true' with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Checkout registry + if: env.BALANCE_CHANGES == 'true' || env.WARP_CONFIG_CHANGES == 'true' uses: ./.github/actions/checkout-registry - name: Balance Tests - run: | - if git diff $PR_BASE...HEAD --name-only | grep -qE '^typescript/infra/config/environments/mainnet3/balances|^.registryrc$'; then - yarn --cwd typescript/infra test:balance - fi + if: env.BALANCE_CHANGES == 'true' + run: yarn --cwd typescript/infra test:balance - name: Warp Config Tests - run: | - if git diff $PR_BASE...HEAD --name-only | grep -qE '^typescript/infra/|^.registryrc$'; then - yarn --cwd typescript/infra test:warp - fi + if: env.WARP_CONFIG_CHANGES == 'true' + run: yarn --cwd typescript/infra test:warp - cli-install-test: + cli-install-test-run: runs-on: ubuntu-latest + needs: [rust-only] + if: needs.rust-only.outputs.only_rust == 'false' steps: - uses: actions/checkout@v4 with: @@ -178,8 +265,22 @@ jobs: - name: Test run the CLI run: hyperlane --version + cli-install-test: + runs-on: ubuntu-latest + needs: [cli-install-test-run] + if: always() + steps: + - uses: actions/checkout@v4 + - name: Check cli-install-test status + uses: ./.github/actions/check-job-status + with: + job_name: 'CLI Install Test' + result: ${{ needs.cli-install-test-run.result }} + cli-e2e-matrix: runs-on: ubuntu-latest + needs: [rust-only] + if: needs.rust-only.outputs.only_rust == 'false' strategy: fail-fast: false matrix: @@ -234,17 +335,20 @@ jobs: cli-e2e: runs-on: ubuntu-latest - needs: cli-e2e-matrix + needs: [cli-e2e-matrix] if: always() steps: + - uses: actions/checkout@v4 - name: Check cli-e2e matrix status - if: ${{ needs.cli-e2e-matrix.result != 'success' }} - run: | - echo "CLI E2E tests failed" - exit 1 + uses: ./.github/actions/check-job-status + with: + job_name: 'CLI E2E' + result: ${{ needs.cli-e2e-matrix.result }} - cosmos-sdk-e2e: + cosmos-sdk-e2e-run: runs-on: ubuntu-latest + needs: [rust-only] + if: needs.rust-only.outputs.only_rust == 'false' steps: - uses: actions/checkout@v4 with: @@ -260,6 +364,18 @@ jobs: - name: Cosmos SDK e2e tests run: yarn --cwd typescript/cosmos-sdk test:e2e + cosmos-sdk-e2e: + runs-on: ubuntu-latest + needs: [cosmos-sdk-e2e-run] + if: always() + steps: + - uses: actions/checkout@v4 + - name: Check cosmos-sdk-e2e status + uses: ./.github/actions/check-job-status + with: + job_name: 'Cosmos SDK E2E' + result: ${{ needs.cosmos-sdk-e2e-run.result }} + agent-configs: runs-on: ubuntu-latest strategy: @@ -464,8 +580,10 @@ jobs: echo "E2E tests failed" exit 1 - env-test: + env-test-matrix: runs-on: ubuntu-latest + needs: [rust-only] + if: needs.rust-only.outputs.only_rust == 'false' env: MAINNET3_ARBITRUM_RPC_URLS: ${{ secrets.MAINNET3_ARBITRUM_RPC_URLS }} MAINNET3_OPTIMISM_RPC_URLS: ${{ secrets.MAINNET3_OPTIMISM_RPC_URLS }} @@ -510,8 +628,22 @@ jobs: on_retry_command: | echo "Test failed, waiting before retry..." - coverage: + env-test: runs-on: ubuntu-latest + needs: env-test-matrix + if: always() + steps: + - uses: actions/checkout@v4 + - name: Check env-test matrix status + uses: ./.github/actions/check-job-status + with: + job_name: 'Env Test' + result: ${{ needs.env-test-matrix.result }} + + coverage-run: + runs-on: ubuntu-latest + needs: [rust-only] + if: needs.rust-only.outputs.only_rust == 'false' steps: - uses: actions/checkout@v4 with: @@ -535,3 +667,15 @@ jobs: uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} + + coverage: + runs-on: ubuntu-latest + needs: [coverage-run] + if: always() + steps: + - uses: actions/checkout@v4 + - name: Check coverage status + uses: ./.github/actions/check-job-status + with: + job_name: 'Coverage' + result: ${{ needs.coverage-run.result }} From b960238d6fa09097f3b95afe71222b6dec48a780 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 2 May 2025 09:40:56 +0100 Subject: [PATCH 118/223] feat: ugly Squads verification tooling (#5984) ### Description For each chain, you can run a command to parse the proposed txs. Usage: 1. `cd rust/sealevel/client` 2. Fill in this command with the correct RPC URL and `--tx-pubkeys` flags. You can supply as many tx pubkeys as you want. a tx pubkey is meant to be the address from the backup UI here relating to a tx Screen Shot 2025-04-20 at 11 45 55 PM Now run it, e.g.: ``` $ cargo run -- squads verify --environment mainnet3 --environments-dir ../environments --chain --chain-config-file ../environments/mainnet3/chain-config.json --tx-pubkeys ZKhe8bVVnicv219yYDuWkx1C8n2m1N9FsLLnMXj2bqd --tx-pubkeys 7qmqfH7yz9nTdDYhkdU9bFV7TTsBPAzNvAdjrtCcjw3A --tx-pubkeys CWFmX39RiZfpu8NsGuwsbJjuYJuh5mpsVsZLKTMHeTMK --tx-pubkeys DeD7nRUmgvQshgdvH7jxGeR2shZFok48YUh6XJ9iaV1Y --tx-pubkeys 2YySbjssWsjKq8jB1GmmYXA2xiHrzCNJohYiBkH8rke2 ``` Example output here: ``` $ cargo run -- squads verify --environment mainnet3 --environments-dir ../environments --chain soon --chain-config-file ../environments/mainnet3/chain-config.json --tx-pubkeys ZKhe8bVVnicv219yYDuWkx1C8n2m1N9FsLLnMXj2bqd --tx-pubkeys 7qmqfH7yz9nTdDYhkdU9bFV7TTsBPAzNvAdjrtCcjw3A --tx-pubkeys CWFmX39RiZfpu8NsGuwsbJjuYJuh5mpsVsZLKTMHeTMK --tx-pubkeys DeD7nRUmgvQshgdvH7jxGeR2shZFok48YUh6XJ9iaV1Y --tx-pubkeys 2YySbjssWsjKq8jB1GmmYXA2xiHrzCNJohYiBkH8rke2 ================================================= ================================================= ================================================= Tx proposal pubkey: ZKhe8bVVnicv219yYDuWkx1C8n2m1N9FsLLnMXj2bqd (1 of 5) Raw vault transaction: VaultTransaction { multisig: LBFgZb6r4JsJaWToMz2qvx681zMgDA7rNXzAKdk4fve, creator: 9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf, index: 2, bump: 254, vault_index: 0, vault_bump: 252, ephemeral_signer_bumps: [], message: VaultTransactionMessage { num_signers: 1, num_writable_signers: 1, num_writable_non_signers: 1, account_keys: [7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y, AYtK78d8vu7jJNwXKi6RPCjPosEKjbepUJJtqr2cdrC7, ComputeBudget111111111111111111111111111111, Mj7GE6LZiJUZFTgabmNkFoY2o6JCEwCMKopSUUC3kqj], instructions: [MultisigCompiledInstruction { program_id_index: 2, account_indexes: [], data: [2, 192, 92, 21, 0] }, MultisigCompiledInstruction { program_id_index: 3, account_indexes: [1, 0], data: [9, 1, 97, 29, 89, 141, 157, 135, 6, 141, 72, 231, 152, 240, 73, 196, 15, 59, 133, 176, 125, 125, 137, 49, 158, 105, 232, 231, 55, 81, 20, 70, 124, 155] }], address_table_lookups: [] } } ------- Instructions: ------ Instruction 1 of 2 Program called: "ComputeBudget111111111111111111111111111111 (Compute Budget program)" ComputeBudget instruction (e.g. setting limit or price), can ignore ------ Instruction 2 of 2 Program called: "Mj7GE6LZiJUZFTgabmNkFoY2o6JCEwCMKopSUUC3kqj (Mailbox)" Mailbox instruction: TransferOwnership(Some(7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y)) Transfer ownership to Some("7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y (NEW post-TGE owner - soon)") ================================================= ================================================= ================================================= Tx proposal pubkey: 7qmqfH7yz9nTdDYhkdU9bFV7TTsBPAzNvAdjrtCcjw3A (2 of 5) Raw vault transaction: VaultTransaction { multisig: LBFgZb6r4JsJaWToMz2qvx681zMgDA7rNXzAKdk4fve, creator: 9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf, index: 4, bump: 253, vault_index: 0, vault_bump: 252, ephemeral_signer_bumps: [], message: VaultTransactionMessage { num_signers: 1, num_writable_signers: 1, num_writable_non_signers: 1, account_keys: [E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62, DFvv3YYnHP5UX77vjdB7Fd58q2ynNs1iXhXxMEesaKwU, BPFLoaderUpgradeab1e11111111111111111111111, 7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y], instructions: [MultisigCompiledInstruction { program_id_index: 2, account_indexes: [1, 0, 3], data: [4, 0, 0, 0] }], address_table_lookups: [] } } ------- Instructions: ------ Instruction 1 of 1 Program called: "BPFLoaderUpgradeab1e11111111111111111111111 (BPF Loader Upgradeable program)" BPF Loader Upgradeable instruction (how the system manages programs) Setting the upgrade authority: Target program: DFvv3YYnHP5UX77vjdB7Fd58q2ynNs1iXhXxMEesaKwU (Program Data account of Mj7GE6LZiJUZFTgabmNkFoY2o6JCEwCMKopSUUC3kqj (Mailbox)) Old upgrade authority: E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62 (OLD pre-TGE owner - soon) New upgrade authority: 7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y (NEW post-TGE owner - soon) ================================================= ================================================= ================================================= Tx proposal pubkey: CWFmX39RiZfpu8NsGuwsbJjuYJuh5mpsVsZLKTMHeTMK (3 of 5) Raw vault transaction: VaultTransaction { multisig: LBFgZb6r4JsJaWToMz2qvx681zMgDA7rNXzAKdk4fve, creator: 9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf, index: 5, bump: 254, vault_index: 0, vault_bump: 252, ephemeral_signer_bumps: [], message: VaultTransactionMessage { num_signers: 1, num_writable_signers: 1, num_writable_non_signers: 1, account_keys: [E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62, GmtSBd2w11CrPY4RoKiMWXKfsANQ6Te4n1CQjUBzoQSh, BPFLoaderUpgradeab1e11111111111111111111111, 7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y], instructions: [MultisigCompiledInstruction { program_id_index: 2, account_indexes: [1, 0, 3], data: [4, 0, 0, 0] }], address_table_lookups: [] } } ------- Instructions: ------ Instruction 1 of 1 Program called: "BPFLoaderUpgradeab1e11111111111111111111111 (BPF Loader Upgradeable program)" BPF Loader Upgradeable instruction (how the system manages programs) Setting the upgrade authority: Target program: GmtSBd2w11CrPY4RoKiMWXKfsANQ6Te4n1CQjUBzoQSh (Program Data account of GFqF38mSacfvbJRKkhmjJvSkzTEKemSNVoWi4Q94ZPvz (Validator Announce)) Old upgrade authority: E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62 (OLD pre-TGE owner - soon) New upgrade authority: 7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y (NEW post-TGE owner - soon) ================================================= ================================================= ================================================= Tx proposal pubkey: DeD7nRUmgvQshgdvH7jxGeR2shZFok48YUh6XJ9iaV1Y (4 of 5) Raw vault transaction: VaultTransaction { multisig: LBFgZb6r4JsJaWToMz2qvx681zMgDA7rNXzAKdk4fve, creator: 9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf, index: 3, bump: 255, vault_index: 0, vault_bump: 252, ephemeral_signer_bumps: [], message: VaultTransactionMessage { num_signers: 1, num_writable_signers: 1, num_writable_non_signers: 1, account_keys: [7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y, 5erqPoUNZs5CXxo3WoMeYhzjhVG1R25nW6rFLa4z3Uu1, ComputeBudget111111111111111111111111111111, 4fPoa26ayqY1cwY3AcqUPvdLLNHpjJGdDQevtnTLAAnH], instructions: [MultisigCompiledInstruction { program_id_index: 2, account_indexes: [], data: [2, 192, 92, 21, 0] }, MultisigCompiledInstruction { program_id_index: 3, account_indexes: [0, 1], data: [1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 97, 29, 89, 141, 157, 135, 6, 141, 72, 231, 152, 240, 73, 196, 15, 59, 133, 176, 125, 125, 137, 49, 158, 105, 232, 231, 55, 81, 20, 70, 124, 155] }], address_table_lookups: [] } } ------- Instructions: ------ Instruction 1 of 2 Program called: "ComputeBudget111111111111111111111111111111 (Compute Budget program)" ComputeBudget instruction (e.g. setting limit or price), can ignore ------ Instruction 2 of 2 Program called: "4fPoa26ayqY1cwY3AcqUPvdLLNHpjJGdDQevtnTLAAnH (Multisig ISM Message ID)" Multisig ISM instruction: TransferOwnership(Some(7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y)) Transfer ownership to Some("7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y (NEW post-TGE owner - soon)") ================================================= ================================================= ================================================= Tx proposal pubkey: 2YySbjssWsjKq8jB1GmmYXA2xiHrzCNJohYiBkH8rke2 (5 of 5) Raw vault transaction: VaultTransaction { multisig: LBFgZb6r4JsJaWToMz2qvx681zMgDA7rNXzAKdk4fve, creator: 9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf, index: 6, bump: 255, vault_index: 0, vault_bump: 252, ephemeral_signer_bumps: [], message: VaultTransactionMessage { num_signers: 1, num_writable_signers: 1, num_writable_non_signers: 1, account_keys: [E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62, 6NGzvvDYJAWh79mCeb1Q7xuHcbbsjg27fWB4EXGvHQJA, BPFLoaderUpgradeab1e11111111111111111111111, 7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y], instructions: [MultisigCompiledInstruction { program_id_index: 2, account_indexes: [1, 0, 3], data: [4, 0, 0, 0] }], address_table_lookups: [] } } ------- Instructions: ------ Instruction 1 of 1 Program called: "BPFLoaderUpgradeab1e11111111111111111111111 (BPF Loader Upgradeable program)" BPF Loader Upgradeable instruction (how the system manages programs) Setting the upgrade authority: Target program: 6NGzvvDYJAWh79mCeb1Q7xuHcbbsjg27fWB4EXGvHQJA (Program Data account of 4fPoa26ayqY1cwY3AcqUPvdLLNHpjJGdDQevtnTLAAnH (Multisig ISM Message ID)) Old upgrade authority: E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62 (OLD pre-TGE owner - soon) New upgrade authority: 7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y (NEW post-TGE owner - soon) ``` For each transaction, you should verify: 1. the program ID is what you expect (search it in the monorepo or registry to find out what it is, e.g. a Mailbox or ISM etc). For the upgrade authority setting, this will always be `BPFLoaderUpgradeab1e11111111111111111111111` 2. Make sure the instruction does what you expect, e.g. a TransferOwnership(), there are only known pubkeys, etc ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/sealevel/client/src/context.rs | 5 + rust/sealevel/client/src/main.rs | 6 +- rust/sealevel/client/src/squads.rs | 449 ++++++++++++++++++ .../solanamainnet/core/program-ids.json | 2 +- 4 files changed, 460 insertions(+), 2 deletions(-) create mode 100644 rust/sealevel/client/src/squads.rs diff --git a/rust/sealevel/client/src/context.rs b/rust/sealevel/client/src/context.rs index c3912ee4bdd..72ef00412a2 100644 --- a/rust/sealevel/client/src/context.rs +++ b/rust/sealevel/client/src/context.rs @@ -174,6 +174,11 @@ impl<'ctx, 'rpc> TxnBuilder<'ctx, 'rpc> { "\t==== Transaction in base58: ====\n\t{}", bs58::encode(bincode::serialize(&txn).unwrap()).into_string() ); + + println!( + "\t==== Message in base58: ====\n\t{}", + bs58::encode(bincode::serialize(&message).unwrap()).into_string() + ); } pub(crate) fn send_with_payer(self) -> Option { diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index 0b680b37f07..626d45e0e8f 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -58,6 +58,7 @@ use hyperlane_sealevel_validator_announce::{ replay_protection_pda_seeds, validator_announce_pda_seeds, validator_storage_locations_pda_seeds, }; +use squads::{process_squads_cmd, SquadsCmd}; use warp_route::parse_token_account_data; mod artifacts; @@ -69,6 +70,7 @@ mod igp; mod multisig_ism; mod router; mod serde; +mod squads; mod warp_route; use crate::helloworld::process_helloworld_cmd; @@ -113,10 +115,11 @@ enum HyperlaneSealevelCmd { MultisigIsmMessageId(MultisigIsmMessageIdCmd), WarpRoute(WarpRouteCmd), HelloWorld(HelloWorldCmd), + Squads(SquadsCmd), } #[derive(Args)] -struct EnvironmentArgs { +pub struct EnvironmentArgs { #[arg(long)] environment: String, #[arg(long)] @@ -775,6 +778,7 @@ fn main() { HyperlaneSealevelCmd::WarpRoute(cmd) => process_warp_route_cmd(ctx, cmd), HyperlaneSealevelCmd::HelloWorld(cmd) => process_helloworld_cmd(ctx, cmd), HyperlaneSealevelCmd::Igp(cmd) => process_igp_cmd(ctx, cmd), + HyperlaneSealevelCmd::Squads(cmd) => process_squads_cmd(ctx, cmd), } } diff --git a/rust/sealevel/client/src/squads.rs b/rust/sealevel/client/src/squads.rs new file mode 100644 index 00000000000..acac04e7ae2 --- /dev/null +++ b/rust/sealevel/client/src/squads.rs @@ -0,0 +1,449 @@ +use std::{collections::HashMap, fs::File, path::PathBuf}; + +use account_utils::DiscriminatorDecode; +use borsh::{BorshDeserialize, BorshSerialize}; +use clap::{Args, Subcommand}; +use hyperlane_sealevel_mailbox::instruction::Instruction as MailboxInstruction; +use hyperlane_sealevel_multisig_ism_message_id::instruction::Instruction as MultisigIsmInstruction; +use solana_client::rpc_client::RpcClient; +use solana_program::pubkey; +use solana_sdk::{ + account::Account, account_utils::StateMut, bpf_loader_upgradeable::UpgradeableLoaderState, + pubkey::Pubkey, +}; + +use crate::{read_core_program_ids, router::ChainMetadata, Context, EnvironmentArgs}; + +const COMPUTE_BUDGET_PROGRAM_ID: Pubkey = pubkey!("ComputeBudget111111111111111111111111111111"); +const BPF_LOADER_UPGRADEABLE_PROGRAM_ID: Pubkey = + pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"); + +const CHAIN_CORE_OWNERS: &[(&str, &[(&str, Pubkey)])] = &[ + ( + "soon", + &[ + ( + "OLD pre-TGE owner", + pubkey!("E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62"), + ), + ( + "NEW post-TGE owner", + pubkey!("7Y6WDpMfNeb1b4YYbyUkF41z1DuPhvDDuWWJCHPRNa9Y"), + ), + ], + ), + ( + "solanamainnet", + &[ + ( + "OLD pre-TGE owner", + pubkey!("BNGDJ1h9brgt6FFVd8No1TVAH48Fp44d7jkuydr1URwJ"), + ), + ( + "NEW post-TGE owner", + pubkey!("3oocunLfAgATEqoRyW7A5zirsQuHJh6YjD4kReiVVKLa"), + ), + ], + ), + ( + "eclipsemainnet", + &[ + ( + "OLD pre-TGE owner", + pubkey!("E4TncCw3WMqQZbkACVcomX3HqcSzLfNyhTnqKN1DimGr"), + ), + ( + "NEW post-TGE owner", + pubkey!("D742EWw9wpV47jRAvEenG1oWHfMmpiQNJLjHTBfXhuRm"), + ), + ], + ), + ( + "sonicsvm", + &[ + ( + "OLD pre-TGE owner", + pubkey!("FeJQJrHNEeg9TNMpTmTg6h1JoKqSqctJbMj4H8CksPdD"), + ), + ( + "NEW post-TGE owner", + pubkey!("8ECSwp5yo2EeZkozSrpPnMj5Rmcwa4VBYCETE9LHmc9y"), + ), + ], + ), +]; + +#[derive(Args)] +pub(crate) struct SquadsCmd { + #[command(subcommand)] + cmd: SquadsSubCmd, +} + +#[derive(Subcommand)] +pub(crate) enum SquadsSubCmd { + Verify(SquadsVerifyCmd), +} + +#[derive(Args)] +pub(crate) struct SquadsVerifyCmd { + /// Environment + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain_config_file: PathBuf, + /// The path to the squads file to verify + #[arg(long, short)] + tx_pubkeys: Vec, + #[arg(long)] + chain: String, +} + +pub fn process_squads_cmd(ctx: Context, cmd: SquadsCmd) { + match cmd.cmd { + SquadsSubCmd::Verify(verify) => { + let chain_config_file = File::open(verify.chain_config_file).unwrap(); + let chain_configs: HashMap = + serde_json::from_reader(chain_config_file).unwrap(); + + let chain_config = chain_configs + .get(&verify.chain) + .expect("No chain config found"); + + let client = chain_config.client(); + + // Read existing core program IDs + let core_program_ids = read_core_program_ids( + &verify.env_args.environments_dir, + &verify.env_args.environment, + &verify.chain, + ); + let core_programs = vec![ + ProgramIdWithMetadata::new("Mailbox".into(), core_program_ids.mailbox), + ProgramIdWithMetadata::new( + "Validator Announce".into(), + core_program_ids.validator_announce, + ), + ProgramIdWithMetadata::new( + "Multisig ISM Message ID".into(), + core_program_ids.multisig_ism_message_id, + ), + ProgramIdWithMetadata::new("IGP program".into(), core_program_ids.igp_program_id), + ]; + + // Chain -> (Label, Owner) + let chain_owner_lookups: HashMap> = CHAIN_CORE_OWNERS + .iter() + .map(|c| { + ( + c.0.to_owned(), + c.1.iter() + .map(|inner| (inner.1, format!("{} - {}", inner.0, c.0))) + .collect(), + ) + }) + .collect(); + let chain_owner_lookup = chain_owner_lookups + .get(&verify.chain) + .expect("No expected core chain owners") + .clone(); + + let mut classification_accounts = vec![ + (COMPUTE_BUDGET_PROGRAM_ID, "Compute Budget program".into()), + ( + BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + "BPF Loader Upgradeable program".into(), + ), + ]; + classification_accounts.extend(chain_owner_lookup); + + let pubkey_classifier = + PubkeyClassifier::new(&client, classification_accounts, core_programs); + + let accounts = client + .get_multiple_accounts_with_commitment(&verify.tx_pubkeys, ctx.commitment) + .unwrap() + .value; + + for (i, account) in accounts.iter().enumerate() { + println!("\n\n\n\n================================================="); + println!("================================================="); + println!("================================================="); + println!( + "Tx proposal pubkey: {:?} ({} of {})", + verify.tx_pubkeys[i], + i + 1, + verify.tx_pubkeys.len() + ); + if let Some(account) = account { + parse_tx_account(account.clone(), &pubkey_classifier); + } else { + println!("Account not found"); + } + } + } + } +} + +fn parse_tx_account(account: Account, pubkey_classifier: &PubkeyClassifier) { + let mut data = account.data.as_slice(); + let discriminator = &data[..8]; + if discriminator != VAULT_TRANSACTION_DISCRIMINATOR { + panic!("Invalid discriminator"); + } + data = &data[8..]; + + let vault_transaction: VaultTransaction = VaultTransaction::try_from_slice(data).unwrap(); + + println!("Raw vault transaction: {:?}", vault_transaction); + + println!("-------\nInstructions:"); + + let instruction_count = vault_transaction.message.instructions.len(); + + for (i, instruction) in vault_transaction.message.instructions.iter().enumerate() { + println!("------"); + println!("Instruction {} of {}", i + 1, instruction_count); + let Some(program_id) = vault_transaction + .message + .account_keys + .get(instruction.program_id_index as usize) + else { + println!("\tA system program instruction that is ignored by the vault but our tooling sets anyways. Just ignore it."); + continue; + }; + println!( + "\tProgram called: {}", + pubkey_classifier.classify(program_id) + ); + + if *program_id == COMPUTE_BUDGET_PROGRAM_ID { + println!("\tComputeBudget instruction (e.g. setting limit or price), can ignore"); + continue; + } + + if *program_id == BPF_LOADER_UPGRADEABLE_PROGRAM_ID { + println!("\tBPF Loader Upgradeable instruction (how the system manages programs)"); + // Setting the upgrade authority, found here https://explorer.eclipse.xyz/tx/3RQ9V2HSbg4aZwr3LTMMwzrEBHa18KHMVwngXsHF2t5YZJ1Hb4MiBd7hovdPanLJT7Lmy2uuide55WmQvXDPjGx5 + if instruction.data == [4, 0, 0, 0] { + println!("\tSetting the upgrade authority:"); + let target_program = vault_transaction + .message + .account_keys + .get(instruction.account_indexes[0] as usize) + .unwrap(); + println!( + "\t\tTarget program: {}", + pubkey_classifier.classify(target_program) + ); + let old_upgrade_authority = vault_transaction + .message + .account_keys + .get(instruction.account_indexes[1] as usize) + .unwrap(); + println!( + "\t\tOld upgrade authority: {}", + pubkey_classifier.classify(old_upgrade_authority) + ); + let new_upgrade_authority = vault_transaction + .message + .account_keys + .get(instruction.account_indexes[2] as usize) + .unwrap(); + println!( + "\t\tNew upgrade authority: {}", + pubkey_classifier.classify(new_upgrade_authority) + ); + } else { + println!("⚠️ Unknown instruction") + } + continue; + } + + // Try to parse as a MailboxInstruction + if let Ok(instruction) = MailboxInstruction::try_from_slice(&instruction.data) { + println!("\tMailbox instruction: {:?}", instruction); + if let MailboxInstruction::TransferOwnership(new_owner) = instruction { + println!( + "\tTransfer ownership to {}", + new_owner.map_or("None".into(), |owner| pubkey_classifier.classify(&owner)) + ); + } + continue; + } + + // Else, try to parse as a MultisigIsmInstruction + if let Ok(instruction) = MultisigIsmInstruction::decode(&instruction.data) { + println!("\tMultisig ISM instruction: {:?}", instruction); + if let MultisigIsmInstruction::TransferOwnership(new_owner) = instruction { + println!( + "\tTransfer ownership to {}", + new_owner.map_or("None".into(), |owner| pubkey_classifier.classify(&owner)) + ); + } + continue; + } + println!("\n ⚠️ Unknown instruction! ⚠️"); + } +} + +#[derive(Debug, Clone)] +struct ProgramIdWithMetadata { + name: String, + program_id: Pubkey, +} + +impl ProgramIdWithMetadata { + fn new(name: String, program_id: Pubkey) -> Self { + Self { name, program_id } + } +} + +struct PubkeyClassifier { + lookup: HashMap, + programdata_to_program_id: HashMap, +} + +impl PubkeyClassifier { + pub fn new( + client: &RpcClient, + general_accounts: Vec<(Pubkey, String)>, + programs: Vec, + ) -> Self { + let accounts = client + .get_multiple_accounts_with_commitment( + &programs.iter().map(|p| p.program_id).collect::>(), + Default::default(), + ) + .unwrap() + .value; + + let mut lookup: HashMap = general_accounts.into_iter().collect(); + let mut programdata_to_program_id = HashMap::new(); + + for (i, account) in accounts.iter().enumerate() { + let program = programs[i].clone(); + + let Some(program_account) = account else { + panic!("Expected account for program {:?}", program); + }; + + // Get the program data account address by looking at the program state + let programdata_address = if let Ok(UpgradeableLoaderState::Program { + programdata_address, + }) = program_account.state() + { + programdata_address + } else { + panic!("Unable to deserialize program account {:?}", program); + }; + + programdata_to_program_id.insert(programdata_address, program.program_id); + lookup.insert(program.program_id, program.name); + } + + Self { + lookup, + programdata_to_program_id, + } + } + + pub fn classify(&self, pubkey: &Pubkey) -> String { + if let Some(lookup_match) = self.lookup.get(pubkey) { + return format!("{} ({})", pubkey, lookup_match); + } + + if let Some(programdata_match) = self.programdata_to_program_id.get(pubkey) { + return format!( + "{} (Program Data account of {})", + pubkey, + self.classify(programdata_match) + ); + }; + + format!("{} (⚠️ Unknown ⚠️)", pubkey) + } +} + +// Vendored (and slightly altered, to not require Anchor) to avoid needing +// to import from Squads directly and going through the dependency pain + +const VAULT_TRANSACTION_DISCRIMINATOR: &[u8] = &[168, 250, 162, 100, 81, 14, 162, 207]; + +/// Stores data required for tracking the voting and execution status of a vault transaction. +/// Vault transaction is a transaction that's executed on behalf of the multisig vault PDA +/// and wraps arbitrary Solana instructions, typically calling into other Solana programs. +// #[account] +#[derive(Default, BorshSerialize, BorshDeserialize, Clone, Debug)] +pub struct VaultTransaction { + /// The multisig this belongs to. + pub multisig: Pubkey, + /// Member of the Multisig who submitted the transaction. + pub creator: Pubkey, + /// Index of this transaction within the multisig. + pub index: u64, + /// bump for the transaction seeds. + pub bump: u8, + /// Index of the vault this transaction belongs to. + pub vault_index: u8, + /// Derivation bump of the vault PDA this transaction belongs to. + pub vault_bump: u8, + /// Derivation bumps for additional signers. + /// Some transactions require multiple signers. Often these additional signers are "ephemeral" keypairs + /// that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after. + /// When wrapping such transactions into multisig ones, we replace these "ephemeral" signing keypairs + /// with PDAs derived from the MultisigTransaction's `transaction_index` and controlled by the Multisig Program; + /// during execution the program includes the seeds of these PDAs into the `invoke_signed` calls, + /// thus "signing" on behalf of these PDAs. + pub ephemeral_signer_bumps: Vec, + /// data required for executing the transaction. + pub message: VaultTransactionMessage, +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Default, Debug)] +pub struct VaultTransactionMessage { + /// The number of signer pubkeys in the account_keys vec. + pub num_signers: u8, + /// The number of writable signer pubkeys in the account_keys vec. + pub num_writable_signers: u8, + /// The number of writable non-signer pubkeys in the account_keys vec. + pub num_writable_non_signers: u8, + /// Unique account pubkeys (including program IDs) required for execution of the tx. + /// The signer pubkeys appear at the beginning of the vec, with writable pubkeys first, and read-only pubkeys following. + /// The non-signer pubkeys follow with writable pubkeys first and read-only ones following. + /// Program IDs are also stored at the end of the vec along with other non-signer non-writable pubkeys: + /// + /// ```plaintext + /// [pubkey1, pubkey2, pubkey3, pubkey4, pubkey5, pubkey6, pubkey7, pubkey8] + /// |---writable---| |---readonly---| |---writable---| |---readonly---| + /// |------------signers-------------| |----------non-singers-----------| + /// ``` + pub account_keys: Vec, + /// List of instructions making up the tx. + pub instructions: Vec, + /// List of address table lookups used to load additional accounts + /// for this transaction. + pub address_table_lookups: Vec, +} + +/// Concise serialization schema for instructions that make up a transaction. +/// Closely mimics the Solana transaction wire format. +#[derive(BorshSerialize, BorshDeserialize, Clone, Default, Debug)] +pub struct MultisigCompiledInstruction { + pub program_id_index: u8, + /// Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction. + pub account_indexes: Vec, + /// Instruction data. + pub data: Vec, +} + +/// Address table lookups describe an on-chain address lookup table to use +/// for loading more readonly and writable accounts into a transaction. +#[derive(BorshSerialize, BorshDeserialize, Clone, Default, Debug)] +pub struct MultisigMessageAddressTableLookup { + /// Address lookup table account key. + pub account_key: Pubkey, + /// List of indexes used to load writable accounts. + pub writable_indexes: Vec, + /// List of indexes used to load readonly accounts. + pub readonly_indexes: Vec, +} diff --git a/rust/sealevel/environments/mainnet3/solanamainnet/core/program-ids.json b/rust/sealevel/environments/mainnet3/solanamainnet/core/program-ids.json index fb347daf42a..f96c4f41daf 100644 --- a/rust/sealevel/environments/mainnet3/solanamainnet/core/program-ids.json +++ b/rust/sealevel/environments/mainnet3/solanamainnet/core/program-ids.json @@ -1,7 +1,7 @@ { "mailbox": "E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi", "validator_announce": "pRgs5vN4Pj7WvFbxf6QDHizo2njq2uksqEUbaSghVA8", - "multisig_ism_message_id": "HPNQHcc7wRcwsPG1aVBpPgpYuV7h2MJiVgKnAgTdDRBM", + "multisig_ism_message_id": "NtVfGz6mMXe17Jy8Mt8pvStgwFbGKHkSvxPeWn1FMNu", "igp_program_id": "BhNcatUDC2D5JTyeaqrdSukiVFsEHK7e3hVmKMztwefv", "overhead_igp_account": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", "igp_account": "JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M" From 7654d9c7418fdca3bb4d157889bae55dfd7d9d7b Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 2 May 2025 13:16:01 +0100 Subject: [PATCH 119/223] fix: Expose operation ID as separate field on logs (#6083) ### Description Expose operation ID as separate field on logs so that it is easier to search and the field is indexed by log consolidation tools. ### Backward compatibility Yes ### Testing None Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/agents/relayer/src/msg/op_submitter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index a4db53a5633..6d76632f7b3 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -960,7 +960,7 @@ async fn process_confirm_result( ) -> PendingOperationResult { match &operation_result { PendingOperationResult::Success => { - debug!(?op, "Operation confirmed"); + debug!(id=?op.id(), ?op, "Operation confirmed"); metrics.ops_confirmed.inc(); op.decrement_metric_if_exists(); } From ee62f06b62ec560996b6fe3e38acc55a564c1ab0 Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion Date: Fri, 2 May 2025 14:22:37 +0200 Subject: [PATCH 120/223] fix: update transaction hash extraction in getConstructorArgsFromExplorer function --- typescript/sdk/src/deploy/verify/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/sdk/src/deploy/verify/utils.ts b/typescript/sdk/src/deploy/verify/utils.ts index 37b30c23c39..10016dc9778 100644 --- a/typescript/sdk/src/deploy/verify/utils.ts +++ b/typescript/sdk/src/deploy/verify/utils.ts @@ -202,7 +202,7 @@ async function getConstructorArgsFromExplorer({ url.searchParams.append('apikey', blockExplorerApiKey); const explorerResp = await fetch(url); - const creationTx = (await explorerResp.json()).result[0].txHash; + const creationTx = (await explorerResp.json()).result[0]; // Fetch deployment bytecode (includes constructor args) assert(creationTx, 'Contract creation transaction not found!'); From 8fd2f9d56b17e55c90bff5fd302afac3456d04cd Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 2 May 2025 13:54:40 +0100 Subject: [PATCH 121/223] fix: persist key addresses in GCP (#6111) ### Description - Starts persisting key addresses in GCP again, fixing key funder infra - Regression introduced in https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3159/files#diff-604c29d425429647a37bc7b46d72cfddb8f5ba0c40d81609234c36bb2a203b35L372 ages ago, just never realized because we didn't add new keys ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: ljankovic-txfusion --- typescript/infra/src/agents/key-utils.ts | 45 +++++++++++++++++++++- typescript/infra/test/warp-configs.test.ts | 4 +- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/typescript/infra/src/agents/key-utils.ts b/typescript/infra/src/agents/key-utils.ts index 69392470b36..350c9086ea9 100644 --- a/typescript/infra/src/agents/key-utils.ts +++ b/typescript/infra/src/agents/key-utils.ts @@ -1,7 +1,7 @@ import { join } from 'path'; import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; -import { Address, objMap, rootLogger } from '@hyperlane-xyz/utils'; +import { Address, deepEquals, objMap, rootLogger } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { helloworld } from '../../config/environments/helloworld.js'; @@ -12,6 +12,7 @@ import { getJustHelloWorldConfig } from '../../scripts/helloworld/utils.js'; import { AgentContextConfig, RootAgentConfig } from '../config/agent/agent.js'; import { DeployEnvironment } from '../config/environment.js'; import { Role } from '../roles.js'; +import { fetchGCPSecret, setGCPSecretUsingClient } from '../utils/gcloud.js'; import { execCmd, getInfraPath, @@ -340,6 +341,12 @@ export async function createAgentKeysIfNotExists( ); await persistAddressesLocally(agentConfig, keys); + // Key funder expects the serialized addresses in GCP + await persistAddressesInGcp( + agentConfig.runEnv, + agentConfig.context, + keys.map((key) => key.serializeAsAddress()), + ); return; } @@ -366,6 +373,42 @@ export async function rotateKey( await persistAddressesLocally(agentConfig, [key]); } +async function persistAddressesInGcp( + environment: DeployEnvironment, + context: Contexts, + keys: KeyAsAddress[], +) { + try { + const existingSecret = (await fetchGCPSecret( + addressesIdentifier(environment, context), + true, + )) as KeyAsAddress[]; + if (deepEquals(keys, existingSecret)) { + debugLog( + `Addresses already persisted to GCP for ${context} context in ${environment} environment`, + ); + return; + } + } catch (e) { + // If the secret doesn't exist, we'll create it below. + debugLog( + `No existing secret found for ${context} context in ${environment} environment`, + ); + } + + debugLog( + `Persisting addresses to GCP for ${context} context in ${environment} environment`, + ); + await setGCPSecretUsingClient( + addressesIdentifier(environment, context), + JSON.stringify(keys), + { + environment, + context, + }, + ); +} + async function persistAddressesLocally( agentConfig: AgentContextConfig, keys: CloudAgentKey[], diff --git a/typescript/infra/test/warp-configs.test.ts b/typescript/infra/test/warp-configs.test.ts index 29fa1544a6a..d84b8d4b165 100644 --- a/typescript/infra/test/warp-configs.test.ts +++ b/typescript/infra/test/warp-configs.test.ts @@ -24,8 +24,8 @@ const DEFAULT_TIMEOUT = 100000; const warpIdsToSkip = [ 'EZETH/arbitrum-base-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-swell-taiko-zircuit', 'EZETHSTAGE/arbitrum-base-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-swell-taiko-zircuit', - 'USDT/base-celo-fraxtal-ink-lisk-mode-optimism-soneium-superseed-unichain-worldchain-staging', - 'USDT/base-celo-fraxtal-ink-lisk-mode-optimism-soneium-superseed-unichain-worldchain', + 'USDT/base-bitlayer-celo-ethereum-fraxtal-ink-linea-lisk-mantle-metal-metis-mode-optimism-ronin-soneium-sonic-superseed-unichain-worldchain', + 'USDTSTAGING/base-bitlayer-celo-ethereum-fraxtal-ink-linea-lisk-mantle-metal-metis-mode-optimism-ronin-soneium-sonic-superseed-unichain-worldchain', ]; async function getConfigsForBranch(branch: string) { From 9f36fa3c00cdf8c77a40615df37872a0be515e43 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 2 May 2025 15:17:38 +0100 Subject: [PATCH 122/223] fix(submitter): svm adapter commitment levels (#6108) ### Description - All chain reads for confirming the status of a tx happened at the `finalized` [commitment](https://docs.anza.xyz/consensus/commitments) level, which explains why the Finality Pool is always empty in prod and why it takes 10-30s to get a tx included in Sealevel E2E - This PR reads the tx hash at the `processed` status - if that fails, we return an error like before. Then, if the tx's slot can be read at `finalized` status, the tx is final. If the slot can be read at `confirmed` status, the tx is included. Otherwise the tx is pending inclusion, because the hash does exist. - Removes the `reorgPeriod` from the SVM adapter, since I noticed we actually [set that to zero](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/f692b35be7bf9d7083877bfe259e18e8bf124ab6/rust/main/config/mainnet_config.json#L2870). We rely on using the right commitment levels in code (e.g. `finalized`) as opposed to setting an arbitrary number of blocks ### Related issues Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6103 ### Backward compatibility Yes ### Testing sealevel E2E --- .../hyperlane-sealevel/src/rpc/client.rs | 30 +++++++-- .../hyperlane-sealevel/src/rpc/fallback.rs | 40 ++++++++++-- .../chains/sealevel/adapter.rs | 65 +++++++++---------- .../chains/sealevel/adapter/tests/common.rs | 32 +++++++-- .../chains/sealevel/adapter/tests/config.rs | 2 - rust/main/submitter/src/error.rs | 1 - 6 files changed, 118 insertions(+), 52 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 0acd9e2d972..da21868193c 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -96,10 +96,14 @@ impl SealevelRpcClient { Ok(balance.into()) } - /// get block - pub async fn get_block(&self, slot: u64) -> ChainResult { + /// get block with commitment + pub async fn get_block_with_commitment( + &self, + slot: u64, + commitment: CommitmentConfig, + ) -> ChainResult { let config = RpcBlockConfig { - commitment: Some(CommitmentConfig::finalized()), + commitment: Some(commitment), max_supported_transaction_version: Some(0), ..Default::default() }; @@ -111,6 +115,12 @@ impl SealevelRpcClient { .map_err(Into::into) } + /// get block + pub async fn get_block(&self, slot: u64) -> ChainResult { + self.get_block_with_commitment(slot, CommitmentConfig::finalized()) + .await + } + /// get block_height pub async fn get_block_height(&self) -> ChainResult { self.0 @@ -199,13 +209,14 @@ impl SealevelRpcClient { } /// get transaction - pub async fn get_transaction( + pub async fn get_transaction_with_commitment( &self, signature: &Signature, + commitment: CommitmentConfig, ) -> ChainResult { let config = RpcTransactionConfig { encoding: Some(UiTransactionEncoding::JsonParsed), - commitment: Some(CommitmentConfig::finalized()), + commitment: Some(commitment), max_supported_transaction_version: Some(0), }; self.0 @@ -216,6 +227,15 @@ impl SealevelRpcClient { .map_err(Into::into) } + /// get transaction + pub async fn get_transaction( + &self, + signature: &Signature, + ) -> ChainResult { + self.get_transaction_with_commitment(signature, CommitmentConfig::finalized()) + .await + } + /// check if block hash is valid pub async fn is_blockhash_valid(&self, hash: &Hash) -> ChainResult { self.0 diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/fallback.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/fallback.rs index 27f6ce774fe..c0df14a759b 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/fallback.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/fallback.rs @@ -25,12 +25,32 @@ use crate::client_builder::SealevelRpcClientBuilder; #[async_trait] pub trait SubmitSealevelRpc: Send + Sync { /// Requests block from node - async fn get_block(&self, slot: u64) -> ChainResult; + async fn get_block(&self, slot: u64) -> ChainResult { + self.get_block_with_commitment(slot, CommitmentConfig::finalized()) + .await + } + + /// Requests block from node, with a specific commitment + async fn get_block_with_commitment( + &self, + slot: u64, + commitment: CommitmentConfig, + ) -> ChainResult; /// Requests transaction from node async fn get_transaction( &self, signature: Signature, + ) -> ChainResult { + self.get_transaction_with_commitment(signature, CommitmentConfig::finalized()) + .await + } + + /// Requests transaction from node, with a specific commitment + async fn get_transaction_with_commitment( + &self, + signature: Signature, + commitment: CommitmentConfig, ) -> ChainResult; /// Simulates Sealevel transaction @@ -49,24 +69,34 @@ pub struct SealevelFallbackRpcClient { #[async_trait] impl SubmitSealevelRpc for SealevelFallbackRpcClient { /// get block - async fn get_block(&self, slot: u64) -> ChainResult { + async fn get_block_with_commitment( + &self, + slot: u64, + commitment: CommitmentConfig, + ) -> ChainResult { self.fallback_provider .call(move |client| { - let future = async move { client.get_block(slot).await }; + let future = + async move { client.get_block_with_commitment(slot, commitment).await }; Box::pin(future) }) .await } /// get transaction - async fn get_transaction( + async fn get_transaction_with_commitment( &self, signature: Signature, + commitment: CommitmentConfig, ) -> ChainResult { self.fallback_provider .call(move |client| { let signature = signature; - let future = async move { client.get_transaction(&signature).await }; + let future = async move { + client + .get_transaction_with_commitment(&signature, commitment) + .await + }; Box::pin(future) }) .await diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index 2835cf2b719..35d21a76af5 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -59,7 +59,6 @@ const TX_RESUBMISSION_MIN_DELAY_SECS: u64 = 15; pub struct SealevelTxAdapter { estimated_block_time: Duration, max_batch_size: u32, - reorg_period: ReorgPeriod, keypair: SealevelKeypair, client: Box, provider: Box, @@ -120,14 +119,12 @@ impl SealevelTxAdapter { submitter: Box, ) -> eyre::Result { let estimated_block_time = conf.estimated_block_time; - let reorg_period = conf.reorg_period.clone(); let max_batch_size = Self::batch_size(&conf)?; let keypair = create_keypair(&conf)?; Ok(Self { estimated_block_time, max_batch_size, - reorg_period, keypair, provider, client, @@ -147,7 +144,6 @@ impl SealevelTxAdapter { Self { estimated_block_time: Duration::from_secs(1), max_batch_size: 1, - reorg_period: ReorgPeriod::default(), keypair: SealevelKeypair::default(), provider, client, @@ -219,44 +215,45 @@ impl SealevelTxAdapter { #[instrument(skip(self))] async fn get_tx_hash_status(&self, tx_hash: H512) -> Result { let signature = Signature::new(tx_hash.as_ref()); - let transaction_search_result = self.client.get_transaction(signature).await; - let transaction = match transaction_search_result { - Ok(transaction) => transaction, - Err(err) => { - warn!(?err, "Failed to get transaction status by hash"); - return Err(SubmitterError::TxSubmissionError( - "Transaction hash not found".to_string(), - )); - } - }; + // query the tx hash from most to least finalized to learn what level of finality it has + // the calls below can be parallelized if needed, but for now avoid rate limiting - // slot at which transaction was included into blockchain - let inclusion_slot = transaction.slot; - - info!(slot = ?inclusion_slot, "found transaction"); - - // if block with this slot is added to the chain, transaction is considered to be confirmed - let confirming_slot = inclusion_slot + self.reorg_period.as_blocks()? as u64; - - let confirming_block = self.client.get_block(confirming_slot).await; - - if confirming_block.is_ok() { - info!("finalized transaction"); + if self + .client + .get_transaction_with_commitment(signature, CommitmentConfig::finalized()) + .await + .is_ok() + { + info!("transaction finalized"); return Ok(TransactionStatus::Finalized); } - // block which includes transaction into blockchain - let including_block = self.client.get_block(inclusion_slot).await; + // the "confirmed" commitment is equivalent to being "included" in a block on evm + if self + .client + .get_transaction_with_commitment(signature, CommitmentConfig::confirmed()) + .await + .is_ok() + { + info!("transaction included"); + return Ok(TransactionStatus::Included); + } - match including_block { + match self + .client + .get_transaction_with_commitment(signature, CommitmentConfig::processed()) + .await + { Ok(_) => { - info!("included transaction"); - Ok(TransactionStatus::Included) + info!("transaction pending inclusion"); + return Ok(TransactionStatus::PendingInclusion); } - Err(_) => { - info!("pending transaction"); - Ok(TransactionStatus::PendingInclusion) + Err(err) => { + warn!(?err, "Failed to get transaction status by hash"); + return Err(SubmitterError::TxSubmissionError( + "Transaction hash not found".to_string(), + )); } } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/common.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/common.rs index 249a25720f7..eea04a40f20 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/common.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/common.rs @@ -37,8 +37,28 @@ mock! { #[async_trait] impl SubmitSealevelRpc for Client { async fn get_block(&self, slot: u64) -> ChainResult; - async fn get_transaction(&self, signature: Signature) -> ChainResult; - async fn simulate_transaction(&self, transaction: &SealevelTransaction) -> ChainResult; + + async fn get_block_with_commitment( + &self, + slot: u64, + commitment: CommitmentConfig, + ) -> ChainResult; + + async fn get_transaction( + &self, + signature: Signature, + ) -> ChainResult; + + async fn get_transaction_with_commitment( + &self, + signature: Signature, + commitment: CommitmentConfig, + ) -> ChainResult; + + async fn simulate_transaction( + &self, + transaction: &SealevelTransaction, + ) -> ChainResult; } } @@ -177,10 +197,12 @@ fn mock_client() -> MockClient { }; let mut client = MockClient::new(); - client.expect_get_block().returning(move |_| Ok(block())); client - .expect_get_transaction() - .returning(move |_| Ok(encoded_transaction())); + .expect_get_block_with_commitment() + .returning(move |_, _| Ok(block())); + client + .expect_get_transaction_with_commitment() + .returning(move |_, _| Ok(encoded_transaction())); client .expect_simulate_transaction() .returning(move |_| Ok(result.clone())); diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs index 56ba4305a58..42f97932aec 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/config.rs @@ -43,10 +43,8 @@ fn test_configuration_fields() { // when let estimated_block_time = adapter.estimated_block_time(); let max_batch_size = adapter.max_batch_size(); - let reorg_period = adapter.reorg_period.clone(); // then assert_eq!(estimated_block_time, &expected_estimated_block_time); assert_eq!(max_batch_size, expected_max_batch_size); - assert_eq!(reorg_period, expected_reorg_period); } diff --git a/rust/main/submitter/src/error.rs b/rust/main/submitter/src/error.rs index 64d04458c6b..ba5cb8f276d 100644 --- a/rust/main/submitter/src/error.rs +++ b/rust/main/submitter/src/error.rs @@ -65,7 +65,6 @@ impl IsRetryable for SubmitterError { // TODO: add logic to classify based on the error message false } - // tx submission errors are not retryable so gas gets escalated SubmitterError::ChainCommunicationError(_) => { // TODO: add logic to classify based on the error message false From 0cd15ae26e9348d7c13132daf2bb6c7606509082 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 14:53:12 +0000 Subject: [PATCH 123/223] Version Packages (#6105) 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 publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). 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@12.5.0 ### Minor Changes - 862f14f: feat(cli): Add registry-based warp route lookup and config validation ## @hyperlane-xyz/core@7.1.4 ### Patch Changes - @hyperlane-xyz/utils@12.5.0 ## @hyperlane-xyz/cosmos-sdk@12.5.0 ### Patch Changes - @hyperlane-xyz/cosmos-types@12.5.0 ## @hyperlane-xyz/helloworld@12.5.0 ### Patch Changes - Updated dependencies [c8ace88] - @hyperlane-xyz/sdk@12.5.0 - @hyperlane-xyz/core@7.1.4 ## @hyperlane-xyz/sdk@12.5.0 ### Patch Changes - c8ace88: Export HypTokenRouterConfigMailboxOptionalSchema and HypTokenRouterConfigMailboxOptional - @hyperlane-xyz/cosmos-sdk@12.5.0 - @hyperlane-xyz/utils@12.5.0 - @hyperlane-xyz/core@7.1.4 ## @hyperlane-xyz/widgets@12.5.0 ### Patch Changes - Updated dependencies [c8ace88] - @hyperlane-xyz/sdk@12.5.0 - @hyperlane-xyz/cosmos-sdk@12.5.0 - @hyperlane-xyz/utils@12.5.0 ## @hyperlane-xyz/cosmos-types@12.5.0 ## @hyperlane-xyz/utils@12.5.0 ## @hyperlane-xyz/infra@12.5.0 ### Patch Changes - Updated dependencies [c8ace88] - @hyperlane-xyz/sdk@12.5.0 - @hyperlane-xyz/helloworld@12.5.0 - @hyperlane-xyz/utils@12.5.0 ## @hyperlane-xyz/ccip-server@12.5.0 ## @hyperlane-xyz/github-proxy@12.5.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/grumpy-cats-call.md | 5 --- .changeset/three-schools-happen.md | 5 --- solidity/CHANGELOG.md | 6 ++++ solidity/contracts/PackageVersioned.sol | 2 +- solidity/package.json | 4 +-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 6 ++++ typescript/cli/package.json | 6 ++-- typescript/cli/src/version.ts | 2 +- typescript/cosmos-sdk/CHANGELOG.md | 6 ++++ typescript/cosmos-sdk/package.json | 4 +-- typescript/cosmos-types/CHANGELOG.md | 2 ++ typescript/cosmos-types/package.json | 2 +- typescript/github-proxy/CHANGELOG.md | 2 ++ typescript/github-proxy/package.json | 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 | 9 ++++++ typescript/sdk/package.json | 8 ++--- typescript/utils/CHANGELOG.md | 2 ++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 9 ++++++ typescript/widgets/package.json | 8 ++--- yarn.lock | 42 ++++++++++++------------- 27 files changed, 110 insertions(+), 59 deletions(-) delete mode 100644 .changeset/grumpy-cats-call.md delete mode 100644 .changeset/three-schools-happen.md diff --git a/.changeset/grumpy-cats-call.md b/.changeset/grumpy-cats-call.md deleted file mode 100644 index 6348dc906ab..00000000000 --- a/.changeset/grumpy-cats-call.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -feat(cli): Add registry-based warp route lookup and config validation diff --git a/.changeset/three-schools-happen.md b/.changeset/three-schools-happen.md deleted file mode 100644 index 923048f1aea..00000000000 --- a/.changeset/three-schools-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': patch ---- - -Export HypTokenRouterConfigMailboxOptionalSchema and HypTokenRouterConfigMailboxOptional diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index b7c1735f2cf..697c4ea8d81 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/core +## 7.1.4 + +### Patch Changes + +- @hyperlane-xyz/utils@12.5.0 + ## 7.1.3 ### Patch Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index 01263dcab62..75451fea043 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.1.3"; + string public constant PACKAGE_VERSION = "7.1.4"; } diff --git a/solidity/package.json b/solidity/package.json index c896bd53edd..41326ed33c4 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.1.3", + "version": "7.1.4", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.4.0", + "@hyperlane-xyz/utils": "12.5.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index d9ffbc6ba50..1724bca2a39 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 12.5.0 + ## 12.4.0 ## 12.3.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 5d3ce1725a4..eb69ad7c914 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.4.0", + "version": "12.5.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 bb240bbe1de..8e543f869d8 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/cli +## 12.5.0 + +### Minor Changes + +- 862f14f: feat(cli): Add registry-based warp route lookup and config validation + ## 12.4.0 ## 12.3.0 diff --git a/typescript/cli/package.json b/typescript/cli/package.json index aaf805207ec..278d34bc822 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.4.0", + "version": "12.5.0", "description": "A command-line utility for common Hyperlane operations", "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", @@ -9,8 +9,8 @@ "@ethersproject/abi": "*", "@ethersproject/providers": "*", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.4.0", - "@hyperlane-xyz/utils": "12.4.0", + "@hyperlane-xyz/sdk": "12.5.0", + "@hyperlane-xyz/utils": "12.5.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index aa1cff221bd..12eb1b0e454 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.4.0'; +export const VERSION = '12.5.0'; diff --git a/typescript/cosmos-sdk/CHANGELOG.md b/typescript/cosmos-sdk/CHANGELOG.md index b1654ef41be..77447efc3e1 100644 --- a/typescript/cosmos-sdk/CHANGELOG.md +++ b/typescript/cosmos-sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/cosmos-sdk +## 12.5.0 + +### Patch Changes + +- @hyperlane-xyz/cosmos-types@12.5.0 + ## 12.4.0 ### Patch Changes diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 82228dc642b..6c384a3f7cc 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-sdk", - "version": "12.4.0", + "version": "12.5.0", "description": "Hyperlane TypeScript SDK for the Cosmos Hyperlane SDK module", "type": "module", "exports": { @@ -46,6 +46,6 @@ }, "dependencies": { "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/cosmos-types": "12.4.0" + "@hyperlane-xyz/cosmos-types": "12.5.0" } } diff --git a/typescript/cosmos-types/CHANGELOG.md b/typescript/cosmos-types/CHANGELOG.md index bd4f009b9b0..f5fd40abdd0 100644 --- a/typescript/cosmos-types/CHANGELOG.md +++ b/typescript/cosmos-types/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cosmos-types +## 12.5.0 + ## 12.4.0 ## 12.3.0 diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index 40c2e531da6..1cf57bbaa96 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-types", - "version": "12.4.0", + "version": "12.5.0", "description": "Hyperlane TypeScript SDK types for the Cosmos Hyperlane SDK module", "type": "module", "exports": { diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index d42179fef8d..e31647aeeaa 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 12.5.0 + ## 12.4.0 ## 12.3.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index f60ebc39284..4f7a6e9ffde 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.4.0", + "version": "12.5.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 3871e08cc4d..c7f20d8bcb2 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/helloworld +## 12.5.0 + +### Patch Changes + +- Updated dependencies [c8ace88] + - @hyperlane-xyz/sdk@12.5.0 + - @hyperlane-xyz/core@7.1.4 + ## 12.4.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index c2d7c8c7bb1..ddf204a2661 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": "12.4.0", + "version": "12.5.0", "dependencies": { - "@hyperlane-xyz/core": "7.1.3", + "@hyperlane-xyz/core": "7.1.4", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.4.0", + "@hyperlane-xyz/sdk": "12.5.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index b0404110e8d..fc6308a2e69 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/infra +## 12.5.0 + +### Patch Changes + +- Updated dependencies [c8ace88] + - @hyperlane-xyz/sdk@12.5.0 + - @hyperlane-xyz/helloworld@12.5.0 + - @hyperlane-xyz/utils@12.5.0 + ## 12.4.0 ### Patch Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 127efbfd308..33d1f41a54d 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": "12.4.0", + "version": "12.5.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.4.0", + "@hyperlane-xyz/helloworld": "12.5.0", "@hyperlane-xyz/registry": "11.1.0", - "@hyperlane-xyz/sdk": "12.4.0", - "@hyperlane-xyz/utils": "12.4.0", + "@hyperlane-xyz/sdk": "12.5.0", + "@hyperlane-xyz/utils": "12.5.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index d03078f5e49..de41c56362c 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/sdk +## 12.5.0 + +### Patch Changes + +- c8ace88: Export HypTokenRouterConfigMailboxOptionalSchema and HypTokenRouterConfigMailboxOptional + - @hyperlane-xyz/cosmos-sdk@12.5.0 + - @hyperlane-xyz/utils@12.5.0 + - @hyperlane-xyz/core@7.1.4 + ## 12.4.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index b4ca79028ec..7eeaf970c2a 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,16 +1,16 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.4.0", + "version": "12.5.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.122", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.1.3", - "@hyperlane-xyz/cosmos-sdk": "12.4.0", - "@hyperlane-xyz/utils": "12.4.0", + "@hyperlane-xyz/core": "7.1.4", + "@hyperlane-xyz/cosmos-sdk": "12.5.0", + "@hyperlane-xyz/utils": "12.5.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 2da4f812a83..23584624c4e 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/utils +## 12.5.0 + ## 12.4.0 ## 12.3.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 3f040634612..812cd21fa86 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": "12.4.0", + "version": "12.5.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index d43991663be..93b80544879 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/widgets +## 12.5.0 + +### Patch Changes + +- Updated dependencies [c8ace88] + - @hyperlane-xyz/sdk@12.5.0 + - @hyperlane-xyz/cosmos-sdk@12.5.0 + - @hyperlane-xyz/utils@12.5.0 + ## 12.4.0 ### Patch Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 58724266ade..403d26f443d 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.4.0", + "version": "12.5.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -10,9 +10,9 @@ "@cosmjs/stargate": "^0.32.4", "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/cosmos-sdk": "12.4.0", - "@hyperlane-xyz/sdk": "12.4.0", - "@hyperlane-xyz/utils": "12.4.0", + "@hyperlane-xyz/cosmos-sdk": "12.5.0", + "@hyperlane-xyz/sdk": "12.5.0", + "@hyperlane-xyz/utils": "12.5.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index 47f9d6f885f..504d506fbd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7645,8 +7645,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.4.0" - "@hyperlane-xyz/utils": "npm:12.4.0" + "@hyperlane-xyz/sdk": "npm:12.5.0" + "@hyperlane-xyz/utils": "npm:12.5.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7686,14 +7686,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.1.3, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.4, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.4.0" + "@hyperlane-xyz/utils": "npm:12.5.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7733,13 +7733,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-sdk@npm:12.4.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": +"@hyperlane-xyz/cosmos-sdk@npm:12.5.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk" dependencies: "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/cosmos-types": "npm:12.4.0" + "@hyperlane-xyz/cosmos-types": "npm:12.5.0" "@types/mocha": "npm:^10.0.1" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" @@ -7755,7 +7755,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-types@npm:12.4.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": +"@hyperlane-xyz/cosmos-types@npm:12.5.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" dependencies: @@ -7789,14 +7789,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.4.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:12.5.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.3" + "@hyperlane-xyz/core": "npm:7.1.4" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.4.0" + "@hyperlane-xyz/sdk": "npm:12.5.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7845,10 +7845,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.4.0" + "@hyperlane-xyz/helloworld": "npm:12.5.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.4.0" - "@hyperlane-xyz/utils": "npm:12.4.0" + "@hyperlane-xyz/sdk": "npm:12.5.0" + "@hyperlane-xyz/utils": "npm:12.5.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7920,7 +7920,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.4.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:12.5.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7930,9 +7930,9 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.3" - "@hyperlane-xyz/cosmos-sdk": "npm:12.4.0" - "@hyperlane-xyz/utils": "npm:12.4.0" + "@hyperlane-xyz/core": "npm:7.1.4" + "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" + "@hyperlane-xyz/utils": "npm:12.5.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -7977,7 +7977,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.4.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:12.5.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8019,10 +8019,10 @@ __metadata: "@emotion/styled": "npm:^11.13.0" "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" - "@hyperlane-xyz/cosmos-sdk": "npm:12.4.0" + "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" "@hyperlane-xyz/registry": "npm:11.1.0" - "@hyperlane-xyz/sdk": "npm:12.4.0" - "@hyperlane-xyz/utils": "npm:12.4.0" + "@hyperlane-xyz/sdk": "npm:12.5.0" + "@hyperlane-xyz/utils": "npm:12.5.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From d42d7f50cca872687cb69828dea24a8cd377139e Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 2 May 2025 11:30:25 -0400 Subject: [PATCH 124/223] feat: validator announce uses chain signer instead of validator key (#6007) ### Description Use the chain's signer instead of validator key when checking balance. - updated `announce_tokens_needed` to take a `chain_signer: H256,` parameter, to be used for checking balance instead of validator key - add `address_h256()` to `ChainSigner` trait ### Related issues - fixes: https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3850 - fixes: https://linear.app/hyperlane-xyz/issue/BACK-178/validator-doesnt-announce-without-funds-on-the-validator-key-but-the ### Backward compatibility Yes ### Testing Add tests --- .../validator_announced_storages/tests.rs | 2 +- rust/main/agents/validator/src/validator.rs | 13 +- .../hyperlane-cosmos-native/src/mailbox.rs | 2 +- .../src/providers/rpc.rs | 2 +- .../hyperlane-cosmos-native/src/signers.rs | 16 +- .../src/validator_announce.rs | 3 +- .../hyperlane-cosmos/src/providers/grpc.rs | 8 +- .../chains/hyperlane-cosmos/src/signers.rs | 17 +- .../src/validator_announce.rs | 6 +- .../src/contracts/validator_announce.rs | 12 +- .../hyperlane-fuel/src/validator_announce.rs | 6 +- .../src/validator_announce.rs | 1 + .../hyperlane-base/src/settings/signers.rs | 145 +++++++++++++++++- .../src/traits/validator_announce.rs | 6 +- .../src/mocks/validator_announce.rs | 6 +- 15 files changed, 213 insertions(+), 32 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/base_builder/validator_announced_storages/tests.rs b/rust/main/agents/relayer/src/msg/metadata/base_builder/validator_announced_storages/tests.rs index 8a44ca2ccf1..0b0f0a9b9a5 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base_builder/validator_announced_storages/tests.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base_builder/validator_announced_storages/tests.rs @@ -24,7 +24,7 @@ mock! { async fn announce(&self, announcement: SignedType) -> ChainResult; - async fn announce_tokens_needed(&self, announcement: SignedType) -> Option; + async fn announce_tokens_needed(&self, announcement: SignedType, chain_signer: H256) -> Option; } impl HyperlaneContract for ValidatorAnnounceMock { diff --git a/rust/main/agents/validator/src/validator.rs b/rust/main/agents/validator/src/validator.rs index 5dd1c316377..8c23f8b494e 100644 --- a/rust/main/agents/validator/src/validator.rs +++ b/rust/main/agents/validator/src/validator.rs @@ -425,18 +425,21 @@ impl Validator { .chain_signer() .await? { - let chain_signer = chain_signer.address_string(); - info!(eth_validator_address=?announcement.validator, ?chain_signer, "Attempting self announce"); + let chain_signer_string = chain_signer.address_string(); + let chain_signer_h256 = chain_signer.address_h256(); + info!(eth_validator_address=?announcement.validator, ?chain_signer_string, ?chain_signer_h256, "Attempting self announce"); + let balance_delta = self .validator_announce - .announce_tokens_needed(signed_announcement.clone()) + .announce_tokens_needed(signed_announcement.clone(), chain_signer_h256) .await .unwrap_or_default(); if balance_delta > U256::zero() { warn!( tokens_needed=%balance_delta, eth_validator_address=?announcement.validator, - ?chain_signer, + ?chain_signer_string, + ?chain_signer_h256, "Please send tokens to your chain signer address to announce", ); } else { @@ -444,7 +447,7 @@ impl Validator { .validator_announce .announce(signed_announcement.clone()) .await; - Self::log_on_announce_failure(result, &chain_signer); + Self::log_on_announce_failure(result, &chain_signer_string); } } else { warn!(origin_chain=%self.origin_chain, "Cannot announce validator without a signer; make sure a signer is set for the origin chain"); diff --git a/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs b/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs index 76ae7253739..e1b4ad34f49 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs @@ -41,7 +41,7 @@ impl CosmosNativeMailbox { let mailbox_id: String = self.address.encode_hex(); let message = hex::encode(RawHyperlaneMessage::from(message)); let metadata = hex::encode(metadata); - let signer = self.provider.rpc().get_signer()?.address.clone(); + let signer = self.provider.rpc().get_signer()?.address_string.clone(); let process = MsgProcessMessage { mailbox_id: "0x".to_string() + &mailbox_id, metadata, diff --git a/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs b/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs index fb7269d54c4..0d831745506 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/providers/rpc.rs @@ -333,7 +333,7 @@ impl RpcProvider { // As this function is only used for estimating gas or sending transactions, // we can reasonably expect to have a signer. let signer = self.get_signer()?; - let account_info = self.get_account(signer.address.clone()).await?; + let account_info = self.get_account(signer.address_string.clone()).await?; // timeout height of zero means that we do not have a timeout height TODO: double check let tx_body = tx::Body::new(msgs, String::default(), 0u32); diff --git a/rust/main/chains/hyperlane-cosmos-native/src/signers.rs b/rust/main/chains/hyperlane-cosmos-native/src/signers.rs index f12ff972f98..500082564c3 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/signers.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/signers.rs @@ -1,6 +1,6 @@ use cosmrs::crypto::{secp256k1::SigningKey, PublicKey}; -use hyperlane_core::{AccountAddressType, ChainResult}; +use hyperlane_core::{AccountAddressType, ChainResult, H256}; use crate::{CosmosAddress, HyperlaneCosmosError}; @@ -9,9 +9,11 @@ use crate::{CosmosAddress, HyperlaneCosmosError}; pub struct Signer { /// public key pub public_key: PublicKey, + /// cosmos address + pub address: CosmosAddress, /// precomputed address, because computing it is a fallible operation /// and we want to avoid returning `Result` - pub address: String, + pub address_string: String, /// address prefix pub prefix: String, private_key: Vec, @@ -29,14 +31,15 @@ impl Signer { prefix: String, account_address_type: &AccountAddressType, ) -> ChainResult { - let address = - CosmosAddress::from_privkey(&private_key, &prefix, account_address_type)?.address(); + let address = CosmosAddress::from_privkey(&private_key, &prefix, account_address_type)?; + let address_string = address.address(); let signing_key = Self::build_signing_key(&private_key)?; let public_key = signing_key.public_key(); Ok(Self { public_key, private_key, address, + address_string, prefix, }) } @@ -51,4 +54,9 @@ impl Signer { Ok(SigningKey::from_slice(private_key.as_slice()) .map_err(Into::::into)?) } + + /// gets digest of the cosmos account + pub fn address_h256(&self) -> H256 { + self.address.digest() + } } diff --git a/rust/main/chains/hyperlane-cosmos-native/src/validator_announce.rs b/rust/main/chains/hyperlane-cosmos-native/src/validator_announce.rs index 3c5981269f8..97c5fc57ce7 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/validator_announce.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/validator_announce.rs @@ -75,7 +75,7 @@ impl ValidatorAnnounce for CosmosNativeValidatorAnnounce { } async fn announce(&self, announcement: SignedType) -> ChainResult { - let signer = self.provider.rpc().get_signer()?.address.to_owned(); + let signer = self.provider.rpc().get_signer()?.address_string.to_owned(); let announce = MsgAnnounceValidator { validator: announcement.value.validator.encode_hex(), storage_location: announcement.value.storage_location.clone(), @@ -108,6 +108,7 @@ impl ValidatorAnnounce for CosmosNativeValidatorAnnounce { async fn announce_tokens_needed( &self, _announcement: SignedType, + _chain_signer: H256, ) -> Option { // TODO: check user balance. For now, just try announcing and // allow the announce attempt to fail if there are not enough tokens. diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs b/rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs index ff525bebb56..306f93e73b1 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs @@ -379,7 +379,7 @@ impl WasmGrpcProvider { // As this function is only used for estimating gas or sending transactions, // we can reasonably expect to have a signer. let signer = self.get_signer()?; - let account_info = self.account_query(signer.address.clone()).await?; + let account_info = self.account_query(signer.address_string.clone()).await?; let current_height = self.latest_block_height().await?; let timeout_height = current_height + TIMEOUT_BLOCKS; @@ -628,7 +628,7 @@ impl WasmProvider for WasmGrpcProvider { let signer = self.get_signer()?; let contract_address = self.get_contract_address(); let msg = MsgExecuteContract { - sender: signer.address.clone(), + sender: signer.address_string.clone(), contract: contract_address.address(), msg: serde_json::to_string(&payload)?.as_bytes().to_vec(), funds: vec![], @@ -649,7 +649,7 @@ impl WasmProvider for WasmGrpcProvider { // Check if the signer has enough funds to pay for the fee so we can get // a more informative error. let signer_balance = self - .get_balance(signer.address.clone(), fee.denom.to_string()) + .get_balance(signer.address_string.clone(), fee.denom.to_string()) .await?; let fee_amount: U256 = fee.amount.into(); if signer_balance < fee_amount { @@ -680,7 +680,7 @@ impl WasmProvider for WasmGrpcProvider { let signer = self.get_signer()?; let contract_address = self.get_contract_address(); let msg = MsgExecuteContract { - sender: signer.address.clone(), + sender: signer.address_string.clone(), contract: contract_address.address(), msg: serde_json::to_string(&payload)?.as_bytes().to_vec(), funds: vec![], diff --git a/rust/main/chains/hyperlane-cosmos/src/signers.rs b/rust/main/chains/hyperlane-cosmos/src/signers.rs index 6edfe0e400e..9d5a7796d08 100644 --- a/rust/main/chains/hyperlane-cosmos/src/signers.rs +++ b/rust/main/chains/hyperlane-cosmos/src/signers.rs @@ -1,5 +1,5 @@ use cosmrs::crypto::{secp256k1::SigningKey, PublicKey}; -use hyperlane_core::{AccountAddressType, ChainResult}; +use hyperlane_core::{AccountAddressType, ChainResult, H256}; use crate::{CosmosAddress, HyperlaneCosmosError}; @@ -8,11 +8,14 @@ use crate::{CosmosAddress, HyperlaneCosmosError}; pub struct Signer { /// public key pub public_key: PublicKey, + /// cosmos address + pub address: CosmosAddress, /// precomputed address, because computing it is a fallible operation /// and we want to avoid returning `Result` - pub address: String, + pub address_string: String, /// address prefix pub prefix: String, + /// private key private_key: Vec, } @@ -28,14 +31,15 @@ impl Signer { prefix: String, account_address_type: &AccountAddressType, ) -> ChainResult { - let address = - CosmosAddress::from_privkey(&private_key, &prefix, account_address_type)?.address(); + let address = CosmosAddress::from_privkey(&private_key, &prefix, account_address_type)?; + let address_string = address.address(); let signing_key = Self::build_signing_key(&private_key)?; let public_key = signing_key.public_key(); Ok(Self { public_key, private_key, address, + address_string, prefix, }) } @@ -51,4 +55,9 @@ impl Signer { .map_err(Box::new) .map_err(Into::::into)?) } + + /// gets digest of the cosmos account + pub fn address_h256(&self) -> H256 { + self.address.digest() + } } diff --git a/rust/main/chains/hyperlane-cosmos/src/validator_announce.rs b/rust/main/chains/hyperlane-cosmos/src/validator_announce.rs index d79970e4a3e..32a3daf5d0f 100644 --- a/rust/main/chains/hyperlane-cosmos/src/validator_announce.rs +++ b/rust/main/chains/hyperlane-cosmos/src/validator_announce.rs @@ -100,7 +100,11 @@ impl ValidatorAnnounce for CosmosValidatorAnnounce { Ok(tx_response_to_outcome(response)?) } - async fn announce_tokens_needed(&self, announcement: SignedType) -> Option { + async fn announce_tokens_needed( + &self, + announcement: SignedType, + _chain_signer: H256, + ) -> Option { // TODO: check user balance. For now, just try announcing and // allow the announce attempt to fail if there are not enough tokens. Some(0u64.into()) diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs index a51a255e439..762d8e520ec 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs @@ -150,16 +150,18 @@ where } #[instrument(ret, skip(self))] - async fn announce_tokens_needed(&self, announcement: SignedType) -> Option { - let validator = announcement.value.validator; - let eth_h160: ethers::types::H160 = validator.into(); - + async fn announce_tokens_needed( + &self, + announcement: SignedType, + chain_signer: H256, + ) -> Option { let Ok(contract_call) = self.announce_contract_call(announcement).await else { trace!("Unable to get announce contract call"); return None; }; - let Ok(balance) = self.provider.get_balance(eth_h160, None).await else { + let chain_signer_h160 = ethers::types::H160::from(chain_signer); + let Ok(balance) = self.provider.get_balance(chain_signer_h160, None).await else { trace!("Unable to query balance"); return None; }; diff --git a/rust/main/chains/hyperlane-fuel/src/validator_announce.rs b/rust/main/chains/hyperlane-fuel/src/validator_announce.rs index 10402db4b41..18a432665e1 100644 --- a/rust/main/chains/hyperlane-fuel/src/validator_announce.rs +++ b/rust/main/chains/hyperlane-fuel/src/validator_announce.rs @@ -38,7 +38,11 @@ impl ValidatorAnnounce for FuelValidatorAnnounce { todo!() } - async fn announce_tokens_needed(&self, announcement: SignedType) -> Option { + async fn announce_tokens_needed( + &self, + announcement: SignedType, + _chain_signer: H256, + ) -> Option { todo!() } } diff --git a/rust/main/chains/hyperlane-sealevel/src/validator_announce.rs b/rust/main/chains/hyperlane-sealevel/src/validator_announce.rs index 1684a9d44af..f64ee2a1c8c 100644 --- a/rust/main/chains/hyperlane-sealevel/src/validator_announce.rs +++ b/rust/main/chains/hyperlane-sealevel/src/validator_announce.rs @@ -105,6 +105,7 @@ impl ValidatorAnnounce for SealevelValidatorAnnounce { async fn announce_tokens_needed( &self, _announcement: SignedType, + _chain_signer: H256, ) -> Option { Some(U256::zero()) } diff --git a/rust/main/hyperlane-base/src/settings/signers.rs b/rust/main/hyperlane-base/src/settings/signers.rs index c8cf55bdfe9..6ea6ed4a1e2 100644 --- a/rust/main/hyperlane-base/src/settings/signers.rs +++ b/rust/main/hyperlane-base/src/settings/signers.rs @@ -53,6 +53,8 @@ impl SignerConf { pub trait ChainSigner: Send { /// The address of the signer, formatted in the chain's own address format. fn address_string(&self) -> String; + /// The address of the signer, in h256 format + fn address_h256(&self) -> H256; } /// Builder trait for signers @@ -96,6 +98,9 @@ impl ChainSigner for hyperlane_ethereum::Signers { fn address_string(&self) -> String { ethers::signers::Signer::address(self).encode_hex() } + fn address_h256(&self) -> H256 { + ethers::types::H256::from(ethers::signers::Signer::address(self)).into() + } } #[async_trait] @@ -117,6 +122,9 @@ impl ChainSigner for fuels::prelude::WalletUnlocked { fn address_string(&self) -> String { self.address().to_string() } + fn address_h256(&self) -> H256 { + H256::from_slice(fuels::types::Address::from(self.address()).as_slice()) + } } #[async_trait] @@ -134,6 +142,9 @@ impl ChainSigner for hyperlane_sealevel::Keypair { fn address_string(&self) -> String { solana_sdk::signer::Signer::pubkey(self).to_string() } + fn address_h256(&self) -> H256 { + H256::from_slice(&solana_sdk::signer::Signer::pubkey(self).to_bytes()) + } } #[async_trait] @@ -158,7 +169,10 @@ impl BuildableWithSignerConf for hyperlane_cosmos::Signer { impl ChainSigner for hyperlane_cosmos::Signer { fn address_string(&self) -> String { - self.address.clone() + self.address_string.clone() + } + fn address_h256(&self) -> H256 { + self.address_h256() } } @@ -184,6 +198,133 @@ impl BuildableWithSignerConf for hyperlane_cosmos_native::Signer { impl ChainSigner for hyperlane_cosmos_native::Signer { fn address_string(&self) -> String { - self.address.clone() + self.address_string.clone() + } + fn address_h256(&self) -> H256 { + self.address_h256() + } +} + +#[cfg(test)] +mod tests { + use ethers::{signers::LocalWallet, utils::hex}; + use hyperlane_core::{AccountAddressType, Encode, H256}; + + use crate::settings::ChainSigner; + + #[test] + fn address_h256_ethereum() { + const PRIVATE_KEY: &str = + "2bcd4cb33dc9b879d74aebb847b0fdd27868ade2b3a999988debcaae763283c6"; + const ADDRESS: &str = "0000000000000000000000000bec35c9af305b1b8849d652f4b542d19ef7e8f9"; + + let wallet = PRIVATE_KEY + .parse::() + .expect("Failed to parse private key"); + + let chain_signer = hyperlane_ethereum::Signers::Local(wallet); + + let address_h256 = H256::from_slice( + hex::decode(ADDRESS) + .expect("Failed to decode public key") + .as_slice(), + ); + assert_eq!(chain_signer.address_h256(), address_h256); + } + + #[test] + fn address_h256_sealevel() { + const PRIVATE_KEY: &str = + "0d861aa9ee7b09fe0305a649ec9aa0dfede421817dbe995b48964e5a79fc89e50f8ac473c042cdd96a1fc81eac32221188807572521429fb871a856a668502a5"; + const ADDRESS: &str = "0f8ac473c042cdd96a1fc81eac32221188807572521429fb871a856a668502a5"; + + let chain_signer = hyperlane_sealevel::Keypair::from_bytes( + hex::decode(PRIVATE_KEY) + .expect("Failed to decode private key") + .as_slice(), + ) + .expect("Failed to decode keypair"); + + let address_h256 = H256::from_slice( + hex::decode(ADDRESS) + .expect("Failed to decode public key") + .as_slice(), + ); + assert_eq!(chain_signer.address_h256(), address_h256); + } + + #[test] + fn address_h256_fuel() { + const PRIVATE_KEY: &str = + "0a83ee2a87f328704512567198ee25578c27c707b26fdf3be9ea8bf8588f3b65"; + const PUBLIC_KEY: &str = "b43425b2256e7dcdd61752808b137b23f4f697cfaf21175ed81d0610ebab5a87"; + + let private_key = fuels::crypto::SecretKey::try_from( + hex::decode(PRIVATE_KEY) + .expect("Failed to decode private key") + .as_slice(), + ) + .expect("Failed to create secret key"); + + let chain_signer = fuels::prelude::WalletUnlocked::new_from_private_key(private_key, None); + + let address_h256 = H256::from_slice( + hex::decode(PUBLIC_KEY) + .expect("Failed to decode public key") + .as_slice(), + ); + assert_eq!(chain_signer.address_h256(), address_h256); + } + + #[test] + fn address_h256_cosmos() { + const PRIVATE_KEY: &str = + "5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26"; + const ADDRESS: &str = "000000000000000000000000b5a79b48c87e7a37bdb625096140ee7054816942"; + + let key = H256::from_slice( + hex::decode(PRIVATE_KEY) + .expect("Failed to decode public key") + .as_slice(), + ); + let chain_signer = hyperlane_cosmos::Signer::new( + key.to_vec(), + "neutron".to_string(), + &AccountAddressType::Bitcoin, + ) + .expect("Failed to create cosmos signer"); + + let address_h256 = H256::from_slice( + hex::decode(ADDRESS) + .expect("Failed to decode public key") + .as_slice(), + ); + assert_eq!(chain_signer.address_h256(), address_h256); + } + + #[test] + fn address_h256_cosmosnative() { + const PRIVATE_KEY: &str = + "5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26"; + const ADDRESS: &str = "000000000000000000000000b5a79b48c87e7a37bdb625096140ee7054816942"; + + let key = H256::from_slice( + hex::decode(PRIVATE_KEY) + .expect("Failed to decode public key") + .as_slice(), + ); + let chain_signer = hyperlane_cosmos_native::Signer::new( + key.to_vec(), + "neutron".to_string(), + &AccountAddressType::Bitcoin, + ) + .expect("Failed to create cosmos signer"); + + let address_h256 = H256::from_slice( + hex::decode(ADDRESS) + .expect("Failed to decode public key") + .as_slice(), + ); + assert_eq!(chain_signer.address_h256(), address_h256); } } diff --git a/rust/main/hyperlane-core/src/traits/validator_announce.rs b/rust/main/hyperlane-core/src/traits/validator_announce.rs index 691aa48d120..16a58f960af 100644 --- a/rust/main/hyperlane-core/src/traits/validator_announce.rs +++ b/rust/main/hyperlane-core/src/traits/validator_announce.rs @@ -21,5 +21,9 @@ pub trait ValidatorAnnounce: HyperlaneContract + Send + Sync + Debug { /// Returns the number of additional tokens needed to pay for the announce /// transaction. Return `None` if the needed tokens cannot be determined. - async fn announce_tokens_needed(&self, announcement: SignedType) -> Option; + async fn announce_tokens_needed( + &self, + announcement: SignedType, + chain_signer: H256, + ) -> Option; } diff --git a/rust/main/hyperlane-test/src/mocks/validator_announce.rs b/rust/main/hyperlane-test/src/mocks/validator_announce.rs index 849aec6808b..f4d44b2862b 100644 --- a/rust/main/hyperlane-test/src/mocks/validator_announce.rs +++ b/rust/main/hyperlane-test/src/mocks/validator_announce.rs @@ -59,7 +59,11 @@ impl ValidatorAnnounce for MockValidatorAnnounceContract { async fn announce(&self, announcement: SignedType) -> ChainResult { self._announce(announcement) } - async fn announce_tokens_needed(&self, announcement: SignedType) -> Option { + async fn announce_tokens_needed( + &self, + announcement: SignedType, + _chain_signer: H256, + ) -> Option { self._announce_tokens_needed(announcement) } } From f64b0be29442b2e96893ed6602796d5ced81eff0 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Fri, 2 May 2025 11:32:45 -0400 Subject: [PATCH 125/223] feat: add es warp route (#6100) ### Description Adds config getter for Eclipse route ### Drive-by changes - None ### Related issues ### Backward compatibility - YES ### Testing - Manual --- .registryrc | 2 +- .../mainnet3/warp-routes/es/program-ids.json | 10 ++++++ .../mainnet3/warp-routes/es/token-config.json | 17 ++++++++++ .../getEclipseEthereumESWarpConfig.ts | 32 +++++++++++++++++++ .../environments/mainnet3/warp/warpIds.ts | 1 + typescript/infra/config/warp.ts | 2 ++ typescript/infra/src/config/warp.ts | 1 + 7 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/es/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/es/token-config.json create mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumESWarpConfig.ts diff --git a/.registryrc b/.registryrc index 9ed0ce751b9..3882652e012 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -fad1ad9000a5795ed5c617f6ab677d330f52a3b3 +3206b17e90450cc95ca8a536deda2ec85b1fb47f diff --git a/rust/sealevel/environments/mainnet3/warp-routes/es/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/es/program-ids.json new file mode 100644 index 00000000000..b48b945a12f --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/es/program-ids.json @@ -0,0 +1,10 @@ +{ + "eclipsemainnet": { + "hex": "0x1372fe2c04ba72a16359fbba8a72ca18d9b5ea90f5841fcccb0edaf42b0aca7a", + "base58": "2JvSu7PzquY2b8NDZbnupFZ1jezqMBtNUhi7TuU3GQJD" + }, + "ethereum": { + "hex": "0x0000000000000000000000003ee1bea3a10f7508b76219f109ebe19419b3dd85", + "base58": "111111111111sp2kFqo8XmoJWhvpHgxBanT8CgC" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/es/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/es/token-config.json new file mode 100644 index 00000000000..25ce7dca101 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/es/token-config.json @@ -0,0 +1,17 @@ +{ + "ethereum": { + "type": "collateral", + "decimals": 6, + "token": "0x6055Dc6Ff1077eebe5e6D2BA1a1f53d7Ef8430dE", + "foreignDeployment": "0x3eE1BEA3A10f7508b76219f109eBe19419b3dd85" + }, + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "name": "Eclipse", + "symbol": "ES", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/refs/heads/main/deployments/warp_routes/ES/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj", + "owner": "3KK8L7UYd7NV575w9vWR2o1kNqdSFvEPUwcTA5353cax" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumESWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumESWarpConfig.ts new file mode 100644 index 00000000000..fd8100f559a --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseEthereumESWarpConfig.ts @@ -0,0 +1,32 @@ +import { ChainMap, HypTokenRouterConfig, TokenType } from '@hyperlane-xyz/sdk'; + +import { + RouterConfigWithoutOwner, + tokens, +} from '../../../../../src/config/warp.js'; +import { SEALEVEL_WARP_ROUTE_HANDLER_GAS_AMOUNT } from '../consts.js'; + +const eclipseTeamMultiSigs = { + eclipsemainnet: '3KK8L7UYd7NV575w9vWR2o1kNqdSFvEPUwcTA5353cax', + ethereum: '0x7B2c1CbB33c53c3C6a695e36096AD2cfCE1c0efC', +}; + +export const getEclipseEthereumESWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + return { + eclipsemainnet: { + mailbox: routerConfig.eclipsemainnet.mailbox, + owner: eclipseTeamMultiSigs.eclipsemainnet, + type: TokenType.synthetic, + foreignDeployment: '2JvSu7PzquY2b8NDZbnupFZ1jezqMBtNUhi7TuU3GQJD', + gas: SEALEVEL_WARP_ROUTE_HANDLER_GAS_AMOUNT, + }, + ethereum: { + mailbox: routerConfig.ethereum.mailbox, + type: TokenType.collateral, + owner: eclipseTeamMultiSigs.ethereum, + token: tokens.ethereum.ES, + }, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index 9f3f05c18d7..bfacbcdd101 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -76,6 +76,7 @@ export enum WarpRouteIds { SolanaSonicsvmLrtsSOL = 'lrtsSOL/solanamainnet-sonicsvm', SolanaSonicsvmSonicSOL = 'sonicSOL/solanamainnet-sonicsvm', ArbitrumEthereumSolanaTreasureSMOL = 'SMOL/arbitrum-ethereum-solanamainnet-treasure', + EclipseEthereumES = 'ES/eclipsemainnet-ethereum', BaseSolanaSophonCDX = 'CDX/base-solanamainnet-sophon', ArbitrumSolanaLOGX = 'LOGX/arbitrum-solanamainnet', // TODO: uncomment after merging the staging route to registry diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index 4f3aeaa1f9c..dd40ffc37c9 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -40,6 +40,7 @@ import { getBaseZeroNetworkCBBTCWarpConfig } from './environments/mainnet3/warp/ import { getBobaBsquaredSoneiumSwellUBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBobaBsquaredSwellUBTCWarpConfig.js'; import { getBscHyperevmEnzoBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBscHyperevmEnzoBTCWarpConfig.js'; import { getBscHyperevmSTBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBscHyperevmSTBTCWarpConfig.js'; +import { getEclipseEthereumESWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseEthereumESWarpConfig.js'; import { getEclipseEthereumSolanaUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseEthereumSolanaUSDTWarpConfig.js'; import { getEclipseEthereumWBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseEthereumWBTCWarpConfig.js'; import { getEclipseStrideTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.js'; @@ -126,6 +127,7 @@ export const warpConfigGetterMap: Record = { [WarpRouteIds.EthereumSuperseedUSDC]: getEthereumSuperseedUSDCWarpConfig, [WarpRouteIds.ArbitrumEthereumSolanaTreasureSMOL]: getArbitrumEthereumSolanaTreasureSMOLWarpConfig, + [WarpRouteIds.EclipseEthereumES]: getEclipseEthereumESWarpConfig, // TODO: uncomment after merging the staging route to registry // this has been commented out as it leads to check-warp-deploy cron job failing // [WarpRouteIds.SuperTokenStaging]: getSuperTokenStagingWarpConfig, diff --git a/typescript/infra/src/config/warp.ts b/typescript/infra/src/config/warp.ts index fcadd72bdf9..63568923b91 100644 --- a/typescript/infra/src/config/warp.ts +++ b/typescript/infra/src/config/warp.ts @@ -16,6 +16,7 @@ export const tokens = { PNDR: '0x73624d2dEF952C77a1f3B5AD995eef53E49639EC', rstETH: '0x7a4EffD87C2f3C55CA251080b1343b605f327E3a', WSTETH: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', + ES: '0x6055Dc6Ff1077eebe5e6D2BA1a1f53d7Ef8430dE', FORM: '0xE7deE4823EE18F1347F1Cf7997f70B94eFDe2E1F', Turtle: '0x2BE3be1D8A556954320c2252dF4f891F64699d50', }, From bfff9d48fb641f4b47abe0c5db5cf600188e7a31 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 2 May 2025 17:41:50 +0100 Subject: [PATCH 126/223] fix(submitter): Cache estimate and use it only on the first submission attempt (#6106) ### Description Simulation was removed to make Lander as quick and Classical Submitter. We re-introduce the simulation before submission in the form of estimation. We shall use the result of the estimation on the first attempt of submission so that this does not add latency. ### Drive-by changes Removed the increment of the counter of submission attempts from within submission logic since currently the value which is produced is not correct: - When we retry the transaction submission until success or non-retryable error, we don't retain the updates from unsuccessful submissions since we pass a cloned instance of transaction. - When the submission is successful, we increment the counter in `process_pending_tx` method. It means that in all cases the overall result is that the counter is updated twice. ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../submitter/src/chain_tx_adapter/adapter.rs | 3 + .../src/chain_tx_adapter/chains/cosmos.rs | 4 ++ .../src/chain_tx_adapter/chains/ethereum.rs | 4 ++ .../chains/sealevel/adapter.rs | 71 ++++++++++++++----- .../chains/sealevel/adapter/tests/build.rs | 7 +- .../chains/sealevel/transaction/update.rs | 1 - .../stages/inclusion_stage.rs | 24 ++++++- .../src/payload_dispatcher/test_utils.rs | 1 + .../submitter/src/payload_dispatcher/tests.rs | 39 +++++----- 9 files changed, 119 insertions(+), 35 deletions(-) diff --git a/rust/main/submitter/src/chain_tx_adapter/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/adapter.rs index 79cccd70b5e..0b17643957f 100644 --- a/rust/main/submitter/src/chain_tx_adapter/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/adapter.rs @@ -44,6 +44,9 @@ pub trait AdaptsChain: Send + Sync { /// Simulates a Transaction before submitting it for the first time. Called in the Inclusion Stage (PayloadDispatcher) async fn simulate_tx(&self, tx: &Transaction) -> Result; + /// Estimates a Transaction before submitting it for the first time. Called in the Inclusion Stage (PayloadDispatcher) + async fn estimate_tx(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; + /// Sets / escalates gas price, sets nonce / blockhash and broadcasts the Transaction. Even if broadcasting fails, the Transaction struct remains mutated with the new estimates. Called in the Inclusion Stage (PayloadDispatcher) async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs b/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs index a19d9a4f2c8..a1988b559af 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs @@ -42,6 +42,10 @@ impl AdaptsChain for CosmosTxAdapter { todo!() } + async fn estimate_tx(&self, _tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { + todo!() + } + async fn submit(&self, _tx: &mut Transaction) -> Result<(), SubmitterError> { todo!() } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs index 4837c4c8b7e..a676fb1b557 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs @@ -42,6 +42,10 @@ impl AdaptsChain for EthereumTxAdapter { todo!() } + async fn estimate_tx(&self, _tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { + todo!() + } + async fn submit(&self, _tx: &mut Transaction) -> Result<(), SubmitterError> { todo!() } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index 35d21a76af5..fb3f3e39efa 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -16,6 +16,7 @@ use solana_sdk::{ signature::{Signature, Signer}, transaction::Transaction as SealevelTransaction, }; +use tokio::sync::Mutex; use tracing::{info, instrument, warn}; use uuid::Uuid; @@ -56,6 +57,13 @@ use crate::{ const TX_RESUBMISSION_MIN_DELAY_SECS: u64 = 15; +#[derive(Default, Clone, Copy, serde::Deserialize, serde::Serialize, PartialEq, Eq)] +pub enum EstimateFreshnessCache { + #[default] + Stale, + Fresh, +} + pub struct SealevelTxAdapter { estimated_block_time: Duration, max_batch_size: u32, @@ -64,6 +72,7 @@ pub struct SealevelTxAdapter { provider: Box, oracle: Box, submitter: Box, + estimate_freshness_cache: Arc>>, } impl SealevelTxAdapter { @@ -121,6 +130,7 @@ impl SealevelTxAdapter { let estimated_block_time = conf.estimated_block_time; let max_batch_size = Self::batch_size(&conf)?; let keypair = create_keypair(&conf)?; + let estimate_freshness_cache = Arc::new(Mutex::new(HashMap::new())); Ok(Self { estimated_block_time, @@ -130,6 +140,7 @@ impl SealevelTxAdapter { client, oracle, submitter, + estimate_freshness_cache, }) } @@ -149,6 +160,7 @@ impl SealevelTxAdapter { client, oracle, submitter, + estimate_freshness_cache: Arc::new(Mutex::new(HashMap::new())), } } @@ -162,7 +174,7 @@ impl SealevelTxAdapter { async fn estimate( &self, - precursor: SealevelTxPrecursor, + precursor: &SealevelTxPrecursor, ) -> Result { let estimate = self .provider @@ -173,7 +185,10 @@ impl SealevelTxAdapter { &*self.oracle, ) .await?; - Ok(SealevelTxPrecursor::new(precursor.instruction, estimate)) + Ok(SealevelTxPrecursor::new( + precursor.instruction.clone(), + estimate, + )) } async fn create_unsigned_transaction( @@ -267,7 +282,7 @@ impl AdaptsChain for SealevelTxAdapter { ) -> Result, SubmitterError> { info!(?payload, "estimating payload"); let not_estimated = SealevelTxPrecursor::from_payload(payload); - let estimated = self.estimate(not_estimated).await?; + let estimated = self.estimate(¬_estimated).await?; info!(?payload, ?estimated, "estimated payload"); Ok(Some(estimated.estimate.compute_units.into())) } @@ -281,15 +296,8 @@ impl AdaptsChain for SealevelTxAdapter { let mut transactions = Vec::new(); for (not_estimated, payload) in payloads_and_precursors.into_iter() { - let estimated = match self.estimate(not_estimated).await { - Ok(estimated) => estimated, - Err(err) => { - warn!(?err, ?payload, "failed to estimate payload"); - transactions.push(TxBuildingResult::new(vec![payload.details.clone()], None)); - continue; - } - }; - let transaction = TransactionFactory::build(payload, estimated); + // We are not estimating transaction here since we will estimate it just before submission + let transaction = TransactionFactory::build(payload, not_estimated); transactions.push(TxBuildingResult::new( vec![payload.details.clone()], Some(transaction), @@ -313,12 +321,43 @@ impl AdaptsChain for SealevelTxAdapter { Ok(success) } + async fn estimate_tx(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { + use EstimateFreshnessCache::{Fresh, Stale}; + + info!(?tx, "estimating transaction"); + let not_estimated = tx.precursor(); + let estimated = self.estimate(not_estimated).await?; + + // If cache does not contain estimate type, insert Simulation type so that it can be used on the first submission + { + let mut guard = self.estimate_freshness_cache.lock().await; + if guard.get(&tx.id).copied().unwrap_or_default() == Stale { + guard.insert(tx.id.clone(), Fresh); + } + }; + + tx.vm_specific_data = VmSpecificTxData::Svm(estimated); + info!(?tx, "estimated transaction"); + Ok(()) + } + async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { + use EstimateFreshnessCache::{Fresh, Stale}; + info!(?tx, "submitting transaction"); - let not_estimated = tx.precursor(); - // TODO: the `estimate` call shouldn't happen here - the `Transaction` argument should already contain the precursor, - // set in the `build_transactions` method - let estimated = self.estimate(not_estimated.clone()).await?; + + let previous = tx.precursor(); + let estimated = { + let mut guard = self.estimate_freshness_cache.lock().await; + match guard.get(&tx.id).copied().unwrap_or_default() { + Stale => self.estimate(previous).await?, + Fresh => { + guard.insert(tx.id.clone(), Stale); + previous.clone() + } + } + }; + let svm_transaction = self.create_signed_transaction(&estimated).await?; let signature = self .submitter diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/build.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/build.rs index 385566488bc..2a5a0eec4a2 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/build.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter/tests/build.rs @@ -1,5 +1,7 @@ use eyre::Result; +use hyperlane_sealevel::SealevelTxCostEstimate; + use crate::chain_tx_adapter::chains::sealevel::adapter::tests::common::{ adapter, estimate, instruction, payload, }; @@ -13,7 +15,10 @@ async fn test_build_transactions() { // given let adapter = adapter(); let payload = payload(); - let data = VmSpecificTxData::Svm(SealevelTxPrecursor::new(instruction(), estimate())); + let data = VmSpecificTxData::Svm(SealevelTxPrecursor::new( + instruction(), + SealevelTxCostEstimate::default(), + )); let expected = (payload.details.clone(), data); // when diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs index d76572d44e3..fb8c152516d 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/transaction/update.rs @@ -11,7 +11,6 @@ impl Update for Transaction { fn update_after_submission(&mut self, hash: H512, precursor: SealevelTxPrecursor) -> &mut Self { self.tx_hashes.push(hash); self.last_submission_attempt = Some(chrono::Utc::now()); - self.submission_attempts += 1; // Data is updated since transaction is re-estimated before submission self.vm_specific_data = VmSpecificTxData::Svm(precursor); diff --git a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs index 2199752d49d..f2b067f84ec 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/inclusion_stage.rs @@ -213,6 +213,23 @@ impl InclusionStage { // } // info!(?tx, "Transaction simulation succeeded"); + // Estimating transaction just before we submit it + // TODO we will need to re-classify `ChainCommunicationError` into `SubmitterError::EstimateError` in the future. + // At the moment, both errors are non-retryable, so we can keep them as is. + tx = call_until_success_or_nonretryable_error( + || { + let tx_clone = tx.clone(); + async move { + let mut tx_clone_inner = tx_clone.clone(); + state.adapter.estimate_tx(&mut tx_clone_inner).await?; + Ok(tx_clone_inner) + } + }, + "Simulating and estimating transaction", + state, + ) + .await?; + // successively calling `submit` will result in escalating gas price until the tx is accepted // by the node. // at this point, not all VMs return information about whether the tx was reverted. @@ -320,6 +337,8 @@ mod tests { mock_adapter.expect_simulate_tx().returning(|_| Ok(true)); + mock_adapter.expect_estimate_tx().returning(|_| Ok(())); + mock_adapter.expect_submit().returning(|_| Ok(())); let (txs_created, txs_received, tx_db, payload_db, pool) = @@ -337,7 +356,6 @@ mod tests { } #[tokio::test] - #[ignore] async fn test_failed_simulation() { const TXS_TO_PROCESS: usize = 3; @@ -352,6 +370,10 @@ mod tests { mock_adapter.expect_simulate_tx().returning(|_| Ok(false)); + mock_adapter + .expect_estimate_tx() + .returning(|_| Err(SubmitterError::SimulationFailed)); + let (txs_created, txs_received, tx_db, payload_db, pool) = set_up_test_and_run_stage(mock_adapter, TXS_TO_PROCESS).await; diff --git a/rust/main/submitter/src/payload_dispatcher/test_utils.rs b/rust/main/submitter/src/payload_dispatcher/test_utils.rs index 945d44968da..669a6e78d03 100644 --- a/rust/main/submitter/src/payload_dispatcher/test_utils.rs +++ b/rust/main/submitter/src/payload_dispatcher/test_utils.rs @@ -24,6 +24,7 @@ mockall::mock! { async fn estimate_gas_limit(&self, payload: &FullPayload) -> Result, SubmitterError>; async fn build_transactions(&self, payloads: &[FullPayload]) -> Vec; async fn simulate_tx(&self, tx: &Transaction) -> Result; + async fn estimate_tx(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; async fn tx_status(&self, tx: &Transaction) -> Result; async fn reverted_payloads(&self, tx: &Transaction) -> Result, SubmitterError>; diff --git a/rust/main/submitter/src/payload_dispatcher/tests.rs b/rust/main/submitter/src/payload_dispatcher/tests.rs index 5bdd01f6788..c0580b008bb 100644 --- a/rust/main/submitter/src/payload_dispatcher/tests.rs +++ b/rust/main/submitter/src/payload_dispatcher/tests.rs @@ -10,7 +10,7 @@ use crate::{ BuildingStageQueue, PayloadDbLoader, PayloadDispatcherState, }, Entrypoint, FullPayload, PayloadDispatcher, PayloadDispatcherEntrypoint, PayloadId, - PayloadStatus, TransactionStatus, + PayloadStatus, SubmitterError, TransactionStatus, }; use super::PayloadDb; @@ -105,21 +105,20 @@ async fn test_entrypoint_send_is_finalized_by_dispatcher() { #[tracing_test::traced_test] #[tokio::test] -#[ignore] -async fn test_entrypoint_send_is_dropped_by_dispatcher() { +async fn test_entrypoint_send_fails_simulation_after_first_submission() { let payload = FullPayload::random(); let mut adapter = MockAdapter::new(); let mut counter = 0; - adapter.expect_simulate_tx().returning(move |_| { + adapter.expect_estimate_tx().returning(move |_| { counter += 1; if counter == 1 { // simulation is successful the first time around, and the payload makes it into a tx - Ok(true) + Ok(()) } else { // the second time around, the simulation fails, say due to a network race condition // where the payload was delivered by someone else and now it reverts - Ok(false) + Err(SubmitterError::SimulationFailed) } }); let adapter = mock_adapter_methods(adapter, payload.clone()); @@ -130,7 +129,7 @@ async fn test_entrypoint_send_is_dropped_by_dispatcher() { let _payload_dispatcher = tokio::spawn(async move { dispatcher.spawn().await }); entrypoint.send_payload(&payload).await.unwrap(); - // wait until the payload status is InTransaction(Finalized) + // wait until the payload status is InTransaction(Dropped(_)) wait_until_payload_status( entrypoint.inner.payload_db.clone(), payload.id(), @@ -155,20 +154,21 @@ async fn test_entrypoint_send_is_dropped_by_dispatcher() { dropped_transactions: 1, dropped_payload_reason: "DroppedInTransaction(FailedSimulation)".to_string(), dropped_transaction_reason: "FailedSimulation".to_string(), - transaction_submissions: 0, + transaction_submissions: 1, }; assert_metrics(metrics, metrics_assertion); } #[tracing_test::traced_test] #[tokio::test] -#[ignore] -async fn test_entrypoint_payload_fails_simulation() { +async fn test_entrypoint_send_fails_simulation_before_first_submission() { let payload = FullPayload::random(); let mut adapter = MockAdapter::new(); // the payload always fails simulation - adapter.expect_simulate_tx().returning(move |_| Ok(false)); + adapter + .expect_estimate_tx() + .returning(move |_| Err(SubmitterError::SimulationFailed)); let adapter = mock_adapter_methods(adapter, payload.clone()); let adapter = Arc::new(adapter); let (entrypoint, dispatcher) = mock_entrypoint_and_dispatcher(adapter.clone()).await; @@ -177,11 +177,16 @@ async fn test_entrypoint_payload_fails_simulation() { let _payload_dispatcher = tokio::spawn(async move { dispatcher.spawn().await }); entrypoint.send_payload(&payload).await.unwrap(); - // wait until the payload status is InTransaction(Finalized) + // wait until the payload status is InTransaction(Dropped(_)) wait_until_payload_status( entrypoint.inner.payload_db.clone(), payload.id(), - |payload_status| matches!(payload_status, PayloadStatus::Dropped(_)), + |payload_status| { + matches!( + payload_status, + PayloadStatus::InTransaction(TransactionStatus::Dropped(_)) + ) + }, ) .await; sleep(Duration::from_millis(200)).await; // Wait for the metrics to be updated @@ -193,9 +198,9 @@ async fn test_entrypoint_payload_fails_simulation() { inclusion_stage_pool_length: 0, finality_stage_pool_length: 0, dropped_payloads: 1, - dropped_transactions: 0, - dropped_payload_reason: "FailedSimulation".to_string(), - dropped_transaction_reason: "".to_string(), + dropped_transactions: 1, + dropped_payload_reason: "DroppedInTransaction(FailedSimulation)".to_string(), + dropped_transaction_reason: "FailedSimulation".to_string(), transaction_submissions: 0, }; assert_metrics(metrics, metrics_assertion); @@ -285,6 +290,8 @@ fn mock_adapter_methods(mut adapter: MockAdapter, payload: FullPayload) -> MockA adapter.expect_simulate_tx().returning(|_| Ok(true)); + adapter.expect_estimate_tx().returning(|_| Ok(())); + adapter.expect_submit().returning(|_| Ok(())); adapter } From f6ed6adef54ad9e7c9a0b8a6f7fbbf52e83830b2 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Fri, 2 May 2025 14:21:29 -0400 Subject: [PATCH 127/223] fix(sdk): fix warp apply proxy onwership logic [eng-1399] (#6115) ### Description Updates the proxy admin ownership management logic to default to the warp route owner if no proxy admin config is specified ### Drive-by changes - None ### Related issues ### Backward compatibility - YES ### Testing - Manual - e2e --- .changeset/wise-files-listen.md | 6 ++ typescript/cli/src/tests/commands/helpers.ts | 2 + .../cli/src/tests/warp/warp-apply.e2e-test.ts | 80 +++++++++++++++++++ typescript/sdk/src/deploy/proxy.ts | 36 +++++---- 4 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 .changeset/wise-files-listen.md diff --git a/.changeset/wise-files-listen.md b/.changeset/wise-files-listen.md new file mode 100644 index 00000000000..e4cb0596c5d --- /dev/null +++ b/.changeset/wise-files-listen.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': patch +'@hyperlane-xyz/sdk': patch +--- + +Fixed proxy admin ownership transfer logic when the config is not specified in the input file diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index ba4c2475b05..1b6fd7fe545 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -40,6 +40,8 @@ export const TEMP_PATH = '/tmp'; // /temp gets removed at the end of all-test.sh export const ANVIL_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; +export const ANVIL_DEPLOYER_ADDRESS = + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; export const E2E_TEST_BURN_ADDRESS = '0x0000000000000000000000000000000000000001'; diff --git a/typescript/cli/src/tests/warp/warp-apply.e2e-test.ts b/typescript/cli/src/tests/warp/warp-apply.e2e-test.ts index 2f6e167aa60..3d27a76f0fe 100644 --- a/typescript/cli/src/tests/warp/warp-apply.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-apply.e2e-test.ts @@ -13,6 +13,7 @@ import { import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; import { + ANVIL_DEPLOYER_ADDRESS, ANVIL_KEY, CHAIN_NAME_2, CHAIN_NAME_3, @@ -97,6 +98,85 @@ describe('hyperlane warp apply owner update tests', async function () { ); }); + it('should update the owner of both the warp token and the proxy admin', async () => { + const warpConfigPath = `${TEMP_PATH}/warp-route-deploy-config-2.yaml`; + + const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + WARP_CONFIG_PATH_EXAMPLE, + ); + + // Set to undefined if it was defined in the config + warpConfig.anvil1.proxyAdmin = undefined; + warpConfig.anvil1.owner = E2E_TEST_BURN_ADDRESS; + const anvil2Config = { anvil2: { ...warpConfig.anvil1 } }; + writeYamlOrJson(warpConfigPath, anvil2Config); + + await hyperlaneWarpApply(warpConfigPath, WARP_CORE_CONFIG_PATH_2); + + const updatedWarpDeployConfig1 = await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2, + warpConfigPath, + ); + + expect(updatedWarpDeployConfig1.anvil2.owner).to.eq(E2E_TEST_BURN_ADDRESS); + expect(updatedWarpDeployConfig1.anvil2.proxyAdmin?.owner).to.eq( + E2E_TEST_BURN_ADDRESS, + ); + }); + + it('should update only the owner of the warp token if the proxy admin config is specified', async () => { + const warpConfigPath = `${TEMP_PATH}/warp-route-deploy-config-2.yaml`; + + const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + WARP_CONFIG_PATH_EXAMPLE, + ); + + // Explicitly set it to the deployer address if it was not defined + warpConfig.anvil1.proxyAdmin = { owner: ANVIL_DEPLOYER_ADDRESS }; + warpConfig.anvil1.owner = E2E_TEST_BURN_ADDRESS; + const anvil2Config = { anvil2: { ...warpConfig.anvil1 } }; + writeYamlOrJson(warpConfigPath, anvil2Config); + + await hyperlaneWarpApply(warpConfigPath, WARP_CORE_CONFIG_PATH_2); + + const updatedWarpDeployConfig1 = await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2, + warpConfigPath, + ); + + expect(updatedWarpDeployConfig1.anvil2.owner).to.eq(E2E_TEST_BURN_ADDRESS); + expect(updatedWarpDeployConfig1.anvil2.proxyAdmin?.owner).to.eq( + ANVIL_DEPLOYER_ADDRESS, + ); + }); + + it('should update only the owner of the proxy admin if the proxy admin config is specified', async () => { + const warpConfigPath = `${TEMP_PATH}/warp-route-deploy-config-2.yaml`; + + const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + WARP_CONFIG_PATH_EXAMPLE, + ); + + warpConfig.anvil1.proxyAdmin = { owner: E2E_TEST_BURN_ADDRESS }; + const anvil2Config = { anvil2: { ...warpConfig.anvil1 } }; + writeYamlOrJson(warpConfigPath, anvil2Config); + + await hyperlaneWarpApply(warpConfigPath, WARP_CORE_CONFIG_PATH_2); + + const updatedWarpDeployConfig1 = await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2, + warpConfigPath, + ); + + expect(updatedWarpDeployConfig1.anvil2.owner).to.eq(ANVIL_DEPLOYER_ADDRESS); + expect(updatedWarpDeployConfig1.anvil2.proxyAdmin?.owner).to.eq( + E2E_TEST_BURN_ADDRESS, + ); + }); + it('should update hook configuration', async () => { const warpDeployPath = `${TEMP_PATH}/warp-route-deployment-2.yaml`; diff --git a/typescript/sdk/src/deploy/proxy.ts b/typescript/sdk/src/deploy/proxy.ts index ecd00215977..9a688a4c89f 100644 --- a/typescript/sdk/src/deploy/proxy.ts +++ b/typescript/sdk/src/deploy/proxy.ts @@ -85,43 +85,47 @@ export async function isProxy( export function proxyAdminUpdateTxs( chainId: ChainId, proxyAddress: Address, - actualConfig: Readonly<{ proxyAdmin?: DeployedOwnableConfig }>, - expectedConfig: Readonly<{ proxyAdmin?: DeployedOwnableConfig }>, + actualConfig: Readonly<{ owner: string; proxyAdmin?: DeployedOwnableConfig }>, + expectedConfig: Readonly<{ + owner: string; + proxyAdmin?: DeployedOwnableConfig; + }>, ): AnnotatedEV5Transaction[] { const transactions: AnnotatedEV5Transaction[] = []; - // Return early because old config files did not have the - // proxyAdmin property - if (!expectedConfig.proxyAdmin?.address) { - return transactions; - } - - const actualProxyAdmin = actualConfig.proxyAdmin!; const parsedChainId = typeof chainId === 'string' ? parseInt(chainId) : chainId; if ( - actualProxyAdmin.address && - actualProxyAdmin.address !== expectedConfig.proxyAdmin.address + actualConfig.proxyAdmin?.address && + expectedConfig.proxyAdmin?.address && + actualConfig.proxyAdmin.address !== expectedConfig.proxyAdmin.address ) { transactions.push({ chainId: parsedChainId, - annotation: `Updating ProxyAdmin for proxy at "${proxyAddress}" from "${actualProxyAdmin.address}" to "${expectedConfig.proxyAdmin.address}"`, - to: actualProxyAdmin.address, + annotation: `Updating ProxyAdmin for proxy at "${proxyAddress}" from "${actualConfig.proxyAdmin.address}" to "${expectedConfig.proxyAdmin.address}"`, + to: actualConfig.proxyAdmin.address, data: ProxyAdmin__factory.createInterface().encodeFunctionData( 'changeProxyAdmin(address,address)', [proxyAddress, expectedConfig.proxyAdmin.address], ), }); } else { + const actualOwnershipConfig = actualConfig.proxyAdmin ?? { + owner: actualConfig.owner, + }; + const expectedOwnershipConfig = expectedConfig.proxyAdmin ?? { + owner: expectedConfig.owner, + }; + transactions.push( // Internally the createTransferOwnershipTx method already checks if the // two owner values are the same and produces an empty tx batch if they are ...transferOwnershipTransactions( parsedChainId, - actualProxyAdmin.address!, - actualProxyAdmin, - expectedConfig.proxyAdmin, + actualOwnershipConfig.address!, + actualOwnershipConfig, + expectedOwnershipConfig, ), ); } From f05ebc41bc7271d11d7d5cfe35bf1b0e4aa619ff Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Fri, 2 May 2025 23:38:57 +0100 Subject: [PATCH 128/223] feat: may 1st mainnet batch (#6109) ### Description - Add hashkey, infinityvmmainnet, game7, fluence, peaq - Deprecate infinityvm - Add some config for ontology, will have to revisit for the deployment as we did not have gas tokens ### Drive-by changes - fix warp-config test ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/817 ### Testing Manual --------- Co-authored-by: ljankovic-txfusion Co-authored-by: pbio <10051819+paulbalaji@users.noreply.github.com> --- rust/main/config/mainnet_config.json | 464 ++++++++++++++---- rust/main/config/testnet_config.json | 25 +- .../environments/mainnet3/chain-config.json | 31 -- .../config/environments/mainnet3/agent.ts | 31 +- .../mainnet3/aw-validators/hyperlane.json | 15 + .../mainnet3/balances/dailyRelayerBurn.json | 7 +- .../desiredRelayerBalanceOverrides.json | 3 +- .../balances/desiredRelayerBalances.json | 7 +- .../balances/highUrgencyRelayerBalance.json | 4 + .../lowUrgencyEngKeyFunderBalance.json | 4 + .../balances/lowUrgencyKeyFunderBalance.json | 4 + .../mainnet3/core/verification.json | 350 +++++++++++++ .../config/environments/mainnet3/funding.ts | 2 +- .../environments/mainnet3/gasPrices.json | 100 ++-- .../mainnet3/governance/ica/aw.ts | 1 - .../mainnet3/governance/ica/regular.ts | 1 - .../infra/config/environments/mainnet3/igp.ts | 6 +- .../mainnet3/ism/verification.json | 430 ++++++++++++++++ .../middleware/accounts/verification.json | 105 ++++ .../mainnet3/supportedChainNames.ts | 7 +- .../environments/mainnet3/tokenPrices.json | 271 +++++----- .../environments/mainnet3/validators.ts | 76 ++- .../scripts/validators/announce-validators.ts | 3 +- typescript/infra/src/config/chain.ts | 3 +- typescript/sdk/src/consts/multisigIsm.ts | 58 ++- 25 files changed, 1673 insertions(+), 335 deletions(-) diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 8ae6b56f6af..1ea422d42b0 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -188,7 +188,13 @@ "proxyAdmin": "0xd7CF8c05fd81b8cA7CfF8E6C49B08a9D63265c9B", "rpcUrls": [ { - "http": "https://rpc.ankr.com/avalanche" + "http": "https://avalanche.drpc.org" + }, + { + "http": "https://1rpc.io/avax/c" + }, + { + "http": "https://avalanche-c-chain-rpc.publicnode.com" }, { "http": "https://api.avax.network/ext/bc/C/rpc", @@ -1448,9 +1454,6 @@ { "http": "https://rpc.linea.build" }, - { - "http": "https://linea.blockpi.network/v1/rpc/public" - }, { "http": "https://1rpc.io/linea" }, @@ -2558,7 +2561,10 @@ "proxyAdmin": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", "rpcUrls": [ { - "http": "https://real.drpc.org" + "http": "https://rpc.realforreal.gelato.digital" + }, + { + "http": "https://tangible-real.gateway.tenderly.co" } ], "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", @@ -3081,7 +3087,10 @@ "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "rpcUrls": [ { - "http": "https://rpc.tomochain.com" + "http": "https://rpc.viction.xyz" + }, + { + "http": "https://viction.drpc.org" }, { "http": "https://viction.blockpi.network/v1/rpc/public" @@ -3148,6 +3157,12 @@ "protocolFee": "0x01aE937A7B05d187bBCBE80F44F41879D3D335a4", "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "rpcUrls": [ + { + "http": "https://worldchain.drpc.org" + }, + { + "http": "https://worldchain-mainnet.gateway.tenderly.co" + }, { "http": "https://worldchain-mainnet.g.alchemy.com/public" } @@ -3672,15 +3687,9 @@ { "http": "https://rpc.coredao.org" }, - { - "http": "https://core.public.infstones.com" - }, { "http": "https://rpc.ankr.com/core" }, - { - "http": "https://core.drpc.org" - }, { "http": "https://rpc-core.icecreamswap.com" } @@ -3810,7 +3819,10 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://flare-api.flare.network/ext/C/rpc" + "http": "https://flare-api.flare.network/ext/C/rpc", + "pagination": { + "maxBlockRange": 30 + } }, { "http": "https://flare.solidifi.app/ext/C/rpc" @@ -4549,9 +4561,6 @@ }, { "http": "https://1rpc.io/one" - }, - { - "http": "https://rpc.ankr.com/harmony" } ], "technicalStack": "other", @@ -4748,9 +4757,6 @@ "rpcUrls": [ { "http": "https://rpc.orderly.network" - }, - { - "http": "https://l2-orderly-mainnet-0.t.conduit.xyz" } ], "technicalStack": "opstack", @@ -5638,12 +5644,6 @@ "rpcUrls": [ { "http": "https://rpc.mainnet.rootstock.io/kXhXHf6TnnfW1POvr4UT0YUvujmuju-M" - }, - { - "http": "https://public-node.rsk.co" - }, - { - "http": "https://mycrypto.rsk.co" } ], "technicalStack": "other", @@ -6427,9 +6427,6 @@ "rpcUrls": [ { "http": "https://rpc.zklink.io" - }, - { - "http": "https://rpc.zklink.network" } ], "technicalStack": "zksync", @@ -8573,62 +8570,6 @@ }, "gnosisSafeTransactionServiceUrl": "https://prod.hyperliquid.keypersafe.xyz" }, - "infinityvm": { - "blocks": { - "confirmations": 1, - "estimateBlockTime": 1, - "reorgPeriod": 1 - }, - "chainId": 1032009, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Infinity VM", - "domainId": 1032009, - "gasCurrencyCoinGeckoId": "infinityvm", - "index": { - "from": 157889 - }, - "name": "infinityvm", - "nativeToken": { - "decimals": 18, - "name": "INF", - "symbol": "INF" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://mainnet-endpoint.infinityvm.xyz" - } - ], - "technicalStack": "other", - "aggregationHook": "0x12332AEAac8cAdF1A15fF648B64E7bb8276c6B87", - "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", - "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", - "fallbackRoutingHook": "0x51545389E04c2Ac07d98A40b85d29B480a2AF6ce", - "interchainAccountIsm": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", - "interchainAccountRouter": "0x284226F651eb5cbd696365BC27d333028FCc5D54", - "interchainGasPaymaster": "0x4eB0d97B48711950ecB01871125c4523939c6Fce", - "interchainSecurityModule": "0x9b5B8ba44bA66199b7029e7F75794af76F4B8f0b", - "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", - "merkleTreeHook": "0xB2b0A80b2fa3fC9aB1564A4FaF013d4D6084B325", - "pausableHook": "0x1e4dE25C3b07c8DF66D4c193693d8B5f3b431d51", - "pausableIsm": "0xd386Bb418B61E296e1689C95AfE94A2E321a6eaD", - "protocolFee": "0xC4F464C89184c7870858A8B12ca822daB6ed04F3", - "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", - "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", - "staticAggregationIsm": "0xeCF89850cf5AD363e6B338864C83606640097c94", - "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", - "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", - "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", - "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "storageGasOracle": "0xEa2Bcee14eA30bbBe3018E5E7829F963230F71C3", - "testRecipient": "0x9fE454AA2B01fc7A2a777AE561bc58Ce560CD5a9", - "timelockController": "0x0000000000000000000000000000000000000000", - "validatorAnnounce": "0x74B2b1fC57B28e11A5bAf32a758bbC98FA7837da" - }, "plume": { "blockExplorers": [ { @@ -9091,7 +9032,7 @@ }, "displayName": "MilkyWay", "domainId": 1835625579, - "gasCurrencyCoinGeckoId": "temp-milkyway-milk", + "gasCurrencyCoinGeckoId": "milkyway-2", "gasPrice": { "denom": "umilk", "amount": "0.03" @@ -9135,6 +9076,359 @@ "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000001", "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000" + }, + "fluence": { + "blockExplorers": [ + { + "apiUrl": "https://blockscout.mainnet.fluence.dev/api", + "family": "blockscout", + "name": "Fluence explorer", + "url": "https://blockscout.mainnet.fluence.dev" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 9999999, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Fluence", + "domainId": 9999999, + "gasCurrencyCoinGeckoId": "fluence", + "name": "fluence", + "nativeToken": { + "decimals": 18, + "name": "fluence", + "symbol": "FLT" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.mainnet.fluence.dev" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0xc9f884BE8748eEbB7Fe8b54EbA3866fF831CF0D4", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "interchainAccountIsm": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "interchainAccountRouter": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "interchainGasPaymaster": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "interchainSecurityModule": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "pausableHook": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "pausableIsm": "0x9fE454AA2B01fc7A2a777AE561bc58Ce560CD5a9", + "protocolFee": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "testRecipient": "0xF9aE87E9ACE51aa16AED25Ca38F17D258aECb73f", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "index": { + "from": 10997212 + } + }, + "game7": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet.game7.io/api", + "family": "blockscout", + "name": "Game7 Explorer", + "url": "https://mainnet.game7.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, + "chainId": 2187, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Game7", + "domainId": 2187, + "gasCurrencyCoinGeckoId": "game7", + "index": { + "from": 1635677 + }, + "isTestnet": false, + "name": "game7", + "nativeToken": { + "decimals": 18, + "name": "Game7", + "symbol": "G7" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet-rpc.game7.build" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0xc9f884BE8748eEbB7Fe8b54EbA3866fF831CF0D4", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "interchainAccountIsm": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "interchainAccountRouter": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "interchainGasPaymaster": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "interchainSecurityModule": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "pausableHook": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "pausableIsm": "0x9fE454AA2B01fc7A2a777AE561bc58Ce560CD5a9", + "protocolFee": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "testRecipient": "0xF9aE87E9ACE51aa16AED25Ca38F17D258aECb73f", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0" + }, + "hashkey": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.hsk.xyz/api", + "family": "blockscout", + "name": "Hashkey Explorer", + "url": "https://explorer.hsk.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 177, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Hashkey", + "domainId": 177, + "gasCurrencyCoinGeckoId": "hashkey-ecopoints", + "isTestnet": false, + "name": "hashkey", + "nativeToken": { + "decimals": 18, + "name": "HashKey Platform Token", + "symbol": "HSK" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.hsk.xyz" + } + ], + "technicalStack": "other", + "aggregationHook": "0x3e5C6056103BFf39D108d5251E47c818354D4325", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "interchainAccountIsm": "0x2ed6030D204745aC0Cd6be8301C3a63bf14D97Cc", + "interchainAccountRouter": "0xA9Adb480F10547f10202173a49b7F52116304476", + "interchainGasPaymaster": "0x8452363d5c78bf95538614441Dc8B465e03A89ca", + "interchainSecurityModule": "0x97CA6497B3C49595EED20C838B0a32c206663Fa0", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "pausableHook": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "pausableIsm": "0x76F2cC245882ceFf209A61d75b9F0f1A3b7285fB", + "protocolFee": "0x794Fe7970EE45945b0ad2667f99A5bBc9ddfB5d7", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x97CA6497B3C49595EED20C838B0a32c206663Fa0", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0xBCD18636e5876DFd7AAb5F2B2a5Eb5ca168BA1d8", + "testRecipient": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x6c5012B7eDfE317Be53D13Fc730a460f4810e234", + "index": { + "chunk": 999, + "from": 5883572 + } + }, + "infinityvmmainnet": { + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 1032009, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Infinity VM", + "domainId": 1001032009, + "gasCurrencyCoinGeckoId": "infinityvm", + "index": { + "from": 157889 + }, + "name": "infinityvmmainnet", + "nativeToken": { + "decimals": 18, + "name": "INF", + "symbol": "INF" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.infinityvm.xyz" + } + ], + "technicalStack": "other", + "aggregationHook": "0xc9f884BE8748eEbB7Fe8b54EbA3866fF831CF0D4", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "interchainAccountIsm": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "interchainAccountRouter": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "interchainGasPaymaster": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "interchainSecurityModule": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "pausableHook": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "pausableIsm": "0x9fE454AA2B01fc7A2a777AE561bc58Ce560CD5a9", + "protocolFee": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "testRecipient": "0xF9aE87E9ACE51aa16AED25Ca38F17D258aECb73f", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0" + }, + "ontology": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.ont.io/api", + "family": "other", + "name": "Ontology Explorer", + "url": "https://explorer.ont.io/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 58, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Ontology", + "domainId": 58, + "gasCurrencyCoinGeckoId": "ong", + "isTestnet": false, + "name": "ontology", + "nativeToken": { + "decimals": 18, + "name": "Ontology Gas", + "symbol": "ONG" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://dappnode4.ont.io:10339" + } + ], + "technicalStack": "other" + }, + "peaq": { + "blockExplorers": [ + { + "apiUrl": "https://peaq.subscan.io/api", + "family": "other", + "name": "Peaq Explorer", + "url": "https://peaq.subscan.io/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": "finalized" + }, + "chainId": 3338, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Peaq", + "domainId": 3338, + "gasCurrencyCoinGeckoId": "peaq-2", + "isTestnet": false, + "name": "peaq", + "nativeToken": { + "decimals": 18, + "name": "peaq", + "symbol": "PEAQ" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://peaq.api.onfinality.io/public" + } + ], + "technicalStack": "polkadotsubstrate", + "aggregationHook": "0xc9f884BE8748eEbB7Fe8b54EbA3866fF831CF0D4", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "interchainAccountIsm": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "interchainAccountRouter": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "interchainGasPaymaster": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "interchainSecurityModule": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "pausableHook": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "pausableIsm": "0x9fE454AA2B01fc7A2a777AE561bc58Ce560CD5a9", + "protocolFee": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xEEca5C23f957aD0E4c4134D0c466e45A88437d31", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "testRecipient": "0xF9aE87E9ACE51aa16AED25Ca38F17D258aECb73f", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "index": { + "from": 4997878 + } } }, "defaultRpcConsensusType": "fallback" diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index b9e5494d051..c37a99caaab 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -111,7 +111,10 @@ "proxyAdmin": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", "rpcUrls": [ { - "http": "https://arbitrum-sepolia.blockpi.network/v1/rpc/public" + "http": "https://arbitrum-sepolia.drpc.org" + }, + { + "http": "https://arbitrum-sepolia.gateway.tenderly.co" }, { "http": "https://sepolia-rollup.arbitrum.io/rpc" @@ -252,7 +255,7 @@ "http": "https://bsc-testnet.publicnode.com" }, { - "http": "https://bsc-testnet.blockpi.network/v1/rpc/public" + "http": "https://bsc-testnet.drpc.org" } ], "staticAggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", @@ -750,10 +753,10 @@ "http": "https://polygon-amoy-bor-rpc.publicnode.com" }, { - "http": "https://polygon-amoy.blockpi.network/v1/rpc/public" + "http": "https://polygon-amoy.drpc.org" }, { - "http": "https://rpc.ankr.com/polygon_amoy" + "http": "https://polygon-amoy.gateway.tenderly.co" } ], "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", @@ -824,9 +827,6 @@ { "http": "https://rpc.ankr.com/scroll_sepolia_testnet" }, - { - "http": "https://scroll-sepolia.blockpi.network/v1/rpc/public" - }, { "http": "https://scroll-sepolia.chainstacklabs.com" }, @@ -900,10 +900,13 @@ "http": "https://ethereum-sepolia.publicnode.com" }, { - "http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" + "http": "https://gateway.tenderly.co/public/sepolia" + }, + { + "http": "https://sepolia.drpc.org" }, { - "http": "https://rpc.sepolia.org" + "http": "https://1rpc.io/sepolia" } ], "staticAggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", @@ -1256,7 +1259,7 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://api.hyperliquid-testnet.xyz/evm" + "http": "https://rpc.hyperliquid-testnet.xyz/evm" } ], "aggregationHook": "0x5689Ad17c798d5114dc60Ba3c98e4853dF70403D", @@ -2599,7 +2602,7 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://vsf-rpc.somnia.network" + "http": "https://dream-rpc.somnia.network" } ], "technicalStack": "other", diff --git a/rust/sealevel/environments/mainnet3/chain-config.json b/rust/sealevel/environments/mainnet3/chain-config.json index ab7cb3df457..4c4dec3ab77 100644 --- a/rust/sealevel/environments/mainnet3/chain-config.json +++ b/rust/sealevel/environments/mainnet3/chain-config.json @@ -2270,37 +2270,6 @@ ], "technicalStack": "arbitrumnitro" }, - "infinityvm": { - "blocks": { - "confirmations": 1, - "estimateBlockTime": 1, - "reorgPeriod": 1 - }, - "chainId": 1032009, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Infinity VM", - "domainId": 1032009, - "gasCurrencyCoinGeckoId": "infinityvm", - "index": { - "from": 157889 - }, - "name": "infinityvm", - "nativeToken": { - "decimals": 18, - "name": "INF", - "symbol": "INF" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://mainnet-endpoint.infinityvm.xyz" - } - ], - "technicalStack": "other" - }, "ink": { "blockExplorers": [ { diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index d058fd2d0df..1133e0fc161 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -110,20 +110,23 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< flame: true, flare: true, flowmainnet: true, + fluence: true, form: true, // fractal: false, fraxtal: true, fusemainnet: true, + game7: true, glue: true, gnosis: true, gravity: true, guru: true, harmony: true, + hashkey: true, hemi: true, hyperevm: true, immutablezkevmmainnet: true, inevm: true, - infinityvm: false, + infinityvmmainnet: true, injective: true, ink: true, kaia: true, @@ -149,10 +152,12 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< neutron: true, nibiru: true, oortmainnet: true, + ontology: false, opbnb: true, optimism: true, orderly: true, osmosis: true, + peaq: true, plume: true, polygon: true, polygonzkevm: true, @@ -251,20 +256,23 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< flame: true, flare: true, flowmainnet: true, + fluence: true, form: true, // fractal: false, fraxtal: true, fusemainnet: true, + game7: true, glue: true, gnosis: true, gravity: true, guru: true, harmony: true, + hashkey: true, hemi: true, hyperevm: true, immutablezkevmmainnet: true, inevm: true, - infinityvm: false, + infinityvmmainnet: true, injective: true, ink: true, kaia: true, @@ -290,10 +298,12 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< neutron: true, nibiru: true, oortmainnet: true, + ontology: false, opbnb: true, optimism: true, orderly: true, osmosis: true, + peaq: true, plume: true, polygon: true, polygonzkevm: true, @@ -392,20 +402,23 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< flame: true, flare: true, flowmainnet: true, + fluence: true, form: true, // fractal: false, fraxtal: true, fusemainnet: true, + game7: true, glue: true, gnosis: true, gravity: true, guru: true, harmony: true, + hashkey: true, hemi: true, hyperevm: true, immutablezkevmmainnet: true, inevm: true, - infinityvm: false, + infinityvmmainnet: true, ink: true, injective: true, kaia: true, @@ -431,10 +444,12 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< neutron: true, nibiru: true, oortmainnet: true, + ontology: false, opbnb: true, optimism: true, orderly: true, osmosis: true, + peaq: true, plume: true, polygon: true, polygonzkevm: true, @@ -568,7 +583,7 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ matchingList: [ // Infinity VM is gasless, so ignore outbound txs from InfinityVM to Solana. { - originDomain: getDomainId('infinityvm'), + originDomain: getDomainId('infinityvmmainnet'), destinationDomain: getDomainId('solanamainnet'), }, ], @@ -583,7 +598,7 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ { destinationDomain: getDomainId('torus') }, // Infinity VM is gasless, so enforcing min 1 wei here ensures outbound txs // outside of Solana are ignored. - { originDomain: getDomainId('infinityvm') }, + { originDomain: getDomainId('infinityvmmainnet') }, // Temporary workaround due to funky Zeronetwork gas amounts. { destinationDomain: getDomainId('zeronetwork') }, // Temporary workaround during testing of MilkyWay. @@ -845,7 +860,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: '62073e3-20250426-080512', + tag: '673c6d2-20250502-141150', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -856,7 +871,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '62073e3-20250426-080512', + tag: '673c6d2-20250502-141150', }, resources: scraperResources, }, @@ -871,7 +886,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '3a04631-20250428-170554', + tag: '673c6d2-20250502-141150', }, blacklist: [...blacklist, ...vanguardMatchingList], // We're temporarily (ab)using the RC relayer as a way to increase diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index 15ed37dd927..859d8214e3f 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -158,6 +158,9 @@ "flowmainnet": { "validators": ["0xe132235c958ca1f3f24d772e5970dd58da4c0f6e"] }, + "fluence": { + "validators": ["0xabc8dd7594783c90a3c0fb760943f78c37ea6d75"] + }, "form": { "validators": ["0x58554b2e76167993b5fc000d0070a2f883cd333a"] }, @@ -167,6 +170,9 @@ "fusemainnet": { "validators": ["0x770c8ec9aac8cec4b2ead583b49acfbc5a1cf8a9"] }, + "game7": { + "validators": ["0x691dc4e763514df883155df0952f847b539454c0"] + }, "glue": { "validators": ["0xbe2ded12f7b023916584836506677ea89a0b6924"] }, @@ -186,6 +192,9 @@ "harmony": { "validators": ["0xd677803a67651974b1c264171b5d7ca8838db8d5"] }, + "hashkey": { + "validators": ["0x55007cab8788cdba22844e7a2499cf43347f487a"] + }, "hemi": { "validators": ["0x312dc72c17d01f3fd0abd31dd9b569bc473266dd"] }, @@ -202,6 +211,9 @@ "0xd98c9522cd9d3e3e00bee05ff76c34b91b266ec3" ] }, + "infinityvmmainnet": { + "validators": ["0x777c19c87aaa625486dff5aab0a479100f4249ad"] + }, "injective": { "validators": ["0xbfb8911b72cfb138c7ce517c57d9c691535dc517"] }, @@ -305,6 +317,9 @@ "osmosis": { "validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"] }, + "peaq": { + "validators": ["0x7f7fe70b676f65097e2a1e2683d0fc96ea8fea49"] + }, "plume": { "validators": ["0x63c9b5ea28710d956a51f0f746ee8df81215663f"] }, diff --git a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json index ee5e84912f4..ad7b68f3f51 100644 --- a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json +++ b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json @@ -44,19 +44,22 @@ "flame": 1.3, "flare": 208, "flowmainnet": 8.44, + "fluence": 3580, "form": 0.00195, "fraxtal": 0.0217, "fusemainnet": 254, + "game7": 542, "glue": 28.8, "gnosis": 3.12, "gravity": 219, "guru": 1390, "harmony": 285, + "hashkey": 8.31, "hemi": 0.00207, "hyperevm": 0.294, "immutablezkevmmainnet": 6.74, "inevm": 0.379, - "infinityvm": 3.13, + "infinityvmmainnet": 3.13, "injective": 0.379, "ink": 0.0233, "kaia": 31.1, @@ -81,11 +84,13 @@ "nero": 3.13, "neutron": 25.5, "nibiru": 183, + "ontology": 14.7, "oortmainnet": 73.6, "opbnb": 0.00528, "optimism": 0.0383, "orderly": 0.00281, "osmosis": 14.4, + "peaq": 22.4, "plume": 23, "polygon": 16.4, "polygonzkevm": 0.0244, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json index 3861ccf7bd8..e7590065f49 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json @@ -3,8 +3,9 @@ "arthera": 0.5, "coti": 0.1, "deepbrainchain": 100, - "infinityvm": 0, + "infinityvmmainnet": 0, "nibiru": 10, + "ontology": 0, "osmosis": 0, "plume": 0.05, "reactive": 0.1, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index 92c1ee7303c..43baf93c995 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -44,19 +44,22 @@ "flame": 10.4, "flare": 1660, "flowmainnet": 67.5, + "fluence": 28600, "form": 0.0156, "fraxtal": 0.2, "fusemainnet": 2030, + "game7": 4340, "glue": 230, "gnosis": 25, "gravity": 1750, "guru": 11100, "harmony": 2280, + "hashkey": 66.5, "hemi": 0.0166, "hyperevm": 2.35, "immutablezkevmmainnet": 53.9, "inevm": 3.03, - "infinityvm": 0, + "infinityvmmainnet": 0, "injective": 3.03, "ink": 0.186, "kaia": 250, @@ -81,11 +84,13 @@ "nero": 25, "neutron": 204, "nibiru": 10, + "ontology": 118, "oortmainnet": 2000, "opbnb": 0.0422, "optimism": 0.5, "orderly": 0.05, "osmosis": 0, + "peaq": 179, "plume": 0.05, "polygon": 131, "polygonzkevm": 0.5, diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index a63c91da30c..b9d96195708 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -44,14 +44,17 @@ "flame": 2.6, "flare": 416, "flowmainnet": 16.9, + "fluence": 7160, "form": 0.0039, "fraxtal": 0.0434, "fusemainnet": 508, + "game7": 1080, "glue": 57.6, "gnosis": 6.24, "gravity": 438, "guru": 2780, "harmony": 570, + "hashkey": 16.6, "hemi": 0.00414, "hyperevm": 1, "immutablezkevmmainnet": 13.5, @@ -84,6 +87,7 @@ "opbnb": 0.0106, "optimism": 0.083, "orderly": 0.00562, + "peaq": 44.8, "plume": 0.0125, "polygon": 32.8, "polygonzkevm": 0.0488, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index e6c69a531dd..13a5cb89568 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -44,14 +44,17 @@ "flame": 7.8, "flare": 1250, "flowmainnet": 50.6, + "fluence": 21500, "form": 0.0117, "fraxtal": 0.13, "fusemainnet": 1520, + "game7": 3250, "glue": 173, "gnosis": 18.7, "gravity": 1310, "guru": 8340, "harmony": 1710, + "hashkey": 49.9, "hemi": 0.0124, "hyperevm": 1.76, "immutablezkevmmainnet": 40.4, @@ -84,6 +87,7 @@ "opbnb": 0.0317, "optimism": 0.23, "orderly": 0.0169, + "peaq": 134, "plume": 0.0375, "polygon": 98.4, "polygonzkevm": 0.146, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index c798b612809..3de74ac495b 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -44,14 +44,17 @@ "flame": 15.6, "flare": 2500, "flowmainnet": 101, + "fluence": 43000, "form": 0.0234, "fraxtal": 0.4, "fusemainnet": 3050, + "game7": 6500, "glue": 346, "gnosis": 37.4, "gravity": 2630, "guru": 16700, "harmony": 3420, + "hashkey": 99.7, "hemi": 0.0248, "hyperevm": 10, "immutablezkevmmainnet": 80.9, @@ -84,6 +87,7 @@ "opbnb": 0.0634, "optimism": 1.2, "orderly": 0.1, + "peaq": 269, "plume": 0.075, "polygon": 250, "polygonzkevm": 1.1, diff --git a/typescript/infra/config/environments/mainnet3/core/verification.json b/typescript/infra/config/environments/mainnet3/core/verification.json index 5daa8f8a847..500f249753e 100644 --- a/typescript/infra/config/environments/mainnet3/core/verification.json +++ b/typescript/infra/config/environments/mainnet3/core/verification.json @@ -8572,5 +8572,355 @@ "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", "isProxy": false } + ], + "fluence": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000098967f", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "MerkleTreeHook", + "address": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002ff6cf2651fec512d0618e33c9d1374aacd8b310", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "constructorArguments": "00000000000000000000000089ebf977e83087959ad78e5372f4af15dcdc81430000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143" + }, + { + "name": "ProtocolFee", + "address": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "game7": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000088b", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "MerkleTreeHook", + "address": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002ff6cf2651fec512d0618e33c9d1374aacd8b310", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "constructorArguments": "00000000000000000000000089ebf977e83087959ad78e5372f4af15dcdc81430000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143" + }, + { + "name": "ProtocolFee", + "address": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "hashkey": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000b1", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "MerkleTreeHook", + "address": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000005b7a808caa2c3f1378b07cdd46eb8cca52f67e3b", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xBCD18636e5876DFd7AAb5F2B2a5Eb5ca168BA1d8", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x284226F651eb5cbd696365BC27d333028FCc5D54", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x8452363d5c78bf95538614441Dc8B465e03A89ca", + "constructorArguments": "000000000000000000000000284226f651eb5cbd696365bc27d333028fcc5d54000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x284226F651eb5cbd696365BC27d333028FCc5D54" + }, + { + "name": "ProtocolFee", + "address": "0x794Fe7970EE45945b0ad2667f99A5bBc9ddfB5d7", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x6c5012B7eDfE317Be53D13Fc730a460f4810e234", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "infinityvmmainnet": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003baa8949", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "MerkleTreeHook", + "address": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002ff6cf2651fec512d0618e33c9d1374aacd8b310", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "constructorArguments": "00000000000000000000000089ebf977e83087959ad78e5372f4af15dcdc81430000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143" + }, + { + "name": "ProtocolFee", + "address": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "peaq": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000d0a", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "MerkleTreeHook", + "address": "0x2FF6cf2651fec512D0618E33c9d1374aaCd8b310", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x5B7a808CaA2C3F1378B07cDd46eB8ccA52F67e3B", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002ff6cf2651fec512d0618e33c9d1374aacd8b310", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x5244d3359065C883BDfeEEff5329DE38c0Bd227e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "constructorArguments": "00000000000000000000000089ebf977e83087959ad78e5372f4af15dcdc81430000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x89Ebf977E83087959aD78e5372F4AF15DcdC8143" + }, + { + "name": "ProtocolFee", + "address": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index dbaafcd17db..9069c62126e 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -19,7 +19,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '2d0234b-20250422-165314', + tag: 'cd6230c-20250502-094639', }, // 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/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index 9f418e22531..9ccc5b3533a 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -20,7 +20,7 @@ "decimals": 9 }, "arbitrum": { - "amount": "0.01", + "amount": "0.02539", "decimals": 9 }, "arbitrumnova": { @@ -36,7 +36,7 @@ "decimals": 9 }, "arthera": { - "amount": "1.02503", + "amount": "1.025033", "decimals": 9 }, "astar": { @@ -56,7 +56,7 @@ "decimals": 9 }, "avalanche": { - "amount": "0.097870287", + "amount": "0.226779325", "decimals": 9 }, "b3": { @@ -64,11 +64,11 @@ "decimals": 9 }, "base": { - "amount": "0.003076709", + "amount": "0.002765126", "decimals": 9 }, "berachain": { - "amount": "0.000992963", + "amount": "0.000466166", "decimals": 9 }, "bitlayer": { @@ -76,7 +76,7 @@ "decimals": 9 }, "blast": { - "amount": "0.001450792", + "amount": "0.001000252", "decimals": 9 }, "bob": { @@ -84,7 +84,7 @@ "decimals": 9 }, "boba": { - "amount": "0.001000096", + "amount": "0.001000097", "decimals": 9 }, "bsc": { @@ -156,7 +156,7 @@ "decimals": 9 }, "ethereum": { - "amount": "7.5", + "amount": "1.867802855", "decimals": 9 }, "everclear": { @@ -164,11 +164,11 @@ "decimals": 9 }, "evmos": { - "amount": "33.375931531", + "amount": "27.5", "decimals": 9 }, "fantom": { - "amount": "1.026999", + "amount": "1.599752351", "decimals": 9 }, "flare": { @@ -179,16 +179,24 @@ "amount": "0.1", "decimals": 9 }, + "fluence": { + "amount": "0.1", + "decimals": 9 + }, "form": { "amount": "0.001000252", "decimals": 9 }, "fraxtal": { - "amount": "0.001000253", + "amount": "0.001000254", "decimals": 9 }, "fusemainnet": { - "amount": "20.0", + "amount": "12.1", + "decimals": 9 + }, + "game7": { + "amount": "0.01", "decimals": 9 }, "glue": { @@ -196,7 +204,7 @@ "decimals": 9 }, "gnosis": { - "amount": "1.000000008", + "amount": "0.07216634", "decimals": 9 }, "gravity": { @@ -211,12 +219,16 @@ "amount": "100.0", "decimals": 9 }, + "hashkey": { + "amount": "0.001000252", + "decimals": 9 + }, "hemi": { "amount": "0.001000252", "decimals": 9 }, "hyperevm": { - "amount": "0.1", + "amount": "0.229986783", "decimals": 9 }, "immutablezkevmmainnet": { @@ -227,12 +239,12 @@ "amount": "0.1", "decimals": 9 }, - "infinityvm": { + "infinityvmmainnet": { "amount": "1.000000007", "decimals": 9 }, "ink": { - "amount": "0.001000257", + "amount": "0.001000252", "decimals": 9 }, "injective": { @@ -248,7 +260,7 @@ "decimals": 9 }, "linea": { - "amount": "0.055646648", + "amount": "0.332044669", "decimals": 9 }, "lisk": { @@ -256,19 +268,19 @@ "decimals": 9 }, "lukso": { - "amount": "0.100026017", + "amount": "0.596422652", "decimals": 9 }, "lumia": { - "amount": "1.0", + "amount": "1.86", "decimals": 9 }, "lumiaprism": { - "amount": "1.0", + "amount": "1.86", "decimals": 9 }, "mantapacific": { - "amount": "0.003000488", + "amount": "0.003009504", "decimals": 9 }, "mantle": { @@ -300,7 +312,7 @@ "decimals": 9 }, "mode": { - "amount": "0.001000314", + "amount": "0.001000252", "decimals": 9 }, "molten": { @@ -312,7 +324,7 @@ "decimals": 9 }, "morph": { - "amount": "0.063", + "amount": "0.0769", "decimals": 9 }, "nero": { @@ -331,12 +343,16 @@ "amount": "100.0", "decimals": 9 }, + "ontology": { + "amount": "2500.0", + "decimals": 9 + }, "opbnb": { "amount": "0.001", "decimals": 9 }, "optimism": { - "amount": "0.002404968", + "amount": "0.001000288", "decimals": 9 }, "orderly": { @@ -347,16 +363,20 @@ "amount": "0.025", "decimals": 1 }, + "peaq": { + "amount": "100.0", + "decimals": 9 + }, "plume": { "amount": "100.0", "decimals": 9 }, "polygon": { - "amount": "30.000000034", + "amount": "52.700867781", "decimals": 9 }, "polygonzkevm": { - "amount": "0.01", + "amount": "0.0287", "decimals": 9 }, "polynomialfi": { @@ -396,7 +416,7 @@ "decimals": 9 }, "rootstockmainnet": { - "amount": "0.07", + "amount": "0.0260656", "decimals": 9 }, "sanko": { @@ -404,15 +424,15 @@ "decimals": 9 }, "scroll": { - "amount": "0.046868564", + "amount": "0.025752013", "decimals": 9 }, "sei": { - "amount": "1.100326643", + "amount": "1.1", "decimals": 9 }, "shibarium": { - "amount": "2.523983809", + "amount": "2.403592498", "decimals": 9 }, "snaxchain": { @@ -424,7 +444,7 @@ "decimals": 1 }, "soneium": { - "amount": "0.001134841", + "amount": "0.001015684", "decimals": 9 }, "sonic": { @@ -440,11 +460,11 @@ "decimals": 1 }, "sophon": { - "amount": "2022.206337798", + "amount": "2159.824777033", "decimals": 9 }, "story": { - "amount": "0.001000186", + "amount": "0.001000406", "decimals": 9 }, "stride": { @@ -452,7 +472,7 @@ "decimals": 1 }, "subtensor": { - "amount": "10.14637", + "amount": "10.0", "decimals": 9 }, "superseed": { @@ -468,7 +488,7 @@ "decimals": 9 }, "taiko": { - "amount": "0.008988459", + "amount": "0.0089", "decimals": 9 }, "tangle": { @@ -484,7 +504,7 @@ "decimals": 9 }, "treasure": { - "amount": "1023.72101162", + "amount": "477.327909088", "decimals": 9 }, "trumpchain": { @@ -492,15 +512,15 @@ "decimals": 9 }, "unichain": { - "amount": "0.001000253", + "amount": "0.001000252", "decimals": 9 }, "unitzero": { - "amount": "2.000000007", + "amount": "51.000000007", "decimals": 9 }, "vana": { - "amount": "0.001006296", + "amount": "24.9912", "decimals": 9 }, "viction": { @@ -508,7 +528,7 @@ "decimals": 9 }, "worldchain": { - "amount": "0.001000319", + "amount": "0.00100091", "decimals": 9 }, "xai": { diff --git a/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts b/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts index 77c0e572b5a..0054e0c5c95 100644 --- a/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts +++ b/typescript/infra/config/environments/mainnet3/governance/ica/aw.ts @@ -156,7 +156,6 @@ export const awIcas: ChainMap
= { // Mar 14, 2025 batch // ---------------------------------------------------------- - infinityvm: '0x35460c519b7C71d49C64F060dF89AbAE463F3b9a', plume: '0x61BFbb5FEC57f5470388A80946F0415138630b9c', // Mar 31, 2025 batch diff --git a/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts b/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts index 57e8add8029..ef8453c4cd0 100644 --- a/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts +++ b/typescript/infra/config/environments/mainnet3/governance/ica/regular.ts @@ -49,7 +49,6 @@ export const regularIcas: ChainMap
= { hemi: '0xa16C2860414e52945Bae3eb957cC0897A21f04a6', immutablezkevmmainnet: '0x97827F6191296077587929Ca870620bD5D79F9e7', inevm: '0x8427F6021a84a992008DA6a949cad986CEB0d0b3', - infinityvm: '0xf0c8600D83dC8B517C347cb1D45e02B1F95057bb', ink: '0xf36dC13eE34034709390b6dF3de7305eA298BFec', kaia: '0x83be90021870A93ff794E00E8CFe454547877E3E', kroma: '0xBd887119d776f0c990e9a03B1A157A107CD45033', diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index 23ef3b6ba83..e6ad97b24d8 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -35,6 +35,7 @@ export function getOverheadWithOverrides(local: ChainName, remote: ChainName) { if (remote === 'moonbeam' || remote === 'torus') { overhead *= 4; } + // ZkSync gas usage is different from the EVM and tends to give high // estimates. We double the overhead to help account for this. if (getChain(remote).technicalStack === ChainTechnicalStack.ZkSync) { @@ -46,12 +47,13 @@ export function getOverheadWithOverrides(local: ChainName, remote: ChainName) { overhead *= 3; } } + return overhead; } function getOracleConfigWithOverrides(origin: ChainName) { const oracleConfig = storageGasOracleConfig[origin]; - if (origin === 'infinityvm') { + if (origin === 'infinityvmmainnet') { // For InfinityVM origin, override all remote chain gas configs to use 0 gas for (const remoteConfig of Object.values(oracleConfig)) { remoteConfig.gasPrice = '0'; @@ -59,7 +61,7 @@ function getOracleConfigWithOverrides(origin: ChainName) { } // Solana -> InfinityVM, similarly don't charge gas if (origin === 'solanamainnet') { - oracleConfig['infinityvm'].gasPrice = '0'; + oracleConfig['infinityvmmainnet'].gasPrice = '0'; } return oracleConfig; diff --git a/typescript/infra/config/environments/mainnet3/ism/verification.json b/typescript/infra/config/environments/mainnet3/ism/verification.json index 622038e4978..7c1aeb360b1 100644 --- a/typescript/infra/config/environments/mainnet3/ism/verification.json +++ b/typescript/infra/config/environments/mainnet3/ism/verification.json @@ -10756,5 +10756,435 @@ "constructorArguments": "", "isProxy": true } + ], + "fluence": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "game7": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "peaq": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "hashkey": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "infinityvmmainnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } ] } diff --git a/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json b/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json index f6bbbc57151..982c4a3320a 100644 --- a/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json +++ b/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json @@ -2604,5 +2604,110 @@ "isProxy": true, "expectedimplementation": "0x4A91738390a3D55CB27c2863e8950c9cD1b89d0e" } + ], + "fluence": [ + { + "name": "InterchainAccountIsm", + "address": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "constructorArguments": "000000000000000000000000a28344ac1fc47c1dc212e178540dd0f3e7a781a60000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d233433aec23f8382dad87d808f60557ea35399f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6" + } + ], + "game7": [ + { + "name": "InterchainAccountIsm", + "address": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "constructorArguments": "000000000000000000000000a28344ac1fc47c1dc212e178540dd0f3e7a781a60000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d233433aec23f8382dad87d808f60557ea35399f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6" + } + ], + "peaq": [ + { + "name": "InterchainAccountIsm", + "address": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "constructorArguments": "000000000000000000000000a28344ac1fc47c1dc212e178540dd0f3e7a781a60000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d233433aec23f8382dad87d808f60557ea35399f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6" + } + ], + "infinityvmmainnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "constructorArguments": "000000000000000000000000a28344ac1fc47c1dc212e178540dd0f3e7a781a60000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d233433aec23f8382dad87d808f60557ea35399f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xA28344Ac1Fc47C1dc212E178540dD0F3e7a781A6" + } + ], + "hashkey": [ + { + "name": "InterchainAccountIsm", + "address": "0x2ed6030D204745aC0Cd6be8301C3a63bf14D97Cc", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xA9Adb480F10547f10202173a49b7F52116304476", + "constructorArguments": "0000000000000000000000005cbd4c5f9cd55747285652f815cc7b9a2ef6c586000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ed6030d204745ac0cd6be8301c3a63bf14d97cc000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586" + } ] } diff --git a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts index 0ec17fc3b0e..1f28c86663f 100644 --- a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts +++ b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts @@ -47,20 +47,23 @@ export const mainnet3SupportedChainNames = [ 'fantom', 'flare', 'flowmainnet', + 'fluence', 'form', // 'fractal', 'fraxtal', 'fusemainnet', + 'game7', 'glue', 'gnosis', 'gravity', 'guru', 'harmony', + 'hashkey', 'hemi', 'hyperevm', 'immutablezkevmmainnet', 'inevm', - 'infinityvm', + 'infinityvmmainnet', 'ink', 'injective', 'kaia', @@ -86,10 +89,12 @@ export const mainnet3SupportedChainNames = [ 'neutron', 'nibiru', 'oortmainnet', + 'ontology', 'opbnb', 'optimism', 'orderly', 'osmosis', + 'peaq', 'plume', 'polygon', 'polygonzkevm', diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index 06046488e83..80e0268db5b 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -1,139 +1,144 @@ { - "abstract": "1599.53", - "ancient8": "1599.53", - "alephzeroevmmainnet": "0.092064", - "apechain": "0.443681", - "appchain": "1599.53", - "arbitrum": "1599.53", - "arbitrumnova": "1599.53", - "arcadia": "1599.53", - "artela": "0.00093234", + "abstract": "1865.12", + "ancient8": "1865.12", + "alephzeroevmmainnet": "0.133098", + "apechain": "0.542923", + "appchain": "1865.12", + "arbitrum": "1865.12", + "arbitrumnova": "1865.12", + "arcadia": "1865.12", + "artela": "0.00243229", "arthera": "0.00611031", - "astar": "0.0268412", - "aurora": "1599.53", - "bouncebit": "0.102952", - "flame": "2.41", - "avalanche": "19.48", - "b3": "1599.53", - "base": "1599.53", - "berachain": "3.41", - "bitlayer": "85131", - "blast": "1599.53", - "bob": "1599.53", - "boba": "1599.53", - "bsc": "592.22", - "bsquared": "85131", - "celo": "0.308083", - "cheesechain": "0.00029572", - "chilizmainnet": "0.03718311", - "conflux": "0.070167", - "conwai": "0.00177459", - "coredao": "0.68447", - "corn": "85131", - "coti": "0.066086", - "cyber": "1599.53", - "deepbrainchain": "0.00085848", - "degenchain": "0.00254968", - "dogechain": "0.15876", - "duckchain": "2.99", - "eclipsemainnet": "1599.53", - "endurance": "0.496753", - "ethereum": "1599.53", - "everclear": "1599.53", - "evmos": "0.00314416", - "fantom": "0.464854", - "flare": "0.01602446", - "flowmainnet": "0.370122", - "form": "1599.53", - "fraxtal": "1595.81", - "fusemainnet": "0.01187583", - "glue": "0.123497", - "gnosis": "1", - "gravity": "0.0142485", - "guru": "0.00224974", - "harmony": "0.01097306", - "hemi": "1599.53", - "hyperevm": "18.16", - "immutablezkevmmainnet": "0.463869", - "inevm": "8.25", - "infinityvm": "1", - "ink": "1599.53", - "injective": "8.25", - "kaia": "0.100334", - "kroma": "1599.53", - "linea": "1599.53", - "lisk": "1599.53", - "lukso": "1.015", - "lumia": "0.335583", - "lumiaprism": "0.285301", - "mantapacific": "1599.53", - "mantle": "0.655372", - "matchain": "592.22", - "merlin": "85091", - "metal": "1599.53", - "metis": "13.62", - "milkyway": "1", - "mint": "1599.53", - "mode": "1599.53", - "molten": "0.127992", - "moonbeam": "0.067257", - "morph": "1599.53", + "astar": "0.0297173", + "aurora": "1865.12", + "bouncebit": "0.137209", + "flame": "2.75", + "avalanche": "21.47", + "b3": "1865.12", + "base": "1865.12", + "berachain": "3.54", + "bitlayer": "97137", + "blast": "1865.12", + "bob": "1865.12", + "boba": "1865.12", + "bsc": "604.44", + "bsquared": "97137", + "celo": "0.368131", + "cheesechain": "0.00041855", + "chilizmainnet": "0.04245801", + "conflux": "0.081025", + "conwai": "0.00210481", + "coredao": "0.78874", + "corn": "97137", + "coti": "0.083918", + "cyber": "1865.12", + "deepbrainchain": "0.00083753", + "degenchain": "0.00319211", + "dogechain": "0.181674", + "duckchain": "3.2", + "eclipsemainnet": "1865.12", + "endurance": "0.659458", + "ethereum": "1865.12", + "everclear": "1865.12", + "evmos": "0.00426568", + "fantom": "0.556873", + "flare": "0.01807631", + "flowmainnet": "0.412195", + "fluence": "0.00087302", + "form": "1865.12", + "fraxtal": "1854.4", + "fusemainnet": "0.01305948", + "game7": "0.00576759", + "glue": "0.125938", + "gnosis": "1.004", + "gravity": "0.01557197", + "guru": "0.00278482", + "harmony": "0.01383619", + "hashkey": "0.375991", + "hemi": "1865.12", + "hyperevm": "20.43", + "immutablezkevmmainnet": "0.602689", + "inevm": "10.49", + "infinityvmmainnet": "1", + "ink": "1865.12", + "injective": "10.49", + "kaia": "0.117514", + "kroma": "1865.12", + "linea": "1865.12", + "lisk": "1865.12", + "lukso": "0.87102", + "lumia": "0.35147", + "lumiaprism": "0.334993", + "mantapacific": "1865.12", + "mantle": "0.748373", + "matchain": "604.44", + "merlin": "97169", + "metal": "1865.12", + "metis": "16.28", + "milkyway": "0.115948", + "mint": "1865.12", + "mode": "1865.12", + "molten": "0.155271", + "moonbeam": "0.084758", + "morph": "1865.12", "nero": "1", - "neutron": "0.122576", - "nibiru": "0.01709542", - "oortmainnet": "0.0424777", - "opbnb": "592.22", - "optimism": "1599.53", - "orderly": "1599.53", - "osmosis": "0.216516", - "plume": "0.163739", - "polygon": "0.190008", - "polygonzkevm": "1599.53", - "polynomialfi": "1599.53", - "prom": "5.71", - "proofofplay": "1599.53", - "rarichain": "1599.53", - "reactive": "0.080498", - "real": "1599.53", - "redstone": "1599.53", - "rivalz": "1599.53", - "ronin": "0.501632", - "rootstockmainnet": "84707", - "sanko": "6.47", - "scroll": "1599.53", - "sei": "0.170216", - "shibarium": "0.27927", - "snaxchain": "1599.53", - "solanamainnet": "138.62", - "soneium": "1599.53", - "sonic": "0.464854", - "sonicsvm": "138.62", - "soon": "1599.53", + "neutron": "0.141581", + "nibiru": "0.01669648", + "oortmainnet": "0.050891", + "ontology": "0.21188", + "opbnb": "604.44", + "optimism": "1865.12", + "orderly": "1865.12", + "osmosis": "0.238243", + "peaq": "0.139388", + "plume": "0.191646", + "polygon": "0.243486", + "polygonzkevm": "1865.12", + "polynomialfi": "1865.12", + "prom": "5.49", + "proofofplay": "1865.12", + "rarichain": "1865.12", + "reactive": "0.07914", + "real": "1865.12", + "redstone": "1865.12", + "rivalz": "1865.12", + "ronin": "0.564698", + "rootstockmainnet": "96001", + "sanko": "8.81", + "scroll": "1865.12", + "sei": "0.224395", + "shibarium": "0.297813", + "snaxchain": "1865.12", + "solanamainnet": "153.18", + "soneium": "1865.12", + "sonic": "0.556873", + "sonicsvm": "153.18", + "soon": "1865.12", "sophon": "1", - "story": "3.87", - "stride": "0.193807", - "subtensor": "280.37", - "superseed": "1599.53", - "superpositionmainnet": "1599.53", - "swell": "1599.53", - "taiko": "1599.53", + "story": "4.1", + "stride": "0.293606", + "subtensor": "372.28", + "superseed": "1865.12", + "superpositionmainnet": "1865.12", + "swell": "1865.12", + "taiko": "1865.12", "tangle": "1", - "telos": "0.086451", - "torus": "0.140374", - "treasure": "0.079584", - "trumpchain": "8.31", - "unichain": "1599.53", - "unitzero": "0.235709", - "vana": "5.14", - "viction": "0.200071", - "worldchain": "1599.53", - "xai": "0.04386052", - "xlayer": "50.69", - "xpla": "0.02738309", - "zeronetwork": "1599.53", - "zetachain": "0.234442", - "zircuit": "1599.53", - "zklink": "1599.53", - "zksync": "1599.53", - "zoramainnet": "1599.53" + "telos": "0.088233", + "torus": "0.311983", + "treasure": "0.211862", + "trumpchain": "13.1", + "unichain": "1865.12", + "unitzero": "0.234597", + "vana": "6.11", + "viction": "0.235667", + "worldchain": "1865.12", + "xai": "0.06191", + "xlayer": "51.85", + "xpla": "0.04050335", + "zeronetwork": "1865.12", + "zetachain": "0.271066", + "zircuit": "1865.12", + "zklink": "1865.12", + "zksync": "1865.12", + "zoramainnet": "1865.12" } diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index f0bdf5983fc..d4a988210a5 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -1627,16 +1627,6 @@ export const validatorChainConfig = ( ), }, - infinityvm: { - interval: 5, - reorgPeriod: getReorgPeriod('infinityvm'), - validators: validatorsConfig( - { - [Contexts.Hyperlane]: ['0xeed503e0bd8fae0ad606361492db8711eaf0b935'], - }, - 'infinityvm', - ), - }, plume: { interval: 5, reorgPeriod: getReorgPeriod('plume'), @@ -1709,5 +1699,71 @@ export const validatorChainConfig = ( 'milkyway', ), }, + + hashkey: { + interval: 5, + reorgPeriod: getReorgPeriod('hashkey'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x55007cab8788cdba22844e7a2499cf43347f487a'], + }, + 'hashkey', + ), + }, + + infinityvmmainnet: { + interval: 5, + reorgPeriod: getReorgPeriod('infinityvmmainnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x777c19c87aaa625486dff5aab0a479100f4249ad'], + }, + 'infinityvmmainnet', + ), + }, + + ontology: { + interval: 5, + reorgPeriod: getReorgPeriod('ontology'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x2578b0a330c492e1a1682684e27e6a93649befd5'], + }, + 'ontology', + ), + }, + + game7: { + interval: 5, + reorgPeriod: getReorgPeriod('game7'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x691dc4e763514df883155df0952f847b539454c0'], + }, + 'game7', + ), + }, + + fluence: { + interval: 5, + reorgPeriod: getReorgPeriod('fluence'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xabc8dd7594783c90a3c0fb760943f78c37ea6d75'], + }, + 'fluence', + ), + }, + + peaq: { + interval: 5, + reorgPeriod: getReorgPeriod('peaq'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x7f7fe70b676f65097e2a1e2683d0fc96ea8fea49'], + }, + 'peaq', + ), + }, }; }; diff --git a/typescript/infra/scripts/validators/announce-validators.ts b/typescript/infra/scripts/validators/announce-validators.ts index 5d63fa29e9e..5ed496ad1be 100644 --- a/typescript/infra/scripts/validators/announce-validators.ts +++ b/typescript/infra/scripts/validators/announce-validators.ts @@ -73,7 +73,8 @@ async function main() { Object.entries(agentConfig.validators.chains) .filter(([validatorChain, _]) => { // Ensure we skip lumia, as we don't have the addresses in registry. - if (validatorChain === 'lumia') { + // temporarily skip ontology as we do not have funds, will undo when we deploy + if (validatorChain === 'lumia' || validatorChain === 'ontology') { return false; } diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index a2b2dc213ac..f0f5f5c54b9 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -24,8 +24,7 @@ import { DeployEnvironment } from './environment.js'; // Used by scripts like check-owner-ica.ts to exclude chains that are temporarily // unsupported (e.g. zksync, zeronetwork) or have known issues (e.g. lumia). export const chainsToSkip: ChainName[] = [ - // TODO: complete work when RPC is available again - 'infinityvm', + 'ontology', // TODO: remove once zksync PR is merged into main // mainnets diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index 004c76ffbd0..05a6555d7b4 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -894,6 +894,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + fluence: { + threshold: 1, + validators: [ + { + address: '0xabc8dd7594783c90a3c0fb760943f78c37ea6d75', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + form: { threshold: 2, validators: [ @@ -980,6 +990,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + game7: { + threshold: 1, + validators: [ + { + address: '0x691dc4e763514df883155df0952f847b539454c0', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + glue: { threshold: 2, validators: [ @@ -1041,6 +1061,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + hashkey: { + threshold: 1, + validators: [ + { + address: '0x55007cab8788cdba22844e7a2499cf43347f487a', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + hemi: { threshold: 2, validators: [ @@ -1119,15 +1149,13 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - infinityvm: { - threshold: 2, + infinityvmmainnet: { + threshold: 1, validators: [ { - address: '0xeed503e0bd8fae0ad606361492db8711eaf0b935', + address: '0x777c19c87aaa625486dff5aab0a479100f4249ad', alias: AW_VALIDATOR_ALIAS, }, - DEFAULT_MERKLY_VALIDATOR, - DEFAULT_MITOSIS_VALIDATOR, ], }, @@ -1586,6 +1614,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + ontology: { + threshold: 1, + validators: [ + { + address: '0x2578b0a330c492e1a1682684e27e6a93649befd5', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + oortmainnet: { threshold: 2, validators: [ @@ -1666,6 +1704,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + peaq: { + threshold: 1, + validators: [ + { + address: '0x7f7fe70b676f65097e2a1e2683d0fc96ea8fea49', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + plume: { threshold: 2, validators: [ From f021f869d474915dfb599bb612a22bc7a7680f0e Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Sun, 4 May 2025 22:22:33 +0100 Subject: [PATCH 129/223] chore: spin down vanguard0 (#6121) ### Description chore: spin down vanguard0 - update hyperlane image tag to latest `main` build - remove vanguard blacklisting - remove vanguard from keyfunder config ### Drive-by changes - add `ontology` to keyfunder `chainsToSkip`, as we have not received funding for deploying on that chain yet ### Related issues ### Backward compatibility ### Testing --- .../infra/config/environments/mainnet3/agent.ts | 12 ++++++------ .../infra/config/environments/mainnet3/funding.ts | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 1133e0fc161..23d3550bb3a 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -846,9 +846,9 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '3a04631-20250428-170554', + tag: 'f05ebc4-20250502-225226', }, - blacklist: [...blacklist, ...vanguardMatchingList], + blacklist, gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, @@ -886,9 +886,9 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '673c6d2-20250502-141150', + tag: 'f05ebc4-20250502-225226', }, - blacklist: [...blacklist, ...vanguardMatchingList], + blacklist, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. // whitelist: releaseCandidateHelloworldMatchingList, @@ -924,9 +924,9 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '3a04631-20250428-170554', + tag: 'f05ebc4-20250502-225226', }, - blacklist: [...blacklist, ...vanguardMatchingList], + blacklist, gasPaymentEnforcement, metricAppContextsGetter, ismCacheConfigs, diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index 9069c62126e..a7ca3734d71 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -32,9 +32,8 @@ export const keyFunderConfig: KeyFunderConfig< contextsAndRolesToFund: { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], - [Contexts.Vanguard0]: [Role.Relayer], }, - chainsToSkip: [], + chainsToSkip: ['ontology'], // desired balance config, must be set for each chain desiredBalancePerChain: desiredRelayerBalancePerChain, // if not set, keyfunder defaults to 0 From 07462130c1a801aae4d89f0c75a51ea418b95a2e Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Mon, 5 May 2025 12:01:24 +0100 Subject: [PATCH 130/223] feat: QoL improvements to write alerts script (#5998) ### Description - properly handle port forward process on exit - prompt user to confirm threshold changes have been reviewed ### Testing Manual --- .../infra/scripts/funding/write-alert.ts | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/typescript/infra/scripts/funding/write-alert.ts b/typescript/infra/scripts/funding/write-alert.ts index d934ab3d282..609acd02fe6 100644 --- a/typescript/infra/scripts/funding/write-alert.ts +++ b/typescript/infra/scripts/funding/write-alert.ts @@ -48,6 +48,16 @@ interface RegressionError { async function main() { const { dryRun } = await withDryRun(yargs(process.argv.slice(2))).argv; + const confirmed = await confirm({ + message: + 'Before proceeding, please ensure that any threshold changes have been committed and reviewed in a PR, have all changes been reviewed?', + }); + + if (!confirmed) { + rootLogger.info('Exiting without updating any alerts'); + process.exit(0); + } + // runs a validation check to ensure the threshold configs are valid relative to each other await validateBalanceThresholdConfigs(); @@ -56,25 +66,29 @@ async function main() { PROMETHEUS_LOCAL_PORT, ); - const alertsToUpdate = Object.values(AlertType); - const alertUpdateInfo: AlertUpdateInfo[] = []; - const missingChainErrors: RegressionError[] = []; + process.on('SIGINT', () => { + cleanUp(portForwardProcess); + process.exit(1); + }); + + process.on('beforeExit', () => { + cleanUp(portForwardProcess); + }); try { + const alertsToUpdate = Object.values(AlertType); + const alertUpdateInfo: AlertUpdateInfo[] = []; + const missingChainErrors: RegressionError[] = []; + for (const alert of alertsToUpdate) { // fetch alertRule config from Grafana via the Grafana API const alertRule = await fetchGrafanaAlert(alert, saToken); // read the proposed thresholds from the config file let proposedThresholds: ChainMap = {}; - try { - proposedThresholds = readJSONAtPath( - `${THRESHOLD_CONFIG_PATH}/${alertConfigMapping[alert].configFileName}`, - ); - } catch (e) { - rootLogger.error(`Error reading ${alert} config: ${e}`); - process.exit(1); - } + proposedThresholds = readJSONAtPath( + `${THRESHOLD_CONFIG_PATH}/${alertConfigMapping[alert].configFileName}`, + ); // parse the current thresholds from the existing query const existingQuery = alertRule.queries[0]; @@ -112,6 +126,7 @@ async function main() { rootLogger.info( `Exiting without updating any alerts, this is to avoid thresholds from being out of sync`, ); + cleanUp(portForwardProcess); process.exit(0); } } else { @@ -128,6 +143,7 @@ async function main() { query, currentThresholds, proposedThresholds, + portForwardProcess, ); alertUpdateInfo.push({ @@ -148,7 +164,7 @@ async function main() { rootLogger.info('Dry run, not updating alerts'); } } finally { - portForwardProcess.kill(); + cleanUp(portForwardProcess); } } @@ -220,7 +236,7 @@ async function updateAlerts( `Error updating ${alertInfo.alertType} alert, aborting updating the rest of the alerts: ${e}`, ); // exiting here so we don't continue updating alerts with lower writePriority - portForwardProcess.kill(); + cleanUp(portForwardProcess); process.exit(1); } } @@ -294,6 +310,7 @@ async function confirmFiringAlerts( query: string, currentThresholds: ChainMap, proposedThresholds: ChainMap, + portForwardProcess: ChildProcess, ) { const alertingChains = await fetchFiringThresholdAlert(query); if (alertingChains.length === 0) return; @@ -316,10 +333,18 @@ async function confirmFiringAlerts( rootLogger.info( `Exiting without updating any alerts, this is to avoid thresholds from being out of sync as we do not want to update the ${alert} alert`, ); + cleanUp(portForwardProcess); process.exit(0); } } +function cleanUp(portForwardProcess: ChildProcess) { + if (!portForwardProcess.killed) { + rootLogger.info('Cleaning up port forward process'); + portForwardProcess.kill(); + } +} + main().catch((err) => { console.error(err); process.exit(1); From d6a1b1d2e8de819e108e7496f6e16347b192bd29 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Mon, 5 May 2025 12:36:39 +0100 Subject: [PATCH 131/223] feat: reduce balance thresholds for all remaining chains (#5995) ### Description - manually reduce the thresholds for all chains, we first did this here https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/5731 and have observed that the system is stable for ~2 month, giving us confidence that we can make this change - will wait until vanguards are spun down so we don't overwrite the manual alerts added for launch ### Testing Manual --- .../mainnet3/balances/dailyRelayerBurn.json | 16 ++-- .../desiredRelayerBalanceOverrides.json | 1 + .../balances/desiredRelayerBalances.json | 72 +++++++++--------- .../balances/highUrgencyRelayerBalance.json | 50 ++++++------ .../lowUrgencyEngKeyFunderBalance.json | 38 +++++----- .../balances/lowUrgencyKeyFunderBalance.json | 76 +++++++++---------- 6 files changed, 127 insertions(+), 126 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json index ad7b68f3f51..c015cd6bd72 100644 --- a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json +++ b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json @@ -1,7 +1,7 @@ { "abstract": 0.00428, "alephzeroevmmainnet": 33.9, - "ancient8": 0.00238, + "ancient8": 0.00253, "apechain": 7.04, "appchain": 0.0155, "arbitrum": 0.0344, @@ -10,18 +10,18 @@ "artela": 3350, "arthera": 511, "astar": 116, - "aurora": 0.00195, + "aurora": 0.00214, "avalanche": 0.16, "b3": 0.00284, "base": 0.0931, "berachain": 0.916, - "bitlayer": 0.00114, - "blast": 0.00305, - "bob": 0.00197, - "boba": 0.00195, + "bitlayer": 0.00156, + "blast": 0.00325, + "bob": 0.00249, + "boba": 0.00241, "bouncebit": 30.4, "bsc": 0.264, - "bsquared": 0.00192, + "bsquared": 0.00245, "celo": 10.1, "cheesechain": 10600, "chilizmainnet": 84, @@ -103,7 +103,7 @@ "redstone": 0.00217, "rivalz": 0.00195, "ronin": 6.23, - "rootstockmainnet": 0.00165, + "rootstockmainnet": 0.00277, "sanko": 0.483, "scroll": 0.00951, "sei": 18.4, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json index e7590065f49..63cc4a51204 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json @@ -4,6 +4,7 @@ "coti": 0.1, "deepbrainchain": 100, "infinityvmmainnet": 0, + "fluence": 2000, "nibiru": 10, "ontology": 0, "osmosis": 0, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index 43baf93c995..dd418727ae9 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -1,7 +1,7 @@ { "abstract": 0.0342, "alephzeroevmmainnet": 271, - "ancient8": 0.019, + "ancient8": 0.0202, "apechain": 56.3, "appchain": 0.124, "arbitrum": 0.275, @@ -10,18 +10,18 @@ "artela": 2200, "arthera": 0.5, "astar": 928, - "aurora": 0.0156, + "aurora": 0.0171, "avalanche": 1.28, "b3": 0.0227, "base": 0.745, "berachain": 7.33, - "bitlayer": 0.00912, - "blast": 0.0244, - "bob": 0.0158, - "boba": 0.0156, + "bitlayer": 0.0125, + "blast": 0.026, + "bob": 0.0199, + "boba": 0.0193, "bouncebit": 243, - "bsc": 5, - "bsquared": 0.0154, + "bsc": 2.11, + "bsquared": 0.0196, "celo": 80.8, "cheesechain": 84800, "chilizmainnet": 672, @@ -44,7 +44,7 @@ "flame": 10.4, "flare": 1660, "flowmainnet": 67.5, - "fluence": 28600, + "fluence": 2000, "form": 0.0156, "fraxtal": 0.2, "fusemainnet": 2030, @@ -63,20 +63,20 @@ "injective": 3.03, "ink": 0.186, "kaia": 250, - "kroma": 0.05, - "linea": 1, + "kroma": 0.0156, + "linea": 0.612, "lisk": 0.0832, "lukso": 29.3, "lumia": 74.5, "lumiaprism": 88, - "mantapacific": 0.2, + "mantapacific": 0.0269, "mantle": 60.1, "matchain": 0.05, "merlin": 0.002, "metal": 0.0633, - "metis": 3, + "metis": 1.83, "milkyway": 25, - "mint": 0.05, + "mint": 0.0218, "mode": 0.2, "molten": 195, "moonbeam": 372, @@ -84,36 +84,36 @@ "nero": 25, "neutron": 204, "nibiru": 10, - "ontology": 118, - "oortmainnet": 2000, + "oortmainnet": 589, + "ontology": 0, "opbnb": 0.0422, - "optimism": 0.5, - "orderly": 0.05, + "optimism": 0.306, + "orderly": 0.0225, "osmosis": 0, "peaq": 179, "plume": 0.05, "polygon": 131, - "polygonzkevm": 0.5, - "polynomialfi": 0.05, + "polygonzkevm": 0.195, + "polynomialfi": 0.022, "prom": 18, - "proofofplay": 0.05, - "rarichain": 0.05, + "proofofplay": 0.0156, + "rarichain": 0.0263, "reactive": 0.1, - "real": 0.1, - "redstone": 0.2, - "rivalz": 0.05, + "real": 0.0156, + "redstone": 0.0174, + "rivalz": 0.0156, "ronin": 49.8, - "rootstockmainnet": 0.0132, + "rootstockmainnet": 0.0222, "sanko": 3.86, - "scroll": 0.5, + "scroll": 0.0761, "sei": 147, "shibarium": 90.4, - "snaxchain": 0.05, + "snaxchain": 0.0226, "solanamainnet": 40, "soneium": 0.315, "sonic": 59.1, "sonicsvm": 1.32, - "soon": 0.08, + "soon": 0.0156, "sophon": 50, "story": 13.4, "stride": 124, @@ -121,24 +121,24 @@ "superpositionmainnet": 0.188, "superseed": 0.101, "swell": 0.0685, - "taiko": 0.2, + "taiko": 0.04, "tangle": 6, "telos": 281, "torus": 182, - "treasure": 900, + "treasure": 314, "trumpchain": 3.01, "unichain": 0.0718, "unitzero": 106, "vana": 0.001, "viction": 125, - "worldchain": 0.2, + "worldchain": 0.0216, "xai": 570, "xlayer": 0.912, "xpla": 912, - "zeronetwork": 0.05, + "zeronetwork": 0.0156, "zetachain": 106, "zircuit": 0.0339, - "zklink": 0.05, - "zksync": 0.05, - "zoramainnet": 0.2 + "zklink": 0.0156, + "zksync": 0.0156, + "zoramainnet": 0.0701 } diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index b9d96195708..ac880501182 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -1,7 +1,7 @@ { "abstract": 0.00856, "alephzeroevmmainnet": 67.8, - "ancient8": 0.00476, + "ancient8": 0.00506, "apechain": 14.1, "appchain": 0.031, "arbitrum": 0.0688, @@ -10,18 +10,18 @@ "artela": 550, "arthera": 0.125, "astar": 232, - "aurora": 0.0039, + "aurora": 0.00428, "avalanche": 0.32, "b3": 0.00568, "base": 0.186, "berachain": 1.83, - "bitlayer": 0.00228, - "blast": 0.0061, - "bob": 0.00394, - "boba": 0.0039, + "bitlayer": 0.00312, + "blast": 0.0065, + "bob": 0.00498, + "boba": 0.00482, "bouncebit": 60.8, "bsc": 0.528, - "bsquared": 0.00384, + "bsquared": 0.0049, "celo": 20.2, "cheesechain": 21200, "chilizmainnet": 168, @@ -44,7 +44,7 @@ "flame": 2.6, "flare": 416, "flowmainnet": 16.9, - "fluence": 7160, + "fluence": 500, "form": 0.0039, "fraxtal": 0.0434, "fusemainnet": 508, @@ -55,25 +55,25 @@ "guru": 2780, "harmony": 570, "hashkey": 16.6, - "hemi": 0.00414, - "hyperevm": 1, + "hemi": 0.00616, + "hyperevm": 0.588, "immutablezkevmmainnet": 13.5, "inevm": 0.758, "injective": 0.758, "ink": 0.0466, "kaia": 62.2, "kroma": 0.0039, - "linea": 0.22, + "linea": 0.153, "lisk": 0.0208, "lukso": 7.32, "lumia": 18.6, "lumiaprism": 22, - "mantapacific": 0.02, + "mantapacific": 0.00672, "mantle": 15, "matchain": 0.0107, "merlin": 0.000344, "metal": 0.0158, - "metis": 1, + "metis": 0.458, "milkyway": 6.26, "mint": 0.00544, "mode": 0.0458, @@ -83,9 +83,9 @@ "nero": 6.26, "neutron": 51, "nibiru": 2.5, - "oortmainnet": 400, + "oortmainnet": 147, "opbnb": 0.0106, - "optimism": 0.083, + "optimism": 0.0766, "orderly": 0.00562, "peaq": 44.8, "plume": 0.0125, @@ -100,9 +100,9 @@ "redstone": 0.00434, "rivalz": 0.0039, "ronin": 12.5, - "rootstockmainnet": 0.0033, + "rootstockmainnet": 0.00554, "sanko": 0.966, - "scroll": 0.022, + "scroll": 0.019, "sei": 36.8, "shibarium": 22.6, "snaxchain": 0.00566, @@ -110,7 +110,7 @@ "soneium": 0.0788, "sonic": 14.8, "sonicsvm": 0.5, - "soon": 0.02, + "soon": 0.0039, "sophon": 12.5, "story": 3.34, "stride": 31, @@ -118,24 +118,24 @@ "superpositionmainnet": 0.047, "superseed": 0.0252, "swell": 0.0171, - "taiko": 0.019, + "taiko": 0.01, "tangle": 1.5, "telos": 70.2, "torus": 45.4, - "treasure": 250, + "treasure": 78.6, "trumpchain": 0.752, "unichain": 0.0179, "unitzero": 26.6, "vana": 0.00025, "viction": 31.2, - "worldchain": 0.05, + "worldchain": 0.0054, "xai": 142, "xlayer": 0.228, "xpla": 228, - "zeronetwork": 0.0125, + "zeronetwork": 0.0039, "zetachain": 26.6, "zircuit": 0.0094, - "zklink": 0.0125, - "zksync": 0.0125, - "zoramainnet": 0.05 + "zklink": 0.0039, + "zksync": 0.0039, + "zoramainnet": 0.0175 } diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index 13a5cb89568..520e1fb9aff 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -1,7 +1,7 @@ { "abstract": 0.0257, "alephzeroevmmainnet": 203, - "ancient8": 0.0143, + "ancient8": 0.0152, "apechain": 42.2, "appchain": 0.093, "arbitrum": 0.206, @@ -10,18 +10,18 @@ "artela": 1650, "arthera": 0.375, "astar": 696, - "aurora": 0.0117, + "aurora": 0.0128, "avalanche": 0.96, "b3": 0.017, "base": 0.559, "berachain": 5.5, - "bitlayer": 0.00684, - "blast": 0.0183, - "bob": 0.0118, - "boba": 0.0117, + "bitlayer": 0.00936, + "blast": 0.0195, + "bob": 0.0149, + "boba": 0.0145, "bouncebit": 182, "bsc": 1.58, - "bsquared": 0.0115, + "bsquared": 0.0147, "celo": 60.6, "cheesechain": 63600, "chilizmainnet": 504, @@ -44,7 +44,7 @@ "flame": 7.8, "flare": 1250, "flowmainnet": 50.6, - "fluence": 21500, + "fluence": 1500, "form": 0.0117, "fraxtal": 0.13, "fusemainnet": 1520, @@ -68,12 +68,12 @@ "lukso": 22, "lumia": 55.9, "lumiaprism": 66, - "mantapacific": 0.06, + "mantapacific": 0.0202, "mantle": 45.1, "matchain": 0.0322, "merlin": 0.00103, "metal": 0.0475, - "metis": 2.5, + "metis": 1.37, "milkyway": 18.8, "mint": 0.0163, "mode": 0.137, @@ -83,7 +83,7 @@ "nero": 18.8, "neutron": 153, "nibiru": 7.5, - "oortmainnet": 1800, + "oortmainnet": 442, "opbnb": 0.0317, "optimism": 0.23, "orderly": 0.0169, @@ -100,7 +100,7 @@ "redstone": 0.013, "rivalz": 0.0117, "ronin": 37.4, - "rootstockmainnet": 0.0099, + "rootstockmainnet": 0.0166, "sanko": 2.9, "scroll": 0.0571, "sei": 110, @@ -110,7 +110,7 @@ "soneium": 0.236, "sonic": 44.3, "sonicsvm": 0.99, - "soon": 0.05, + "soon": 0.0117, "sophon": 37.5, "story": 10, "stride": 93, @@ -122,20 +122,20 @@ "tangle": 4.5, "telos": 211, "torus": 136, - "treasure": 618, + "treasure": 236, "trumpchain": 2.26, "unichain": 0.0538, "unitzero": 79.8, "vana": 0.00075, "viction": 93.6, - "worldchain": 0.15, + "worldchain": 0.0162, "xai": 427, "xlayer": 0.684, "xpla": 684, - "zeronetwork": 0.0375, + "zeronetwork": 0.0117, "zetachain": 79.8, "zircuit": 0.0254, - "zklink": 0.0375, - "zksync": 0.0375, - "zoramainnet": 0.15 + "zklink": 0.0117, + "zksync": 0.0117, + "zoramainnet": 0.0526 } diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index 3de74ac495b..f195b59fde7 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -1,7 +1,7 @@ { "abstract": 0.0514, "alephzeroevmmainnet": 407, - "ancient8": 0.0286, + "ancient8": 0.0304, "apechain": 100, "appchain": 0.186, "arbitrum": 0.413, @@ -10,19 +10,19 @@ "artela": 3300, "arthera": 0.75, "astar": 1390, - "aurora": 0.0234, + "aurora": 0.0257, "avalanche": 1.92, "b3": 0.0341, "base": 1.12, "berachain": 11, - "bitlayer": 0.0137, - "blast": 0.0366, - "bob": 0.0236, - "boba": 0.0234, + "bitlayer": 0.0187, + "blast": 0.039, + "bob": 0.0299, + "boba": 0.0289, "bouncebit": 365, - "bsc": 8, - "bsquared": 0.023, - "celo": 308, + "bsc": 3.17, + "bsquared": 0.0294, + "celo": 121, "cheesechain": 127000, "chilizmainnet": 1010, "conflux": 534, @@ -44,7 +44,7 @@ "flame": 15.6, "flare": 2500, "flowmainnet": 101, - "fluence": 43000, + "fluence": 3000, "form": 0.0234, "fraxtal": 0.4, "fusemainnet": 3050, @@ -55,27 +55,27 @@ "guru": 16700, "harmony": 3420, "hashkey": 99.7, - "hemi": 0.0248, - "hyperevm": 10, + "hemi": 0.037, + "hyperevm": 3.53, "immutablezkevmmainnet": 80.9, "inevm": 6.1, "injective": 4.55, "ink": 0.28, "kaia": 500, - "kroma": 0.1, - "linea": 2, + "kroma": 0.0234, + "linea": 0.918, "lisk": 0.2, "lukso": 43.9, "lumia": 112, "lumiaprism": 132, - "mantapacific": 0.4, + "mantapacific": 0.0403, "mantle": 90.1, "matchain": 0.0644, "merlin": 0.003, "metal": 0.1, - "metis": 6, + "metis": 2.75, "milkyway": 37.6, - "mint": 0.1, + "mint": 0.0326, "mode": 0.4, "molten": 293, "moonbeam": 700, @@ -83,34 +83,34 @@ "nero": 37.6, "neutron": 306, "nibiru": 15, - "oortmainnet": 4000, + "oortmainnet": 883, "opbnb": 0.0634, - "optimism": 1.2, - "orderly": 0.1, + "optimism": 0.46, + "orderly": 0.0337, "peaq": 269, "plume": 0.075, "polygon": 250, - "polygonzkevm": 1.1, - "polynomialfi": 0.1, + "polygonzkevm": 0.293, + "polynomialfi": 0.033, "prom": 36, - "proofofplay": 0.075, - "rarichain": 0.1, + "proofofplay": 0.0234, + "rarichain": 0.0395, "reactive": 0.15, - "real": 0.2, - "redstone": 0.4, - "rivalz": 0.1, + "real": 0.0234, + "redstone": 0.026, + "rivalz": 0.0234, "ronin": 74.8, - "rootstockmainnet": 0.0198, + "rootstockmainnet": 0.0332, "sanko": 5.8, - "scroll": 1.1, + "scroll": 0.114, "sei": 221, "shibarium": 136, - "snaxchain": 0.075, + "snaxchain": 0.034, "solanamainnet": 60, "soneium": 0.473, "sonic": 88.7, "sonicsvm": 1.98, - "soon": 0.1, + "soon": 0.0234, "sophon": 75, "story": 20, "stride": 186, @@ -118,24 +118,24 @@ "superpositionmainnet": 0.282, "superseed": 0.151, "swell": 0.103, - "taiko": 0.4, + "taiko": 0.06, "tangle": 9, "telos": 421, "torus": 272, - "treasure": 1800, + "treasure": 472, "trumpchain": 4.51, "unichain": 0.2, "unitzero": 160, "vana": 0.0015, "viction": 187, - "worldchain": 0.4, + "worldchain": 0.0324, "xai": 854, "xlayer": 1.37, "xpla": 1370, - "zeronetwork": 0.1, + "zeronetwork": 0.0234, "zetachain": 160, "zircuit": 0.0509, - "zklink": 0.075, - "zksync": 0.1, - "zoramainnet": 0.4 + "zklink": 0.0234, + "zksync": 0.0234, + "zoramainnet": 0.105 } From 1f370e61eb949f25fd47b5e786663c2bb4233482 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 5 May 2025 15:28:22 -0400 Subject: [PATCH 132/223] fix: Hook.update deploys new hook with nested addresses (#6117) ### Description This PR fixes Hook.update() when provided configs with nested addresses causing hook update callers such as `warp apply`to trigger new deployments. For example: ``` type: aggregationHook hooks: - "0x7937CB2886f01F38210506491A69B0D107Ea0ad9" - beneficiary: "0x865BA5789D82F2D4C5595a3968dad729A8C3daE6" maxProtocolFee: "100000000000000000000" owner: "0x865BA5789D82F2D4C5595a3968dad729A8C3daE6" protocolFee: "50000000000000000" type: protocolFee ``` ### Related issues - Resolves: [ENG-1067](https://linear.app/hyperlane-xyz/issue/ENG-1067/hookmodule-update-cannot-handle-address-strings) ### Backward compatibility Yes ### Testing Manual --------- Co-authored-by: Le --- .changeset/modern-owls-beam.md | 5 + .../src/hook/EvmHookModule.hardhat-test.ts | 243 ++++++++++++++++++ typescript/sdk/src/hook/EvmHookModule.ts | 8 +- typescript/sdk/src/hook/EvmHookReader.ts | 56 +++- 4 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 .changeset/modern-owls-beam.md diff --git a/.changeset/modern-owls-beam.md b/.changeset/modern-owls-beam.md new file mode 100644 index 00000000000..85cd9e274af --- /dev/null +++ b/.changeset/modern-owls-beam.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Add HookModule.resolveHookAddresses() to resolve all HookConfig addresses diff --git a/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts b/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts index 65b31eb3c9f..7ab20bda1c1 100644 --- a/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts +++ b/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts @@ -6,6 +6,7 @@ import { Address, WithAddress, assert, + deepCopy, deepEquals, eqAddress, } from '@hyperlane-xyz/utils'; @@ -30,6 +31,8 @@ import { normalizeConfig } from '../utils/ism.js'; import { EvmHookModule } from './EvmHookModule.js'; import { AggregationHookConfig, + AmountRoutingHookConfig, + DerivedHookConfig, DomainRoutingHookConfig, FallbackRoutingHookConfig, HookConfig, @@ -350,6 +353,246 @@ describe('EvmHookModule', async () => { .false; }); + it('should not update if aggregation hook includes an address of an existing hook', async () => { + const config: AggregationHookConfig = { + type: HookType.AGGREGATION, + hooks: [randomHookConfig(0, 2), randomHookConfig(0, 2)], + }; + + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + const [firstChildHook, secondSecondHook] = ( + (await hook.read()) as AggregationHookConfig + ).hooks as DerivedHookConfig[]; + const expectedConfig = { + ...config, + hooks: [firstChildHook.address, secondSecondHook], + }; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(initialHookAddress).to.be.equal(hook.serialize().deployedHook); + }); + + it('should not update if aggregation hook includes an address of an existing hook (depth 2)', async () => { + const owner = await multiProvider.getSignerAddress(chain); + const config: AggregationHookConfig = { + type: HookType.AGGREGATION, + hooks: [ + { + type: HookType.AGGREGATION, + hooks: [ + { + type: HookType.MERKLE_TREE, + }, + { + owner, + type: HookType.PROTOCOL_FEE, + maxProtocolFee: '1', + protocolFee: '0', + beneficiary: owner, + }, + ], + }, + ], + }; + + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + // Set the deepest hooks to their addresses + const expectedConfig: any = deepCopy(await hook.read()); + expectedConfig.hooks[0].hooks[0] = + expectedConfig.hooks[0].hooks[0].address; + expectedConfig.hooks[0].hooks[1] = + expectedConfig.hooks[0].hooks[1].address; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(initialHookAddress).to.be.equal(hook.serialize().deployedHook); + }); + + it('should not update if a domain routing hook includes an address of an existing hook', async () => { + const owner = await multiProvider.getSignerAddress(chain); + const config: DomainRoutingHookConfig = { + type: HookType.ROUTING, + owner, + domains: { + 9913371: randomHookConfig(0, 2), + 9913372: randomHookConfig(0, 2), + }, + }; + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + const { test1: firstHook, test2: secondHook } = ( + (await hook.read()) as DomainRoutingHookConfig + ).domains; + const expectedConfig = { + ...config, + domains: { + test1: (firstHook as DerivedHookConfig).address, + test2: secondHook, + }, + }; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(eqAddress(initialHookAddress, hook.serialize().deployedHook)).to.be + .true; + }); + + it('should not update if a domain routing hook includes an address of an existing hook (depth 2)', async () => { + const owner = await multiProvider.getSignerAddress(chain); + const config: DomainRoutingHookConfig = { + type: HookType.ROUTING, + owner, + domains: { + 9913371: { + type: HookType.AGGREGATION, + hooks: [ + { + type: HookType.MERKLE_TREE, + }, + { + owner, + type: HookType.PROTOCOL_FEE, + maxProtocolFee: '1', + protocolFee: '0', + beneficiary: owner, + }, + ], + }, + 9913372: { + type: HookType.AGGREGATION, + hooks: [ + { + type: HookType.MERKLE_TREE, + }, + { + owner, + type: HookType.PROTOCOL_FEE, + maxProtocolFee: '1', + protocolFee: '0', + beneficiary: owner, + }, + ], + }, + }, + }; + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + // Set the deepest hooks to their addresses + const expectedConfig: any = deepCopy(await hook.read()); + expectedConfig.domains.test1.hooks[0] = + expectedConfig.domains.test1.hooks[0].address; + expectedConfig.domains.test2.hooks[0] = + expectedConfig.domains.test2.hooks[0].address; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(eqAddress(initialHookAddress, hook.serialize().deployedHook)).to.be + .true; + }); + + it('should not update if a fallback routing hook includes an address of an existing hook', async () => { + const owner = await multiProvider.getSignerAddress(chain); + const config: FallbackRoutingHookConfig = { + type: HookType.FALLBACK_ROUTING, + owner, + domains: { + 9913371: randomHookConfig(0, 2), + 9913372: randomHookConfig(0, 2), + }, + fallback: randomHookConfig(0, 2), + }; + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + const derivedHook = (await hook.read()) as FallbackRoutingHookConfig; + const { test1: firstHook, test2: secondHook } = derivedHook.domains; + + const expectedConfig = { + ...config, + domains: { + test1: (firstHook as DerivedHookConfig).address, + test2: secondHook, + }, + fallback: (derivedHook.fallback as DerivedHookConfig).address, + }; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(eqAddress(initialHookAddress, hook.serialize().deployedHook)).to.be + .true; + }); + + it('should not update if a fallback routing hook includes an address of an existing hook (depth 2)', async () => { + const owner = await multiProvider.getSignerAddress(chain); + const config: FallbackRoutingHookConfig = { + type: HookType.FALLBACK_ROUTING, + owner, + domains: { + 9913371: randomHookConfig(0, 2), + 9913372: randomHookConfig(0, 2), + }, + fallback: { + type: HookType.AGGREGATION, + hooks: [ + { + type: HookType.MERKLE_TREE, + }, + { + owner, + type: HookType.PROTOCOL_FEE, + maxProtocolFee: '1', + protocolFee: '0', + beneficiary: owner, + }, + ], + }, + }; + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + const derivedHook: any = await hook.read(); + + const expectedConfig: FallbackRoutingHookConfig = { + ...derivedHook, + fallback: { + type: HookType.AGGREGATION, + hooks: [ + derivedHook.fallback.hooks[0], + derivedHook.fallback.hooks[1].address, + ], + }, + }; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(initialHookAddress).to.be.equal(hook.serialize().deployedHook); + }); + + it('should not update if a amount routing hook includes an address of an existing hook', async () => { + const config: AmountRoutingHookConfig = { + type: HookType.AMOUNT_ROUTING, + threshold: 1, + lowerHook: randomHookConfig(0, 2), + upperHook: randomHookConfig(0, 2), + }; + // create a new hook + const { hook, initialHookAddress } = await createHook(config); + + const derivedHook = (await hook.read()) as AmountRoutingHookConfig; + const { lowerHook, upperHook } = derivedHook; + + const expectedConfig = { + ...config, + lowerHook: (lowerHook as DerivedHookConfig).address, + upperHook: upperHook, + }; + + await expectTxsAndUpdate(hook, expectedConfig, 0); + expect(eqAddress(initialHookAddress, hook.serialize().deployedHook)).to.be + .true; + }); + const createDeployerOwnedIgpHookConfig = async (): Promise => { const owner = await multiProvider.getSignerAddress(chain); diff --git a/typescript/sdk/src/hook/EvmHookModule.ts b/typescript/sdk/src/hook/EvmHookModule.ts index cb3cafe8d6a..3971a7951a5 100644 --- a/typescript/sdk/src/hook/EvmHookModule.ts +++ b/typescript/sdk/src/hook/EvmHookModule.ts @@ -154,13 +154,7 @@ export class EvmHookModule extends HyperlaneModule< } targetConfig = HookConfigSchema.parse(targetConfig); - - // Do not support updating to a custom Hook address - if (typeof targetConfig === 'string') { - throw new Error( - 'Invalid targetConfig: Updating to a custom Hook address is not supported. Please provide a valid Hook configuration.', - ); - } + targetConfig = await this.reader.deriveHookConfig(targetConfig); // Update the config this.args.config = targetConfig; diff --git a/typescript/sdk/src/hook/EvmHookReader.ts b/typescript/sdk/src/hook/EvmHookReader.ts index 97b22edaa90..4e7a03ee71c 100644 --- a/typescript/sdk/src/hook/EvmHookReader.ts +++ b/typescript/sdk/src/hook/EvmHookReader.ts @@ -25,6 +25,8 @@ import { concurrentMap, eqAddress, getLogLevel, + objMap, + promiseObjAll, rootLogger, } from '@hyperlane-xyz/utils'; @@ -55,7 +57,7 @@ import { } from './types.js'; export interface HookReader { - deriveHookConfig(address: Address): Promise>; + deriveHookConfig(address: HookConfig): Promise>; deriveMerkleTreeConfig( address: Address, ): Promise>; @@ -109,7 +111,9 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { super(multiProvider, chain); } - async deriveHookConfig(address: Address): Promise { + async deriveHookConfigFromAddress( + address: Address, + ): Promise { this.logger.debug('Deriving HookConfig:', { address }); const cachedValue = this._cache.get(address); @@ -197,6 +201,54 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { return derivedHookConfig; } + /** + * Recursively resolves the HookConfigs as addresses, e.g. + * hook: + * type: aggregationHook + * hooks: + * - "0x7937CB2886f01F38210506491A69B0D107Ea0ad9" + * - beneficiary: "0x865BA5789D82F2D4C5595a3968dad729A8C3daE6" + * maxProtocolFee: "100000000000000000000" + * owner: "0x865BA5789D82F2D4C5595a3968dad729A8C3daE6" + * protocolFee: "50000000000000000" + * type: protocolFee + * + * This may throw if the Hook address is not a derivable hook (e.g. Custom Hook) + */ + public async deriveHookConfig( + config: HookConfig, + ): Promise { + if (typeof config === 'string') + return this.deriveHookConfigFromAddress(config); + + // Extend the inner hooks + switch (config.type) { + case HookType.FALLBACK_ROUTING: + case HookType.ROUTING: + config.domains = await promiseObjAll( + objMap(config.domains, async (_, hook) => + this.deriveHookConfig(hook), + ), + ); + + if (config.type === HookType.FALLBACK_ROUTING) + config.fallback = await this.deriveHookConfig(config.fallback); + break; + case HookType.AGGREGATION: + config.hooks = await Promise.all( + config.hooks.map(async (hook) => this.deriveHookConfig(hook)), + ); + break; + case HookType.AMOUNT_ROUTING: + [config.lowerHook, config.upperHook] = await Promise.all([ + this.deriveHookConfig(config.lowerHook), + this.deriveHookConfig(config.upperHook), + ]); + break; + } + return config as DerivedHookConfig; + } + async deriveMailboxDefaultHookConfig( address: Address, ): Promise> { From 248d2e13b0fcd2c260156ddf16d907319d33bfa9 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Mon, 5 May 2025 17:17:45 -0400 Subject: [PATCH 133/223] fix(sdk, cli): fix cli checker not working with non evm chains in config [eng-1550] (#6119) ### Description This PR updates the warp check command to successfully check warp routes that include non-EVM deployments by expanding the config to populate expected values like the remote routers and gas mappings, and then filter out unsupported deployments from the config. ### Drive-by changes - Removes code that assumed that the remote routers are EVM addresses forcing the code to treat them as 32 bytes hex string because that is how non-EVM deployments addresses are set on chain ### Related issues - Fixes #[ENG-1550](https://linear.app/hyperlane-xyz/issue/ENG-1550/update-cli-checker-to-support-non-evm-chains) ### Backward compatibility - YES ### Testing - Manual - e2e - Unit --- .changeset/soft-foxes-sit.md | 6 ++ typescript/cli/src/commands/warp.ts | 29 ++++-- .../cli/src/tests/warp/warp-check.e2e-test.ts | 94 ++++++++++++++++++- .../tests/warp/warp-extend-config.e2e-test.ts | 3 +- .../token/EvmERC20WarpModule.hardhat-test.ts | 4 +- .../sdk/src/token/EvmERC20WarpModule.ts | 10 +- .../EvmERC20WarpRouteReader.hardhat-test.ts | 4 +- .../sdk/src/token/EvmERC20WarpRouteReader.ts | 21 +++-- typescript/sdk/src/token/configUtils.ts | 36 ++++++- 9 files changed, 178 insertions(+), 29 deletions(-) create mode 100644 .changeset/soft-foxes-sit.md diff --git a/.changeset/soft-foxes-sit.md b/.changeset/soft-foxes-sit.md new file mode 100644 index 00000000000..94dcf8e8c10 --- /dev/null +++ b/.changeset/soft-foxes-sit.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +Enables the CLI to warp check routes that include non EVM routes diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index 9092726d757..33f2e0993a6 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -7,7 +7,7 @@ import { expandWarpDeployConfig, getRouterAddressesFromWarpCoreConfig, } from '@hyperlane-xyz/sdk'; -import { assert, objFilter } from '@hyperlane-xyz/utils'; +import { ProtocolType, assert, objFilter } from '@hyperlane-xyz/utils'; import { runWarpRouteCheck } from '../check/warp.js'; import { @@ -386,18 +386,33 @@ export const check: CommandModuleWithContext<{ warpCoreConfig, )); + // Expand the config before removing non-EVM chain configs to correctly expand + // the remote routers + let expandedWarpDeployConfig = await expandWarpDeployConfig( + context.multiProvider, + warpDeployConfig, + getRouterAddressesFromWarpCoreConfig(warpCoreConfig), + ); + + // Remove any non EVM chain configs to avoid the checker crashing + warpCoreConfig.tokens = warpCoreConfig.tokens.filter( + (config) => + context.multiProvider.getProtocol(config.chainName) === + ProtocolType.Ethereum, + ); + + expandedWarpDeployConfig = objFilter( + expandedWarpDeployConfig, + (chain, _config): _config is any => + context.multiProvider.getProtocol(chain) === ProtocolType.Ethereum, + ); + // Get on-chain config const onChainWarpConfig = await getWarpRouteConfigsByCore({ context, warpCoreConfig, }); - const expandedWarpDeployConfig = await expandWarpDeployConfig( - context.multiProvider, - warpDeployConfig, - getRouterAddressesFromWarpCoreConfig(warpCoreConfig), - ); - await runWarpRouteCheck({ onChainWarpConfig, warpRouteConfig: expandedWarpDeployConfig, diff --git a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts index 57203378e43..ce80acab03d 100644 --- a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts @@ -1,19 +1,21 @@ import { expect } from 'chai'; -import { Wallet } from 'ethers'; +import { Signer, Wallet, ethers } from 'ethers'; import { zeroAddress } from 'viem'; -import { ERC20Test } from '@hyperlane-xyz/core'; +import { ERC20Test, HypERC20Collateral__factory } from '@hyperlane-xyz/core'; import { ChainAddresses, createWarpRouteConfigId, } from '@hyperlane-xyz/registry'; import { + ChainMetadata, HookConfig, HookType, IsmConfig, IsmType, MUTABLE_HOOK_TYPE, MUTABLE_ISM_TYPE, + TokenStandard, TokenType, WarpCoreConfig, WarpRouteDeployConfig, @@ -21,11 +23,18 @@ import { randomHookConfig, randomIsmConfig, } from '@hyperlane-xyz/sdk'; -import { Address, assert, deepCopy } from '@hyperlane-xyz/utils'; +import { + Address, + addressToBytes32, + assert, + deepCopy, +} from '@hyperlane-xyz/utils'; import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; import { + ANVIL_DEPLOYER_ADDRESS, ANVIL_KEY, + CHAIN_2_METADATA_PATH, CHAIN_NAME_2, CHAIN_NAME_3, CORE_CONFIG_PATH, @@ -44,6 +53,7 @@ import { describe('hyperlane warp check e2e tests', async function () { this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT); + let signer: Signer; let chain2Addresses: ChainAddresses = {}; let chain3Addresses: ChainAddresses = {}; let token: ERC20Test; @@ -58,6 +68,14 @@ describe('hyperlane warp check e2e tests', async function () { deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY), ]); + const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + + const provider = new ethers.providers.JsonRpcProvider( + chainMetadata.rpcUrls[0].http, + ); + + signer = new Wallet(ANVIL_KEY).connect(provider); + token = await deployToken(ANVIL_KEY, CHAIN_NAME_2); tokenSymbol = await token.symbol(); @@ -156,6 +174,74 @@ describe('hyperlane warp check e2e tests', async function () { expect(output.exitCode).to.equal(0); expect(output.text()).to.include('No violations found'); }); + + it(`should successfully check warp routes that are not deployed as proxies`, async () => { + // Deploy the token and the hyp adapter + const symbol = 'NTAP'; + const tokenName = 'NOTAPROXY'; + const tokenDecimals = 10; + const collateral = await deployToken( + ANVIL_KEY, + CHAIN_NAME_2, + tokenDecimals, + symbol, + ); + + const contract = new HypERC20Collateral__factory(signer); + const tx = await contract.deploy( + collateral.address, + 1, + chain2Addresses.mailbox, + ); + + const deployedContract = await tx.deployed(); + const tx2 = await deployedContract.initialize( + zeroAddress, + zeroAddress, + ANVIL_DEPLOYER_ADDRESS, + ); + + await tx2.wait(); + + // Manually add config files to the registry + const routePath = getCombinedWarpRoutePath(symbol, [CHAIN_NAME_2]); + const warpDeployConfig: WarpRouteDeployConfig = { + [CHAIN_NAME_2]: { + type: TokenType.collateral, + token: collateral.address, + owner: ANVIL_DEPLOYER_ADDRESS, + }, + }; + writeYamlOrJson( + routePath.replace('-config.yaml', '-deploy.yaml'), + warpDeployConfig, + ); + + const warpCoreConfig: WarpCoreConfig = { + tokens: [ + { + addressOrDenom: deployedContract.address, + chainName: CHAIN_NAME_2, + decimals: tokenDecimals, + collateralAddressOrDenom: token.address, + name: tokenName, + standard: TokenStandard.EvmHypCollateral, + symbol, + }, + ], + }; + writeYamlOrJson(routePath, warpCoreConfig); + + // Finally run warp check + const output = await hyperlaneWarpCheckRaw({ + warpRouteId: createWarpRouteConfigId(symbol, [CHAIN_NAME_2]), + }) + .stdio('pipe') + .nothrow(); + + expect(output.exitCode).to.equal(0); + expect(output.text()).to.include('No violations found'); + }); }); describe('hyperlane warp check --config ... --warp ...', () => { @@ -285,7 +371,7 @@ describe('hyperlane warp check e2e tests', async function () { ); const expectedActualText = `ACTUAL: ""\n`; const expectedDiffText = ` EXPECTED: - address: "${warpCore.tokens[0].addressOrDenom!.toLowerCase()}"`; + address: "${addressToBytes32(warpCore.tokens[0].addressOrDenom!)}"`; const output = await hyperlaneWarpCheckRaw({ warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, diff --git a/typescript/cli/src/tests/warp/warp-extend-config.e2e-test.ts b/typescript/cli/src/tests/warp/warp-extend-config.e2e-test.ts index 69640521ee1..66cfbdee55a 100644 --- a/typescript/cli/src/tests/warp/warp-extend-config.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-extend-config.e2e-test.ts @@ -9,6 +9,7 @@ import { normalizeConfig, randomAddress, } from '@hyperlane-xyz/sdk'; +import { addressToBytes32 } from '@hyperlane-xyz/utils'; import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; import { @@ -168,7 +169,7 @@ describe('hyperlane warp apply config extension tests', async function () { updatedConfig[CHAIN_NAME_2].remoteRouters![ chain3Id ].address.toLowerCase(), - ).to.equal(newRouterAddress.toLowerCase()); + ).to.equal(addressToBytes32(newRouterAddress)); }); it('should preserve deploy config when extending warp route', async () => { diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts index f351af43f0c..5839dd27376 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts @@ -27,7 +27,7 @@ import { proxyAdmin, serializeContracts, } from '@hyperlane-xyz/sdk'; -import { randomInt } from '@hyperlane-xyz/utils'; +import { addressToBytes32, randomInt } from '@hyperlane-xyz/utils'; import { TestCoreApp } from '../core/TestCoreApp.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; @@ -629,7 +629,7 @@ describe('EvmERC20WarpHyperlaneModule', async () => { updatedConfig = await evmERC20WarpModule.read(); expect(Object.keys(updatedConfig.remoteRouters!).length).to.be.equal(1); expect(updatedConfig.remoteRouters?.['3'].address.toLowerCase()).to.be.eq( - extendedRemoteRouter['3'].address.toLowerCase(), + addressToBytes32(extendedRemoteRouter['3'].address), ); }); diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.ts b/typescript/sdk/src/token/EvmERC20WarpModule.ts index 487edebd18c..443e189b254 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.ts @@ -37,6 +37,7 @@ import { EvmHookModule } from '../hook/EvmHookModule.js'; import { EvmIsmModule } from '../ism/EvmIsmModule.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; +import { RemoteRouters } from '../router/types.js'; import { ChainName, ChainNameOrId } from '../types.js'; import { extractIsmAndHookFactoryAddresses } from '../utils/ism.js'; @@ -162,13 +163,14 @@ export class EvmERC20WarpModule extends HyperlaneModule< const { remoteRouters: expectedRemoteRouters } = expectedConfig; const routesToEnroll = Object.entries(expectedRemoteRouters) + .map(([domain, rawRouter]): [string, RemoteRouters[string]] => [ + domain, + { address: addressToBytes32(rawRouter.address) }, + ]) .filter(([domain, expectedRouter]) => { const actualRouter = actualRemoteRouters[domain]; // Enroll if router doesn't exist for domain or has different address - return ( - !actualRouter || - !eqAddress(actualRouter.address, expectedRouter.address) - ); + return !actualRouter || actualRouter.address !== expectedRouter.address; }) .map(([domain]) => domain); diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts index 98d5e802c4f..48c6364c36a 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts @@ -20,7 +20,7 @@ import { WarpRouteDeployConfigMailboxRequired, test3, } from '@hyperlane-xyz/sdk'; -import { assert } from '@hyperlane-xyz/utils'; +import { addressToBytes32, assert } from '@hyperlane-xyz/utils'; import { TestCoreApp } from '../core/TestCoreApp.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; @@ -461,6 +461,6 @@ describe('ERC20WarpRouterReader', async () => { expect(Object.keys(derivedConfig.remoteRouters!).length).to.equal(1); expect( derivedConfig.remoteRouters![otherChainMetadata.domainId!].address, - ).to.be.equal(warpRoute[otherChain].collateral.address); + ).to.be.equal(addressToBytes32(warpRoute[otherChain].collateral.address)); }); }); diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 66775184398..1d31f4c3e71 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -13,9 +13,10 @@ import { } from '@hyperlane-xyz/core'; import { Address, - bytes32ToAddress, + assert, eqAddress, getLogLevel, + isZeroishAddress, rootLogger, } from '@hyperlane-xyz/utils'; @@ -32,7 +33,7 @@ import { import { ChainNameOrId, DeployedOwnableConfig } from '../types.js'; import { HyperlaneReader } from '../utils/HyperlaneReader.js'; -import { proxyAdmin } from './../deploy/proxy.js'; +import { isProxy, proxyAdmin } from './../deploy/proxy.js'; import { NON_ZERO_SENDER_ADDRESS, TokenType } from './config.js'; import { DerivedTokenRouterConfig, @@ -75,7 +76,11 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { await this.fetchMailboxClientConfig(warpRouteAddress); const tokenConfig = await this.fetchTokenConfig(type, warpRouteAddress); const remoteRouters = await this.fetchRemoteRouters(warpRouteAddress); - const proxyAdmin = await this.fetchProxyAdminConfig(warpRouteAddress); + // if the token has not been deployed as a proxy do not derive the config + // inevm warp routes are an example + const proxyAdmin = (await isProxy(this.provider, warpRouteAddress)) + ? await this.fetchProxyAdminConfig(warpRouteAddress) + : undefined; const destinationGas = await this.fetchDestinationGas(warpRouteAddress); return { @@ -360,10 +365,7 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { const routers = Object.fromEntries( await Promise.all( domains.map(async (domain) => { - return [ - domain, - { address: bytes32ToAddress(await warpRoute.routers(domain)) }, - ]; + return [domain, { address: await warpRoute.routers(domain) }]; }), ), ); @@ -375,6 +377,11 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { tokenAddress: Address, ): Promise { const proxyAdminAddress = await proxyAdmin(this.provider, tokenAddress); + assert( + !isZeroishAddress(proxyAdminAddress), + `ProxyAdmin config for warp token at address "${tokenAddress}" can't be derived because it is not a proxy.`, + ); + const proxyAdminInstance = ProxyAdmin__factory.connect( proxyAdminAddress, this.provider, diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index fc4995ccf9a..bb2b8289722 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -2,11 +2,15 @@ import { zeroAddress } from 'viem'; import { Address, + ProtocolType, TransformObjectTransformer, + addressToBytes32, objMap, + promiseObjAll, transformObj, } from '@hyperlane-xyz/utils'; +import { isProxy } from '../deploy/proxy.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { DestinationGas, RemoteRouters } from '../router/types.js'; import { ChainMap } from '../types.js'; @@ -83,6 +87,19 @@ export async function expandWarpDeployConfig( multiProvider, warpDeployConfig, ); + + // If the token is on an EVM chain check if it is deployed as a proxy + // to expand the proxy config too + const isDeployedAsProxyByChain = await promiseObjAll( + objMap(deployedRoutersAddresses, async (chain, address) => { + if (!(multiProvider.getProtocol(chain) === ProtocolType.Ethereum)) { + return false; + } + + return isProxy(multiProvider.getProvider(chain), address); + }), + ); + return objMap(warpDeployConfig, (chain, config) => { const [remoteRouters, destinationGas] = getDefaultRemoteRouterAndDestinationGasConfig( @@ -92,18 +109,33 @@ export async function expandWarpDeployConfig( warpDeployConfig, ); - return { + const chainConfig: WarpRouteDeployConfigMailboxRequired[string] = { // Default Expansion ...derivedTokenMetadata, remoteRouters, destinationGas, hook: zeroAddress, interchainSecurityModule: zeroAddress, - proxyAdmin: { owner: config.owner }, + proxyAdmin: isDeployedAsProxyByChain[chain] + ? { owner: config.owner } + : undefined, // User-specified config takes precedence ...config, }; + + // Properly set the remote routers addresses to their 32 bytes representation + // as that is how they are set on chain + const formattedRemoteRouters = objMap( + chainConfig.remoteRouters ?? {}, + (_domainId, { address }) => ({ + address: addressToBytes32(address), + }), + ); + + chainConfig.remoteRouters = formattedRemoteRouters; + + return chainConfig; }); } From 03ac4d900703bc3721c42c5b4cd9b90bbc2c1ddd Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Mon, 5 May 2025 20:25:43 -0400 Subject: [PATCH 134/223] feat: Add typescript relayer support for CCIP-read ISM (#6141) ### Description Adds CCIP-read ISM support for the typescript relayer (will rename in a follow-up PR). Adds sdk test for it. ### Backward compatibility Yes ### Testing Unit Tests --- .../isms/ccip-read/TestCccipReadIsm.sol | 44 ++++++++ typescript/sdk/src/ism/EvmIsmReader.ts | 7 +- typescript/sdk/src/ism/metadata/builder.ts | 9 ++ .../src/ism/metadata/ccipread.hardhat-test.ts | 103 ++++++++++++++++++ typescript/sdk/src/ism/metadata/ccipread.ts | 76 +++++++++++++ typescript/sdk/src/ism/types.ts | 11 ++ 6 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol create mode 100644 typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts create mode 100644 typescript/sdk/src/ism/metadata/ccipread.ts diff --git a/solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol b/solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol new file mode 100644 index 00000000000..0d70177ec3d --- /dev/null +++ b/solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +error OffchainLookup( + address sender, + string[] urls, + bytes callData, + bytes4 callbackFunction, + bytes extraData +); + +import "../../interfaces/isms/ICcipReadIsm.sol"; +import "../../interfaces/IInterchainSecurityModule.sol"; +import "../../interfaces/IMailbox.sol"; +import "../../libs/Message.sol"; +import "./AbstractCcipReadIsm.sol"; + +/** + * @title TestCcipReadIsm + * @notice A test CCIP-Read ISM that simply checks the passed metadata as a boolean. + */ +contract TestCcipReadIsm is AbstractCcipReadIsm { + string[] public urls; + + constructor(string[] memory _urls) { + urls = _urls; + } + + function getOffchainVerifyInfo( + bytes calldata _message + ) external view override { + // Revert with OffchainLookup to instruct off-chain resolution + revert OffchainLookup(address(this), urls, _message, bytes4(0), ""); + } + + function verify( + bytes calldata metadata, + bytes calldata + ) external view override returns (bool) { + bool ok = abi.decode(metadata, (bool)); + require(ok, "TestCcipReadIsm: invalid metadata"); + return true; + } +} diff --git a/typescript/sdk/src/ism/EvmIsmReader.ts b/typescript/sdk/src/ism/EvmIsmReader.ts index 0b182fe4ac2..8568fbaf39e 100644 --- a/typescript/sdk/src/ism/EvmIsmReader.ts +++ b/typescript/sdk/src/ism/EvmIsmReader.ts @@ -34,6 +34,7 @@ import { HyperlaneReader } from '../utils/HyperlaneReader.js'; import { AggregationIsmConfig, ArbL2ToL1IsmConfig, + CCIPReadIsmConfig, DerivedIsmConfig, DomainRoutingIsmConfig, IsmType, @@ -118,7 +119,11 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { derivedIsmConfig = await this.deriveNullConfig(address); break; case ModuleType.CCIP_READ: - throw new Error('CCIP_READ does not have a corresponding IsmType'); + // CCIP-Read ISM: metadata fetched off-chain + return { + address, + type: IsmType.CCIP_READ, + } as WithAddress; case ModuleType.ARB_L2_TO_L1: return this.deriveArbL2ToL1Config(address); default: diff --git a/typescript/sdk/src/ism/metadata/builder.ts b/typescript/sdk/src/ism/metadata/builder.ts index f92f16cded5..0689bd45aa0 100644 --- a/typescript/sdk/src/ism/metadata/builder.ts +++ b/typescript/sdk/src/ism/metadata/builder.ts @@ -16,6 +16,7 @@ import { IsmType } from '../types.js'; import { AggregationMetadataBuilder } from './aggregation.js'; import { ArbL2ToL1MetadataBuilder } from './arbL2ToL1.js'; +import { CcipReadMetadataBuilder } from './ccipread.js'; import { decodeIsmMetadata } from './decode.js'; import { MultisigMetadataBuilder } from './multisig.js'; import { NullMetadataBuilder } from './null.js'; @@ -32,6 +33,7 @@ export class BaseMetadataBuilder implements MetadataBuilder { public aggregationMetadataBuilder: AggregationMetadataBuilder; public routingMetadataBuilder: DefaultFallbackRoutingMetadataBuilder; public arbL2ToL1MetadataBuilder: ArbL2ToL1MetadataBuilder; + public ccipReadMetadataBuilder: CcipReadMetadataBuilder; public multiProvider: MultiProvider; protected logger = rootLogger.child({ module: 'BaseMetadataBuilder' }); @@ -44,6 +46,7 @@ export class BaseMetadataBuilder implements MetadataBuilder { ); this.nullMetadataBuilder = new NullMetadataBuilder(core.multiProvider); this.arbL2ToL1MetadataBuilder = new ArbL2ToL1MetadataBuilder(core); + this.ccipReadMetadataBuilder = new CcipReadMetadataBuilder(core); this.multiProvider = core.multiProvider; } @@ -108,6 +111,12 @@ export class BaseMetadataBuilder implements MetadataBuilder { }); } + case IsmType.CCIP_READ: + return this.ccipReadMetadataBuilder.build({ + ...context, + ism, + }); + default: throw new Error(`Unsupported ISM: ${ism}`); } diff --git a/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts b/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts new file mode 100644 index 00000000000..93c32f290d5 --- /dev/null +++ b/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts @@ -0,0 +1,103 @@ +import { expect } from 'chai'; +import hre from 'hardhat'; +import sinon from 'sinon'; + +import { TestCcipReadIsm__factory } from '@hyperlane-xyz/core'; +import { WithAddress } from '@hyperlane-xyz/utils'; + +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 { MultiProvider } from '../../providers/MultiProvider.js'; +import { EvmIsmReader } from '../EvmIsmReader.js'; +import { HyperlaneIsmFactory } from '../HyperlaneIsmFactory.js'; +import { CCIPReadIsmConfig } from '../types.js'; + +import { BaseMetadataBuilder } from './builder.js'; +import type { MetadataContext } from './types.js'; + +describe('CCIP-Read ISM Integration', () => { + let core: HyperlaneCore; + let multiProvider: MultiProvider; + let testRecipient: any; + let ccipReadIsm: any; + let metadataBuilder: BaseMetadataBuilder; + let ismFactory: HyperlaneIsmFactory; + let fetchStub: sinon.SinonStub; + + before(async () => { + // Set up a local test multi-provider and Hyperlane core + const signers = await hre.ethers.getSigners(); + multiProvider = MultiProvider.createTestMultiProvider({ + signer: signers[0], + }); + const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); + const contractsMap = await ismFactoryDeployer.deploy( + multiProvider.mapKnownChains(() => ({})), + ); + ismFactory = new HyperlaneIsmFactory(contractsMap, multiProvider); + core = await new TestCoreDeployer(multiProvider, ismFactory).deployApp(); + + // Deploy a TestRecipient on test1 + const deployments = await new TestRecipientDeployer(multiProvider).deploy({ + test2: {}, + }); + testRecipient = (deployments.test2 as any).testRecipient; + + // Deploy the TestCcipReadIsm on test1 domain + const domain = multiProvider.getDomainId('test1'); + ccipReadIsm = await multiProvider.handleDeploy( + domain, + new TestCcipReadIsm__factory(), + // Pass in desired offchain URLs for the ISM constructor: + [['http://example.com/{data}']], + ); + + // Configure the TestRecipient to use the CCIP-Read ISM + await testRecipient.setInterchainSecurityModule(ccipReadIsm.address); + + // Prepare the metadata builder + metadataBuilder = new BaseMetadataBuilder(core); + + fetchStub = sinon.stub(global, 'fetch').resolves({ + ok: true, + json: async () => ({ + data: '0x0000000000000000000000000000000000000000000000000000000000000001', + }), + } as Response); + }); + + it('should process a message protected by CCIP-Read ISM', async () => { + // Send a message from test1 to test2 + const { dispatchTx, message } = await core.sendMessage( + 'test1', + 'test2', + testRecipient.address, + '0x1234', + ); + + // Derive the on-chain ISM config for CCIP-Read + const derivedIsm = (await new EvmIsmReader( + multiProvider, + 'test2', + ).deriveIsmConfig(ccipReadIsm.address)) as WithAddress; + + // Build the metadata using the CCIP-Read builder + const context: MetadataContext> = { + ism: derivedIsm, + message, + dispatchTx, + hook: {} as any, + }; + const metadata = await metadataBuilder.build(context); + + // Finally, call mailbox.process on test2 with the metadata and message + const mailbox = core.getContracts('test2').mailbox; + await expect(mailbox.process(metadata, message.message)).to.not.be.reverted; + }); + + after(() => { + fetchStub.restore(); + }); +}); diff --git a/typescript/sdk/src/ism/metadata/ccipread.ts b/typescript/sdk/src/ism/metadata/ccipread.ts new file mode 100644 index 00000000000..cd61a21bb0c --- /dev/null +++ b/typescript/sdk/src/ism/metadata/ccipread.ts @@ -0,0 +1,76 @@ +import { utils } from 'ethers'; + +import { ICcipReadIsm__factory } from '@hyperlane-xyz/core'; +import { WithAddress } from '@hyperlane-xyz/utils'; + +import { HyperlaneCore } from '../../core/HyperlaneCore.js'; +import { CCIPReadIsmConfig, IsmType } from '../types.js'; + +import type { MetadataBuilder, MetadataContext } from './types.js'; + +export class CcipReadMetadataBuilder implements MetadataBuilder { + readonly type = IsmType.CCIP; + private core: HyperlaneCore; + + constructor(core: HyperlaneCore) { + this.core = core; + } + + async build( + context: MetadataContext>, + ): Promise { + const { ism, message } = context; + const provider = this.core.multiProvider.getProvider(message.parsed.origin); + const contract = ICcipReadIsm__factory.connect(ism.address, provider); + + let revertData: string; + try { + // Should revert with OffchainLookup + await contract.getOffchainVerifyInfo(message.message); + throw new Error('Expected OffchainLookup revert'); + } catch (err: any) { + revertData = err.error?.data || err.data; + if (!revertData) throw err; + } + + const parsed = contract.interface.parseError(revertData); + if (parsed.name !== 'OffchainLookup') { + throw new Error(`Unexpected error ${parsed.name}`); + } + const [sender, urls, callData] = parsed.args as [ + string, + string[], + Uint8Array, + ]; + const callDataHex = utils.hexlify(callData); + + for (const urlTemplate of urls) { + const url = urlTemplate + .replace('{sender}', sender) + .replace('{data}', callDataHex); + try { + let responseJson: any; + if (urlTemplate.includes('{data}')) { + const res = await fetch(url); + responseJson = await res.json(); + } else { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sender, data: callDataHex }), + }); + responseJson = await res.json(); + } + const rawHex = responseJson.data as string; + return rawHex.startsWith('0x') ? rawHex : `0x${rawHex}`; + } catch (error: any) { + this.core.logger.warn( + `CCIP-read metadata fetch failed for ${url}: ${error}`, + ); + // try next URL + } + } + + throw new Error('Could not fetch CCIP-read metadata'); + } +} diff --git a/typescript/sdk/src/ism/types.ts b/typescript/sdk/src/ism/types.ts index cf40129c9f9..6e44ced0718 100644 --- a/typescript/sdk/src/ism/types.ts +++ b/typescript/sdk/src/ism/types.ts @@ -4,6 +4,7 @@ import { ArbL2ToL1Ism, CCIPIsm, IAggregationIsm, + ICcipReadIsm, IInterchainSecurityModule, IMultisigIsm, IRoutingIsm, @@ -66,6 +67,7 @@ export enum IsmType { WEIGHTED_MERKLE_ROOT_MULTISIG = 'weightedMerkleRootMultisigIsm', WEIGHTED_MESSAGE_ID_MULTISIG = 'weightedMessageIdMultisigIsm', CCIP = 'ccipIsm', + CCIP_READ = 'ccipReadIsm', } // ISM types that can be updated in-place @@ -118,6 +120,8 @@ export function ismTypeToModuleType(ismType: IsmType): ModuleType { return ModuleType.WEIGHTED_MERKLE_ROOT_MULTISIG; case IsmType.WEIGHTED_MESSAGE_ID_MULTISIG: return ModuleType.WEIGHTED_MESSAGE_ID_MULTISIG; + case IsmType.CCIP_READ: + return ModuleType.CCIP_READ; } } @@ -143,6 +147,7 @@ export type TrustedRelayerIsmConfig = z.infer< >; export type CCIPIsmConfig = z.infer; export type ArbL2ToL1IsmConfig = z.infer; +export type CCIPReadIsmConfig = z.infer; export type NullIsmConfig = | TestIsmConfig @@ -210,6 +215,7 @@ export type DeployedIsmType = { [IsmType.ARB_L2_TO_L1]: ArbL2ToL1Ism; [IsmType.WEIGHTED_MERKLE_ROOT_MULTISIG]: IStaticWeightedMultisigIsm; [IsmType.WEIGHTED_MESSAGE_ID_MULTISIG]: IStaticWeightedMultisigIsm; + [IsmType.CCIP_READ]: ICcipReadIsm; }; export type DeployedIsm = ValueOf; @@ -262,6 +268,10 @@ export const ArbL2ToL1IsmConfigSchema = z.object({ bridge: z.string(), }); +export const CCIPReadIsmConfigSchema = z.object({ + type: z.literal(IsmType.CCIP_READ), +}); + export const PausableIsmConfigSchema = PausableSchema.and( z.object({ type: z.literal(IsmType.PAUSABLE), @@ -335,4 +345,5 @@ export const IsmConfigSchema = z.union([ RoutingIsmConfigSchema, AggregationIsmConfigSchema, ArbL2ToL1IsmConfigSchema, + CCIPReadIsmConfigSchema, ]); From 76f0eba2e597ee977e26ce5d5e53d2459d4c4283 Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Tue, 6 May 2025 11:24:04 +0200 Subject: [PATCH 135/223] chore: implemented cosmos native ism module (#6113) ### Description This PR implements the ISM module for Cosmos Native. Following PRs will use this to support the goal of having full cosmos native support in the hyperlane CLI. Note that this is the first PR of https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6050 which is being split up in order to make it easier to review ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual testing with local cosmos chain --- .changeset/chilly-lizards-watch.md | 6 + .../hyperlane/interchain_security/query.ts | 36 ++-- .../tests/1_interchain_security.e2e-test.ts | 30 ++-- typescript/sdk/src/index.ts | 2 + .../sdk/src/ism/CosmosNativeIsmModule.ts | 160 ++++++++++++++++++ .../sdk/src/ism/CosmosNativeIsmReader.ts | 89 ++++++++++ typescript/sdk/src/providers/ProviderType.ts | 2 + 7 files changed, 292 insertions(+), 33 deletions(-) create mode 100644 .changeset/chilly-lizards-watch.md create mode 100644 typescript/sdk/src/ism/CosmosNativeIsmModule.ts create mode 100644 typescript/sdk/src/ism/CosmosNativeIsmReader.ts diff --git a/.changeset/chilly-lizards-watch.md b/.changeset/chilly-lizards-watch.md new file mode 100644 index 00000000000..b3feec545a9 --- /dev/null +++ b/.changeset/chilly-lizards-watch.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cosmos-sdk': minor +'@hyperlane-xyz/sdk': minor +--- + +Add Cosmos Native ISM Reader & Module diff --git a/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts b/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts index 8f472a390ec..83a30b6e836 100644 --- a/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts +++ b/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts @@ -7,22 +7,28 @@ type ISM = | isTypes.MerkleRootMultisigISM | isTypes.MessageIdMultisigISM; -type QueryDecodedIsmResponse = { - ism: ISM; +type QueryDecodedIsmResponse = { + ism: T; }; -type QueryDecodedIsmsResponse = { - isms: ISM[]; +type QueryDecodedIsmsResponse = { + isms: T[]; pagination: pagination.PageResponse | undefined; }; +export enum IsmTypes { + NoopISM = '/hyperlane.core.interchain_security.v1.NoopISM', + MerkleRootMultisigISM = '/hyperlane.core.interchain_security.v1.MerkleRootMultisigISM', + MessageIdMultisigISM = '/hyperlane.core.interchain_security.v1.MessageIdMultisigISM', +} + export const decodeIsm = (ism: any.Any | undefined): ISM => { switch (ism?.type_url) { - case '/hyperlane.core.interchain_security.v1.NoopISM': + case IsmTypes.NoopISM: return isTypes.NoopISM.decode(ism.value); - case '/hyperlane.core.interchain_security.v1.MerkleRootMultisigISM': + case IsmTypes.MerkleRootMultisigISM: return isTypes.MerkleRootMultisigISM.decode(ism.value); - case '/hyperlane.core.interchain_security.v1.MessageIdMultisigISM': + case IsmTypes.MessageIdMultisigISM: return isTypes.MessageIdMultisigISM.decode(ism.value); default: throw new Error(`can not decode ISM with type url ${ism?.type_url}`); @@ -48,13 +54,13 @@ export interface InterchainSecurityExtension { req: isQuery.QueryIsmRequest, ) => Promise; /** DecodedIsms ... */ - readonly DecodedIsms: ( + readonly DecodedIsms: ( req: isQuery.QueryIsmsRequest, - ) => Promise; + ) => Promise>; /** DecodedIsm ... */ - readonly DecodedIsm: ( + readonly DecodedIsm: ( req: isQuery.QueryIsmRequest, - ) => Promise; + ) => Promise>; }; } @@ -76,16 +82,16 @@ export function setupInterchainSecurityExtension( ) => queryService.LatestAnnouncedStorageLocation(req), Isms: async (req: isQuery.QueryIsmsRequest) => queryService.Isms(req), Ism: async (req: isQuery.QueryIsmRequest) => queryService.Ism(req), - DecodedIsms: async (req: isQuery.QueryIsmsRequest) => { + DecodedIsms: async (req: isQuery.QueryIsmsRequest) => { const { isms, pagination } = await queryService.Isms(req); return { - isms: isms.map((ism) => decodeIsm(ism)), + isms: isms.map((ism) => decodeIsm(ism) as T), pagination, }; }, - DecodedIsm: async (req: isQuery.QueryIsmRequest) => { + DecodedIsm: async (req: isQuery.QueryIsmRequest) => { const { ism } = await queryService.Ism(req); - return { ism: decodeIsm(ism) }; + return { ism: decodeIsm(ism) as T }; }, }, }; diff --git a/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts b/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts index 576486b57c8..85a9d347cb3 100644 --- a/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts +++ b/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts @@ -94,19 +94,16 @@ describe('1. cosmos sdk interchain security e2e tests', async function () { '/hyperlane.core.interchain_security.v1.MessageIdMultisigISM', ); - let decodedIsm = await signer.query.interchainSecurity.DecodedIsm({ - id: messageIdIsm.id, - }); + let decodedIsm = + await signer.query.interchainSecurity.DecodedIsm({ + id: messageIdIsm.id, + }); expect(decodedIsm.ism.id).to.equal(messageIdIsm.id); expect(decodedIsm.ism.owner).to.equal(signer.account.address); - expect((decodedIsm.ism as MessageIdMultisigISM).threshold).to.equal( - threshold, - ); - expect((decodedIsm.ism as MessageIdMultisigISM).validators).deep.equal( - validators, - ); + expect(decodedIsm.ism.threshold).to.equal(threshold); + expect(decodedIsm.ism.validators).deep.equal(validators); }); step('create new MerkleRootMultisig ISM', async () => { @@ -149,18 +146,15 @@ describe('1. cosmos sdk interchain security e2e tests', async function () { '/hyperlane.core.interchain_security.v1.MerkleRootMultisigISM', ); - let decodedIsm = await signer.query.interchainSecurity.DecodedIsm({ - id: merkleRootIsm.id, - }); + let decodedIsm = + await signer.query.interchainSecurity.DecodedIsm({ + id: merkleRootIsm.id, + }); expect(decodedIsm.ism.id).to.equal(merkleRootIsm.id); expect(decodedIsm.ism.owner).to.equal(signer.account.address); - expect((decodedIsm.ism as MerkleRootMultisigISM).threshold).to.equal( - threshold, - ); - expect((decodedIsm.ism as MerkleRootMultisigISM).validators).deep.equal( - validators, - ); + expect(decodedIsm.ism.threshold).to.equal(threshold); + expect(decodedIsm.ism.validators).deep.equal(validators); }); }); diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 0a2b1a6d400..d594efc3daf 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -180,6 +180,7 @@ export { ProtocolFeeSchema, } from './hook/types.js'; export { EvmIsmReader } from './ism/EvmIsmReader.js'; +export { CosmosNativeIsmReader } from './ism/CosmosNativeIsmReader.js'; export { HyperlaneIsmFactory } from './ism/HyperlaneIsmFactory.js'; export { BaseMetadataBuilder } from './ism/metadata/builder.js'; export { decodeIsmMetadata } from './ism/metadata/decode.js'; @@ -466,6 +467,7 @@ export { RemoteIcaRouterConfigSchema, } from './ica/types.js'; export { EvmIsmModule } from './ism/EvmIsmModule.js'; +export { CosmosNativeIsmModule } from './ism/CosmosNativeIsmModule.js'; export { chainMetadataToCosmosChain, chainMetadataToViemChain, diff --git a/typescript/sdk/src/ism/CosmosNativeIsmModule.ts b/typescript/sdk/src/ism/CosmosNativeIsmModule.ts new file mode 100644 index 00000000000..f2ddfa7505a --- /dev/null +++ b/typescript/sdk/src/ism/CosmosNativeIsmModule.ts @@ -0,0 +1,160 @@ +import { SigningHyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; +import { + Address, + ChainId, + Domain, + ProtocolType, + assert, + deepEquals, + rootLogger, +} from '@hyperlane-xyz/utils'; + +import { + HyperlaneModule, + HyperlaneModuleParams, +} from '../core/AbstractHyperlaneModule.js'; +import { MultiProvider } from '../providers/MultiProvider.js'; +import { AnnotatedCosmJsNativeTransaction } from '../providers/ProviderType.js'; +import { ChainName, ChainNameOrId } from '../types.js'; +import { normalizeConfig } from '../utils/ism.js'; + +import { CosmosNativeIsmReader } from './CosmosNativeIsmReader.js'; +import { IsmConfig, IsmConfigSchema, IsmType } from './types.js'; + +type IsmModuleAddresses = { + deployedIsm: Address; + mailbox: Address; +}; + +export class CosmosNativeIsmModule extends HyperlaneModule< + ProtocolType.CosmosNative, + IsmConfig, + IsmModuleAddresses +> { + protected readonly logger = rootLogger.child({ + module: 'CosmosNativeIsmModule', + }); + protected readonly reader: CosmosNativeIsmReader; + protected readonly mailbox: Address; + + // Adding these to reduce how often we need to grab from MultiProvider. + public readonly chain: ChainName; + public readonly chainId: ChainId; + public readonly domainId: Domain; + + constructor( + protected readonly multiProvider: MultiProvider, + params: HyperlaneModuleParams, + protected readonly signer: SigningHyperlaneModuleClient, + ) { + params.config = IsmConfigSchema.parse(params.config); + super(params); + + this.mailbox = params.addresses.mailbox; + this.chain = multiProvider.getChainName(this.args.chain); + this.chainId = multiProvider.getChainId(this.chain); + this.domainId = multiProvider.getDomainId(this.chain); + + this.reader = new CosmosNativeIsmReader(signer); + } + + public async read(): Promise { + return this.reader.deriveIsmConfig(this.args.addresses.deployedIsm); + } + + // whoever calls update() needs to ensure that targetConfig has a valid owner + public async update( + expectedConfig: IsmConfig, + ): Promise { + expectedConfig = IsmConfigSchema.parse(expectedConfig); + + // Do not support updating to a custom ISM address + if (typeof expectedConfig === 'string') { + throw new Error( + 'Invalid targetConfig: Updating to a custom ISM address is not supported. Please provide a valid ISM configuration.', + ); + } + + // save current config for comparison + // normalize the config to ensure it's in a consistent format for comparison + const actualConfig = normalizeConfig(await this.read()); + expectedConfig = normalizeConfig(expectedConfig); + + assert( + typeof expectedConfig === 'object', + 'normalized expectedConfig should be an object', + ); + + // If configs match, no updates needed + if (deepEquals(actualConfig, expectedConfig)) { + return []; + } + + this.args.addresses.deployedIsm = await this.deploy({ + config: expectedConfig, + }); + + return []; + } + + // manually write static create function + public static async create({ + chain, + config, + addresses, + multiProvider, + signer, + }: { + chain: ChainNameOrId; + config: IsmConfig; + addresses: IsmModuleAddresses; + multiProvider: MultiProvider; + signer: SigningHyperlaneModuleClient; + }): Promise { + const module = new CosmosNativeIsmModule( + multiProvider, + { + addresses, + chain, + config, + }, + signer, + ); + + module.args.addresses.deployedIsm = await module.deploy({ config }); + return module; + } + + protected async deploy({ config }: { config: IsmConfig }): Promise
{ + if (typeof config === 'string') { + return config; + } + const ismType = config.type; + this.logger.info(`Deploying ${ismType} to ${this.chain}`); + + switch (ismType) { + case IsmType.MERKLE_ROOT_MULTISIG: { + const { response: merkleRootResponse } = + await this.signer.createMerkleRootMultisigIsm({ + validators: config.validators, + threshold: config.threshold, + }); + return merkleRootResponse.id; + } + case IsmType.MESSAGE_ID_MULTISIG: { + const { response: messageIdResponse } = + await this.signer.createMessageIdMultisigIsm({ + validators: config.validators, + threshold: config.threshold, + }); + return messageIdResponse.id; + } + case IsmType.TEST_ISM: { + const { response: noopResponse } = await this.signer.createNoopIsm({}); + return noopResponse.id; + } + default: + throw new Error(`ISM type ${ismType} is not supported on Cosmos`); + } + } +} diff --git a/typescript/sdk/src/ism/CosmosNativeIsmReader.ts b/typescript/sdk/src/ism/CosmosNativeIsmReader.ts new file mode 100644 index 00000000000..6d01eeb72a2 --- /dev/null +++ b/typescript/sdk/src/ism/CosmosNativeIsmReader.ts @@ -0,0 +1,89 @@ +import { + HyperlaneModuleClient, + IsmTypes, + SigningHyperlaneModuleClient, +} from '@hyperlane-xyz/cosmos-sdk'; +import { isTypes } from '@hyperlane-xyz/cosmos-types'; +import { Address, WithAddress, assert, rootLogger } from '@hyperlane-xyz/utils'; + +import { DerivedIsmConfig, IsmType, MultisigIsmConfig } from './types.js'; + +export class CosmosNativeIsmReader { + protected readonly logger = rootLogger.child({ + module: 'CosmosNativeIsmReader', + }); + + constructor( + protected readonly cosmosProviderOrSigner: + | HyperlaneModuleClient + | SigningHyperlaneModuleClient, + ) {} + + async deriveIsmConfig(address: Address): Promise { + try { + const { ism } = + await this.cosmosProviderOrSigner.query.interchainSecurity.Ism({ + id: address, + }); + + assert(ism, `ISM with id ${address} not found`); + + switch (ism.type_url) { + case IsmTypes.MerkleRootMultisigISM: + return this.deriveMerkleRootMultisigConfig(address); + case IsmTypes.MessageIdMultisigISM: + return this.deriveMessageIdMultisigConfig(address); + case IsmTypes.NoopISM: + return this.deriveTestConfig(address); + default: + throw new Error(`Unknown ISM ModuleType: ${ism.type_url}`); + } + } catch (error) { + this.logger.error(`Failed to derive ISM config for ${address}`, error); + throw error; + } + } + + private async deriveMerkleRootMultisigConfig( + address: Address, + ): Promise> { + const { ism } = + await this.cosmosProviderOrSigner.query.interchainSecurity.DecodedIsm( + { + id: address, + }, + ); + + return { + type: IsmType.MERKLE_ROOT_MULTISIG, + address, + validators: ism.validators, + threshold: ism.threshold, + }; + } + + private async deriveMessageIdMultisigConfig( + address: Address, + ): Promise { + const { ism } = + await this.cosmosProviderOrSigner.query.interchainSecurity.DecodedIsm( + { + id: address, + }, + ); + + return { + type: IsmType.MESSAGE_ID_MULTISIG, + address, + validators: ism.validators, + threshold: ism.threshold, + }; + } + + private async deriveTestConfig(address: Address): Promise { + return { + type: IsmType.TEST_ISM, + address, + }; + } +} diff --git a/typescript/sdk/src/providers/ProviderType.ts b/typescript/sdk/src/providers/ProviderType.ts index 44b0c2dfb97..590d8a2e68f 100644 --- a/typescript/sdk/src/providers/ProviderType.ts +++ b/typescript/sdk/src/providers/ProviderType.ts @@ -257,6 +257,8 @@ export interface EthersV5Transaction export type AnnotatedEV5Transaction = Annotated; +export type AnnotatedCosmJsNativeTransaction = Annotated; + export interface ViemTransaction extends TypedTransactionBase { type: ProviderType.Viem; transaction: VTransaction; From 5cb05c495eb04e8f70d3b5a112e37fc1a57ae313 Mon Sep 17 00:00:00 2001 From: Keefer Taylor | Tessellated Date: Tue, 6 May 2025 04:04:14 -0700 Subject: [PATCH 136/223] chore: bump ethers-rs dependencies to 2025-05-02 tag (#6118) ### Description This is a bump of the `ethers-rs` deps to the latest tagged version. See: - https://github.com/hyperlane-xyz/ethers-rs/pull/40 for a description of these changes - https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/5625 for the PR that will utilize the updated dependencies ### Drive-by changes n/a ### Related issues n/a ### Backward compatibility Yes ### Testing Manual testing via using #5625 --- rust/main/Cargo.lock | 29 +++++++++---------- rust/main/Cargo.toml | 10 +++---- .../hyperlane-base/src/settings/signers.rs | 2 +- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index ea0ea2a23a0..5bea5890754 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -3555,7 +3555,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3569,7 +3569,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "ethers-core", "once_cell", @@ -3580,7 +3580,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -3598,7 +3598,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "Inflector", "cfg-if", @@ -3622,7 +3622,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -3636,18 +3636,18 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "arrayvec", "bytes", "cargo_metadata", "chrono", "convert_case 0.6.0", - "elliptic-curve 0.12.3", + "elliptic-curve 0.13.8", "ethabi", "generic-array 0.14.7", "hex 0.4.3", - "k256 0.11.6", + "k256 0.13.4", "once_cell", "open-fastrlp", "proc-macro2 1.0.93", @@ -3666,7 +3666,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3682,7 +3682,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3733,7 +3733,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3769,12 +3769,12 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-04-23#6ba6829655aec6aa949529cde70f133c6fbf9954" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" dependencies = [ "async-trait", "coins-bip32 0.7.0", "coins-bip39 0.7.0", - "elliptic-curve 0.12.3", + "elliptic-curve 0.13.8", "eth-keystore", "ethers-core", "hex 0.4.3", @@ -6058,7 +6058,6 @@ dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", "sha2 0.10.8", - "sha3 0.10.8", ] [[package]] diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 951d2725c94..28f33c318ae 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -219,27 +219,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-23" +tag = "2025-05-02" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-23" +tag = "2025-05-02" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-23" +tag = "2025-05-02" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-23" +tag = "2025-05-02" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-04-23" +tag = "2025-05-02" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" diff --git a/rust/main/hyperlane-base/src/settings/signers.rs b/rust/main/hyperlane-base/src/settings/signers.rs index 6ea6ed4a1e2..3f094e6b548 100644 --- a/rust/main/hyperlane-base/src/settings/signers.rs +++ b/rust/main/hyperlane-base/src/settings/signers.rs @@ -70,7 +70,7 @@ impl BuildableWithSignerConf for hyperlane_ethereum::Signers { Ok(match conf { SignerConf::HexKey { key } => hyperlane_ethereum::Signers::Local(LocalWallet::from( ethers::core::k256::ecdsa::SigningKey::from( - ethers::core::k256::SecretKey::from_be_bytes(key.as_bytes()) + ethers::core::k256::SecretKey::from_slice(key.as_bytes()) .context("Invalid ethereum signer key")?, ), )), From 41065ea6d07a0eba6ad297180f39bbe07797298a Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 6 May 2025 12:06:23 +0100 Subject: [PATCH 137/223] feat(submitter): tx status checker, evm provider in dispatcher (#6139) ### Description - adds EVM provider builder for the submitter's EVM adapter - creates the tx status checker by querying the tx's receipt, and checking the receipt's block along with the configured `reorgPeriod` to decide if the tx is `PendingInclusion`, `Included`, or `Finalized` ### Drive-by changes - the PayloadDispatcher constructor had to be made async to build evm providers, to any call sites also had to make async calls now - the `build_ethereum` fn is made public so that the `EthereumTxAdapter` can call it to create a provider ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6129 ### Backward compatibility Yes ### Testing No testing yet. Will rely on E2E and, once we set up the mock provider, unit tests --- rust/main/Cargo.lock | 2 + rust/main/agents/relayer/src/relayer.rs | 45 +++++++--- .../hyperlane-ethereum/src/contracts/utils.rs | 7 +- .../src/rpc_clients/provider.rs | 68 ++++++++++++++- .../hyperlane-base/src/settings/chains.rs | 3 +- rust/main/submitter/Cargo.toml | 2 + .../submitter/src/chain_tx_adapter/adapter.rs | 26 +++++- .../src/chain_tx_adapter/chains/cosmos.rs | 11 ++- .../src/chain_tx_adapter/chains/ethereum.rs | 84 +++++++++++++++++-- .../src/chain_tx_adapter/chains/factory.rs | 24 ++++-- .../chains/sealevel/adapter.rs | 11 ++- rust/main/submitter/src/error.rs | 4 + .../src/payload_dispatcher/dispatcher.rs | 4 +- .../src/payload_dispatcher/entrypoint.rs | 4 +- .../src/payload_dispatcher/stages/state.rs | 5 +- .../src/payload_dispatcher/test_utils.rs | 1 + 16 files changed, 252 insertions(+), 49 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 5bea5890754..e003085fd0e 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -10621,10 +10621,12 @@ dependencies = [ "async-trait", "chrono", "derive-new", + "ethers", "eyre", "futures-util", "hyperlane-base", "hyperlane-core", + "hyperlane-ethereum", "hyperlane-sealevel", "mockall", "prometheus", diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index e8a69eb74f5..a26227568d6 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -908,16 +908,32 @@ impl Relayer { dispatcher_metrics: DispatcherMetrics, db: DB, ) -> HashMap { - settings.destination_chains.iter() + let entrypoint_futures: Vec<_> = settings + .destination_chains + .iter() .filter(|chain| SubmitterType::Lander == settings.chains[&chain.to_string()].submitter) - .map(|chain| (chain.clone(), PayloadDispatcherSettings { - chain_conf: settings.chains[&chain.to_string()].clone(), - raw_chain_conf: Default::default(), - domain: chain.clone(), - db: DatabaseOrPath::Database(db.clone()), - metrics: core_metrics.clone(), - })) - .map(|(chain, s)| (chain, PayloadDispatcherEntrypoint::try_from_settings(s, dispatcher_metrics.clone()))) + .map(|chain| { + ( + chain.clone(), + PayloadDispatcherSettings { + chain_conf: settings.chains[&chain.to_string()].clone(), + raw_chain_conf: Default::default(), + domain: chain.clone(), + db: DatabaseOrPath::Database(db.clone()), + metrics: core_metrics.clone(), + }, + ) + }) + .map(|(chain, s)| async { + ( + chain, + PayloadDispatcherEntrypoint::try_from_settings(s, dispatcher_metrics.clone()) + .await, + ) + }) + .collect(); + let results = futures::future::join_all(entrypoint_futures).await; + results.into_iter() .filter_map(|(chain, result)| match result { Ok(entrypoint) => Some((chain, entrypoint)), Err(err) => { @@ -940,7 +956,7 @@ impl Relayer { dispatcher_metrics: DispatcherMetrics, db: DB, ) -> HashMap { - settings + let dispatcher_futures: Vec<_> = settings .destination_chains .iter() .filter(|chain| SubmitterType::Lander == settings.chains[&chain.to_string()].submitter) @@ -956,13 +972,18 @@ impl Relayer { }, ) }) - .map(|(chain, s)| { + .map(|(chain, s)| async { let chain_name = chain.to_string(); ( chain, - PayloadDispatcher::try_from_settings(s, chain_name, dispatcher_metrics.clone()), + PayloadDispatcher::try_from_settings(s, chain_name, dispatcher_metrics.clone()) + .await, ) }) + .collect(); + let results = futures::future::join_all(dispatcher_futures).await; + results + .into_iter() .filter_map(|(chain, result)| match result { Ok(entrypoint) => Some((chain, entrypoint)), Err(err) => { diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs index 5e9c782013f..c0411bec6be 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{ops::Deref, sync::Arc}; use ethers::{ abi::RawLog, @@ -49,12 +49,13 @@ where } #[instrument(level = "trace", err, ret, skip(provider))] -pub async fn get_finalized_block_number( - provider: &M, +pub async fn get_finalized_block_number( + provider: T, reorg_period: &EthereumReorgPeriod, ) -> ChainResult where M: Middleware + 'static, + T: Deref, { let number = match *reorg_period { EthereumReorgPeriod::Blocks(blocks) => provider diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index 44a9f99e731..fcce2c4777a 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -5,7 +5,7 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; -use ethers::prelude::Middleware; +use ethers::{prelude::Middleware, types::TransactionReceipt}; use ethers_core::{abi::Address, types::BlockNumber}; use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, H512, U256}; use tokio::time::sleep; @@ -16,7 +16,9 @@ use hyperlane_core::{ HyperlaneDomain, HyperlaneProvider, HyperlaneProviderError, TxnInfo, TxnReceiptInfo, H256, }; -use crate::{BuildableWithProvider, ConnectionConf}; +use crate::{ + get_finalized_block_number, BuildableWithProvider, ConnectionConf, EthereumReorgPeriod, +}; /// Connection to an ethereum provider. Useful for querying information about /// the blockchain. @@ -42,6 +44,47 @@ where } } +/// Methods of provider which are used in submitter +#[async_trait] +pub trait EvmProviderForSubmitter: Send + Sync { + /// Get the transaction receipt for a given transaction hash + async fn get_transaction_receipt( + &self, + transaction_hash: H256, + ) -> ChainResult>; + + /// Get the finalized block number + async fn get_finalized_block_number( + &self, + reorg_period: &EthereumReorgPeriod, + ) -> ChainResult; +} + +#[async_trait] +impl EvmProviderForSubmitter for EthereumProvider +where + M: Middleware + 'static, +{ + async fn get_transaction_receipt( + &self, + transaction_hash: H256, + ) -> ChainResult> { + let receipt = self + .provider + .get_transaction_receipt(transaction_hash) + .await + .map_err(ChainCommunicationError::from_other)?; + Ok(receipt) + } + + async fn get_finalized_block_number( + &self, + reorg_period: &EthereumReorgPeriod, + ) -> ChainResult { + get_finalized_block_number(&*self.provider, reorg_period).await + } +} + #[async_trait] impl HyperlaneProvider for EthereumProvider where @@ -195,6 +238,27 @@ where } } +/// Builder for hyperlane providers. +pub struct SubmitterProviderBuilder {} + +#[async_trait] +impl BuildableWithProvider for SubmitterProviderBuilder { + type Output = Box; + const NEEDS_SIGNER: bool = true; + + async fn build_with_provider( + &self, + provider: M, + _conn: &ConnectionConf, + locator: &ContractLocator, + ) -> Self::Output { + Box::new(EthereumProvider::new( + Arc::new(provider), + locator.domain.clone(), + )) + } +} + /// Builder for hyperlane providers. pub struct HyperlaneProviderBuilder {} diff --git a/rust/main/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs index 50594b88e34..a2d0d1bbbcc 100644 --- a/rust/main/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -1080,7 +1080,8 @@ impl ChainConf { } } - async fn build_ethereum( + /// Try to convert the chain settings into a provider + pub async fn build_ethereum( &self, conf: &h_eth::ConnectionConf, locator: &ContractLocator<'_>, diff --git a/rust/main/submitter/Cargo.toml b/rust/main/submitter/Cargo.toml index 85d7bc83802..f9b45c9be4c 100644 --- a/rust/main/submitter/Cargo.toml +++ b/rust/main/submitter/Cargo.toml @@ -10,12 +10,14 @@ version.workspace = true [dependencies] hyperlane-base = { path = "../hyperlane-base", features = ["test-utils"] } hyperlane-core = { path = "../hyperlane-core" } +hyperlane-ethereum = { path = "../chains/hyperlane-ethereum" } hyperlane-sealevel = { path = "../chains/hyperlane-sealevel" } async-trait.workspace = true chrono.workspace = true derive-new.workspace = true eyre.workspace = true +ethers.workspace = true futures-util.workspace = true prometheus.workspace = true serde.workspace = true diff --git a/rust/main/submitter/src/chain_tx_adapter/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/adapter.rs index 0b17643957f..b1d99b02864 100644 --- a/rust/main/submitter/src/chain_tx_adapter/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/adapter.rs @@ -6,10 +6,12 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; use eyre::Report; +use futures_util::future::join_all; use tokio::sync::mpsc::error::SendError; +use tracing::{info, instrument}; use uuid::Uuid; -use hyperlane_core::U256; +use hyperlane_core::{H256, H512, U256}; use crate::{ error::SubmitterError, @@ -50,8 +52,28 @@ pub trait AdaptsChain: Send + Sync { /// Sets / escalates gas price, sets nonce / blockhash and broadcasts the Transaction. Even if broadcasting fails, the Transaction struct remains mutated with the new estimates. Called in the Inclusion Stage (PayloadDispatcher) async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; + async fn get_tx_hash_status(&self, hash: H512) -> Result; + /// Queries the chain by txhash to get the tx status. Called in the Inclusion Stage and Finality Stage of the PayloadDispatcher - async fn tx_status(&self, tx: &Transaction) -> Result; + #[instrument(skip(self))] + async fn tx_status(&self, tx: &Transaction) -> Result { + info!(?tx, "checking status of transaction"); + + if tx.tx_hashes.is_empty() { + return Ok(TransactionStatus::PendingInclusion); + } + + let hash_status_futures = tx + .tx_hashes + .iter() + .map(|tx_hash| self.get_tx_hash_status(*tx_hash)) + .collect::>(); + // this may lead to rate limiting if too many hashes build up. Consider querying from most recent to oldest + let hash_status_results = join_all(hash_status_futures).await; + Ok(TransactionStatus::classify_tx_status_from_hash_statuses( + hash_status_results, + )) + } /// Return true if the transaction can be resubmitted (such as by escalating the gas price). Called in the Inclusion Stage (PayloadDispatcher). /// Defaults to true, since most chains don't have special rules for tx resubmission. diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs b/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs index a1988b559af..676f587ed8e 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs @@ -50,10 +50,6 @@ impl AdaptsChain for CosmosTxAdapter { todo!() } - async fn tx_status(&self, _tx: &Transaction) -> Result { - todo!() - } - async fn reverted_payloads( &self, _tx: &Transaction, @@ -68,4 +64,11 @@ impl AdaptsChain for CosmosTxAdapter { fn max_batch_size(&self) -> u32 { todo!() } + + async fn get_tx_hash_status( + &self, + _hash: hyperlane_core::H512, + ) -> std::result::Result { + todo!() + } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs index a676fb1b557..3a6ef68e45e 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs @@ -1,8 +1,17 @@ use async_trait::async_trait; +use ethers::{providers::Middleware, types::U64}; use eyre::Result; +use hyperlane_core::{ContractLocator, H256}; +use hyperlane_ethereum::{ + ConnectionConf, EthereumReorgPeriod, EvmProviderForSubmitter, SubmitterProviderBuilder, +}; +use tracing::warn; use uuid::Uuid; -use hyperlane_base::settings::{ChainConf, RawChainConf}; +use hyperlane_base::{ + settings::{ChainConf, RawChainConf}, + CoreMetrics, +}; use crate::{ chain_tx_adapter::{adapter::TxBuildingResult, AdaptsChain, GasLimit}, @@ -14,13 +23,63 @@ use crate::{ pub struct EthereumTxAdapter { _conf: ChainConf, _raw_conf: RawChainConf, + provider: Box, + reorg_period: EthereumReorgPeriod, } impl EthereumTxAdapter { - pub fn new(conf: ChainConf, raw_conf: RawChainConf) -> Self { - Self { + pub async fn new( + conf: ChainConf, + connection_conf: ConnectionConf, + raw_conf: RawChainConf, + metrics: &CoreMetrics, + ) -> eyre::Result { + let locator = ContractLocator { + domain: &conf.domain, + address: H256::zero(), + }; + let provider = conf + .build_ethereum( + &connection_conf, + &locator, + metrics, + SubmitterProviderBuilder {}, + ) + .await?; + let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; + + Ok(Self { _conf: conf, _raw_conf: raw_conf, + provider, + reorg_period, + }) + } + + async fn block_number_result_to_tx_hash(&self, block_number: Option) -> TransactionStatus { + let Some(block_number) = block_number else { + return TransactionStatus::PendingInclusion; + }; + let block_number = block_number.as_u64(); + match self + .provider + .get_finalized_block_number(&self.reorg_period) + .await + { + Ok(finalized_block) => { + if finalized_block as u64 >= block_number { + TransactionStatus::Finalized + } else { + TransactionStatus::Included + } + } + Err(err) => { + warn!( + ?err, + "Error checking block finality. Assuming tx is pending inclusion" + ); + TransactionStatus::PendingInclusion + } } } } @@ -50,10 +109,6 @@ impl AdaptsChain for EthereumTxAdapter { todo!() } - async fn tx_status(&self, _tx: &Transaction) -> Result { - todo!() - } - async fn reverted_payloads( &self, _tx: &Transaction, @@ -68,4 +123,19 @@ impl AdaptsChain for EthereumTxAdapter { fn max_batch_size(&self) -> u32 { todo!() } + + async fn get_tx_hash_status( + &self, + hash: hyperlane_core::H512, + ) -> Result { + match self.provider.get_transaction_receipt(hash.into()).await { + Ok(None) => Err(SubmitterError::TxHashNotFound( + "Transaction not found".to_string(), + )), + Ok(Some(receipt)) => Ok(self + .block_number_result_to_tx_hash(receipt.block_number) + .await), + Err(err) => Err(SubmitterError::TxHashNotFound(err.to_string())), + } + } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs b/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs index 54d9ea34be9..307f4978e68 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs @@ -5,10 +5,11 @@ use std::sync::Arc; use eyre::Result; use hyperlane_base::{ - settings::{ChainConf, RawChainConf}, + settings::{ChainConf, ChainConnectionConf, RawChainConf}, CoreMetrics, }; -use hyperlane_core::{HyperlaneDomain, HyperlaneDomainProtocol}; +use hyperlane_core::{ContractLocator, HyperlaneDomain, HyperlaneDomainProtocol, H256}; +use hyperlane_ethereum::{EvmProviderForSubmitter, SubmitterProviderBuilder}; use crate::chain_tx_adapter::{ chains::{cosmos::CosmosTxAdapter, ethereum::EthereumTxAdapter, sealevel::SealevelTxAdapter}, @@ -18,23 +19,28 @@ use crate::chain_tx_adapter::{ pub struct ChainTxAdapterFactory {} impl ChainTxAdapterFactory { - pub fn build( + pub async fn build( conf: &ChainConf, raw_conf: &RawChainConf, metrics: &CoreMetrics, ) -> Result> { use HyperlaneDomainProtocol::*; - let adapter: Arc = match conf.domain.domain_protocol() { - Ethereum => Arc::new(EthereumTxAdapter::new(conf.clone(), raw_conf.clone())), - Fuel => todo!(), - Sealevel => Arc::new(SealevelTxAdapter::new( + let adapter: Arc = match conf.connection.clone() { + ChainConnectionConf::Ethereum(connection_conf) => Arc::new( + EthereumTxAdapter::new(conf.clone(), connection_conf, raw_conf.clone(), metrics) + .await?, + ), + ChainConnectionConf::Fuel(_) => todo!(), + ChainConnectionConf::Sealevel(_) => Arc::new(SealevelTxAdapter::new( conf.clone(), raw_conf.clone(), metrics, )?), - Cosmos => Arc::new(CosmosTxAdapter::new(conf.clone(), raw_conf.clone())), - CosmosNative => todo!(), + ChainConnectionConf::Cosmos(_) => { + Arc::new(CosmosTxAdapter::new(conf.clone(), raw_conf.clone())) + } + ChainConnectionConf::CosmosNative(_) => todo!(), }; Ok(adapter) diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index fb3f3e39efa..d87ae8ce69f 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -266,9 +266,7 @@ impl SealevelTxAdapter { } Err(err) => { warn!(?err, "Failed to get transaction status by hash"); - return Err(SubmitterError::TxSubmissionError( - "Transaction hash not found".to_string(), - )); + return Err(SubmitterError::TxHashNotFound(err.to_string())); } } } @@ -398,6 +396,13 @@ impl AdaptsChain for SealevelTxAdapter { )) } + async fn get_tx_hash_status(&self, hash: H512) -> Result { + info!(?hash, "getting transaction hash status"); + let status = self.get_tx_hash_status(hash).await?; + info!(?hash, ?status, "got transaction hash status"); + Ok(status) + } + async fn reverted_payloads( &self, _tx: &Transaction, diff --git a/rust/main/submitter/src/error.rs b/rust/main/submitter/src/error.rs index ba5cb8f276d..4f788307528 100644 --- a/rust/main/submitter/src/error.rs +++ b/rust/main/submitter/src/error.rs @@ -14,6 +14,8 @@ pub enum SubmitterError { TxAlreadyExists, #[error("The transaction reverted")] TxReverted, + #[error("The transaction hash was not found: {0}")] + TxHashNotFound(String), #[error("Failed to send over a channel {0}")] ChannelSendFailure(#[from] tokio::sync::mpsc::error::SendError), #[error("Channel closed")] @@ -49,6 +51,7 @@ impl SubmitterError { SubmitterError::NonRetryableError(_) => "NonRetryableError".to_string(), SubmitterError::DbError(_) => "DbError".to_string(), SubmitterError::ChainCommunicationError(_) => "ChainCommunicationError".to_string(), + SubmitterError::TxHashNotFound(_) => "TxHashNotFound".to_string(), } } } @@ -81,6 +84,7 @@ impl IsRetryable for SubmitterError { SubmitterError::PayloadNotFound => false, SubmitterError::TxAlreadyExists => false, SubmitterError::DbError(_) => false, + SubmitterError::TxHashNotFound(_) => false, } } } diff --git a/rust/main/submitter/src/payload_dispatcher/dispatcher.rs b/rust/main/submitter/src/payload_dispatcher/dispatcher.rs index 29523bf2aa3..55441f247d2 100644 --- a/rust/main/submitter/src/payload_dispatcher/dispatcher.rs +++ b/rust/main/submitter/src/payload_dispatcher/dispatcher.rs @@ -54,12 +54,12 @@ pub struct PayloadDispatcher { } impl PayloadDispatcher { - pub fn try_from_settings( + pub async fn try_from_settings( settings: PayloadDispatcherSettings, domain: String, metrics: DispatcherMetrics, ) -> Result { - let state = PayloadDispatcherState::try_from_settings(settings, metrics)?; + let state = PayloadDispatcherState::try_from_settings(settings, metrics).await?; Ok(Self { inner: state, domain, diff --git a/rust/main/submitter/src/payload_dispatcher/entrypoint.rs b/rust/main/submitter/src/payload_dispatcher/entrypoint.rs index 79be279fc99..7966552b997 100644 --- a/rust/main/submitter/src/payload_dispatcher/entrypoint.rs +++ b/rust/main/submitter/src/payload_dispatcher/entrypoint.rs @@ -28,12 +28,12 @@ pub struct PayloadDispatcherEntrypoint { } impl PayloadDispatcherEntrypoint { - pub fn try_from_settings( + pub async fn try_from_settings( settings: PayloadDispatcherSettings, metrics: DispatcherMetrics, ) -> Result { Ok(Self { - inner: PayloadDispatcherState::try_from_settings(settings, metrics)?, + inner: PayloadDispatcherState::try_from_settings(settings, metrics).await?, }) } diff --git a/rust/main/submitter/src/payload_dispatcher/stages/state.rs b/rust/main/submitter/src/payload_dispatcher/stages/state.rs index e914b881f7d..dcc442a3017 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/state.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/state.rs @@ -53,7 +53,7 @@ impl PayloadDispatcherState { } } - pub fn try_from_settings( + pub async fn try_from_settings( settings: PayloadDispatcherSettings, metrics: DispatcherMetrics, ) -> Result { @@ -61,7 +61,8 @@ impl PayloadDispatcherState { &settings.chain_conf, &settings.raw_chain_conf, &settings.metrics, - )?; + ) + .await?; let db = match settings.db { DatabaseOrPath::Database(db) => db, DatabaseOrPath::Path(path) => DB::from_path(&path)?, diff --git a/rust/main/submitter/src/payload_dispatcher/test_utils.rs b/rust/main/submitter/src/payload_dispatcher/test_utils.rs index 669a6e78d03..d91e1c3c68f 100644 --- a/rust/main/submitter/src/payload_dispatcher/test_utils.rs +++ b/rust/main/submitter/src/payload_dispatcher/test_utils.rs @@ -27,6 +27,7 @@ mockall::mock! { async fn estimate_tx(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError>; async fn tx_status(&self, tx: &Transaction) -> Result; + async fn get_tx_hash_status(&self, hash: hyperlane_core::H512) -> Result; async fn reverted_payloads(&self, tx: &Transaction) -> Result, SubmitterError>; async fn nonce_gap_exists(&self) -> bool; async fn replace_tx(&self, _tx: &Transaction) -> Result<(), SubmitterError>; From 7d56f2c26ef0f5d14cc71479d5abaadb3f3146eb Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Tue, 6 May 2025 13:03:19 +0100 Subject: [PATCH 138/223] feat: Manually reduce desired balance and alert threshold for chains (#6145) ### Description - adds a script `print-dollar-value-key-funder-threshold.ts`, that checks whether the proposed deployer target balance values would trigger and low urgency key funder alert. (we cont have to merge this script) - adds `target-dollar-balances.json`, this is the deployer target balance values in dollars. ![image](https://github.com/user-attachments/assets/76d9d9c5-abdd-4304-9805-da4712221040) - initial run of the scrip suggested that alerts would trigger for a set of chains (base, bsc, bitlayer, solana, linea) - I checked without we could safely reduce the balance thresholds for these chains but running `calculate-relayer-daily-burn.ts` in review mode. For the following chains, we were maintaining a desired relayer balance that was higher than the recommended image - I reduced the desired balance thresholds for these chains, not using the recommendations but values that are higher to be conservative. ![image](https://github.com/user-attachments/assets/435fb981-b877-4e0b-889b-787a3f3d7fdd) - post these reductions, we were still going to trigger alerts for some chains so I increased the targets for some of these chains. I increased targets for Linea and Sonium to include a funding buffer - See the commits to view the changes in the different stages ### Drive-by changes - pass remote chain for logging `Too much underflow when downscaling exchange rate` error, makes it easier to identify the culprit chain ### Testing Manual --- .changeset/nasty-bugs-cross.md | 5 + .../balances/desiredRelayerBalances.json | 10 +- .../balances/highUrgencyRelayerBalance.json | 8 +- .../lowUrgencyEngKeyFunderBalance.json | 8 +- .../balances/lowUrgencyKeyFunderBalance.json | 8 +- .../environments/mainnet3/tokenPrices.json | 270 +++++++++--------- typescript/sdk/src/gas/utils.ts | 7 +- 7 files changed, 163 insertions(+), 153 deletions(-) create mode 100644 .changeset/nasty-bugs-cross.md diff --git a/.changeset/nasty-bugs-cross.md b/.changeset/nasty-bugs-cross.md new file mode 100644 index 00000000000..da87565008b --- /dev/null +++ b/.changeset/nasty-bugs-cross.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': patch +--- + +Pass remote chain to adjustForPrecisionLoss for better error logging diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index dd418727ae9..93a39b54ba8 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -13,14 +13,14 @@ "aurora": 0.0171, "avalanche": 1.28, "b3": 0.0227, - "base": 0.745, + "base": 0.3, "berachain": 7.33, "bitlayer": 0.0125, "blast": 0.026, "bob": 0.0199, "boba": 0.0193, "bouncebit": 243, - "bsc": 2.11, + "bsc": 1.5, "bsquared": 0.0196, "celo": 80.8, "cheesechain": 84800, @@ -64,7 +64,7 @@ "ink": 0.186, "kaia": 250, "kroma": 0.0156, - "linea": 0.612, + "linea": 0.3, "lisk": 0.0832, "lukso": 29.3, "lumia": 74.5, @@ -84,8 +84,8 @@ "nero": 25, "neutron": 204, "nibiru": 10, - "oortmainnet": 589, "ontology": 0, + "oortmainnet": 589, "opbnb": 0.0422, "optimism": 0.306, "orderly": 0.0225, @@ -109,7 +109,7 @@ "sei": 147, "shibarium": 90.4, "snaxchain": 0.0226, - "solanamainnet": 40, + "solanamainnet": 10, "soneium": 0.315, "sonic": 59.1, "sonicsvm": 1.32, diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index ac880501182..b1223007c4e 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -13,14 +13,14 @@ "aurora": 0.00428, "avalanche": 0.32, "b3": 0.00568, - "base": 0.186, + "base": 0.075, "berachain": 1.83, "bitlayer": 0.00312, "blast": 0.0065, "bob": 0.00498, "boba": 0.00482, "bouncebit": 60.8, - "bsc": 0.528, + "bsc": 0.375, "bsquared": 0.0049, "celo": 20.2, "cheesechain": 21200, @@ -63,7 +63,7 @@ "ink": 0.0466, "kaia": 62.2, "kroma": 0.0039, - "linea": 0.153, + "linea": 0.075, "lisk": 0.0208, "lukso": 7.32, "lumia": 18.6, @@ -106,7 +106,7 @@ "sei": 36.8, "shibarium": 22.6, "snaxchain": 0.00566, - "solanamainnet": 10, + "solanamainnet": 2.5, "soneium": 0.0788, "sonic": 14.8, "sonicsvm": 0.5, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index 520e1fb9aff..f2f2b49f836 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -13,14 +13,14 @@ "aurora": 0.0128, "avalanche": 0.96, "b3": 0.017, - "base": 0.559, + "base": 0.225, "berachain": 5.5, "bitlayer": 0.00936, "blast": 0.0195, "bob": 0.0149, "boba": 0.0145, "bouncebit": 182, - "bsc": 1.58, + "bsc": 1.13, "bsquared": 0.0147, "celo": 60.6, "cheesechain": 63600, @@ -63,7 +63,7 @@ "ink": 0.14, "kaia": 187, "kroma": 0.0117, - "linea": 0.459, + "linea": 0.225, "lisk": 0.0624, "lukso": 22, "lumia": 55.9, @@ -106,7 +106,7 @@ "sei": 110, "shibarium": 67.8, "snaxchain": 0.017, - "solanamainnet": 30, + "solanamainnet": 7.5, "soneium": 0.236, "sonic": 44.3, "sonicsvm": 0.99, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index f195b59fde7..f975d38e0e6 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -13,14 +13,14 @@ "aurora": 0.0257, "avalanche": 1.92, "b3": 0.0341, - "base": 1.12, + "base": 0.45, "berachain": 11, "bitlayer": 0.0187, "blast": 0.039, "bob": 0.0299, "boba": 0.0289, "bouncebit": 365, - "bsc": 3.17, + "bsc": 2.25, "bsquared": 0.0294, "celo": 121, "cheesechain": 127000, @@ -63,7 +63,7 @@ "ink": 0.28, "kaia": 500, "kroma": 0.0234, - "linea": 0.918, + "linea": 0.45, "lisk": 0.2, "lukso": 43.9, "lumia": 112, @@ -106,7 +106,7 @@ "sei": 221, "shibarium": 136, "snaxchain": 0.034, - "solanamainnet": 60, + "solanamainnet": 15, "soneium": 0.473, "sonic": 88.7, "sonicsvm": 1.98, diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index 80e0268db5b..1a37134e800 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -1,144 +1,144 @@ { - "abstract": "1865.12", - "ancient8": "1865.12", - "alephzeroevmmainnet": "0.133098", - "apechain": "0.542923", - "appchain": "1865.12", - "arbitrum": "1865.12", - "arbitrumnova": "1865.12", + "abstract": "1796.24", + "ancient8": "1796.24", + "alephzeroevmmainnet": "0.121652", + "apechain": "0.480125", + "appchain": "1796.24", + "arbitrum": "1796.24", + "arbitrumnova": "1796.24", "arcadia": "1865.12", - "artela": "0.00243229", + "artela": "0.00146389", "arthera": "0.00611031", - "astar": "0.0297173", - "aurora": "1865.12", - "bouncebit": "0.137209", - "flame": "2.75", - "avalanche": "21.47", - "b3": "1865.12", - "base": "1865.12", - "berachain": "3.54", - "bitlayer": "97137", - "blast": "1865.12", - "bob": "1865.12", - "boba": "1865.12", - "bsc": "604.44", - "bsquared": "97137", - "celo": "0.368131", - "cheesechain": "0.00041855", - "chilizmainnet": "0.04245801", - "conflux": "0.081025", - "conwai": "0.00210481", - "coredao": "0.78874", - "corn": "97137", - "coti": "0.083918", - "cyber": "1865.12", - "deepbrainchain": "0.00083753", - "degenchain": "0.00319211", - "dogechain": "0.181674", - "duckchain": "3.2", - "eclipsemainnet": "1865.12", - "endurance": "0.659458", - "ethereum": "1865.12", - "everclear": "1865.12", - "evmos": "0.00426568", - "fantom": "0.556873", - "flare": "0.01807631", - "flowmainnet": "0.412195", + "astar": "0.02668186", + "aurora": "1796.24", + "bouncebit": "0.107801", + "flame": "2.36", + "avalanche": "19.68", + "b3": "1796.24", + "base": "1796.24", + "berachain": "2.89", + "bitlayer": "94134", + "blast": "1796.24", + "bob": "1796.24", + "boba": "1796.24", + "bsc": "596.43", + "bsquared": "94134", + "celo": "0.321799", + "cheesechain": "0.00034753", + "chilizmainnet": "0.03888301", + "conflux": "0.07161", + "conwai": "0.00199735", + "coredao": "0.713972", + "corn": "94134", + "coti": "0.073506", + "cyber": "1796.24", + "deepbrainchain": "0.00071976", + "degenchain": "0.00272733", + "dogechain": "0.167176", + "duckchain": "2.98", + "eclipsemainnet": "1796.24", + "endurance": "0.55117", + "ethereum": "1796.24", + "everclear": "1796.24", + "evmos": "0.00439545", + "fantom": "0.505402", + "flare": "0.01811458", + "flowmainnet": "0.366913", "fluence": "0.00087302", - "form": "1865.12", - "fraxtal": "1854.4", - "fusemainnet": "0.01305948", - "game7": "0.00576759", - "glue": "0.125938", - "gnosis": "1.004", - "gravity": "0.01557197", - "guru": "0.00278482", - "harmony": "0.01383619", - "hashkey": "0.375991", - "hemi": "1865.12", - "hyperevm": "20.43", - "immutablezkevmmainnet": "0.602689", - "inevm": "10.49", + "form": "1796.24", + "fraxtal": "1795.02", + "fusemainnet": "0.01295633", + "game7": "0.00674", + "glue": "0.116752", + "gnosis": "0.997818", + "gravity": "0.01398705", + "guru": "0.00286817", + "harmony": "0.01201095", + "hashkey": "0.355686", + "hemi": "1796.24", + "hyperevm": "19.91", + "immutablezkevmmainnet": "0.535637", + "inevm": "9.14", "infinityvmmainnet": "1", - "ink": "1865.12", - "injective": "10.49", - "kaia": "0.117514", - "kroma": "1865.12", - "linea": "1865.12", - "lisk": "1865.12", - "lukso": "0.87102", - "lumia": "0.35147", - "lumiaprism": "0.334993", - "mantapacific": "1865.12", - "mantle": "0.748373", - "matchain": "604.44", - "merlin": "97169", - "metal": "1865.12", - "metis": "16.28", - "milkyway": "0.115948", - "mint": "1865.12", - "mode": "1865.12", - "molten": "0.155271", - "moonbeam": "0.084758", - "morph": "1865.12", + "ink": "1796.24", + "injective": "9.14", + "kaia": "0.110084", + "kroma": "1796.24", + "linea": "1796.24", + "lisk": "1796.24", + "lukso": "0.889957", + "lumia": "0.320038", + "lumiaprism": "0.279322", + "mantapacific": "1796.24", + "mantle": "0.709835", + "matchain": "596.43", + "merlin": "94140", + "metal": "1796.24", + "metis": "14.33", + "milkyway": "0.127219", + "mint": "1796.24", + "mode": "1796.24", + "molten": "0.146111", + "moonbeam": "0.074762", + "morph": "1796.24", "nero": "1", - "neutron": "0.141581", - "nibiru": "0.01669648", - "oortmainnet": "0.050891", - "ontology": "0.21188", - "opbnb": "604.44", - "optimism": "1865.12", - "orderly": "1865.12", - "osmosis": "0.238243", - "peaq": "0.139388", - "plume": "0.191646", - "polygon": "0.243486", - "polygonzkevm": "1865.12", - "polynomialfi": "1865.12", - "prom": "5.49", - "proofofplay": "1865.12", - "rarichain": "1865.12", - "reactive": "0.07914", - "real": "1865.12", - "redstone": "1865.12", - "rivalz": "1865.12", - "ronin": "0.564698", - "rootstockmainnet": "96001", - "sanko": "8.81", - "scroll": "1865.12", - "sei": "0.224395", - "shibarium": "0.297813", - "snaxchain": "1865.12", - "solanamainnet": "153.18", - "soneium": "1865.12", - "sonic": "0.556873", - "sonicsvm": "153.18", - "soon": "1865.12", + "neutron": "0.120649", + "nibiru": "0.01580474", + "oortmainnet": "0.04892131", + "ontology": "0.193459", + "opbnb": "596.43", + "optimism": "1796.24", + "orderly": "1796.24", + "osmosis": "0.212174", + "peaq": "0.120167", + "plume": "0.171268", + "polygon": "0.218084", + "polygonzkevm": "1796.24", + "polynomialfi": "1796.24", + "prom": "5.43", + "proofofplay": "1796.24", + "rarichain": "1796.24", + "reactive": "0.05047", + "real": "1796.24", + "redstone": "1796.24", + "rivalz": "1796.24", + "ronin": "0.481724", + "rootstockmainnet": "93965", + "sanko": "8.27", + "scroll": "1796.24", + "sei": "0.196636", + "shibarium": "0.299249", + "snaxchain": "1796.24", + "solanamainnet": "144.04", + "soneium": "1796.24", + "sonic": "0.505402", + "sonicsvm": "144.04", + "soon": "1796.24", "sophon": "1", - "story": "4.1", - "stride": "0.293606", - "subtensor": "372.28", - "superseed": "1865.12", - "superpositionmainnet": "1865.12", - "swell": "1865.12", - "taiko": "1865.12", + "story": "3.58", + "stride": "0.262717", + "subtensor": "364.88", + "superseed": "1796.24", + "superpositionmainnet": "1796.24", + "swell": "1796.24", + "taiko": "1796.24", "tangle": "1", - "telos": "0.088233", - "torus": "0.311983", - "treasure": "0.211862", - "trumpchain": "13.1", - "unichain": "1865.12", - "unitzero": "0.234597", - "vana": "6.11", - "viction": "0.235667", - "worldchain": "1865.12", - "xai": "0.06191", - "xlayer": "51.85", - "xpla": "0.04050335", - "zeronetwork": "1865.12", - "zetachain": "0.271066", - "zircuit": "1865.12", - "zklink": "1865.12", - "zksync": "1865.12", - "zoramainnet": "1865.12" + "telos": "0.082682", + "torus": "0.297775", + "treasure": "0.161518", + "trumpchain": "10.7", + "unichain": "1796.24", + "unitzero": "0.2153", + "vana": "5.23", + "viction": "0.207322", + "worldchain": "1796.24", + "xai": "0.051256", + "xlayer": "50.68", + "xpla": "0.03802228", + "zeronetwork": "1796.24", + "zetachain": "0.241362", + "zircuit": "1796.24", + "zklink": "1796.24", + "zksync": "1796.24", + "zoramainnet": "1796.24" } diff --git a/typescript/sdk/src/gas/utils.ts b/typescript/sdk/src/gas/utils.ts index 09ad8b6156f..0cb00b9cadc 100644 --- a/typescript/sdk/src/gas/utils.ts +++ b/typescript/sdk/src/gas/utils.ts @@ -230,6 +230,7 @@ export function getLocalStorageGasOracleConfig({ gasPrice, exchangeRate, remoteDecimals, + remote, ); // Apply the modifier if provided. @@ -239,6 +240,7 @@ export function getLocalStorageGasOracleConfig({ gasPriceModifier(local, remote, gasOracleConfig), BigNumber.from(gasOracleConfig.tokenExchangeRate), remoteDecimals, + remote, ); } @@ -253,6 +255,7 @@ function adjustForPrecisionLoss( gasPrice: BigNumberJs.Value, exchangeRate: BigNumber, remoteDecimals: number, + remote?: ChainName, ): ProtocolAgnositicGasOracleConfig { let newGasPrice = new BigNumberJs(gasPrice); let newExchangeRate = exchangeRate; @@ -271,7 +274,9 @@ function adjustForPrecisionLoss( gasPriceScalingFactor, ); if (recoveredExchangeRate.mul(100).div(newExchangeRate).lt(99)) { - throw new Error('Too much underflow when downscaling exchange rate'); + throw new Error( + `Too much underflow when downscaling exchange rate for remote chain ${remote}`, + ); } newGasPrice = newGasPrice.times(gasPriceScalingFactor); From 292dbfa2691d0a8834f4bd503c24e792ba9948a2 Mon Sep 17 00:00:00 2001 From: Christopher Brumm <97845034+christopherbrumm@users.noreply.github.com> Date: Tue, 6 May 2025 14:45:08 +0200 Subject: [PATCH 139/223] chore: add `wfragSOL` & `wfragJTO` (#6075) ### Description This PR adds `wfragSOL` & `wfragJTO` warp routes from Solana (collateral) to Soon and Eclipse (both synthetic). ### Related PRs - https://github.com/hyperlane-xyz/hyperlane-registry/pull/802 ### Backward compatibility Yes ### Testing Executed test txs and verified balance. --- .../program-ids.json | 14 ++++++++++ .../token-config.json | 27 +++++++++++++++++++ .../program-ids.json | 14 ++++++++++ .../token-config.json | 27 +++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/token-config.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/token-config.json diff --git a/rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/program-ids.json new file mode 100644 index 00000000000..634d280e287 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/program-ids.json @@ -0,0 +1,14 @@ +{ + "eclipsemainnet": { + "hex": "0x35e1bd6cb9d6043463c33a1d6706c7b9f0b5d292600636c708c4e253ca470f14", + "base58": "4dLFhsXeK6QPxV2HEbXsF6eSTZdTFaGcGY5eEj5c8CbD" + }, + "soon": { + "hex": "0x869d897816529c9b7e2a2097564174d350c63c0f332692470fa2ff942f06c77d", + "base58": "A4UxZkWrEtfmhdtBE3VSS6n4E7Tnj7EMs5PA3hLgmFL8" + }, + "solanamainnet": { + "hex": "0x0f05104d8edad413ff9669b42113fd8aad1d552d64e7292de6dac75f9785d82c", + "base58": "21dbSbBdoEqUtJcvBkvhjpEiqb3LTrUFGQxKHWjY3WwV" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/token-config.json new file mode 100644 index 00000000000..ad99acf34b0 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGJTO-eclipse-solanamainnet-soon/token-config.json @@ -0,0 +1,27 @@ +{ + "solanamainnet": { + "type": "collateral", + "decimals": 9, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "token": "WFRGJnQt5pK8Dv4cDAbrSsgPcmboysrmX3RYhmRRyTR", + "owner": "XEhpR3UauMkARQ8ztwaU9Kbv16jEpBbXs9ftELka9wj" + }, + "eclipsemainnet": { + "type": "synthetic", + "decimals": 9, + "name": "Wrapped Fragmetric Restaked JTO", + "symbol": "wfragJTO", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/f4e41f22aabd09d15d41f35559c1a9ebf65b9616/deployments/warp_routes/WFRAGJTO/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj", + "owner": "TPP6hFE9AKyc5rTxRh5P5igBzDmSpPLaqi6pdph9KHR" + }, + "soon": { + "type": "synthetic", + "decimals": 9, + "name": "Wrapped Fragmetric Restaked JTO", + "symbol": "wfragJTO", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/f4e41f22aabd09d15d41f35559c1a9ebf65b9616/deployments/warp_routes/WFRAGJTO/metadata.json", + "interchainGasPaymaster": "GctmRsKQM5VLPrUPpsQ7Kc7RaVKNC2Fev31n1KtLkj8A", + "owner": "BAH4FXCTtuKYJ6KLeQUyYhFcsZxvqKJL9Uhf7eHQVaGc" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/program-ids.json new file mode 100644 index 00000000000..121b4dcc656 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/program-ids.json @@ -0,0 +1,14 @@ +{ + "eclipsemainnet": { + "hex": "0xd39fd7304d721dabdc5d962c2d364ba8faac21f9e5399ca8966321867121a26f", + "base58": "FF6PWPjiY1hwy3Kt5qTDoqhZ3PbmaXKYPzADEjZbK9iN" + }, + "soon": { + "hex": "0x5792d50388215ce30fe6e77e49494ca8fe19224d6dbd6bab95d657a96adf2b2f", + "base58": "6trL4uE7KANMqrPT5rw4uiN6TSTw7c5Ssv4GdAkhTzVc" + }, + "solanamainnet": { + "hex": "0x0f5dd5e59e5f473e7b35692fcc7587a43e95edc670a42fbdbf91f2c788e8c1b7", + "base58": "22z73A4E7QjwjTsHGJptnALgBGuovEabCcQtPdBaQPpJ" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/token-config.json new file mode 100644 index 00000000000..e4352712112 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/WFRAGSOL-eclipse-solanamainnet-soon/token-config.json @@ -0,0 +1,27 @@ +{ + "solanamainnet": { + "type": "collateral", + "decimals": 9, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "token": "WFRGSWjaz8tbAxsJitmbfRuFV2mSNwy7BMWcCwaA28U", + "owner": "XEhpR3UauMkARQ8ztwaU9Kbv16jEpBbXs9ftELka9wj" + }, + "eclipsemainnet": { + "type": "synthetic", + "decimals": 9, + "name": "Wrapped Fragmetric Restaked SOL", + "symbol": "wfragSOL", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/f4e41f22aabd09d15d41f35559c1a9ebf65b9616/deployments/warp_routes/WFRAGSOL/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj", + "owner": "TPP6hFE9AKyc5rTxRh5P5igBzDmSpPLaqi6pdph9KHR" + }, + "soon": { + "type": "synthetic", + "decimals": 9, + "name": "Wrapped Fragmetric Restaked SOL", + "symbol": "wfragSOL", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/f4e41f22aabd09d15d41f35559c1a9ebf65b9616/deployments/warp_routes/WFRAGSOL/metadata.json", + "interchainGasPaymaster": "GctmRsKQM5VLPrUPpsQ7Kc7RaVKNC2Fev31n1KtLkj8A", + "owner": "BAH4FXCTtuKYJ6KLeQUyYhFcsZxvqKJL9Uhf7eHQVaGc" + } +} \ No newline at end of file From 3f28921ecc4c825721afc23024b77c7541616e40 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 6 May 2025 13:56:00 +0100 Subject: [PATCH 140/223] feat(submitter): evm-specific fields in Payload and Transaction (#6135) ### Description The `VmSpecificTxData` enum is changed for the `Evm` to now wrap a `TypedTransaction`. No changes to `Payload` are required to support EVM chains, since @ameten suggested serializing everything as a vec of bytes in the `data` field. - One concern I have is that the serialization of `VmSpecificTxData` may change as a result of this. Memory is allocated for Enums as: "The size will be the size of an integer specifying which of the two variants it is, plus the size of the largest variant, plus (sometimes) padding between the integer and the payload." ([source](https://www.reddit.com/r/rust/comments/t6fzcv/comment/hzaxlrx/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button), from one of the tokio maintainers). More details here too: https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#layout-of-a-data-carrying-enums-without-a-repr-annotation. - The changes may cause the `VmSpecificTxData` stored in rocksdb for sealevel to be interpreted differently now - but hopefully that's not a big concern since it's just for Transactions on RC. ### Drive-by changes Adds `ethers` as a dependency on the submitter, to gain access to `TypedTransaction` ### Related issues Fixes: https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6127 ### Backward compatibility This change may be breaking to the `VmSpecificTxData` format stored in the RC db ### Testing Covered by unit tests and E2E --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/submitter/src/chain_tx_adapter.rs | 1 + .../submitter/src/chain_tx_adapter/chains.rs | 1 + .../src/chain_tx_adapter/chains/ethereum.rs | 5 +++ .../chains/ethereum/payload.rs | 8 ++++ .../chains/ethereum/precursor.rs | 37 +++++++++++++++++++ rust/main/submitter/src/payload/types.rs | 2 +- .../src/payload_dispatcher/test_utils.rs | 2 +- rust/main/submitter/src/transaction/types.rs | 7 ++-- 8 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs diff --git a/rust/main/submitter/src/chain_tx_adapter.rs b/rust/main/submitter/src/chain_tx_adapter.rs index af85d841b72..07dd81f593d 100644 --- a/rust/main/submitter/src/chain_tx_adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter.rs @@ -3,6 +3,7 @@ pub use adapter::{AdaptsChain, GasLimit, TxBuildingResult}; pub use chains::ChainTxAdapterFactory; +pub use chains::EthereumTxPrecursor; pub use chains::SealevelTxPrecursor; mod adapter; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains.rs b/rust/main/submitter/src/chain_tx_adapter/chains.rs index 25a11921883..9f71b714335 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains.rs @@ -1,3 +1,4 @@ +pub use ethereum::EthereumTxPrecursor; pub use factory::ChainTxAdapterFactory; pub use sealevel::SealevelTxPrecursor; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs index 3a6ef68e45e..52b1aa6083b 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs @@ -20,6 +20,11 @@ use crate::{ transaction::{Transaction, TransactionStatus}, }; +pub use precursor::EthereumTxPrecursor; + +mod payload; +mod precursor; + pub struct EthereumTxAdapter { _conf: ChainConf, _raw_conf: RawChainConf, diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs new file mode 100644 index 00000000000..fbe4e8faa4c --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs @@ -0,0 +1,8 @@ +use crate::FullPayload; +use ethers::abi::Function; +use ethers::types::transaction::eip2718::TypedTransaction; + +pub fn parse_data(payload: &FullPayload) -> (TypedTransaction, Function) { + serde_json::from_slice::<(TypedTransaction, Function)>(&payload.data) + .expect("Payload should contain tuple of TypedTransaction and Function for Ethereum") +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs new file mode 100644 index 00000000000..5a6d3cbc2ae --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs @@ -0,0 +1,37 @@ +use std::fmt::Debug; + +use ethers::abi::Function; +use ethers::types::transaction::eip2718::TypedTransaction; + +use crate::payload::FullPayload; + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct EthereumTxPrecursor { + pub tx: TypedTransaction, + pub function: Function, +} + +impl PartialEq for EthereumTxPrecursor { + fn eq(&self, other: &Self) -> bool { + self.tx == other.tx + && self.function.name == other.function.name + && self.function.inputs == other.function.inputs + && self.function.outputs == other.function.outputs + && self.function.state_mutability == other.function.state_mutability + } +} + +impl Eq for EthereumTxPrecursor {} + +impl EthereumTxPrecursor { + pub fn new(tx: TypedTransaction, function: Function) -> Self { + Self { tx, function } + } + + pub fn from_payload(payload: &FullPayload) -> Self { + use crate::chain_tx_adapter::chains::ethereum::payload::parse_data; + + let (tx, function) = parse_data(payload); + EthereumTxPrecursor::new(tx, function) + } +} diff --git a/rust/main/submitter/src/payload/types.rs b/rust/main/submitter/src/payload/types.rs index bc1914614fa..df12123f315 100644 --- a/rust/main/submitter/src/payload/types.rs +++ b/rust/main/submitter/src/payload/types.rs @@ -43,7 +43,7 @@ impl PayloadDetails { pub struct FullPayload { /// reference to payload used by other components pub details: PayloadDetails, - /// calldata on EVM. On SVM, it is the serialized instructions and account list. On Cosmos, it is the serialized vec of msgs + /// serialized `ContractCall` on EVM. On SVM, it is the serialized instructions and account list. On Cosmos, it is the serialized vec of msgs pub data: Vec, /// defaults to the hyperlane mailbox pub to: Address, diff --git a/rust/main/submitter/src/payload_dispatcher/test_utils.rs b/rust/main/submitter/src/payload_dispatcher/test_utils.rs index d91e1c3c68f..0abd2c60dcc 100644 --- a/rust/main/submitter/src/payload_dispatcher/test_utils.rs +++ b/rust/main/submitter/src/payload_dispatcher/test_utils.rs @@ -55,7 +55,7 @@ pub(crate) fn dummy_tx(payloads: Vec, status: TransactionStatus) -> Transaction { id: UniqueIdentifier::random(), tx_hashes: vec![], - vm_specific_data: VmSpecificTxData::Evm, + vm_specific_data: VmSpecificTxData::CosmWasm, payload_details: details.clone(), status, submission_attempts: 0, diff --git a/rust/main/submitter/src/transaction/types.rs b/rust/main/submitter/src/transaction/types.rs index f0f48b3e85a..a99f3038c38 100644 --- a/rust/main/submitter/src/transaction/types.rs +++ b/rust/main/submitter/src/transaction/types.rs @@ -5,12 +5,11 @@ use std::collections::HashMap; use std::ops::Deref; use chrono::{DateTime, Utc}; -use uuid::Uuid; use hyperlane_core::{identifiers::UniqueIdentifier, H256, H512}; -use crate::chain_tx_adapter::SealevelTxPrecursor; -use crate::payload::{FullPayload, PayloadDetails, PayloadId}; +use crate::chain_tx_adapter::{EthereumTxPrecursor, SealevelTxPrecursor}; +use crate::payload::PayloadDetails; use crate::SubmitterError; pub type TransactionId = UniqueIdentifier; @@ -104,7 +103,7 @@ pub enum DropReason { // add nested enum entries as we add VMs #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq)] pub enum VmSpecificTxData { - Evm, + Evm(EthereumTxPrecursor), Svm(SealevelTxPrecursor), CosmWasm, } From 166f8492fb360b30568cafa5eff4f1f0d82e8428 Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Tue, 6 May 2025 09:15:02 -0400 Subject: [PATCH 141/223] feat: Remove outputting isNft in warp init config (#6095) ### Description Remove outputting isNft in warp init config (since we don't support NFTs practically anyways) ### Backward compatibility Yes ### Testing None --- .changeset/curly-bobcats-doubt.md | 5 +++++ typescript/cli/src/config/warp.ts | 29 ++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 .changeset/curly-bobcats-doubt.md diff --git a/.changeset/curly-bobcats-doubt.md b/.changeset/curly-bobcats-doubt.md new file mode 100644 index 00000000000..45e93eb1dca --- /dev/null +++ b/.changeset/curly-bobcats-doubt.md @@ -0,0 +1,5 @@ +--- +"@hyperlane-xyz/cli": patch +--- + +Remove outputting isNft in warp init config diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index e61ae89fc58..91942b0e5fa 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -166,21 +166,27 @@ export async function createWarpRouteDeployConfig({ choices: typeChoices, }); - // TODO: restore NFT prompting - const isNft = - type === TokenType.syntheticUri || type === TokenType.collateralUri; - switch (type) { case TokenType.collateral: case TokenType.XERC20: case TokenType.XERC20Lockbox: case TokenType.collateralFiat: + result[chain] = { + type, + owner, + proxyAdmin, + interchainSecurityModule, + token: await input({ + message: `Enter the existing token address on chain ${chain}`, + }), + }; + break; case TokenType.collateralUri: result[chain] = { type, owner, proxyAdmin, - isNft, + isNft: true, interchainSecurityModule, token: await input({ message: `Enter the existing token address on chain ${chain}`, @@ -191,7 +197,6 @@ export async function createWarpRouteDeployConfig({ result[chain] = { type, owner, - isNft, proxyAdmin, collateralChainName: '', // This will be derived correctly by zod.parse() below interchainSecurityModule, @@ -206,7 +211,6 @@ export async function createWarpRouteDeployConfig({ type, owner, proxyAdmin, - isNft, interchainSecurityModule, token: await input({ message: `Enter the ERC-4626 vault address on chain ${chain}`, @@ -220,19 +224,26 @@ export async function createWarpRouteDeployConfig({ type, owner, proxyAdmin, - isNft, interchainSecurityModule, token: await input({ message: `Enter the ERC-4626 vault address on chain ${chain}`, }), }; break; + case TokenType.syntheticUri: + result[chain] = { + type, + owner, + proxyAdmin, + interchainSecurityModule, + isNft: true, + }; + break; default: result[chain] = { type, owner, proxyAdmin, - isNft, interchainSecurityModule, }; } From a36d5c13a204d9ed55c6c2886f7c7aba07c59fd5 Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Wed, 7 May 2025 08:40:48 +0200 Subject: [PATCH 142/223] chore: add cosmos native hook module (#6123) ### Description This PR implements the Hook module for Cosmos Native. Following PRs will use this to support the goal of having full cosmos native support in the hyperlane CLI. Note that this is the second PR of https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6050 which is being split up in order to make it easier to review ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual testing with local cosmos chain --- .changeset/yummy-toes-pick.md | 5 + .../sdk/src/hook/CosmosNativeHookModule.ts | 213 ++++++++++++++++++ .../sdk/src/hook/CosmosNativeHookReader.ts | 157 +++++++++++++ typescript/sdk/src/index.ts | 2 + 4 files changed, 377 insertions(+) create mode 100644 .changeset/yummy-toes-pick.md create mode 100644 typescript/sdk/src/hook/CosmosNativeHookModule.ts create mode 100644 typescript/sdk/src/hook/CosmosNativeHookReader.ts diff --git a/.changeset/yummy-toes-pick.md b/.changeset/yummy-toes-pick.md new file mode 100644 index 00000000000..467813a0ff0 --- /dev/null +++ b/.changeset/yummy-toes-pick.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +add cosmos native hook module & reader diff --git a/typescript/sdk/src/hook/CosmosNativeHookModule.ts b/typescript/sdk/src/hook/CosmosNativeHookModule.ts new file mode 100644 index 00000000000..112e6a448b5 --- /dev/null +++ b/typescript/sdk/src/hook/CosmosNativeHookModule.ts @@ -0,0 +1,213 @@ +import { zeroAddress } from 'viem'; + +import { SigningHyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; +import { + Address, + ChainId, + Domain, + ProtocolType, + assert, + deepEquals, + rootLogger, +} from '@hyperlane-xyz/utils'; + +import { + HyperlaneModule, + HyperlaneModuleParams, +} from '../core/AbstractHyperlaneModule.js'; +import { MultiProvider } from '../providers/MultiProvider.js'; +import { AnnotatedCosmJsNativeTransaction } from '../providers/ProviderType.js'; +import { ChainName, ChainNameOrId } from '../types.js'; +import { normalizeConfig } from '../utils/ism.js'; + +import { CosmosNativeHookReader } from './CosmosNativeHookReader.js'; +import { + HookConfig, + HookConfigSchema, + HookType, + IgpHookConfig, +} from './types.js'; + +type HookModuleAddresses = { + deployedHook: Address; + mailbox: Address; +}; + +export class CosmosNativeHookModule extends HyperlaneModule< + ProtocolType.CosmosNative, + HookConfig, + HookModuleAddresses +> { + protected readonly logger = rootLogger.child({ + module: 'CosmosNativeHookModule', + }); + protected readonly reader: CosmosNativeHookReader; + + // Adding these to reduce how often we need to grab from MultiProvider. + public readonly chain: ChainName; + public readonly chainId: ChainId; + public readonly domainId: Domain; + + constructor( + protected readonly multiProvider: MultiProvider, + params: HyperlaneModuleParams, + protected readonly signer: SigningHyperlaneModuleClient, + ) { + params.config = HookConfigSchema.parse(params.config); + super(params); + + this.reader = new CosmosNativeHookReader(multiProvider, signer); + + this.chain = multiProvider.getChainName(this.args.chain); + this.chainId = multiProvider.getChainId(this.chain); + this.domainId = multiProvider.getDomainId(this.chain); + } + + public async read(): Promise { + return this.reader.deriveHookConfig(this.args.addresses.deployedHook); + } + + public async update( + targetConfig: HookConfig, + ): Promise { + if (targetConfig === zeroAddress) { + return Promise.resolve([]); + } + + targetConfig = HookConfigSchema.parse(targetConfig); + + // Do not support updating to a custom Hook address + if (typeof targetConfig === 'string') { + throw new Error( + 'Invalid targetConfig: Updating to a custom Hook address is not supported. Please provide a valid Hook configuration.', + ); + } + + this.args.config = targetConfig; + + // We need to normalize the current and target configs to compare. + const normalizedCurrentConfig = normalizeConfig(await this.read()); + const normalizedTargetConfig = normalizeConfig(targetConfig); + + if (deepEquals(normalizedCurrentConfig, normalizedTargetConfig)) { + return []; + } + + this.args.addresses.deployedHook = await this.deploy({ + config: normalizedTargetConfig, + }); + + return []; + } + + public static async create({ + chain, + config, + addresses, + multiProvider, + signer, + }: { + chain: ChainNameOrId; + config: HookConfig; + addresses: HookModuleAddresses; + multiProvider: MultiProvider; + signer: SigningHyperlaneModuleClient; + }): Promise { + const module = new CosmosNativeHookModule( + multiProvider, + { + addresses, + chain, + config, + }, + signer, + ); + + module.args.addresses.deployedHook = await module.deploy({ config }); + + return module; + } + + protected async deploy({ config }: { config: HookConfig }): Promise
{ + config = HookConfigSchema.parse(config); + + if (typeof config === 'string') { + return config; + } + const hookType = config.type; + this.logger.info(`Deploying ${hookType} to ${this.chain}`); + + switch (hookType) { + case HookType.INTERCHAIN_GAS_PAYMASTER: + return this.deployIgpHook({ config }); + case HookType.MERKLE_TREE: + return this.deployMerkleTreeHook(); + case HookType.MAILBOX_DEFAULT: + return this.deployNoopHook(); + default: + throw new Error(`Hook type ${hookType} is not supported on Cosmos`); + } + } + + protected async deployIgpHook({ + config, + }: { + config: IgpHookConfig; + }): Promise
{ + this.logger.debug('Deploying IGP as hook...'); + + const { nativeToken } = this.multiProvider.getChainMetadata(this.chain); + + assert(nativeToken?.denom, `found no native token for chain ${this.chain}`); + + const { response: igp } = await this.signer.createIgp({ + denom: nativeToken.denom, + }); + + for (const [remote, c] of Object.entries(config.oracleConfig)) { + const remoteDomain = this.multiProvider.tryGetDomainId(remote); + if (remoteDomain === null) { + this.logger.warn(`Skipping gas oracle ${this.chain} -> ${remote}.`); + continue; + } + + await this.signer.setDestinationGasConfig({ + igp_id: igp.id, + destination_gas_config: { + remote_domain: remoteDomain, + gas_overhead: config.overhead[remote].toString(), + gas_oracle: { + token_exchange_rate: c.tokenExchangeRate, + gas_price: c.gasPrice, + }, + }, + }); + } + + if (config.owner && this.signer.account.address !== config.owner) { + await this.signer.setIgpOwner({ + igp_id: igp.id, + new_owner: config.owner, + }); + } + + return igp.id; + } + + protected async deployMerkleTreeHook(): Promise
{ + this.logger.debug('Deploying Merkle Tree Hook...'); + + const { response: merkleTree } = await this.signer.createMerkleTreeHook({ + mailbox_id: this.args.addresses.mailbox, + }); + + return merkleTree.id; + } + + protected async deployNoopHook(): Promise
{ + this.logger.debug('Deploying Noop Hook...'); + + const { response: noopResponse } = await this.signer.createNoopHook({}); + return noopResponse.id; + } +} diff --git a/typescript/sdk/src/hook/CosmosNativeHookReader.ts b/typescript/sdk/src/hook/CosmosNativeHookReader.ts new file mode 100644 index 00000000000..11e4ba24ebe --- /dev/null +++ b/typescript/sdk/src/hook/CosmosNativeHookReader.ts @@ -0,0 +1,157 @@ +import { + HyperlaneModuleClient, + SigningHyperlaneModuleClient, +} from '@hyperlane-xyz/cosmos-sdk'; +import { Address, WithAddress, assert, rootLogger } from '@hyperlane-xyz/utils'; + +import { MultiProvider } from '../providers/MultiProvider.js'; + +import { + HookConfig, + HookType, + IgpHookConfig, + MailboxDefaultHookConfig, + MerkleTreeHookConfig, +} from './types.js'; + +export class CosmosNativeHookReader { + protected readonly logger = rootLogger.child({ + module: 'CosmosNativeHookReader', + }); + + constructor( + protected readonly multiProvider: MultiProvider, + protected readonly cosmosProviderOrSigner: + | HyperlaneModuleClient + | SigningHyperlaneModuleClient, + ) {} + + async deriveHookConfig(address: Address): Promise { + try { + if (await this.isIgpHook(address)) { + return this.deriveIgpConfig(address); + } else if (await this.isMerkleTreeHook(address)) { + return this.deriveMerkleTreeConfig(address); + } else if (await this.isNoopHook(address)) { + return this.deriveNoopConfig(address); + } else { + throw new Error(`Unsupported hook type for address: ${address}`); + } + } catch (error) { + this.logger.error(`Failed to derive Hook config for ${address}`, error); + throw error; + } + } + + private async deriveIgpConfig( + address: Address, + ): Promise> { + const { igp } = await this.cosmosProviderOrSigner.query.postDispatch.Igp({ + id: address, + }); + + assert(igp, `IGP not found for address ${address}`); + + const { destination_gas_configs } = + await this.cosmosProviderOrSigner.query.postDispatch.DestinationGasConfigs( + { + id: igp.id, + }, + ); + + const overhead: IgpHookConfig['overhead'] = {}; + const oracleConfig: IgpHookConfig['oracleConfig'] = {}; + + destination_gas_configs.forEach((gasConfig) => { + const { name, nativeToken } = this.multiProvider.getChainMetadata( + gasConfig.remote_domain, + ); + overhead[name] = parseInt(gasConfig.gas_overhead); + oracleConfig[name] = { + gasPrice: gasConfig.gas_oracle?.gas_price ?? '', + tokenExchangeRate: gasConfig.gas_oracle?.token_exchange_rate ?? '', + tokenDecimals: nativeToken?.decimals, + }; + }); + + return { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + owner: igp.owner, + beneficiary: igp.owner, + oracleKey: igp.owner, + overhead, + oracleConfig, + address: igp.id, + }; + } + + private async deriveMerkleTreeConfig( + address: Address, + ): Promise> { + const { merkle_tree_hook } = + await this.cosmosProviderOrSigner.query.postDispatch.MerkleTreeHook({ + id: address, + }); + + assert( + merkle_tree_hook, + `Merkle Tree Hook not found for address ${address}`, + ); + + return { + type: HookType.MERKLE_TREE, + address: merkle_tree_hook.id, + }; + } + + private async deriveNoopConfig( + address: Address, + ): Promise> { + const { noop_hook } = + await this.cosmosProviderOrSigner.query.postDispatch.NoopHook({ + id: address, + }); + + assert(noop_hook, `Noop Hook not found for address ${address}`); + + return { + type: HookType.MAILBOX_DEFAULT, + address: noop_hook.id, + }; + } + + private async isIgpHook(address: Address): Promise { + try { + const { igp } = await this.cosmosProviderOrSigner.query.postDispatch.Igp({ + id: address, + }); + return !!igp; + } catch { + return false; + } + } + + private async isMerkleTreeHook(address: Address): Promise { + try { + const { merkle_tree_hook } = + await this.cosmosProviderOrSigner.query.postDispatch.MerkleTreeHook({ + id: address, + }); + return !!merkle_tree_hook; + } catch { + return false; + } + } + + private async isNoopHook(address: Address): Promise { + try { + const { noop_hook } = + await this.cosmosProviderOrSigner.query.postDispatch.NoopHook({ + id: address, + }); + return !!noop_hook; + } catch { + return false; + } + } +} diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index d594efc3daf..131c7b4b44b 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -459,6 +459,8 @@ export { } from './gas/utils.js'; export { GcpValidator } from './gcp/validator.js'; export { EvmHookModule } from './hook/EvmHookModule.js'; +export { CosmosNativeHookModule } from './hook/CosmosNativeHookModule.js'; +export { CosmosNativeHookReader } from './hook/CosmosNativeHookReader.js'; export { DerivedIcaRouterConfig, DerivedIcaRouterConfigSchema, From f5d6790cbd2526294e103196cd82095f7c9f4cd7 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 7 May 2025 09:50:34 +0100 Subject: [PATCH 143/223] feat(submitter): Build and submit transaction for EVM chains (#6148) ### Description Build and submit transaction for EVM chains - Serialise data required to build a ContractCall into byte vector in EthereumMailbox - Desialise data required to build a ContractCall into transaction precursor - Submit transaction to chain using ContractCall ### Drive-by changes - Reformat of workspace Cargo.toml ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/Cargo.lock | 20 +- rust/main/Cargo.toml | 76 ++++---- .../src/contracts/mailbox.rs | 11 +- .../src/rpc_clients/provider.rs | 24 +++ rust/main/submitter/Cargo.toml | 2 +- .../src/chain_tx_adapter/chains/ethereum.rs | 145 +------------- .../chains/ethereum/adapter.rs | 177 ++++++++++++++++++ .../chains/ethereum/transaction.rs | 5 + .../chains/ethereum/transaction/factory.rs | 22 +++ .../chains/ethereum/transaction/precursor.rs | 15 ++ .../src/chain_tx_adapter/chains/factory.rs | 5 +- .../src/payload_dispatcher/test_utils.rs | 3 +- 12 files changed, 305 insertions(+), 200 deletions(-) create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction.rs create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/factory.rs create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index e003085fd0e..f029929efee 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -3555,7 +3555,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3569,7 +3569,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "ethers-core", "once_cell", @@ -3580,7 +3580,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -3598,7 +3598,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "Inflector", "cfg-if", @@ -3622,7 +3622,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -3636,7 +3636,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "arrayvec", "bytes", @@ -3666,7 +3666,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3682,7 +3682,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3733,7 +3733,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3769,7 +3769,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-02#602b5c7f141cb03d0c090e01728f1809145ebe04" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" dependencies = [ "async-trait", "coins-bip32 0.7.0", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 28f33c318ae..93e91f1a368 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -1,27 +1,27 @@ [workspace] members = [ - "agents/relayer", - "agents/scraper", - "agents/validator", - "applications/hyperlane-application", - "applications/hyperlane-operation-verifier", - "applications/hyperlane-warp-route", - "chains/hyperlane-cosmos", - "chains/hyperlane-cosmos-native", - "chains/hyperlane-ethereum", - "chains/hyperlane-fuel", - "chains/hyperlane-sealevel", - "ethers-prometheus", - "hyperlane-base", - "hyperlane-core", - "hyperlane-metric", - "hyperlane-test", - "submitter", - "utils/abigen", - "utils/backtrace-oneline", - "utils/crypto", - "utils/hex", - "utils/run-locally", + "agents/relayer", + "agents/scraper", + "agents/validator", + "applications/hyperlane-application", + "applications/hyperlane-operation-verifier", + "applications/hyperlane-warp-route", + "chains/hyperlane-cosmos", + "chains/hyperlane-cosmos-native", + "chains/hyperlane-ethereum", + "chains/hyperlane-fuel", + "chains/hyperlane-sealevel", + "ethers-prometheus", + "hyperlane-base", + "hyperlane-core", + "hyperlane-metric", + "hyperlane-test", + "submitter", + "utils/abigen", + "utils/backtrace-oneline", + "utils/crypto", + "utils/hex", + "utils/run-locally", ] resolver = "2" @@ -61,10 +61,10 @@ config = "0.13.3" console-subscriber = "0.2.0" convert_case = "0.6" cosmrs = { version = "0.21.0", default-features = false, features = [ - "cosmwasm", - "rpc", - "tokio", - "grpc", + "cosmwasm", + "rpc", + "tokio", + "grpc", ] } cosmwasm-std = "*" crunchy = "0.2" @@ -126,15 +126,15 @@ rlp = "=0.5.2" rocksdb = "0.21.0" rstest = "0.25.0" sea-orm = { version = "1.1.10", features = [ - "sqlx-postgres", - "runtime-tokio-native-tls", - "with-bigdecimal", - "with-time", - "macros", + "sqlx-postgres", + "runtime-tokio-native-tls", + "with-bigdecimal", + "with-time", + "macros", ] } sea-orm-migration = { version = "1.1.10", features = [ - "sqlx-postgres", - "runtime-tokio-native-tls", + "sqlx-postgres", + "runtime-tokio-native-tls", ] } semver = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -219,27 +219,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-02" +tag = "2025-05-06" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-02" +tag = "2025-05-06" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-02" +tag = "2025-05-06" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-02" +tag = "2025-05-06" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-02" +tag = "2025-05-06" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index 6d896ad0c20..72a27a2ef3e 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -683,10 +683,15 @@ where async fn process_calldata( &self, - _message: &HyperlaneMessage, - _metadata: &[u8], + message: &HyperlaneMessage, + metadata: &[u8], ) -> ChainResult> { - todo!() + let contract_call = self.contract.process( + metadata.to_vec().into(), + RawHyperlaneMessage::from(message).to_vec().into(), + ); + let data = (contract_call.tx, contract_call.function); + serde_json::to_vec(&data).map_err(Into::into) } } diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index fcce2c4777a..a67862102a3 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -1,11 +1,15 @@ use std::fmt::Debug; use std::future::Future; +use std::marker::PhantomData; use std::sync::Arc; use std::time::Duration; use async_trait::async_trait; use derive_new::new; use ethers::{prelude::Middleware, types::TransactionReceipt}; +use ethers_contract::builders::ContractCall; +use ethers_core::abi::Function; +use ethers_core::types::transaction::eip2718::TypedTransaction; use ethers_core::{abi::Address, types::BlockNumber}; use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, H512, U256}; use tokio::time::sleep; @@ -58,6 +62,9 @@ pub trait EvmProviderForSubmitter: Send + Sync { &self, reorg_period: &EthereumReorgPeriod, ) -> ChainResult; + + /// Send transaction into blockchain + async fn send(&self, tx: TypedTransaction, function: Function) -> ChainResult; } #[async_trait] @@ -83,6 +90,23 @@ where ) -> ChainResult { get_finalized_block_number(&*self.provider, reorg_period).await } + + async fn send(&self, tx: TypedTransaction, function: Function) -> ChainResult { + let contract_call = ContractCall { + tx, + function, + block: None, + client: self.provider.clone(), + datatype: PhantomData::<()>, + }; + + let pending = contract_call + .send() + .await + .map_err(|e| ChainCommunicationError::CustomError(e.to_string()))?; + + Ok(pending.tx_hash().into()) + } } #[async_trait] diff --git a/rust/main/submitter/Cargo.toml b/rust/main/submitter/Cargo.toml index f9b45c9be4c..bd9eae377d4 100644 --- a/rust/main/submitter/Cargo.toml +++ b/rust/main/submitter/Cargo.toml @@ -16,8 +16,8 @@ hyperlane-sealevel = { path = "../chains/hyperlane-sealevel" } async-trait.workspace = true chrono.workspace = true derive-new.workspace = true -eyre.workspace = true ethers.workspace = true +eyre.workspace = true futures-util.workspace = true prometheus.workspace = true serde.workspace = true diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs index 52b1aa6083b..63081ffe292 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs @@ -1,146 +1,7 @@ -use async_trait::async_trait; -use ethers::{providers::Middleware, types::U64}; -use eyre::Result; -use hyperlane_core::{ContractLocator, H256}; -use hyperlane_ethereum::{ - ConnectionConf, EthereumReorgPeriod, EvmProviderForSubmitter, SubmitterProviderBuilder, -}; -use tracing::warn; -use uuid::Uuid; - -use hyperlane_base::{ - settings::{ChainConf, RawChainConf}, - CoreMetrics, -}; - -use crate::{ - chain_tx_adapter::{adapter::TxBuildingResult, AdaptsChain, GasLimit}, - error::SubmitterError, - payload::{FullPayload, PayloadDetails}, - transaction::{Transaction, TransactionStatus}, -}; - +pub use adapter::EthereumTxAdapter; pub use precursor::EthereumTxPrecursor; +mod adapter; mod payload; mod precursor; - -pub struct EthereumTxAdapter { - _conf: ChainConf, - _raw_conf: RawChainConf, - provider: Box, - reorg_period: EthereumReorgPeriod, -} - -impl EthereumTxAdapter { - pub async fn new( - conf: ChainConf, - connection_conf: ConnectionConf, - raw_conf: RawChainConf, - metrics: &CoreMetrics, - ) -> eyre::Result { - let locator = ContractLocator { - domain: &conf.domain, - address: H256::zero(), - }; - let provider = conf - .build_ethereum( - &connection_conf, - &locator, - metrics, - SubmitterProviderBuilder {}, - ) - .await?; - let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; - - Ok(Self { - _conf: conf, - _raw_conf: raw_conf, - provider, - reorg_period, - }) - } - - async fn block_number_result_to_tx_hash(&self, block_number: Option) -> TransactionStatus { - let Some(block_number) = block_number else { - return TransactionStatus::PendingInclusion; - }; - let block_number = block_number.as_u64(); - match self - .provider - .get_finalized_block_number(&self.reorg_period) - .await - { - Ok(finalized_block) => { - if finalized_block as u64 >= block_number { - TransactionStatus::Finalized - } else { - TransactionStatus::Included - } - } - Err(err) => { - warn!( - ?err, - "Error checking block finality. Assuming tx is pending inclusion" - ); - TransactionStatus::PendingInclusion - } - } - } -} - -#[async_trait] -impl AdaptsChain for EthereumTxAdapter { - async fn estimate_gas_limit( - &self, - _payload: &FullPayload, - ) -> Result, SubmitterError> { - todo!() - } - - async fn build_transactions(&self, _payloads: &[FullPayload]) -> Vec { - todo!() - } - - async fn simulate_tx(&self, _tx: &Transaction) -> Result { - todo!() - } - - async fn estimate_tx(&self, _tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { - todo!() - } - - async fn submit(&self, _tx: &mut Transaction) -> Result<(), SubmitterError> { - todo!() - } - - async fn reverted_payloads( - &self, - _tx: &Transaction, - ) -> Result, SubmitterError> { - todo!() - } - - fn estimated_block_time(&self) -> &std::time::Duration { - todo!() - } - - fn max_batch_size(&self) -> u32 { - todo!() - } - - async fn get_tx_hash_status( - &self, - hash: hyperlane_core::H512, - ) -> Result { - match self.provider.get_transaction_receipt(hash.into()).await { - Ok(None) => Err(SubmitterError::TxHashNotFound( - "Transaction not found".to_string(), - )), - Ok(Some(receipt)) => Ok(self - .block_number_result_to_tx_hash(receipt.block_number) - .await), - Err(err) => Err(SubmitterError::TxHashNotFound(err.to_string())), - } - } -} +mod transaction; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs new file mode 100644 index 00000000000..7eec6a4d2f8 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -0,0 +1,177 @@ +use std::marker::PhantomData; +use std::sync::Arc; + +use async_trait::async_trait; +use ethers::contract::builders::ContractCall; +use ethers::prelude::U64; +use ethers::providers::Middleware; +use ethers::types::H256; +use eyre::Result; +use tracing::{info, warn}; +use uuid::Uuid; + +use hyperlane_base::settings::parser::h_eth::{BuildableWithProvider, ConnectionConf}; +use hyperlane_base::settings::{ChainConf, RawChainConf}; +use hyperlane_base::CoreMetrics; +use hyperlane_core::ContractLocator; +use hyperlane_ethereum::{EthereumReorgPeriod, EvmProviderForSubmitter, SubmitterProviderBuilder}; + +use crate::chain_tx_adapter::chains::ethereum::transaction::Precursor; +use crate::{ + chain_tx_adapter::{adapter::TxBuildingResult, AdaptsChain, EthereumTxPrecursor, GasLimit}, + error::SubmitterError, + payload::{FullPayload, PayloadDetails}, + transaction::{Transaction, TransactionStatus}, +}; + +pub struct EthereumTxAdapter { + _conf: ChainConf, + _raw_conf: RawChainConf, + provider: Box, + reorg_period: EthereumReorgPeriod, +} + +impl EthereumTxAdapter { + pub async fn new( + conf: ChainConf, + connection_conf: ConnectionConf, + raw_conf: RawChainConf, + metrics: &CoreMetrics, + ) -> eyre::Result { + let locator = ContractLocator { + domain: &conf.domain, + address: hyperlane_core::H256::zero(), + }; + let provider = conf + .build_ethereum( + &connection_conf, + &locator, + metrics, + SubmitterProviderBuilder {}, + ) + .await?; + let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; + + Ok(Self { + _conf: conf, + _raw_conf: raw_conf, + provider, + reorg_period, + }) + } + + async fn block_number_result_to_tx_hash(&self, block_number: Option) -> TransactionStatus { + let Some(block_number) = block_number else { + return TransactionStatus::PendingInclusion; + }; + let block_number = block_number.as_u64(); + match self + .provider + .get_finalized_block_number(&self.reorg_period) + .await + { + Ok(finalized_block) => { + if finalized_block as u64 >= block_number { + TransactionStatus::Finalized + } else { + TransactionStatus::Included + } + } + Err(err) => { + warn!( + ?err, + "Error checking block finality. Assuming tx is pending inclusion" + ); + TransactionStatus::PendingInclusion + } + } + } +} + +#[async_trait] +impl AdaptsChain for EthereumTxAdapter { + async fn estimate_gas_limit( + &self, + _payload: &FullPayload, + ) -> Result, SubmitterError> { + todo!() + } + + async fn build_transactions(&self, payloads: &[FullPayload]) -> Vec { + use super::transaction::TransactionFactory; + + info!(?payloads, "building transactions for payloads"); + let payloads_and_precursors = payloads + .iter() + .map(|payload| (EthereumTxPrecursor::from_payload(payload), payload)) + .collect::>(); + + let mut transactions = Vec::new(); + for (precursor, payload) in payloads_and_precursors.into_iter() { + let transaction = TransactionFactory::build(payload, precursor); + transactions.push(TxBuildingResult::new( + vec![payload.details.clone()], + Some(transaction), + )) + } + + info!(?payloads, ?transactions, "built transactions for payloads"); + transactions + } + + async fn simulate_tx(&self, _tx: &Transaction) -> Result { + todo!() + } + + async fn estimate_tx(&self, _tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { + todo!() + } + + async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { + use super::transaction::Precursor; + + info!(?tx, "submitting transaction"); + + let precursor = tx.precursor(); + let hash = self + .provider + .send(precursor.tx.clone(), precursor.function.clone()) + .await?; + + tx.tx_hashes.push(hash.into()); + + info!(?tx, "submitted transaction"); + + Ok(()) + } + + async fn get_tx_hash_status( + &self, + hash: hyperlane_core::H512, + ) -> Result { + match self.provider.get_transaction_receipt(hash.into()).await { + Ok(None) => Err(SubmitterError::TxHashNotFound( + "Transaction not found".to_string(), + )), + Ok(Some(receipt)) => Ok(self + .block_number_result_to_tx_hash(receipt.block_number) + .await), + Err(err) => Err(SubmitterError::TxHashNotFound(err.to_string())), + } + } + + async fn reverted_payloads( + &self, + _tx: &Transaction, + ) -> Result, SubmitterError> { + todo!() + } + + fn estimated_block_time(&self) -> &std::time::Duration { + todo!() + } + + fn max_batch_size(&self) -> u32 { + todo!() + } +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction.rs new file mode 100644 index 00000000000..0e8dad62bcd --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction.rs @@ -0,0 +1,5 @@ +pub use factory::TransactionFactory; +pub use precursor::Precursor; + +mod factory; +mod precursor; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/factory.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/factory.rs new file mode 100644 index 00000000000..14cb14513ea --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/factory.rs @@ -0,0 +1,22 @@ +use uuid::Uuid; + +use crate::chain_tx_adapter::chains::ethereum::precursor::EthereumTxPrecursor; +use crate::payload::FullPayload; +use crate::transaction::{Transaction, TransactionId, TransactionStatus, VmSpecificTxData}; + +pub struct TransactionFactory {} + +impl TransactionFactory { + pub fn build(payload: &FullPayload, precursor: EthereumTxPrecursor) -> Transaction { + Transaction { + id: TransactionId::new(Uuid::new_v4()), + tx_hashes: vec![], + vm_specific_data: VmSpecificTxData::Evm(precursor), + payload_details: vec![payload.details.clone()], + status: TransactionStatus::PendingInclusion, + submission_attempts: 0, + creation_timestamp: chrono::Utc::now(), + last_submission_attempt: None, + } + } +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs new file mode 100644 index 00000000000..d11c4c0c0e5 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs @@ -0,0 +1,15 @@ +use crate::chain_tx_adapter::EthereumTxPrecursor; +use crate::transaction::{Transaction, VmSpecificTxData}; + +pub trait Precursor { + fn precursor(&self) -> &EthereumTxPrecursor; +} + +impl Precursor for Transaction { + fn precursor(&self) -> &EthereumTxPrecursor { + match &self.vm_specific_data { + VmSpecificTxData::Evm(precursor) => precursor, + _ => panic!(), + } + } +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs b/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs index 307f4978e68..27acc6b1de7 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/factory.rs @@ -4,12 +4,11 @@ use std::sync::Arc; use eyre::Result; + use hyperlane_base::{ settings::{ChainConf, ChainConnectionConf, RawChainConf}, CoreMetrics, }; -use hyperlane_core::{ContractLocator, HyperlaneDomain, HyperlaneDomainProtocol, H256}; -use hyperlane_ethereum::{EvmProviderForSubmitter, SubmitterProviderBuilder}; use crate::chain_tx_adapter::{ chains::{cosmos::CosmosTxAdapter, ethereum::EthereumTxAdapter, sealevel::SealevelTxAdapter}, @@ -24,8 +23,6 @@ impl ChainTxAdapterFactory { raw_conf: &RawChainConf, metrics: &CoreMetrics, ) -> Result> { - use HyperlaneDomainProtocol::*; - let adapter: Arc = match conf.connection.clone() { ChainConnectionConf::Ethereum(connection_conf) => Arc::new( EthereumTxAdapter::new(conf.clone(), connection_conf, raw_conf.clone(), metrics) diff --git a/rust/main/submitter/src/payload_dispatcher/test_utils.rs b/rust/main/submitter/src/payload_dispatcher/test_utils.rs index 0abd2c60dcc..600e444afdb 100644 --- a/rust/main/submitter/src/payload_dispatcher/test_utils.rs +++ b/rust/main/submitter/src/payload_dispatcher/test_utils.rs @@ -3,11 +3,10 @@ use std::sync::Arc; use async_trait::async_trait; use eyre::Result; -use hyperlane_base::db::{DbResult, HyperlaneRocksDB, DB}; +use hyperlane_base::db::{HyperlaneRocksDB, DB}; use hyperlane_core::identifiers::UniqueIdentifier; use hyperlane_core::KnownHyperlaneDomain; use tokio::sync::Mutex; -use uuid::Uuid; use super::*; use crate::chain_tx_adapter::*; From 2eef44150389d558252c5c228fe41156dc5983dc Mon Sep 17 00:00:00 2001 From: Jamin <57451149+yjamin@users.noreply.github.com> Date: Wed, 7 May 2025 11:53:58 +0200 Subject: [PATCH 144/223] fix(validator): point to fixed ethers-rs fork (#6156) ### Description Fix validator announce by pointing to the new `ethers-rs` fork ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/Cargo.lock | 27 ++++++++++--------- rust/main/Cargo.toml | 10 +++---- .../hyperlane-base/src/settings/signers.rs | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index f029929efee..36d4b26a958 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -3555,7 +3555,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3569,7 +3569,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "ethers-core", "once_cell", @@ -3580,7 +3580,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -3598,7 +3598,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "Inflector", "cfg-if", @@ -3622,7 +3622,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -3636,18 +3636,18 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "arrayvec", "bytes", "cargo_metadata", "chrono", "convert_case 0.6.0", - "elliptic-curve 0.13.8", + "elliptic-curve 0.12.3", "ethabi", "generic-array 0.14.7", "hex 0.4.3", - "k256 0.13.4", + "k256 0.11.6", "once_cell", "open-fastrlp", "proc-macro2 1.0.93", @@ -3666,7 +3666,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3682,7 +3682,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3733,7 +3733,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3769,12 +3769,12 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-06#c2932761f9af1b0300d684cd51f89a5c97b73f82" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" dependencies = [ "async-trait", "coins-bip32 0.7.0", "coins-bip39 0.7.0", - "elliptic-curve 0.13.8", + "elliptic-curve 0.12.3", "eth-keystore", "ethers-core", "hex 0.4.3", @@ -6058,6 +6058,7 @@ dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", "sha2 0.10.8", + "sha3 0.10.8", ] [[package]] diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 93e91f1a368..123acd9e58f 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -219,27 +219,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-06" +tag = "2025-05-07" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-06" +tag = "2025-05-07" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-06" +tag = "2025-05-07" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-06" +tag = "2025-05-07" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-06" +tag = "2025-05-07" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" diff --git a/rust/main/hyperlane-base/src/settings/signers.rs b/rust/main/hyperlane-base/src/settings/signers.rs index 3f094e6b548..6ea6ed4a1e2 100644 --- a/rust/main/hyperlane-base/src/settings/signers.rs +++ b/rust/main/hyperlane-base/src/settings/signers.rs @@ -70,7 +70,7 @@ impl BuildableWithSignerConf for hyperlane_ethereum::Signers { Ok(match conf { SignerConf::HexKey { key } => hyperlane_ethereum::Signers::Local(LocalWallet::from( ethers::core::k256::ecdsa::SigningKey::from( - ethers::core::k256::SecretKey::from_slice(key.as_bytes()) + ethers::core::k256::SecretKey::from_be_bytes(key.as_bytes()) .context("Invalid ethereum signer key")?, ), )), From c7f1b49c61c3dfaa79f4c8418295ec7997e8218f Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Wed, 7 May 2025 15:05:26 +0100 Subject: [PATCH 145/223] fix: build-programs.sh downloading newer solana CLI releases and the old 1.14.20 version (#6158) ### Description Solana CLI releases were migrated from https://release.solana.com to https://release.anza.xyz, but along the way some old versions weren't ported over. This includes 1.14.20, which is the version we require to build the programs. A couple alternatives were considered (trying to use brew to find an older version, more directly querying github, etc), but in the end I think the best approach is to just try to emulate their old installation script and make sure that it fetches from the right github release. This PR adds the installation script adapted from https://release.anza.xyz/v1.18.18/install with the following changes: - The version is hardcoded to 1.14.20 - The URL is hardcoded to read from the old solana-labs repo - All agave references are replaced with solana It also moves away from using the old release.solana.com url to release.anza.xyz when installing newer versions (like 1.18.18). ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/sealevel/programs/build-programs.sh | 6 +- .../programs/install-solana-1.14.20.sh | 202 ++++++++++++++++++ 2 files changed, 207 insertions(+), 1 deletion(-) create mode 100755 rust/sealevel/programs/install-solana-1.14.20.sh diff --git a/rust/sealevel/programs/build-programs.sh b/rust/sealevel/programs/build-programs.sh index c6dcaef4dc5..32c94641402 100755 --- a/rust/sealevel/programs/build-programs.sh +++ b/rust/sealevel/programs/build-programs.sh @@ -62,7 +62,11 @@ get_current_solana_cli_version () { set_solana_cli_version () { NEW_VERSION=$1 - sh -c "$(curl -sSfL https://release.solana.com/v$NEW_VERSION/install)" + if [ $NEW_VERSION == $SOLANA_CLI_VERSION_FOR_BUILDING_PROGRAMS ]; then + ./install-solana-1.14.20.sh + else + sh -c "$(curl -sSfL https://release.anza.xyz/v$NEW_VERSION/install)" + fi } log () { diff --git a/rust/sealevel/programs/install-solana-1.14.20.sh b/rust/sealevel/programs/install-solana-1.14.20.sh new file mode 100755 index 00000000000..1db89c92190 --- /dev/null +++ b/rust/sealevel/programs/install-solana-1.14.20.sh @@ -0,0 +1,202 @@ +#!/bin/sh + +# Solana CLI releases were migrated from https://release.solana.com to +# https://release.anza.xyz, but along the way some old versions weren't ported over. +# This includes 1.14.20, which is the version we require to build the programs. + +# This script is adapted from https://release.anza.xyz/v1.18.18/install with the following +# changes: +# - The version is hardcoded to 1.14.20 +# - The URL is hardcoded to read from the old solana-labs repo +# - All agave references are replaced with solana + +SOLANA_RELEASE=v1.14.20 +SOLANA_INSTALL_INIT_ARGS=v1.14.20 +SOLANA_DOWNLOAD_ROOT=https://github.com/solana-labs/solana/releases/download/ + +# Copyright 2016 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +# This is just a little script that can be downloaded from the internet to +# install solana-install. It just does platform detection, downloads the installer +# and runs it. + +{ # this ensures the entire script is downloaded # + +if [ -z "$SOLANA_DOWNLOAD_ROOT" ]; then + SOLANA_DOWNLOAD_ROOT="https://github.com/solana-labs/solana/releases/download/" +fi +GH_LATEST_RELEASE="https://api.github.com/repos/solana-labs/solana/releases/latest" + +set -e + +usage() { + cat 1>&2 < --pubkey + +FLAGS: + -h, --help Prints help information + --no-modify-path Don't configure the PATH environment variable + +OPTIONS: + -d, --data-dir Directory to store install data + -u, --url JSON RPC URL for the solana cluster + -p, --pubkey Public key of the update manifest +EOF +} + +main() { + downloader --check + need_cmd uname + need_cmd mktemp + need_cmd chmod + need_cmd mkdir + need_cmd rm + need_cmd sed + need_cmd grep + + for arg in "$@"; do + case "$arg" in + -h|--help) + usage + exit 0 + ;; + *) + ;; + esac + done + + _ostype="$(uname -s)" + _cputype="$(uname -m)" + + case "$_ostype" in + Linux) + _ostype=unknown-linux-gnu + ;; + Darwin) + if [[ $_cputype = arm64 ]]; then + _cputype=aarch64 + fi + _ostype=apple-darwin + ;; + *) + err "machine architecture is currently unsupported" + ;; + esac + TARGET="${_cputype}-${_ostype}" + + temp_dir="$(mktemp -d 2>/dev/null || ensure mktemp -d -t solana-install-init)" + ensure mkdir -p "$temp_dir" + + # Check for SOLANA_RELEASE environment variable override. Otherwise fetch + # the latest release tag from github + if [ -n "$SOLANA_RELEASE" ]; then + release="$SOLANA_RELEASE" + else + release_file="$temp_dir/release" + printf 'looking for latest release\n' 1>&2 + ensure downloader "$GH_LATEST_RELEASE" "$release_file" + release=$(\ + grep -m 1 \"tag_name\": "$release_file" \ + | sed -ne 's/^ *"tag_name": "\([^"]*\)",$/\1/p' \ + ) + if [ -z "$release" ]; then + err 'Unable to figure latest release' + fi + fi + + download_url="$SOLANA_DOWNLOAD_ROOT/$release/solana-install-init-$TARGET" + solana_install_init="$temp_dir/solana-install-init" + + printf 'downloading %s installer\n' "$release" 1>&2 + + ensure mkdir -p "$temp_dir" + ensure downloader "$download_url" "$solana_install_init" + ensure chmod u+x "$solana_install_init" + if [ ! -x "$solana_install_init" ]; then + printf '%s\n' "Cannot execute $solana_install_init (likely because of mounting /tmp as noexec)." 1>&2 + printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./solana-install-init." 1>&2 + exit 1 + fi + + if [ -z "$1" ]; then + #shellcheck disable=SC2086 + ignore "$solana_install_init" $SOLANA_INSTALL_INIT_ARGS + else + ignore "$solana_install_init" "$@" + fi + retval=$? + + ignore rm "$solana_install_init" + ignore rm -rf "$temp_dir" + + return "$retval" +} + +err() { + printf 'solana-install-init: %s\n' "$1" >&2 + exit 1 +} + +need_cmd() { + if ! check_cmd "$1"; then + err "need '$1' (command not found)" + fi +} + +check_cmd() { + command -v "$1" > /dev/null 2>&1 +} + +# Run a command that should never fail. If the command fails execution +# will immediately terminate with an error showing the failing +# command. +ensure() { + if ! "$@"; then + err "command failed: $*" + fi +} + +# This is just for indicating that commands' results are being +# intentionally ignored. Usually, because it's being executed +# as part of error handling. +ignore() { + "$@" +} + +# This wraps curl or wget. Try curl first, if not installed, +# use wget instead. +downloader() { + if check_cmd curl; then + program=curl + elif check_cmd wget; then + program=wget + else + program='curl or wget' # to be used in error message of need_cmd + fi + + if [ "$1" = --check ]; then + need_cmd "$program" + elif [ "$program" = curl ]; then + curl -sSfL "$1" -o "$2" + elif [ "$program" = wget ]; then + wget "$1" -O "$2" + else + err "Unknown downloader" # should not reach here + fi +} + +main "$@" + +} # this ensures the entire script is downloaded # From 31ee1c6eae8148186cdb124c68f83aa3affc5d12 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Wed, 7 May 2025 11:18:19 -0400 Subject: [PATCH 146/223] feat(sdk): fiat collateral token derivation support in sdk [eng-1552] (#6143) ### Description This PR updates the `EvmERC20WarpRouteReader` to correctly derive the type of a fiat collateral warp route deployment that was incorrectly inferred as collateral. ### Drive-by changes - Refactors the `fetchTokenConfig` to delegate token config derivation to a separate function instead of having the whole derivation logic in one place with nested if statements ### Related issues - Fixes [ENG-1552](https://linear.app/hyperlane-xyz/issue/ENG-1552/add-derivation-support-for-fiat-collateral-in-the-sdk) ### Backward compatibility - YES ### Testing - Manual - e2e - unit --- .changeset/wise-hairs-hope.md | 5 + .../EvmERC20WarpRouteReader.hardhat-test.ts | 38 +++ .../sdk/src/token/EvmERC20WarpRouteReader.ts | 296 +++++++++++++----- typescript/sdk/src/token/configUtils.ts | 1 + 4 files changed, 262 insertions(+), 78 deletions(-) create mode 100644 .changeset/wise-hairs-hope.md diff --git a/.changeset/wise-hairs-hope.md b/.changeset/wise-hairs-hope.md new file mode 100644 index 00000000000..6d41b57f697 --- /dev/null +++ b/.changeset/wise-hairs-hope.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Adds fiatCollateral token on chain config derivation logic as it was incorrectly inferred as collateral diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts index 48c6364c36a..6b692758873 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts @@ -8,6 +8,8 @@ import { ERC20Test__factory, ERC4626, ERC4626Test__factory, + FiatTokenTest, + FiatTokenTest__factory, Mailbox, Mailbox__factory, XERC20LockboxTest__factory, @@ -54,6 +56,7 @@ describe('ERC20WarpRouterReader', async () => { let mailbox: Mailbox; let evmERC20WarpRouteReader: EvmERC20WarpRouteReader; let vault: ERC4626; + let collateralFiatToken: FiatTokenTest; before(async () => { [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); @@ -80,6 +83,14 @@ describe('ERC20WarpRouterReader', async () => { const vaultFactory = new ERC4626Test__factory(signer); vault = await vaultFactory.deploy(token.address, TOKEN_NAME, TOKEN_NAME); + + const fiatCollateralFactory = new FiatTokenTest__factory(signer); + collateralFiatToken = await fiatCollateralFactory.deploy( + TOKEN_NAME, + TOKEN_NAME, + TOKEN_SUPPLY, + TOKEN_DECIMALS, + ); }); beforeEach(async () => { @@ -412,6 +423,33 @@ describe('ERC20WarpRouterReader', async () => { expect(derivedConfig.token).to.equal(token.address); }); + // FiatTokenTest + it('should derive collateral fiat token type correctly', async () => { + // Create config + const config: WarpRouteDeployConfigMailboxRequired = { + [chain]: { + type: TokenType.collateralFiat, + token: collateralFiatToken.address, + ...baseConfig, + }, + }; + // Deploy with config + const warpRoute = await deployer.deploy(config); + // Derive config and check if each value matches + const derivedConfig = await evmERC20WarpRouteReader.deriveWarpRouteConfig( + warpRoute[chain].collateralFiat.address, + ); + + assert( + derivedConfig.type === TokenType.collateralFiat, + `Must be ${TokenType.collateralFiat}`, + ); + expect(derivedConfig.type).to.equal(config[chain].type); + expect(derivedConfig.mailbox).to.equal(config[chain].mailbox); + expect(derivedConfig.owner).to.equal(config[chain].owner); + expect(derivedConfig.token).to.equal(collateralFiatToken.address); + }); + it('should return 0x0 if ism is not set onchain', async () => { // Create config const config: WarpRouteDeployConfigMailboxRequired = { diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 1d31f4c3e71..78a5b9e25b4 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -7,6 +7,8 @@ import { HypERC4626OwnerCollateral__factory, HypERC4626__factory, HypXERC20Lockbox__factory, + HypXERC20__factory, + IFiatToken__factory, IXERC20__factory, ProxyAdmin__factory, TokenRouter__factory, @@ -36,8 +38,10 @@ import { HyperlaneReader } from '../utils/HyperlaneReader.js'; import { isProxy, proxyAdmin } from './../deploy/proxy.js'; import { NON_ZERO_SENDER_ADDRESS, TokenType } from './config.js'; import { + CollateralTokenConfig, DerivedTokenRouterConfig, HypTokenConfig, + HypTokenConfigSchema, TokenMetadata, XERC20TokenMetadata, } from './types.js'; @@ -47,6 +51,13 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { protected readonly logger = rootLogger.child({ module: 'EvmERC20WarpRouteReader', }); + + // Using null instead of undefined to force + // a compile error when adding a new token type + protected readonly deriveTokenConfigMap: Record< + TokenType, + ((address: Address) => Promise) | null + >; evmHookReader: EvmHookReader; evmIsmReader: EvmIsmReader; @@ -58,6 +69,26 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { super(multiProvider, chain); this.evmHookReader = new EvmHookReader(multiProvider, chain, concurrency); this.evmIsmReader = new EvmIsmReader(multiProvider, chain, concurrency); + + this.deriveTokenConfigMap = { + [TokenType.XERC20]: this.deriveHypXERC20TokenConfig.bind(this), + [TokenType.XERC20Lockbox]: + this.deriveHypXERC20LockboxTokenConfig.bind(this), + [TokenType.collateral]: this.deriveHypCollateralTokenConfig.bind(this), + [TokenType.collateralFiat]: + this.deriveHypCollateralFiatTokenConfig.bind(this), + [TokenType.collateralVault]: + this.deriveHypCollateralVaultTokenConfig.bind(this), + [TokenType.collateralVaultRebase]: + this.deriveHypCollateralVaultRebaseTokenConfig.bind(this), + [TokenType.native]: this.deriveHypNativeTokenConfig.bind(this), + [TokenType.synthetic]: this.deriveHypSyntheticTokenConfig.bind(this), + [TokenType.syntheticRebase]: + this.deriveHypSyntheticRebaseConfig.bind(this), + [TokenType.nativeScaled]: null, + [TokenType.collateralUri]: null, + [TokenType.syntheticUri]: null, + }; } /** @@ -141,12 +172,38 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { await warpRoute[method](); if (tokenType === TokenType.collateral) { const wrappedToken = await warpRoute.wrappedToken(); - const xerc20 = IXERC20__factory.connect(wrappedToken, this.provider); try { + const xerc20 = IXERC20__factory.connect( + wrappedToken, + this.provider, + ); await xerc20['mintingCurrentLimitOf(address)'](warpRouteAddress); return TokenType.XERC20; - // eslint-disable-next-line no-empty - } catch {} + } catch (error) { + this.logger.debug( + `Warp route token at address "${warpRouteAddress}" on chain "${this.chain}" is not a ${TokenType.XERC20}`, + error, + ); + } + + try { + const fiatToken = IFiatToken__factory.connect( + wrappedToken, + this.provider, + ); + + // Simulate minting tokens from the warp route contract + await fiatToken.callStatic.mint(NON_ZERO_SENDER_ADDRESS, 1, { + from: warpRouteAddress, + }); + + return TokenType.collateralFiat; + } catch (error) { + this.logger.debug( + `Warp route token at address "${warpRouteAddress}" on chain "${this.chain}" is not a ${TokenType.collateralFiat}`, + error, + ); + } } return tokenType as TokenType; } catch { @@ -261,87 +318,170 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { type: TokenType, warpRouteAddress: Address, ): Promise { - if ( - type === TokenType.collateral || - type === TokenType.collateralVault || - type === TokenType.collateralVaultRebase || - type === TokenType.XERC20 || - type === TokenType.XERC20Lockbox - ) { - let xerc20Token: Address | undefined; - let lockbox: Address | undefined; - let token: Address; - let xERC20Metadata: XERC20TokenMetadata | {} = {}; - - if (type === TokenType.XERC20Lockbox) { - // XERC20Lockbox is a special case of collateral, we will fetch it from the xerc20 contract - const hypXERC20Lockbox = HypXERC20Lockbox__factory.connect( - warpRouteAddress, - this.provider, - ); - xerc20Token = await hypXERC20Lockbox.xERC20(); - token = xerc20Token; - lockbox = await hypXERC20Lockbox.lockbox(); - } else { - const erc20 = HypERC20Collateral__factory.connect( - warpRouteAddress, - this.provider, - ); - token = await erc20.wrappedToken(); - } + const deriveFunction = this.deriveTokenConfigMap[type]; + if (!deriveFunction) { + throw new Error( + `Provided unsupported token type "${type}" when fetching token metadata on chain "${this.chain}" at address "${warpRouteAddress}"`, + ); + } - const { name, symbol, decimals } = await this.fetchERC20Metadata(token); + const config = await deriveFunction(warpRouteAddress); + return HypTokenConfigSchema.parse(config); + } - if (type === TokenType.XERC20 || type === TokenType.XERC20Lockbox) { - xERC20Metadata = await this.fetchXERC20Config(token, warpRouteAddress); - } + private async deriveHypXERC20TokenConfig( + hypTokenAddress: Address, + ): Promise { + const hypXERC20TokenInstance = HypXERC20__factory.connect( + hypTokenAddress, + this.provider, + ); - return { - ...xERC20Metadata, - type, - name, - symbol, - decimals, - token: lockbox || token, - }; - } else if ( - type === TokenType.synthetic || - type === TokenType.syntheticRebase - ) { - const baseMetadata = await this.fetchERC20Metadata(warpRouteAddress); - - if (type === TokenType.syntheticRebase) { - const hypERC4626 = HypERC4626__factory.connect( - warpRouteAddress, - this.provider, - ); - const collateralChainName = this.multiProvider.getChainName( - await hypERC4626.collateralDomain(), - ); - return { type, ...baseMetadata, collateralChainName }; - } + const collateralTokenAddress = await hypXERC20TokenInstance.wrappedToken(); + const [erc20TokenMetadata, xERC20Metadata] = await Promise.all([ + this.fetchERC20Metadata(collateralTokenAddress), + this.fetchXERC20Config(collateralTokenAddress, hypTokenAddress), + ]); - return { type, ...baseMetadata }; - } else if (type === TokenType.native) { - const chainMetadata = this.multiProvider.getChainMetadata(this.chain); - if (chainMetadata.nativeToken) { - const { name, symbol, decimals } = chainMetadata.nativeToken; - return { - type, - name, - symbol, - decimals, - }; - } else { - throw new Error( - `Warp route config specifies native token but chain metadata for ${this.chain} does not provide native token details`, - ); - } - } else { + return { + ...erc20TokenMetadata, + type: TokenType.XERC20, + token: collateralTokenAddress, + xERC20: xERC20Metadata.xERC20, + }; + } + + private async deriveHypXERC20LockboxTokenConfig( + hypTokenAddress: Address, + ): Promise { + const hypXERC20TokenLockboxTokenInstance = + HypXERC20Lockbox__factory.connect(hypTokenAddress, this.provider); + + const xerc20TokenAddress = + await hypXERC20TokenLockboxTokenInstance.xERC20(); + const [erc20TokenMetadata, xERC20Metadata, lockbox] = await Promise.all([ + this.fetchERC20Metadata(xerc20TokenAddress), + this.fetchXERC20Config(xerc20TokenAddress, hypTokenAddress), + hypXERC20TokenLockboxTokenInstance.lockbox(), + ]); + + return { + ...erc20TokenMetadata, + type: TokenType.XERC20Lockbox, + token: lockbox, + xERC20: xERC20Metadata.xERC20, + }; + } + + private async deriveHypCollateralTokenConfig( + hypToken: Address, + ): Promise { + const hypCollateralTokenInstance = HypERC20Collateral__factory.connect( + hypToken, + this.provider, + ); + + const collateralTokenAddress = + await hypCollateralTokenInstance.wrappedToken(); + const erc20TokenMetadata = await this.fetchERC20Metadata( + collateralTokenAddress, + ); + + return { + ...erc20TokenMetadata, + type: TokenType.collateral, + token: collateralTokenAddress, + }; + } + + private async deriveHypCollateralFiatTokenConfig( + hypToken: Address, + ): Promise { + const erc20TokenMetadata = + await this.deriveHypCollateralTokenConfig(hypToken); + + return { + ...erc20TokenMetadata, + type: TokenType.collateralFiat, + }; + } + + private async deriveHypCollateralVaultTokenConfig( + hypToken: Address, + ): Promise { + const erc20TokenMetadata = + await this.deriveHypCollateralTokenConfig(hypToken); + + return { + ...erc20TokenMetadata, + type: TokenType.collateralVault, + }; + } + + private async deriveHypCollateralVaultRebaseTokenConfig( + hypToken: Address, + ): Promise { + const erc20TokenMetadata = + await this.deriveHypCollateralTokenConfig(hypToken); + + return { + ...erc20TokenMetadata, + type: TokenType.collateralVaultRebase, + }; + } + + private async deriveHypSyntheticTokenConfig( + hypTokenAddress: Address, + ): Promise { + const erc20TokenMetadata = await this.fetchERC20Metadata(hypTokenAddress); + + return { + ...erc20TokenMetadata, + type: TokenType.synthetic, + }; + } + + private async deriveHypNativeTokenConfig( + _address: Address, + ): Promise { + const chainMetadata = this.multiProvider.getChainMetadata(this.chain); + if (!chainMetadata.nativeToken) { throw new Error( - `Unsupported token type ${type} when fetching token metadata`, + `Warp route config specifies native token but chain metadata for chain "${this.chain}" does not provide native token details`, ); } + + const { name, symbol, decimals } = chainMetadata.nativeToken; + return { + type: TokenType.native, + name, + symbol, + decimals, + isNft: false, + }; + } + + private async deriveHypSyntheticRebaseConfig( + hypTokenAddress: Address, + ): Promise { + const hypERC4626 = HypERC4626__factory.connect( + hypTokenAddress, + this.provider, + ); + + const [erc20TokenMetadata, collateralDomainId] = await Promise.all([ + this.fetchERC20Metadata(hypTokenAddress), + hypERC4626.collateralDomain(), + ]); + + const collateralChainName = + this.multiProvider.getChainName(collateralDomainId); + + return { + ...erc20TokenMetadata, + type: TokenType.syntheticRebase, + collateralChainName, + }; } async fetchERC20Metadata(tokenAddress: Address): Promise { @@ -352,7 +492,7 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { erc20.decimals(), ]); - return { name, symbol, decimals }; + return { name, symbol, decimals, isNft: false }; } async fetchRemoteRouters(warpRouteAddress: Address): Promise { diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index bb2b8289722..b2fddf41c2f 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -119,6 +119,7 @@ export async function expandWarpDeployConfig( proxyAdmin: isDeployedAsProxyByChain[chain] ? { owner: config.owner } : undefined, + isNft: false, // User-specified config takes precedence ...config, From 462f0da164220e40b5ffcf4547417cf04d16f561 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 7 May 2025 16:59:02 +0100 Subject: [PATCH 147/223] feat(submitter): evm GasLimitEstimator (#6159) ### Description Implement `GasLimitEstimator` component, by refactoring logic out of `rust/main/chains/hyperlane-ethereum/src/tx.rs` ### Drive-by changes - The logic for the `TxStatusChecker` and `GasLimitEstimator` is moved into their own modules. The motivation is to use dependency injection for ease of testing in the future - The logic for building a `ContractCall` from its subcomponent fields is refactored and reused ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6134 ### Backward compatibility Yes ### Testing Not yet --------- Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> --- .../src/rpc_clients/provider.rs | 58 +++++++++++-- .../chains/ethereum/adapter.rs | 60 +++++-------- .../ethereum/adapter/gas_limit_estimator.rs | 84 +++++++++++++++++++ .../ethereum/adapter/tx_status_checker.rs | 55 ++++++++++++ .../chains/ethereum/transaction/precursor.rs | 8 ++ 5 files changed, 215 insertions(+), 50 deletions(-) create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/tx_status_checker.rs diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index a67862102a3..58ea8a08d09 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -6,6 +6,7 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; +use ethers::types::{Block, H256 as EthersH256}; use ethers::{prelude::Middleware, types::TransactionReceipt}; use ethers_contract::builders::ContractCall; use ethers_core::abi::Function; @@ -32,6 +33,23 @@ pub struct EthereumProvider { domain: HyperlaneDomain, } +impl EthereumProvider { + /// Create a ContractCall object for a given transaction and function. + pub fn build_contract_call( + &self, + tx: TypedTransaction, + function: Function, + ) -> ContractCall { + ContractCall { + tx, + function, + block: None, + client: self.provider.clone(), + datatype: PhantomData::<()>, + } + } +} + impl HyperlaneChain for EthereumProvider where M: Middleware + 'static, @@ -63,8 +81,18 @@ pub trait EvmProviderForSubmitter: Send + Sync { reorg_period: &EthereumReorgPeriod, ) -> ChainResult; + /// Get the block for a given block number + async fn get_block(&self, block_number: BlockNumber) -> ChainResult>>; + + /// Estimate the gas limit for a transaction + async fn estimate_gas_limit( + &self, + tx: &TypedTransaction, + function: &Function, + ) -> Result; + /// Send transaction into blockchain - async fn send(&self, tx: TypedTransaction, function: Function) -> ChainResult; + async fn send(&self, tx: &TypedTransaction, function: &Function) -> ChainResult; } #[async_trait] @@ -91,15 +119,27 @@ where get_finalized_block_number(&*self.provider, reorg_period).await } - async fn send(&self, tx: TypedTransaction, function: Function) -> ChainResult { - let contract_call = ContractCall { - tx, - function, - block: None, - client: self.provider.clone(), - datatype: PhantomData::<()>, - }; + async fn get_block(&self, block_number: BlockNumber) -> ChainResult>> { + let block = self + .provider + .get_block(block_number) + .await + .map_err(ChainCommunicationError::from_other)?; + Ok(block) + } + + async fn estimate_gas_limit( + &self, + tx: &TypedTransaction, + function: &Function, + ) -> Result { + let contract_call = self.build_contract_call(tx.clone(), function.clone()); + let gas_limit = contract_call.estimate_gas().await?.into(); + Ok(gas_limit) + } + async fn send(&self, tx: &TypedTransaction, function: &Function) -> ChainResult { + let contract_call = self.build_contract_call(tx.clone(), function.clone()); let pending = contract_call .send() .await diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs index 7eec6a4d2f8..e431a8f1a3f 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -24,8 +24,12 @@ use crate::{ transaction::{Transaction, TransactionStatus}, }; +mod gas_limit_estimator; +mod tx_status_checker; + pub struct EthereumTxAdapter { - _conf: ChainConf, + conf: ChainConf, + connection_conf: ConnectionConf, _raw_conf: RawChainConf, provider: Box, reorg_period: EthereumReorgPeriod, @@ -53,39 +57,13 @@ impl EthereumTxAdapter { let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; Ok(Self { - _conf: conf, + conf, + connection_conf, _raw_conf: raw_conf, provider, reorg_period, }) } - - async fn block_number_result_to_tx_hash(&self, block_number: Option) -> TransactionStatus { - let Some(block_number) = block_number else { - return TransactionStatus::PendingInclusion; - }; - let block_number = block_number.as_u64(); - match self - .provider - .get_finalized_block_number(&self.reorg_period) - .await - { - Ok(finalized_block) => { - if finalized_block as u64 >= block_number { - TransactionStatus::Finalized - } else { - TransactionStatus::Included - } - } - Err(err) => { - warn!( - ?err, - "Error checking block finality. Assuming tx is pending inclusion" - ); - TransactionStatus::PendingInclusion - } - } - } } #[async_trait] @@ -123,8 +101,16 @@ impl AdaptsChain for EthereumTxAdapter { todo!() } - async fn estimate_tx(&self, _tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { - todo!() + async fn estimate_tx(&self, tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { + let precursor = tx.precursor_mut(); + gas_limit_estimator::estimate_tx( + &self.provider, + precursor, + &self.connection_conf.transaction_overrides, + &self.conf.domain, + true, + ) + .await } async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { @@ -135,7 +121,7 @@ impl AdaptsChain for EthereumTxAdapter { let precursor = tx.precursor(); let hash = self .provider - .send(precursor.tx.clone(), precursor.function.clone()) + .send(&precursor.tx, &precursor.function) .await?; tx.tx_hashes.push(hash.into()); @@ -149,15 +135,7 @@ impl AdaptsChain for EthereumTxAdapter { &self, hash: hyperlane_core::H512, ) -> Result { - match self.provider.get_transaction_receipt(hash.into()).await { - Ok(None) => Err(SubmitterError::TxHashNotFound( - "Transaction not found".to_string(), - )), - Ok(Some(receipt)) => Ok(self - .block_number_result_to_tx_hash(receipt.block_number) - .await), - Err(err) => Err(SubmitterError::TxHashNotFound(err.to_string())), - } + tx_status_checker::get_tx_hash_status(&self.provider, hash, &self.reorg_period).await } async fn reverted_payloads( diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs new file mode 100644 index 00000000000..fe1110421b1 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs @@ -0,0 +1,84 @@ +// the evm provider-building logic returns a box. `EvmProviderForSubmitter` is only implemented for the underlying type rather than the boxed type. +// implementing the trait for the boxed type would require a lot of boilerplate code. +#![allow(clippy::borrowed_box)] + +use ethers::{providers::ProviderError, types::BlockNumber}; +use hyperlane_core::{ChainCommunicationError, ChainResult, HyperlaneDomain, U256}; +use hyperlane_ethereum::{EvmProviderForSubmitter, TransactionOverrides}; +use tracing::{debug, warn}; + +use crate::{chain_tx_adapter::EthereumTxPrecursor, transaction::Transaction, SubmitterError}; + +/// An amount of gas to add to the estimated gas limit +pub const GAS_LIMIT_BUFFER: u32 = 75_000; + +// A multiplier to apply to the estimated gas limit, i.e. 10%. +pub const DEFAULT_GAS_LIMIT_MULTIPLIER_NUMERATOR: u32 = 11; +pub const DEFAULT_GAS_LIMIT_MULTIPLIER_DENOMINATOR: u32 = 10; + +pub async fn estimate_tx( + provider: &Box, + tx_precursor: &mut EthereumTxPrecursor, + transaction_overrides: &TransactionOverrides, + domain: &HyperlaneDomain, + with_gas_limit_overrides: bool, +) -> std::result::Result<(), SubmitterError> { + // either use the pre-estimated gas limit or estimate it + let mut estimated_gas_limit: U256 = match tx_precursor.tx.gas() { + Some(&estimate) => estimate.into(), + None => { + provider + .estimate_gas_limit(&tx_precursor.tx, &tx_precursor.function) + .await? + } + }; + + if with_gas_limit_overrides { + estimated_gas_limit = apply_gas_estimate_buffer(estimated_gas_limit, domain)?; + if let Some(gas_limit) = transaction_overrides.gas_limit { + estimated_gas_limit = estimated_gas_limit.max(gas_limit) + } + } + let gas_limit = estimated_gas_limit; + + // Cap the gas limit to the block gas limit + let latest_block = provider + .get_block(BlockNumber::Latest) + .await + .map_err(ChainCommunicationError::from_other)? + .ok_or_else(|| eyre::eyre!("Latest block not found"))?; + + let block_gas_limit: U256 = latest_block.gas_limit.into(); + let gas_limit = if gas_limit > block_gas_limit { + warn!( + ?gas_limit, + ?block_gas_limit, + "Gas limit for transaction is higher than the block gas limit. Capping it to the block gas limit." + ); + block_gas_limit + } else { + gas_limit + }; + debug!(?estimated_gas_limit, gas_override=?transaction_overrides.gas_limit, used_gas_limit=?gas_limit, "Gas limit set for transaction"); + + tx_precursor.tx.set_gas(gas_limit); + Ok(()) +} + +pub fn apply_gas_estimate_buffer(gas: U256, domain: &HyperlaneDomain) -> ChainResult { + // Arbitrum Nitro chains use 2d fees and are especially prone to costs increasing + // by the time the transaction lands on chain, requiring a higher gas limit. + // In this case, we apply a multiplier to the gas estimate. + let gas = if domain.is_arbitrum_nitro() { + gas.saturating_mul(DEFAULT_GAS_LIMIT_MULTIPLIER_NUMERATOR.into()) + .checked_div(DEFAULT_GAS_LIMIT_MULTIPLIER_DENOMINATOR.into()) + .ok_or_else(|| { + ChainCommunicationError::from_other_str("Gas estimate buffer divide by zero") + })? + } else { + gas + }; + + // Always add a flat buffer + Ok(gas.saturating_add(GAS_LIMIT_BUFFER.into())) +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/tx_status_checker.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/tx_status_checker.rs new file mode 100644 index 00000000000..c5b96b6ba09 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/tx_status_checker.rs @@ -0,0 +1,55 @@ +// the evm provider-building logic returns a box. `EvmProviderForSubmitter` is only implemented for the underlying type rather than the boxed type. +// implementing the trait for the boxed type would require a lot of boilerplate code. +#![allow(clippy::borrowed_box)] + +use ethers::types::U64; +use hyperlane_ethereum::{EthereumReorgPeriod, EvmProviderForSubmitter}; +use tracing::warn; + +use crate::{SubmitterError, TransactionStatus}; + +async fn block_number_result_to_tx_status( + provider: &Box, + block_number: Option, + reorg_period: &EthereumReorgPeriod, +) -> TransactionStatus { + let Some(block_number) = block_number else { + return TransactionStatus::PendingInclusion; + }; + let block_number = block_number.as_u64(); + match provider.get_finalized_block_number(reorg_period).await { + Ok(finalized_block) => { + if finalized_block as u64 >= block_number { + TransactionStatus::Finalized + } else { + TransactionStatus::Included + } + } + Err(err) => { + warn!( + ?err, + "Error checking block finality. Assuming tx is pending inclusion" + ); + TransactionStatus::PendingInclusion + } + } +} + +pub async fn get_tx_hash_status( + provider: &Box, + hash: hyperlane_core::H512, + reorg_period: &EthereumReorgPeriod, +) -> Result { + match provider.get_transaction_receipt(hash.into()).await { + Ok(None) => Err(SubmitterError::TxHashNotFound( + "Transaction not found".to_string(), + )), + Ok(Some(receipt)) => { + Ok( + block_number_result_to_tx_status(provider, receipt.block_number, reorg_period) + .await, + ) + } + Err(err) => Err(SubmitterError::TxHashNotFound(err.to_string())), + } +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs index d11c4c0c0e5..ee4473cc10d 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/transaction/precursor.rs @@ -3,6 +3,7 @@ use crate::transaction::{Transaction, VmSpecificTxData}; pub trait Precursor { fn precursor(&self) -> &EthereumTxPrecursor; + fn precursor_mut(&mut self) -> &mut EthereumTxPrecursor; } impl Precursor for Transaction { @@ -12,4 +13,11 @@ impl Precursor for Transaction { _ => panic!(), } } + + fn precursor_mut(&mut self) -> &mut EthereumTxPrecursor { + match &mut self.vm_specific_data { + VmSpecificTxData::Evm(precursor) => precursor, + _ => panic!(), + } + } } From 764642b26b20c825d76031d4b2c688843050a3f2 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 7 May 2025 17:08:57 +0100 Subject: [PATCH 148/223] feat: may 6 testnet batch (#6151) ### Description - deploy to nobletestnet, megaethtestnet, basecamptestnet, bepolia - remove berabartio, camptestnet, treasuretopaz ### Drive-by changes ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/831 ### Backward compatibility ### Testing manual - base -> megaeth -> op -> basecamp -> monad -> bepolia -> base --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 20 + rust/main/config/testnet_config.json | 466 +++++++++++------- rust/main/hyperlane-core/src/chain.rs | 7 +- .../environments/testnet4/chain-config.json | 73 --- .../hyperlane/multisig-config.json | 14 - .../hyperlane/multisig-config.json | 14 - .../config/environments/testnet4/agent.ts | 45 +- .../testnet4/aw-validators/hyperlane.json | 21 +- .../testnet4/core/verification.json | 344 ++++++++----- .../config/environments/testnet4/funding.ts | 8 +- .../environments/testnet4/gasPrices.json | 42 +- .../testnet4/ism/verification.json | 302 ++++++++---- .../middleware/accounts/verification.json | 21 - .../config/environments/testnet4/owners.ts | 3 + .../testnet4/supportedChainNames.ts | 6 +- .../environments/testnet4/tokenPrices.json | 6 +- .../environments/testnet4/validators.ts | 65 ++- typescript/infra/src/config/chain.ts | 1 - typescript/sdk/src/consts/multisigIsm.ts | 60 ++- typescript/sdk/src/metadata/agentConfig.ts | 12 +- 21 files changed, 929 insertions(+), 603 deletions(-) diff --git a/.registryrc b/.registryrc index 3882652e012..3201a719ef1 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -3206b17e90450cc95ca8a536deda2ec85b1fb47f +93f9850ce4b89f89b3d4ec7c3cc36500709e507f diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 1ea422d42b0..71812ec7fbd 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -71,6 +71,7 @@ "aggregationHook": "0xe0cb37cFc47296f1c4eD77EFf92Aed478644d10c", "blockExplorers": [ { + "apiKey": "QAI5SWBNHJSFAN6KMS9RC5JGFKV2IYD2Z5", "apiUrl": "https://api.arbiscan.io/api", "family": "etherscan", "name": "Arbiscan", @@ -143,6 +144,7 @@ "aggregationHook": "0x0165a22BA489F7DA37DAf6397781777D9FCB5708", "blockExplorers": [ { + "apiKey": "NIF3616T2AP6EHYWIHJEKR1HCMA2K7D96X", "apiUrl": "https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api", "family": "routescan", "name": "SnowTrace", @@ -222,6 +224,7 @@ "aggregationHook": "0x13f3d4B0Ee0a713430fded9E18f7fb6c91A6E41F", "blockExplorers": [ { + "apiKey": "R8CVTG7HDJYD5JDV2GSD5TGQH3J2KJDSSY", "apiUrl": "https://api.basescan.org/api", "family": "etherscan", "name": "BaseScan", @@ -442,6 +445,7 @@ "aggregationHook": "0x402Fc106576462a892355d69ACF03D46A888ae88", "blockExplorers": [ { + "apiKey": "NXSXUUUAUDD5SYGQUIVS9TZTBCEG6X5THY", "apiUrl": "https://api.bscscan.com/api", "family": "etherscan", "name": "BscScan", @@ -524,6 +528,7 @@ "aggregationHook": "0xc65890329066FB20c339Bc5C22f1756e9D3a4fF5", "blockExplorers": [ { + "apiKey": "IC1UCY8JWIYFWGPCEEE58HKFIFEVN73PVV", "apiUrl": "https://api.celoscan.io/api", "family": "etherscan", "name": "CeloScan", @@ -903,6 +908,7 @@ "aggregationHook": "0xb87AC8EA4533AE017604E44470F7c1E550AC6F10", "blockExplorers": [ { + "apiKey": "CYUPN3Q66JIMRGQWYUDXJKQH4SX8YIYZMW", "apiUrl": "https://api.etherscan.io/api", "family": "etherscan", "name": "Etherscan", @@ -990,6 +996,7 @@ "aggregationHook": "0xD7ff06cDd83642D648baF0d36f77e79349120dA4", "blockExplorers": [ { + "apiKey": "X3G6FVJU5VEZXVNQFRX52EQ5FEVP8IPR6F", "apiUrl": "https://api.fraxscan.com/api", "family": "etherscan", "name": "Fraxscan", @@ -1132,6 +1139,7 @@ "aggregationHook": "0xdD1FA1C12496474c1dDC67a658Ba81437F818861", "blockExplorers": [ { + "apiKey": "98A32SWJE876CHG95TMFRC5H5SBBX9GHAG", "apiUrl": "https://api.gnosisscan.io/api", "family": "etherscan", "name": "GnosisScan", @@ -1407,6 +1415,7 @@ "aggregationHook": "0x43fF73dF1E170D076D9Ed30d4C6922A9D34322dE", "blockExplorers": [ { + "apiKey": "ZCA9836XUX1XPAACHK4BMMYTIFMHIGU8VN", "apiUrl": "https://api.lineascan.build/api", "family": "etherscan", "name": "LineaScan", @@ -2017,6 +2026,7 @@ "aggregationHook": "0x23cca255aE83F57F39EAf9D14fB9FdaDF22D5863", "blockExplorers": [ { + "apiKey": "DDAT4TGIUSSV8MR489UJ21R34WGA1MM2GG", "apiUrl": "https://api-moonbeam.moonscan.io/api", "family": "etherscan", "name": "MoonScan", @@ -2159,6 +2169,7 @@ "aggregationHook": "0x4ccC6d8eB79f2a1EC9bcb0f211fef7907631F91f", "blockExplorers": [ { + "apiKey": "JMYR3W6HHVPQ1HH8T6W8HSZVG88IH3MHRU", "apiUrl": "https://api-optimistic.etherscan.io/api", "family": "etherscan", "name": "Etherscan", @@ -2307,6 +2318,7 @@ "aggregationHook": "0x34dAb05650Cf590088bA18aF9d597f3e081bCc47", "blockExplorers": [ { + "apiKey": "ZPAHJ5A73MC45WJED98YJX4MKJE1UGN8D1", "apiUrl": "https://api.polygonscan.com/api", "family": "etherscan", "name": "PolygonScan", @@ -2382,6 +2394,7 @@ "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", "blockExplorers": [ { + "apiKey": "P8765WMUM4KAM9NPNAY49EACATYC4927HK", "apiUrl": "https://api-zkevm.polygonscan.com/api", "family": "etherscan", "name": "PolygonScan", @@ -2715,6 +2728,7 @@ "aggregationHook": "0x9Bc0FAf446E128a618A88a2F28960Fb2Ca169faE", "blockExplorers": [ { + "apiKey": "8MU9QJVN429TEYSCMB68NC5MG4VM542CFW", "apiUrl": "https://api.scrollscan.com/api", "family": "etherscan", "name": "Scroll Explorer", @@ -2796,6 +2810,7 @@ "aggregationHook": "0x40514BD46C57455933Be8BAedE96C4F0Ba3507D6", "blockExplorers": [ { + "apiKey": "487e1ac5-03f4-4473-9e73-cee721b782b5", "apiUrl": "https://seitrace.com/pacific-1/api", "family": "etherscan", "name": "Seitrace", @@ -2912,6 +2927,7 @@ "aggregationHook": "0x1175A31f66C5e3d0ce0ca3B7F80Abe72c6FcE272", "blockExplorers": [ { + "apiKey": "1CDVSH9KFUVPSFGD1EUFNHD18FUH484IEK", "apiUrl": "https://api.taikoscan.io/api", "family": "etherscan", "name": "Taikoscan", @@ -3114,6 +3130,7 @@ "aggregationHook": "0x8007d1e60991fB9BE1be26f70A7cE284fdE7da97", "blockExplorers": [ { + "apiKey": "BA89EI7MB9Z88UBURAYPRRRUKCQJB96KAE", "apiUrl": "https://api.worldscan.org/api", "family": "etherscan", "name": "Worldscan", @@ -5942,6 +5959,7 @@ "unichain": { "blockExplorers": [ { + "apiKey": "MUPHYQXB8A6GEEKKYA95N7WENSHYTU1UQQ", "apiUrl": "https://api.uniscan.xyz/api", "family": "etherscan", "name": "Unichain Explorer", @@ -7127,6 +7145,7 @@ "sonic": { "blockExplorers": [ { + "apiKey": "V9PP7AFQF5Q6GJSQQ5YS2UBN7GI8QCA443", "apiUrl": "https://api.sonicscan.org/api", "family": "etherscan", "name": "Sonic Explorer", @@ -8882,6 +8901,7 @@ "opbnb": { "blockExplorers": [ { + "apiKey": "GK47JUUDX17PHP8XQ947IYTTPHP6FTAQMK", "apiUrl": "https://api-opbnb.bscscan.com/api", "family": "etherscan", "name": "opBNB Scan", diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index c37a99caaab..4e923a6b649 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -31,7 +31,7 @@ "interchainAccountIsm": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", "interchainAccountRouter": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", - "interchainSecurityModule": "0x1116DC6960b430a3deAdF670A0cfe7f01b5280a3", + "interchainSecurityModule": "0x6eb8681a46098b93a63731e752a7d32970104021", "isTestnet": true, "mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "merkleTreeHook": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", @@ -69,6 +69,7 @@ "aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19", "blockExplorers": [ { + "apiKey": "QAI5SWBNHJSFAN6KMS9RC5JGFKV2IYD2Z5", "apiUrl": "https://api-sepolia.arbiscan.io/api", "family": "etherscan", "name": "Arbiscan", @@ -94,7 +95,7 @@ "from": 49690504 }, "interchainGasPaymaster": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "interchainSecurityModule": "0x67dDC5cD2f82C18D7638d660e6765E9B5A39648D", + "interchainSecurityModule": "0xCcBd620d93097f7bF0699dd530e50a4309Aae746", "isTestnet": true, "mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", "merkleTreeHook": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", @@ -140,6 +141,7 @@ "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", "blockExplorers": [ { + "apiKey": "R8CVTG7HDJYD5JDV2GSD5TGQH3J2KJDSSY", "apiUrl": "https://api-sepolia.basescan.org/api", "family": "etherscan", "name": "BaseScan", @@ -165,7 +167,7 @@ "from": 13851043 }, "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x4259f88Ec1790dE3E9054a363dE963316e5d3EF0", + "interchainSecurityModule": "0x29DA71997b898C79f4Ab11703fb65Bd5dE23BB5c", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", @@ -235,7 +237,7 @@ "interchainAccountIsm": "0xa9D8Ec959F34272B1a56D09AF00eeee58970d3AE", "interchainAccountRouter": "0x6d2B3e304E58c2a19f1492E7cf15CaF63Ce6e0d2", "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", - "interchainSecurityModule": "0x62f08d1B492022A392c5216182C1c03991b56130", + "interchainSecurityModule": "0xB5a4A3EcBcb6fa89Ddd31bD7a61Ea2Fd74b4F735", "isTestnet": true, "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", @@ -304,7 +306,7 @@ "from": 4950 }, "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", - "interchainSecurityModule": "0xCf71BD48503504B74B62c6eaBfA45D1d45CC27a0", + "interchainSecurityModule": "0x9201A25B8D55Fe6Fc3b1E004a029ab4488218F6e", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", @@ -405,7 +407,7 @@ "from": 1606754 }, "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x705dd1E03A281027d728D4F567eb4A0eA4c9BB01", + "interchainSecurityModule": "0x5B083aa0ed5A0ae94cE565FbB0F71C96ABCfBb95", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", @@ -471,7 +473,7 @@ "interchainAccountIsm": "0xfaB4815BDC5c60c6bD625459C8577aFdD79D9311", "interchainAccountRouter": "0xeEF6933122894fF217a7dd07510b3D64b747e29b", "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "interchainSecurityModule": "0x8cDb96f843C3442a2AB358c164725685781C1F3D", + "interchainSecurityModule": "0x2E6f542EC2Bae0634d072C26Fd0383edb28bcD98", "isTestnet": true, "mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", "merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", @@ -537,7 +539,7 @@ "from": 1543015 }, "interchainGasPaymaster": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", - "interchainSecurityModule": "0xB553bBC7Fa0bb3540794500876C0D238B006EE1c", + "interchainSecurityModule": "0x12C12c016eB63D88946089FeB11ea6aB67a7DEFE", "isTestnet": true, "mailbox": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", "merkleTreeHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", @@ -576,6 +578,7 @@ "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", "blockExplorers": [ { + "apiKey": "JMYR3W6HHVPQ1HH8T6W8HSZVG88IH3MHRU", "apiUrl": "https://api-sepolia-optimistic.etherscan.io/api", "family": "etherscan", "name": "OP Sepolia Explorer", @@ -602,7 +605,7 @@ "from": 15833917 }, "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x4aA92b296F5FB271A825C9d903A6e52Ec9cB4A0b", + "interchainSecurityModule": "0xE5c27a7a25C4cd2817eBB03f5f097Ba2c4952Ec2", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", @@ -730,7 +733,7 @@ "from": 10634605 }, "interchainGasPaymaster": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", - "interchainSecurityModule": "0x1f0Ec7Cc255D8DB89196BaC2326723A4b60c7DBE", + "interchainSecurityModule": "0xc900198E7206188C8E57Bad1C231F8D9D899a1CA", "isTestnet": true, "mailbox": "0x54148470292C24345fb828B003461a9444414517", "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", @@ -805,7 +808,7 @@ "interchainAccountIsm": "0xE023239c8dfc172FF008D8087E7442d3eBEd9350", "interchainAccountRouter": "0xe17c37212d785760E8331D4A4395B17b34Ba8cDF", "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "interchainSecurityModule": "0x10f397fBB5c9D3eE1E81d1888B635fd5670c228A", + "interchainSecurityModule": "0x14f74f4CeE5cF7C19D51cE6C28074a39f85b4a50", "isTestnet": true, "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", @@ -852,6 +855,7 @@ "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", "blockExplorers": [ { + "apiKey": "CYUPN3Q66JIMRGQWYUDXJKQH4SX8YIYZMW", "apiUrl": "https://api-sepolia.etherscan.io/api", "family": "etherscan", "name": "Etherscan", @@ -880,7 +884,7 @@ "interchainAccountIsm": "0x83a3068B719F764d413625dA77468ED74789ae02", "interchainAccountRouter": "0x8e131c8aE5BF1Ed38D05a00892b6001a7d37739d", "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", - "interchainSecurityModule": "0xe839cf3AD5C01c687FcCE7de121a9987C568E818", + "interchainSecurityModule": "0x810A345af8ef6e0C973aAD05b0B3e6e7719Ef757", "isTestnet": true, "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", @@ -994,7 +998,7 @@ "from": 3111622 }, "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", - "interchainSecurityModule": "0xA7aD4a35492e64b54EdCD6F5f9B8cd4D2aEd3184", + "interchainSecurityModule": "0x2879fC8b87231273F88eA71711D258ADaA935a21", "isTestnet": true, "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", @@ -1034,70 +1038,6 @@ "interchainAccountRouter": "0x3572a9d808738922194921b275B2A55414BcDA57", "timelockController": "0x0000000000000000000000000000000000000000" }, - "camptestnet": { - "blockExplorers": [ - { - "apiUrl": "https://camp-network-testnet.blockscout.com/api", - "family": "blockscout", - "name": "Camp Network Testnet Explorer", - "url": "https://camp-network-testnet.blockscout.com" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 2, - "reorgPeriod": 1 - }, - "chainId": 325000, - "displayName": "Camp Network Testnet V2", - "domainId": 325000, - "gasCurrencyCoinGeckoId": "ethereum", - "isTestnet": true, - "name": "camptestnet", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://rpc-campnetwork.xyz" - } - ], - "aggregationHook": "0xb97D172479E9EC2501524E02703B42247559A1bD", - "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", - "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "fallbackRoutingHook": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", - "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", - "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", - "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x8C241675A4c755F78B9D4473e7D546169635FA53", - "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", - "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", - "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", - "pausableIsm": "0x04438ef7622f5412f82915F59caD4f704C61eA48", - "protocolFee": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", - "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", - "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "staticAggregationIsm": "0x77d4B4090B666d84b4451C7425682B8F51Dbd827", - "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", - "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "storageGasOracle": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", - "testRecipient": "0x7483faD0Bc297667664A43A064bA7c9911659f57", - "timelockController": "0x0000000000000000000000000000000000000000", - "validatorAnnounce": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", - "index": { - "from": 4591544 - }, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - } - }, "citreatestnet": { "blockExplorers": [ { @@ -1140,7 +1080,7 @@ "interchainAccountIsm": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", "interchainAccountRouter": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", "interchainGasPaymaster": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", - "interchainSecurityModule": "0xfcF18EdA46855C1b7e319B4B2033Bf1f844B3534", + "interchainSecurityModule": "0x76dBe4040c4Ab9134d4A19F1f1481586c8f4C4F9", "mailbox": "0xB08d78F439e55D02C398519eef61606A5926245F", "merkleTreeHook": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "pausableHook": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", @@ -1201,7 +1141,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x2e1e05Ec996ed6A51A7d972B5c63C2BE2034100F", + "interchainSecurityModule": "0xE96C980F01513331062640bE3b5EF15a9bD3D4de", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1269,7 +1209,7 @@ "interchainAccountIsm": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", "interchainAccountRouter": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", "interchainGasPaymaster": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", - "interchainSecurityModule": "0xA20bA09A791B92Fb81d66BeEC391AfF38fDCf2cf", + "interchainSecurityModule": "0xF0Ed934Ba6F35bf99A9Be001b5B20456FB0FfD0C", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x1b33611fCc073aB0737011d5512EF673Bff74962", "pausableHook": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", @@ -1330,7 +1270,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x1df25C93E719602EA6A6e0Ba94516FcfF040C9cd", + "interchainSecurityModule": "0xE9A03170c1C2AE9e9F5a501E05dd3335E92aE5CC", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1394,7 +1334,7 @@ "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "interchainSecurityModule": "0x1CE1Dd90fd90C891e7FDc3933e07a3Fcc8a41989", + "interchainSecurityModule": "0x616C10Af1EadFdd98E1db087a6BDD08c484fC021", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", @@ -1645,7 +1585,7 @@ "interchainAccountIsm": "0x39c85C84876479694A2470c0E8075e9d68049aFc", "interchainAccountRouter": "0x80fE4Cb8c70fc60B745d4ffD4403c27a8cBC9e02", "interchainGasPaymaster": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", - "interchainSecurityModule": "0x912F3785d42B574d52c37Fef293a9545fcF07C19", + "interchainSecurityModule": "0xA5EFE0aE77f15cB9B2f4Dd57Dd1931486779E748", "mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", "merkleTreeHook": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", "pausableHook": "0x4fE19d49F45854Da50b6009258929613EC92C147", @@ -1708,7 +1648,7 @@ "interchainAccountIsm": "0x3ca332A585FDB9d4FF51f2FA8999eA32184D3606", "interchainAccountRouter": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", "interchainGasPaymaster": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", - "interchainSecurityModule": "0x17EB751b02CE7fbe8B70EA4ac013c5ba007841dD", + "interchainSecurityModule": "0xBF2696c172Bc0CDe9deD089f5d1592EE1162989A", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", "pausableHook": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", @@ -1771,7 +1711,7 @@ "interchainAccountIsm": "0xBF2C366530C1269d531707154948494D3fF4AcA7", "interchainAccountRouter": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", "interchainGasPaymaster": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", - "interchainSecurityModule": "0x58Ca79d52F023E9a4a382F69F5D5C296cc91CEdC", + "interchainSecurityModule": "0x1FA5CeF62272925D09b4445E561e2B6347A678fE", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", "pausableHook": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", @@ -1842,7 +1782,7 @@ "interchainAccountIsm": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", "interchainAccountRouter": "0xe036768e48Cb0D42811d2bF0748806FCcBfCd670", "interchainGasPaymaster": "0x867f2089D09903f208AeCac84E599B90E5a4A821", - "interchainSecurityModule": "0xf57F816c65A01e5FAa6Dfd2EED656fFac2EADA63", + "interchainSecurityModule": "0x77595aeeB5C055c032E7Beb31D66eEcaB88BFB11", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", "pausableHook": "0x7483faD0Bc297667664A43A064bA7c9911659f57", @@ -1901,7 +1841,7 @@ "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", "fallbackRoutingHook": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "interchainGasPaymaster": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", - "interchainSecurityModule": "0xCf4d7d78F6A1Afe046cB996aFdE18bFB72F2c70C", + "interchainSecurityModule": "0xa7aDEb8e3e393C7D90933F8712373eff17C2C0C6", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x4fE19d49F45854Da50b6009258929613EC92C147", "pausableHook": "0x01812D60958798695391dacF092BAc4a715B1718", @@ -1982,64 +1922,6 @@ "from": 964305 } }, - "treasuretopaz": { - "blockExplorers": [ - { - "apiUrl": "https://rpc-explorer-verify.topaz.treasure.lol/contract_verification", - "family": "etherscan", - "name": "Treasure Topaz Block Explorer", - "url": "https://topaz.treasurescan.io" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 1, - "reorgPeriod": 0 - }, - "chainId": 978658, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Treasure Topaz Testnet", - "displayNameShort": "Treasure Testnet", - "domainId": 978658, - "isTestnet": true, - "name": "treasuretopaz", - "nativeToken": { - "decimals": 18, - "name": "MAGIC", - "symbol": "MAGIC" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://rpc.topaz.treasure.lol" - } - ], - "technicalStack": "zksync", - "domainRoutingIsm": "0x7ca1b3fa385F3585f8ab58c0bC90A421689141B8", - "domainRoutingIsmFactory": "0x0000000000000000000000000000000000000000", - "fallbackDomainRoutingHook": "0x623f284257f133E8bE7c74f6D4D684B61FE8923a", - "fallbackRoutingHook": "0x623f284257f133E8bE7c74f6D4D684B61FE8923a", - "interchainGasPaymaster": "0xbAaE1B4e953190b05C757F69B2F6C46b9548fa4f", - "interchainSecurityModule": "0x7ca1b3fa385F3585f8ab58c0bC90A421689141B8", - "mailbox": "0x28f448885bEaaF662f8A9A6c9aF20fAd17A5a1DC", - "merkleTreeHook": "0x7fa6009b59F139813eA710dB5496976eE8D80E64", - "proxyAdmin": "0xfbA0c57A6BA24B5440D3e2089222099b4663B98B", - "staticAggregationHookFactory": "0x0000000000000000000000000000000000000000", - "staticAggregationIsmFactory": "0x0000000000000000000000000000000000000000", - "staticMerkleRootMultisigIsmFactory": "0x0000000000000000000000000000000000000000", - "staticMerkleRootWeightedMultisigIsmFactory": "0x0000000000000000000000000000000000000000", - "staticMessageIdMultisigIsmFactory": "0x0000000000000000000000000000000000000000", - "staticMessageIdWeightedMultisigIsmFactory": "0x0000000000000000000000000000000000000000", - "storageGasOracle": "0x5F1bADC7e28B9b4C98f58dB4e5841e5bf63A7A52", - "testRecipient": "0x9EC79CA89DeF61BFa2f38cD4fCC137b9e49d60dD", - "validatorAnnounce": "0xfE9a467831a28Ec3D54deCCf0A2A41fa77dDD1D7", - "index": { - "from": 86008 - } - }, "kyvealpha": { "bech32Prefix": "kyve", "blockExplorers": [], @@ -2166,7 +2048,7 @@ "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "fallbackRoutingHook": "0xCB3c489a2FB67a7Cd555D47B3a9A0E654784eD16", "interchainGasPaymaster": "0x39c85C84876479694A2470c0E8075e9d68049aFc", - "interchainSecurityModule": "0xCc747d6EFB9e71D92dcdea7c8503D3419935D64C", + "interchainSecurityModule": "0xE70A7163807c32117ba43D1cDAb5793bE0246d0a", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x843908541D24d9F6Fa30C8Bb1c39038C947D08fC", "pausableHook": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", @@ -2193,6 +2075,7 @@ "sonicblaze": { "blockExplorers": [ { + "apiKey": "V9PP7AFQF5Q6GJSQQ5YS2UBN7GI8QCA443", "apiUrl": "https://api-testnet.sonicscan.org/api", "family": "etherscan", "name": "Sonic Blaze Testnet Explorer", @@ -2235,7 +2118,7 @@ "interchainAccountIsm": "0x507C18fa4e3b0ce6beBD494488D62d1ed0fB0555", "interchainAccountRouter": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", "interchainGasPaymaster": "0x39c85C84876479694A2470c0E8075e9d68049aFc", - "interchainSecurityModule": "0x6E993E7485aC28fBB92780e24f64F6ABF2E98E5B", + "interchainSecurityModule": "0xb5fC135De8F3B69D1D55d472202126e2CfCd802c", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x843908541D24d9F6Fa30C8Bb1c39038C947D08fC", "pausableHook": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", @@ -2300,7 +2183,7 @@ "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "fallbackRoutingHook": "0x39c85C84876479694A2470c0E8075e9d68049aFc", "interchainGasPaymaster": "0xB589407cf6bEA5CD81AD0946b9F1467933ede74c", - "interchainSecurityModule": "0x21676D1882D8D702A8f428C2eBeFDC28ec297590", + "interchainSecurityModule": "0xA5F6e392e6E245778CC61ba10853C60408B19A05", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", "pausableHook": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", @@ -2354,7 +2237,7 @@ "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "fallbackRoutingHook": "0x39c85C84876479694A2470c0E8075e9d68049aFc", "interchainGasPaymaster": "0xB589407cf6bEA5CD81AD0946b9F1467933ede74c", - "interchainSecurityModule": "0x90e41dCcB1cE47439ABcb1A90507a47DD2565B1f", + "interchainSecurityModule": "0x200EeeF345FfA74FE3D84Fd5DeB2d14Aa1ed9d9d", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", "pausableHook": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", @@ -2420,7 +2303,7 @@ "interchainAccountIsm": "0x4da6f7E710137657008D5BCeF26151aac5c9884f", "interchainAccountRouter": "0x919Af376D02751bFCaD9CBAD6bad0c3089dAE33f", "interchainGasPaymaster": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", - "interchainSecurityModule": "0xE59E61BdC48A85E291e0C0DA78c2e99A83F1749F", + "interchainSecurityModule": "0x05103D4ac6E6ceE67d02F5a61C17AcA070f788b1", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", "pausableHook": "0x8d4f112cffa338D3c3Ef2Cf443179C5a48E678e4", @@ -2446,10 +2329,10 @@ "weavevmtestnet": { "blockExplorers": [ { - "apiUrl": "https://explorer.wvm.dev/api", + "apiUrl": "https://explorer.load.network/api", "family": "blockscout", - "name": "WeaveVM Testnet Explorer", - "url": "https://explorer.wvm.dev" + "name": "Load Network Explorer", + "url": "https://explorer.load.network" } ], "blocks": { @@ -2458,7 +2341,7 @@ "reorgPeriod": 1 }, "chainId": 9496, - "displayName": "Weave VM Testnet", + "displayName": "Load Network Alphanet", "deployer": { "name": "Abacus Works", "url": "https://www.hyperlane.xyz" @@ -2468,13 +2351,13 @@ "name": "weavevmtestnet", "nativeToken": { "decimals": 18, - "name": "tWVM", - "symbol": "tWVM" + "name": "tLOAD", + "symbol": "tLOAD" }, "protocol": "ethereum", "rpcUrls": [ { - "http": "https://testnet-rpc.wvm.dev" + "http": "https://alphanet.load.network" } ], "technicalStack": "other", @@ -2485,7 +2368,7 @@ "interchainAccountIsm": "0x4da6f7E710137657008D5BCeF26151aac5c9884f", "interchainAccountRouter": "0x919Af376D02751bFCaD9CBAD6bad0c3089dAE33f", "interchainGasPaymaster": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", - "interchainSecurityModule": "0xF8845C39CE09101c2AfD4df30039a4ED31b08E8a", + "interchainSecurityModule": "0x171F13EA7a7AEe621AB29f42e912AB98992A9894", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", "pausableHook": "0x8d4f112cffa338D3c3Ef2Cf443179C5a48E678e4", @@ -2552,7 +2435,7 @@ "interchainAccountIsm": "0xD9dc83Ea22C6F1A224e51562B32b580695905A1A", "interchainAccountRouter": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", "interchainGasPaymaster": "0xce0e13f67399375eF0a7acb741E815145A6AAf67", - "interchainSecurityModule": "0xE15869efE56Fc8F49aF9af310E20B93b12E5Bd73", + "interchainSecurityModule": "0x29C25A4af220FaC603F7EC968de9fbb006dbD4Db", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xb3D796584fDeBE2321894eeF31e0C3ec52169C61", "pausableHook": "0x2bD9aF503B9F608beAD63D4ACC328Abf9796b576", @@ -2613,7 +2496,7 @@ "interchainAccountIsm": "0x740bEd6E4eEc7c57a2818177Fba3f9E896D5DE1c", "interchainAccountRouter": "0xD5B70f7Da85F98A5197E55114A38f3eDcDCf020e", "interchainGasPaymaster": "0x919Af376D02751bFCaD9CBAD6bad0c3089dAE33f", - "interchainSecurityModule": "0xaB85ee25c79a4B2e0Ae83c76B0856763F5b10eB6", + "interchainSecurityModule": "0x5B1743D34360582b3e4CAbACeE7c71b2cB4FB332", "mailbox": "0x7d498740A4572f2B5c6b0A1Ba9d1d9DbE207e89E", "merkleTreeHook": "0x7d811da36c48cfDc7C445F14252F102bF06E3Fd7", "pausableHook": "0xCCC126d96efcc342BF2781A7d224D3AB1F25B19C", @@ -2673,7 +2556,7 @@ "interchainAccountIsm": "0x6cB503d97D1c900316583C8D55997A1f17b1ABd1", "interchainAccountRouter": "0x740bEd6E4eEc7c57a2818177Fba3f9E896D5DE1c", "interchainGasPaymaster": "0x48a53E3B176383BC98fcF4a24c9D470c19475164", - "interchainSecurityModule": "0x0d3D3Ef7310c36B1ADedd0BF6f623787d032085F", + "interchainSecurityModule": "0x15b617C1d0C620ED04362d8e6f6399a866AC4517", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", "pausableHook": "0xdb3338da7947dc9beDAB5f8685da721C293E0cbF", @@ -2735,7 +2618,7 @@ "interchainAccountIsm": "0x890eB21B76DCB165A1807cBE279f883716eA47D4", "interchainAccountRouter": "0xB7697612fbfb4ad02a11dCa16e9711eCB6Da4ceA", "interchainGasPaymaster": "0xA9425D5cBcD2c83EB2a5BF453EAA18968db3ef77", - "interchainSecurityModule": "0xE04dd1B83d27EAEbF59FD5cb89bb8572a378B1cb", + "interchainSecurityModule": "0x5c5973eDf3D37Cdd4A35eba14B7b81F068A9dFfB", "mailbox": "0x7FE7EA170cf08A25C2ff315814D96D93C311E692", "merkleTreeHook": "0x413c74F3D034dB54A1ecfFbd0Ad74Cb25E59f579", "pausableHook": "0x86abe4c3493A1eE0Aa42f0231b5594D42aBdA36e", @@ -2799,7 +2682,7 @@ "interchainAccountIsm": "0x6C3132f78260EdD1cE88Ea4FeEB8C2D6309ecc75", "interchainAccountRouter": "0x1681cc382e08a72d4b64A123080896e30f96B740", "interchainGasPaymaster": "0xB261C52241E133f957630AeeFEd48a82963AC33e", - "interchainSecurityModule": "0x8471463482Ba245D4Fd33f65EA83dDDCA4bDcd48", + "interchainSecurityModule": "0xBfd37665B0d80bf79A26ce3c181d9eBc981F2255", "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", "merkleTreeHook": "0xf83416bA0491C8BC80Dad259Fc7C007bC57Bd766", "pausableHook": "0x6cB503d97D1c900316583C8D55997A1f17b1ABd1", @@ -2867,7 +2750,7 @@ "interchainAccountIsm": "0x9667EfF1556A9D092fdbeC09244CB99b677E9D1E", "interchainAccountRouter": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C", "interchainGasPaymaster": "0xD5B70f7Da85F98A5197E55114A38f3eDcDCf020e", - "interchainSecurityModule": "0x2186c92cf6Cae54b71405B7092967Ad37A8738ca", + "interchainSecurityModule": "0x3e0E4a2a47a562CE4BD181c477C58036c048CD6b", "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "merkleTreeHook": "0x0b9A4A46f50f91f353B8Aa0F3Ca80E35E253bDd8", "pausableHook": "0x9450181a7719dAb93483d43a45473Ac2373E25B0", @@ -2996,7 +2879,7 @@ "domainRoutingIsmFactory": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "fallbackRoutingHook": "0xE3D93F9296FA3dF262E1a54f0de02F71E845af6b", "interchainGasPaymaster": "0xF61322936D80cd87B49df48F3DE24fD5c02dE9D1", - "interchainSecurityModule": "0x318FDE0ce35D5460919829F9d5dc1DD6988c71EB", + "interchainSecurityModule": "0xFebaD2f0ae5c7BC1d082C095c3Ac7d8D0a876e15", "mailbox": "0x04438ef7622f5412f82915F59caD4f704C61eA48", "merkleTreeHook": "0x6d6a9bDDea1456673062633b7a4823dB13bDB9fb", "pausableHook": "0x48c94311789194215Fe19002C2D33681A76d63dF", @@ -3072,6 +2955,259 @@ "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000001", "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000" + }, + "basecamptestnet": { + "blockExplorers": [ + { + "apiUrl": "https://basecamp.cloud.blockscout.com/api", + "family": "blockscout", + "name": "Basecamp Explorer", + "url": "https://basecamp.cloud.blockscout.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 123420001114, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Basecamp Testnet", + "domainId": 1000001114, + "isTestnet": true, + "name": "basecamptestnet", + "nativeToken": { + "decimals": 18, + "name": "Camp", + "symbol": "CAMP" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.basecamp.t.raas.gelato.cloud" + } + ], + "aggregationHook": "0x08cB5638d0B4b8D1E313E9317171515Eb84AfEA3", + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0x6916690816a1aE1F6D9163dA9e691124737B5f5b", + "interchainGasPaymaster": "0xF78deCe5Cf97e1bd61C202A5ba1af33b87454878", + "interchainSecurityModule": "0x5978eB2951fEA157408e2BEA93E95AF92DbF5D19", + "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", + "merkleTreeHook": "0x86abe4c3493A1eE0Aa42f0231b5594D42aBdA36e", + "pausableHook": "0x9667EfF1556A9D092fdbeC09244CB99b677E9D1E", + "pausableIsm": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217", + "protocolFee": "0x0E18b28D98C2efDb59252c021320F203305b1B66", + "proxyAdmin": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x5978eB2951fEA157408e2BEA93E95AF92DbF5D19", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMerkleRootWeightedMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMessageIdWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "storageGasOracle": "0x36502C6e24C51ba4839c4c4A070aeB52E1adB672", + "testRecipient": "0xB6a4129c305056d80fFfea96DdbDCf1F58BC8240", + "validatorAnnounce": "0xe63844Ca06Ce8E6D4097Cb33E9b3d62704122307", + "index": { + "from": 7750518 + } + }, + "bepolia": { + "blockExplorers": [ + { + "apiUrl": "https://api.routescan.io/v2/network/testnet/evm/80069/etherscan/api", + "family": "routescan", + "name": "Bepolia Explorer", + "url": "https://bepolia.beratrail.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 80069, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Berachain Bepolia", + "displayNameShort": "Bepolia", + "domainId": 80069, + "isTestnet": true, + "name": "bepolia", + "nativeToken": { + "decimals": 18, + "name": "BERA", + "symbol": "BERA" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://bepolia.rpc.berachain.com" + } + ], + "technicalStack": "other", + "aggregationHook": "0x08cB5638d0B4b8D1E313E9317171515Eb84AfEA3", + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0x6916690816a1aE1F6D9163dA9e691124737B5f5b", + "interchainGasPaymaster": "0xF78deCe5Cf97e1bd61C202A5ba1af33b87454878", + "interchainSecurityModule": "0x5978eB2951fEA157408e2BEA93E95AF92DbF5D19", + "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", + "merkleTreeHook": "0x86abe4c3493A1eE0Aa42f0231b5594D42aBdA36e", + "pausableHook": "0x9667EfF1556A9D092fdbeC09244CB99b677E9D1E", + "pausableIsm": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217", + "protocolFee": "0x0E18b28D98C2efDb59252c021320F203305b1B66", + "proxyAdmin": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x5978eB2951fEA157408e2BEA93E95AF92DbF5D19", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMerkleRootWeightedMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMessageIdWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "storageGasOracle": "0x36502C6e24C51ba4839c4c4A070aeB52E1adB672", + "testRecipient": "0xB6a4129c305056d80fFfea96DdbDCf1F58BC8240", + "validatorAnnounce": "0xe63844Ca06Ce8E6D4097Cb33E9b3d62704122307", + "index": { + "from": 3449778 + } + }, + "megaethtestnet": { + "blockExplorers": [ + { + "apiUrl": "https://www.megaexplorer.xyz/api", + "family": "other", + "name": "Mega Explorer", + "url": "https://www.megaexplorer.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 6342, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "MegaETH Testnet", + "domainId": 6342, + "isTestnet": true, + "name": "megaethtestnet", + "nativeToken": { + "decimals": 18, + "name": "MegaETH Testnet ETH", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://carrot.megaeth.com/rpc" + } + ], + "technicalStack": "other", + "aggregationHook": "0xDA1f9b599B0c9FDda1F13aDC7D5BD4de3B336D66", + "domainRoutingIsm": "0x827C392543D1643c5e921C67Df4A456B80471575", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0x890eB21B76DCB165A1807cBE279f883716eA47D4", + "interchainGasPaymaster": "0x638A831b4d11Be6a72AcB97d1aE79DA05Ae9B1D3", + "interchainSecurityModule": "0xe318A9edE0f0E9BDCe12D59AE50Be55f9280FaDe", + "mailbox": "0xF78deCe5Cf97e1bd61C202A5ba1af33b87454878", + "merkleTreeHook": "0x0C16e730090cb681460516428c3b2D1BBCB1b647", + "pausableHook": "0xBA3f3A84F149842529993bc38A7b4cF8131c17c2", + "pausableIsm": "0xa1C91dd4F3050DE9C4184BD8B4403A0C81D5463C", + "protocolFee": "0xa318ffca299412ce76B7cb980850ceDB15584E7e", + "proxyAdmin": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C", + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0xe318A9edE0f0E9BDCe12D59AE50Be55f9280FaDe", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMerkleRootWeightedMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMessageIdWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "storageGasOracle": "0x1fe349d93078B26C8c7350A401e2B60f100952F5", + "testRecipient": "0x5B30De0c322F7720D144df2AB2e82b160Eba0EBF", + "validatorAnnounce": "0xf3e31239257b300Daa566131aA6b9e45Ef2A4266", + "index": { + "from": 5364572 + } + }, + "nobletestnet": { + "bech32Prefix": "noble", + "blockExplorers": [ + { + "apiUrl": "https://www.mintscan.io/noble-testnet", + "family": "other", + "name": "Mintscan", + "url": "https://www.mintscan.io/noble-testnet" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1.5, + "reorgPeriod": 1 + }, + "canonicalAsset": "uusdn", + "chainId": "grand-1", + "contractAddressBytes": 32, + "deployer": { + "name": "Noble", + "url": "https://noble.xyz" + }, + "displayName": "Noble Testnet", + "domainId": 1196573006, + "gasPrice": { + "denom": "uusdn", + "amount": "0.001" + }, + "index": { + "chunk": 10, + "from": 29035000 + }, + "isTestnet": true, + "name": "nobletestnet", + "nativeToken": { + "decimals": 6, + "denom": "uusdn", + "name": "Noble Dollar", + "symbol": "USDN" + }, + "protocol": "cosmosnative", + "restUrls": [ + { + "http": "https://noble-testnet-api.polkachu.com:443" + }, + { + "http": "https://api.testnet.noble.xyz:443" + } + ], + "rpcUrls": [ + { + "http": "https://noble-testnet-rpc.polkachu.com:443" + }, + { + "http": "https://rpc.testnet.noble.xyz:443" + } + ], + "slip44": 118, + "technicalStack": "other", + "interchainGasPaymaster": "0x726f757465725f706f73745f6469737061746368000000040000000000000001", + "interchainSecurityModule": "0x726f757465725f69736d00000000000000000000000000010000000000000000", + "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", + "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000000", + "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000", + "grpcUrls": [ + { + "http": "http://noble-testnet-grpc.polkachu.com:21590" + } + ] } }, "defaultRpcConsensusType": "fallback" diff --git a/rust/main/hyperlane-core/src/chain.rs b/rust/main/hyperlane-core/src/chain.rs index 937d457ca63..1c5cdb2c2d7 100644 --- a/rust/main/hyperlane-core/src/chain.rs +++ b/rust/main/hyperlane-core/src/chain.rs @@ -211,7 +211,6 @@ pub enum KnownHyperlaneDomain { ScrollSepolia = 534351, Sepolia = 11155111, SuperpositionTestnet = 98985, - Treasuretopaz = 978658, } #[derive(Clone, Serialize)] @@ -333,7 +332,7 @@ impl KnownHyperlaneDomain { ], Testnet: [ Alfajores, BinanceSmartChainTestnet, Chiado, ConnextSepolia, Fuji, Holesky, MoonbaseAlpha, - PlumeTestnet, ScrollSepolia, Sepolia, SuperpositionTestnet, Abstracttestnet, Treasuretopaz + PlumeTestnet, ScrollSepolia, Sepolia, SuperpositionTestnet, Abstracttestnet ], LocalTestChain: [ Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, @@ -351,7 +350,7 @@ impl KnownHyperlaneDomain { DegenChain, Endurance, Ethereum, Fraxtal, Fuji, FuseMainnet, Gnosis, InEvm, Kroma, Linea, Lisk, Lukso, MantaPacific, Mantle, Merlin, Metis, Mint, Mode, Moonbeam, Optimism, Polygon, ProofOfPlay, ReAl, Redstone, Sanko, Sei, Tangle, - Taiko, Treasure, Treasuretopaz, Viction, Worldchain, Xai, Xlayer, Zeronetwork, Zetachain, Zircuit, ZoraMainnet, + Taiko, Treasure, Viction, Worldchain, Xai, Xlayer, Zeronetwork, Zetachain, Zircuit, ZoraMainnet, Zklink, Zksync, // Local chains @@ -399,7 +398,7 @@ impl KnownHyperlaneDomain { Moonbeam, Tangle ], HyperlaneDomainTechnicalStack::ZkSync: [ - Abstracttestnet, Treasure, Treasuretopaz, Zeronetwork, Zklink, Zksync, + Abstracttestnet, Treasure, Zeronetwork, Zklink, Zksync, ], HyperlaneDomainTechnicalStack::Other: [ Avalanche, BinanceSmartChain, Celo, EclipseMainnet, Endurance, Ethereum, diff --git a/rust/sealevel/environments/testnet4/chain-config.json b/rust/sealevel/environments/testnet4/chain-config.json index 6bfa3266ac3..ef7be674f5a 100644 --- a/rust/sealevel/environments/testnet4/chain-config.json +++ b/rust/sealevel/environments/testnet4/chain-config.json @@ -269,42 +269,6 @@ "gasPrice": 8000000000 } }, - "camptestnet": { - "blockExplorers": [ - { - "apiUrl": "https://camp-network-testnet.blockscout.com/api", - "family": "blockscout", - "name": "Camp Network Testnet Explorer", - "url": "https://camp-network-testnet.blockscout.com" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 2, - "reorgPeriod": 1 - }, - "chainId": 325000, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Camp Network Testnet V2", - "domainId": 325000, - "gasCurrencyCoinGeckoId": "ethereum", - "isTestnet": true, - "name": "camptestnet", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://rpc-campnetwork.xyz" - } - ] - }, "carrchaintestnet": { "blockExplorers": [ { @@ -1307,43 +1271,6 @@ ], "technicalStack": "arbitrumnitro" }, - "treasuretopaz": { - "blockExplorers": [ - { - "apiUrl": "https://rpc-explorer-verify.topaz.treasure.lol/contract_verification", - "family": "etherscan", - "name": "Treasure Topaz Block Explorer", - "url": "https://topaz.treasurescan.io" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 1, - "reorgPeriod": 0 - }, - "chainId": 978658, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Treasure Topaz Testnet", - "displayNameShort": "Treasure Testnet", - "domainId": 978658, - "isTestnet": true, - "name": "treasuretopaz", - "nativeToken": { - "decimals": 18, - "name": "MAGIC", - "symbol": "MAGIC" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://rpc.topaz.treasure.lol" - } - ], - "technicalStack": "zksync" - }, "unichaintestnet": { "blockExplorers": [ { diff --git a/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json index 768b0434d43..f2ae74eec39 100644 --- a/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json +++ b/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json @@ -59,13 +59,6 @@ "0x1f030345963c54ff8229720dd3a711c15c554aeb" ] }, - "camptestnet": { - "type": "messageIdMultisigIsm", - "threshold": 1, - "validators": [ - "0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e" - ] - }, "citreatestnet": { "type": "messageIdMultisigIsm", "threshold": 1, @@ -191,13 +184,6 @@ "0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a" ] }, - "treasuretopaz": { - "type": "messageIdMultisigIsm", - "threshold": 1, - "validators": [ - "0x9750849beda0a7870462d4685f953fe39033a5ae" - ] - }, "unichaintestnet": { "type": "messageIdMultisigIsm", "threshold": 1, diff --git a/rust/sealevel/environments/testnet4/multisig-ism-message-id/sonicsvmtestnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet4/multisig-ism-message-id/sonicsvmtestnet/hyperlane/multisig-config.json index 2465950c3b6..c2f4e0cce30 100644 --- a/rust/sealevel/environments/testnet4/multisig-ism-message-id/sonicsvmtestnet/hyperlane/multisig-config.json +++ b/rust/sealevel/environments/testnet4/multisig-ism-message-id/sonicsvmtestnet/hyperlane/multisig-config.json @@ -59,13 +59,6 @@ "0x1f030345963c54ff8229720dd3a711c15c554aeb" ] }, - "camptestnet": { - "type": "messageIdMultisigIsm", - "threshold": 1, - "validators": [ - "0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e" - ] - }, "citreatestnet": { "type": "messageIdMultisigIsm", "threshold": 1, @@ -191,13 +184,6 @@ "0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a" ] }, - "treasuretopaz": { - "type": "messageIdMultisigIsm", - "threshold": 1, - "validators": [ - "0x9750849beda0a7870462d4685f953fe39033a5ae" - ] - }, "unichaintestnet": { "type": "messageIdMultisigIsm", "threshold": 1, diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 37282764d77..1f41ee62455 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -52,9 +52,10 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< arbitrumsepolia: true, arcadiatestnet2: true, auroratestnet: true, + basecamptestnet: true, basesepolia: true, + bepolia: true, bsctestnet: true, - camptestnet: true, carrchaintestnet: true, chronicleyellowstone: true, citreatestnet: true, @@ -69,10 +70,12 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< hyperliquidevmtestnet: true, infinityvmmonza: true, inksepolia: true, - kyvetestnet: false, + kyvetestnet: true, + megaethtestnet: true, milkywaytestnet: true, modetestnet: true, monadtestnet: true, + nobletestnet: true, odysseytestnet: true, optimismsepolia: true, plumetestnet2: true, @@ -87,7 +90,6 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< suavetoliman: true, subtensortestnet: true, superpositiontestnet: true, - treasuretopaz: true, unichaintestnet: true, weavevmtestnet: true, }, @@ -98,9 +100,10 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< arbitrumsepolia: true, arcadiatestnet2: true, auroratestnet: true, + basecamptestnet: true, basesepolia: true, + bepolia: true, bsctestnet: true, - camptestnet: true, carrchaintestnet: true, chronicleyellowstone: true, citreatestnet: true, @@ -115,10 +118,12 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< hyperliquidevmtestnet: false, infinityvmmonza: true, inksepolia: true, - kyvetestnet: false, + kyvetestnet: true, + megaethtestnet: true, milkywaytestnet: true, modetestnet: true, monadtestnet: true, + nobletestnet: true, odysseytestnet: true, optimismsepolia: true, plumetestnet2: true, @@ -133,7 +138,6 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< suavetoliman: true, subtensortestnet: true, superpositiontestnet: true, - treasuretopaz: true, unichaintestnet: true, weavevmtestnet: true, }, @@ -144,9 +148,10 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< arbitrumsepolia: true, arcadiatestnet2: false, auroratestnet: true, + basecamptestnet: true, basesepolia: true, + bepolia: true, bsctestnet: true, - camptestnet: true, carrchaintestnet: true, chronicleyellowstone: true, citreatestnet: true, @@ -161,10 +166,12 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< hyperliquidevmtestnet: false, infinityvmmonza: true, inksepolia: true, - kyvetestnet: false, + kyvetestnet: true, + megaethtestnet: true, milkywaytestnet: true, modetestnet: true, monadtestnet: true, + nobletestnet: true, odysseytestnet: true, optimismsepolia: true, plumetestnet2: true, @@ -179,7 +186,6 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< suavetoliman: true, subtensortestnet: true, superpositiontestnet: false, - treasuretopaz: true, unichaintestnet: true, weavevmtestnet: true, }, @@ -200,6 +206,21 @@ const contextBase = { } as const; const gasPaymentEnforcement: GasPaymentEnforcement[] = [ + { + type: GasPaymentEnforcementPolicyType.None, + matchingList: [ + // For testing nobletestnet<>auroratestnet until we control the IGP + { + originDomain: getDomainId('nobletestnet'), + destinationDomain: getDomainId('auroratestnet'), + }, + // For testing nobletestnet<>hyperliquidevmtestnet until we control the IGP + { + originDomain: getDomainId('nobletestnet'), + destinationDomain: getDomainId('hyperliquidevmtestnet'), + }, + ], + }, { type: GasPaymentEnforcementPolicyType.Minimum, payment: '1', @@ -368,7 +389,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '385b307-20250418-150728', + tag: '1363c76-20250507-120738', }, blacklist: [...releaseCandidateHelloworldMatchingList, ...relayBlacklist], gasPaymentEnforcement, @@ -383,7 +404,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd9e0b4b-20250425-145730', + tag: '1363c76-20250507-120738', }, chains: validatorChainConfig(Contexts.Hyperlane), resources: validatorResources, @@ -392,7 +413,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd9e0b4b-20250425-145730', + tag: '1363c76-20250507-120738', }, resources: scraperResources, }, diff --git a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json index 63f9efb6474..516f5e78c31 100644 --- a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json @@ -21,9 +21,15 @@ "auroratestnet": { "validators": ["0xab1a2c76bf4cced43fde7bc1b5b57b9be3e7f937"] }, + "basecamptestnet": { + "validators": ["0x84441e39ed5251410aa2baa72e7747c46d1e5e9d"] + }, "basesepolia": { "validators": ["0x82e3b437a2944e3ff00258c93e72cd1ba5e0e921"] }, + "bepolia": { + "validators": ["0xdb0128bb3d3f204eb18de7e8268e94fde0382daf"] + }, "bsctestnet": { "validators": [ "0x242d8a855a8c932dec51f7999ae7d1e48b10c95e", @@ -31,9 +37,6 @@ "0x1f030345963c54ff8229720dd3a711c15c554aeb" ] }, - "camptestnet": { - "validators": ["0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e"] - }, "carrchaintestnet": { "validators": ["0xa96dfc4d8c6cabb510701732ee01e52a75776205"] }, @@ -77,6 +80,12 @@ "inksepolia": { "validators": ["0xe61c846aee275070207fcbf43674eb254f06097a"] }, + "kyvetestnet": { + "validators": ["0x3c470ad2640bc0bcb6a790e8cf85e54d34ca92f5"] + }, + "megaethtestnet": { + "validators": ["0xf5c8a82f966d2ec8563a2012ccf556ee3f4b25ef"] + }, "milkywaytestnet": { "validators": ["0x65c7581e14efdf4d9c5320882170f022835bd742"] }, @@ -86,6 +95,9 @@ "monadtestnet": { "validators": ["0x734628f55694d2a5f4de3e755ccb40ecd72b16d9"] }, + "nobletestnet": { + "validators": ["0xc30427bd74fdcf179a15b9a6e3c4e1d66104726a"] + }, "odysseytestnet": { "validators": ["0xcc0a6e2d6aa8560b45b384ced7aa049870b66ea3"] }, @@ -136,9 +148,6 @@ "superpositiontestnet": { "validators": ["0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a"] }, - "treasuretopaz": { - "validators": ["0x9750849beda0a7870462d4685f953fe39033a5ae"] - }, "unichaintestnet": { "validators": ["0x5e99961cf71918308c3b17ef21b5f515a4f86fe5"] }, diff --git a/typescript/infra/config/environments/testnet4/core/verification.json b/typescript/infra/config/environments/testnet4/core/verification.json index 3a83cee6f20..147e45df610 100644 --- a/typescript/infra/config/environments/testnet4/core/verification.json +++ b/typescript/infra/config/environments/testnet4/core/verification.json @@ -1485,82 +1485,6 @@ "isProxy": false } ], - "camptestnet": [ - { - "name": "ProxyAdmin", - "address": "0x54148470292C24345fb828B003461a9444414517", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "Mailbox", - "address": "0x589C201a07c26b4725A4A829d772f24423da480B", - "constructorArguments": "000000000000000000000000000000000000000000000000000000000004f588", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", - "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" - }, - { - "name": "PausableIsm", - "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", - "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", - "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", - "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000d5eb5fa3f470ebbb93a4a58c644c87031268a04a", - "isProxy": false - }, - { - "name": "PausableHook", - "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "InterchainGasPaymaster", - "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "constructorArguments": "000000000000000000000000e0b988062a0c6492177d64823ab95a9c256c2a5f00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "expectedimplementation": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" - }, - { - "name": "ProtocolFee", - "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", - "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", - "isProxy": false - } - ], "suavetoliman": [ { "name": "ProxyAdmin", @@ -2131,64 +2055,6 @@ "isProxy": false } ], - "treasuretopaz": [ - { - "name": "ProxyAdmin", - "address": "0xfbA0c57A6BA24B5440D3e2089222099b4663B98B", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "Mailbox", - "address": "0x5f33Bf018C55CD3034ac06e6DA41162F5acc2fF7", - "constructorArguments": "00000000000000000000000000000000000000000000000000000000000eeee2", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28f448885bEaaF662f8A9A6c9aF20fAd17A5a1DC", - "constructorArguments": "0000000000000000000000005f33bf018c55cd3034ac06e6da41162f5acc2ff7000000000000000000000000fba0c57a6ba24b5440d3e2089222099b4663b98b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "expectedimplementation": "0x5f33Bf018C55CD3034ac06e6DA41162F5acc2fF7" - }, - { - "name": "MerkleTreeHook", - "address": "0x7fa6009b59F139813eA710dB5496976eE8D80E64", - "constructorArguments": "00000000000000000000000028f448885beaaf662f8a9a6c9af20fad17a5a1dc", - "isProxy": false - }, - { - "name": "FallbackDomainRoutingHook", - "address": "0x623f284257f133E8bE7c74f6D4D684B61FE8923a", - "constructorArguments": "00000000000000000000000028f448885beaaf662f8a9a6c9af20fad17a5a1dc000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000007fa6009b59f139813ea710db5496976ee8d80e64", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x5F1bADC7e28B9b4C98f58dB4e5841e5bf63A7A52", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "InterchainGasPaymaster", - "address": "0xeBe8D0e2BD026D12Ca5e51edA3B0D2b413e83c9c", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xbAaE1B4e953190b05C757F69B2F6C46b9548fa4f", - "constructorArguments": "000000000000000000000000ebe8d0e2bd026d12ca5e51eda3b0d2b413e83c9c000000000000000000000000fba0c57a6ba24b5440d3e2089222099b4663b98b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "expectedimplementation": "0xeBe8D0e2BD026D12Ca5e51edA3B0D2b413e83c9c" - }, - { - "name": "ValidatorAnnounce", - "address": "0xfE9a467831a28Ec3D54deCCf0A2A41fa77dDD1D7", - "constructorArguments": "00000000000000000000000028f448885beaaf662f8a9a6c9af20fad17a5a1dc", - "isProxy": false - } - ], "abstracttestnet": [ { "name": "ProxyAdmin", @@ -3169,5 +3035,215 @@ "constructorArguments": "00000000000000000000000004438ef7622f5412f82915f59cad4f704c61ea48", "isProxy": false } + ], + "megaethtestnet": [ + { + "name": "ProxyAdmin", + "address": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xC5BE013eCE7996Cb8F04FF46ea93c57a04408CD5", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000018c6", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xF78deCe5Cf97e1bd61C202A5ba1af33b87454878", + "constructorArguments": "000000000000000000000000c5be013ece7996cb8f04ff46ea93c57a04408cd50000000000000000000000002a9e9188c7e76f3345e91fd4650ac654a9fe355c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xC5BE013eCE7996Cb8F04FF46ea93c57a04408CD5" + }, + { + "name": "MerkleTreeHook", + "address": "0x0C16e730090cb681460516428c3b2D1BBCB1b647", + "constructorArguments": "000000000000000000000000f78dece5cf97e1bd61c202a5ba1af33b87454878", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x890eB21B76DCB165A1807cBE279f883716eA47D4", + "constructorArguments": "000000000000000000000000f78dece5cf97e1bd61c202a5ba1af33b87454878000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000000c16e730090cb681460516428c3b2d1bbcb1b647", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xBA3f3A84F149842529993bc38A7b4cF8131c17c2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x1fe349d93078B26C8c7350A401e2B60f100952F5", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xcCcC2A71810f9D16770F8062dccc47f7F7C7bA2E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x638A831b4d11Be6a72AcB97d1aE79DA05Ae9B1D3", + "constructorArguments": "000000000000000000000000cccc2a71810f9d16770f8062dccc47f7f7c7ba2e0000000000000000000000002a9e9188c7e76f3345e91fd4650ac654a9fe355c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xcCcC2A71810f9D16770F8062dccc47f7F7C7bA2E" + }, + { + "name": "ProtocolFee", + "address": "0xa318ffca299412ce76B7cb980850ceDB15584E7e", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xf3e31239257b300Daa566131aA6b9e45Ef2A4266", + "constructorArguments": "000000000000000000000000f78dece5cf97e1bd61c202a5ba1af33b87454878", + "isProxy": false + } + ], + "bepolia": [ + { + "name": "ProxyAdmin", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000138c5", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "00000000000000000000000054148470292c24345fb828b003461a94444145170000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x54148470292C24345fb828B003461a9444414517" + }, + { + "name": "MerkleTreeHook", + "address": "0x86abe4c3493A1eE0Aa42f0231b5594D42aBdA36e", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6916690816a1aE1F6D9163dA9e691124737B5f5b", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000086abe4c3493a1ee0aa42f0231b5594d42abda36e", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x9667EfF1556A9D092fdbeC09244CB99b677E9D1E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x36502C6e24C51ba4839c4c4A070aeB52E1adB672", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xF78deCe5Cf97e1bd61C202A5ba1af33b87454878", + "constructorArguments": "0000000000000000000000002a9e9188c7e76f3345e91fd4650ac654a9fe355c0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C" + }, + { + "name": "ProtocolFee", + "address": "0x0E18b28D98C2efDb59252c021320F203305b1B66", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xe63844Ca06Ce8E6D4097Cb33E9b3d62704122307", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + } + ], + "basecamptestnet": [ + { + "name": "ProxyAdmin", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9ace5a", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "00000000000000000000000054148470292c24345fb828b003461a94444145170000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x54148470292C24345fb828B003461a9444414517" + }, + { + "name": "MerkleTreeHook", + "address": "0x86abe4c3493A1eE0Aa42f0231b5594D42aBdA36e", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6916690816a1aE1F6D9163dA9e691124737B5f5b", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000086abe4c3493a1ee0aa42f0231b5594d42abda36e", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x9667EfF1556A9D092fdbeC09244CB99b677E9D1E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x36502C6e24C51ba4839c4c4A070aeB52E1adB672", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xF78deCe5Cf97e1bd61C202A5ba1af33b87454878", + "constructorArguments": "0000000000000000000000002a9e9188c7e76f3345e91fd4650ac654a9fe355c0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2A9E9188C7e76f3345e91fD4650aC654A9FE355C" + }, + { + "name": "ProtocolFee", + "address": "0x0E18b28D98C2efDb59252c021320F203305b1B66", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xe63844Ca06Ce8E6D4097Cb33E9b3d62704122307", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 77eed5e26e4..17317a86706 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -10,7 +10,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'd9e0b4b-20250425-145730', + tag: '2f5ddd8-20250506-163536', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron @@ -33,9 +33,10 @@ export const keyFunderConfig: KeyFunderConfig< arbitrumsepolia: '0.1', arcadiatestnet2: '0.1', auroratestnet: '0.05', + basecamptestnet: '0.05', basesepolia: '0.1', + bepolia: '0.05', bsctestnet: '5', - camptestnet: '0.1', carrchaintestnet: '0.1', chronicleyellowstone: '0.001', citreatestnet: '0.001', @@ -52,9 +53,11 @@ export const keyFunderConfig: KeyFunderConfig< infinityvmmonza: '0', inksepolia: '0.1', kyvetestnet: '0', + megaethtestnet: '0.01', milkywaytestnet: '0', modetestnet: '0.05', monadtestnet: '0.1', + nobletestnet: '0', odysseytestnet: '0.1', optimismsepolia: '0.1', plumetestnet2: '0.1', @@ -71,7 +74,6 @@ export const keyFunderConfig: KeyFunderConfig< suavetoliman: '0.1', subtensortestnet: '0.1', superpositiontestnet: '1', - treasuretopaz: '5', unichaintestnet: '0.1', weavevmtestnet: '0.1', }, diff --git a/typescript/infra/config/environments/testnet4/gasPrices.json b/typescript/infra/config/environments/testnet4/gasPrices.json index c6e965c201c..649ca322490 100644 --- a/typescript/infra/config/environments/testnet4/gasPrices.json +++ b/typescript/infra/config/environments/testnet4/gasPrices.json @@ -23,16 +23,20 @@ "amount": "0.07", "decimals": 9 }, + "basecamptestnet": { + "amount": "10.000000007", + "decimals": 9 + }, "basesepolia": { - "amount": "0.001001198", + "amount": "0.001000422", "decimals": 9 }, - "bsctestnet": { - "amount": "1.0", + "bepolia": { + "amount": "40.000000007", "decimals": 9 }, - "camptestnet": { - "amount": "0.001000253", + "bsctestnet": { + "amount": "1.0", "decimals": 9 }, "carrchaintestnet": { @@ -52,7 +56,7 @@ "decimals": 9 }, "cotitestnet": { - "amount": "1.500000007", + "amount": "1.500000003", "decimals": 9 }, "ecotestnet": { @@ -72,11 +76,11 @@ "decimals": 9 }, "fuji": { - "amount": "1.000000001", + "amount": "0.000000002", "decimals": 9 }, "holesky": { - "amount": "0.035590357", + "amount": "0.000470476", "decimals": 9 }, "hyperliquidevmtestnet": { @@ -95,6 +99,10 @@ "amount": "0.001", "decimals": 1 }, + "megaethtestnet": { + "amount": "0.001", + "decimals": 9 + }, "milkywaytestnet": { "amount": "0.001", "decimals": 1 @@ -107,12 +115,16 @@ "amount": "52.0", "decimals": 9 }, + "nobletestnet": { + "amount": "0.001", + "decimals": 1 + }, "odysseytestnet": { - "amount": "1.034000276", + "amount": "1.000000252", "decimals": 9 }, "optimismsepolia": { - "amount": "0.001000345", + "amount": "0.687270693", "decimals": 9 }, "plumetestnet2": { @@ -120,15 +132,15 @@ "decimals": 9 }, "polygonamoy": { - "amount": "100.0", + "amount": "45.000000018", "decimals": 9 }, "scrollsepolia": { - "amount": "0.042895139", + "amount": "0.016203984", "decimals": 9 }, "sepolia": { - "amount": "21.954276416", + "amount": "16.264909839", "decimals": 9 }, "solanatestnet": { @@ -163,10 +175,6 @@ "amount": "0.01", "decimals": 9 }, - "treasuretopaz": { - "amount": "0.453964137", - "decimals": 9 - }, "unichaintestnet": { "amount": "0.001000253", "decimals": 9 diff --git a/typescript/infra/config/environments/testnet4/ism/verification.json b/typescript/infra/config/environments/testnet4/ism/verification.json index 88851e693ac..820a7fff593 100644 --- a/typescript/infra/config/environments/testnet4/ism/verification.json +++ b/typescript/infra/config/environments/testnet4/ism/verification.json @@ -1749,92 +1749,6 @@ "isProxy": true } ], - "camptestnet": [ - { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsmFactory", - "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHookFactory", - "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "DomainRoutingIsmFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMerkleRootWeightedMultisigIsmFactory", - "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootWeightedMultisigIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdWeightedMultisigIsmFactory", - "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdWeightedMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", - "constructorArguments": "", - "isProxy": true - } - ], "formtestnet": [ { "name": "StaticMerkleRootMultisigIsmFactory", @@ -3702,5 +3616,221 @@ "constructorArguments": "", "isProxy": true } + ], + "megaethtestnet": [ + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHook", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomaingRoutingIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + } + ], + "basecamptestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + } + ], + "bepolia": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + } ] } diff --git a/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json b/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json index fdd5cd17b59..15171187edc 100644 --- a/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json +++ b/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json @@ -200,27 +200,6 @@ "expectedimplementation": "0x01812D60958798695391dacF092BAc4a715B1718" } ], - "camptestnet": [ - { - "name": "InterchainAccountIsm", - "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", - "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", - "isProxy": false - }, - { - "name": "InterchainAccountRouter", - "address": "0x01812D60958798695391dacF092BAc4a715B1718", - "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x867f2089D09903f208AeCac84E599B90E5a4A821", - "constructorArguments": "00000000000000000000000001812d60958798695391dacf092bac4a715b171800000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d356c996277efb7f75ee8bd61b31cc781a12f54f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "expectedimplementation": "0x01812D60958798695391dacF092BAc4a715B1718" - } - ], "suavetoliman": [ { "name": "InterchainAccountIsm", diff --git a/typescript/infra/config/environments/testnet4/owners.ts b/typescript/infra/config/environments/testnet4/owners.ts index 45974a668b7..cbde53a6ebd 100644 --- a/typescript/infra/config/environments/testnet4/owners.ts +++ b/typescript/infra/config/environments/testnet4/owners.ts @@ -28,6 +28,9 @@ export const owners: ChainMap = { milkywaytestnet: { owner: 'n/a - CSDK not supported here', }, + nobletestnet: { + owner: 'n/a - CSDK not supported here', + }, }; export const ethereumChainOwners: ChainMap = Object.fromEntries( diff --git a/typescript/infra/config/environments/testnet4/supportedChainNames.ts b/typescript/infra/config/environments/testnet4/supportedChainNames.ts index 0efc5c6e792..7f42acc2c10 100644 --- a/typescript/infra/config/environments/testnet4/supportedChainNames.ts +++ b/typescript/infra/config/environments/testnet4/supportedChainNames.ts @@ -6,9 +6,10 @@ export const testnet4SupportedChainNames = [ 'arbitrumsepolia', 'arcadiatestnet2', 'auroratestnet', + 'basecamptestnet', 'basesepolia', + 'bepolia', 'bsctestnet', - 'camptestnet', 'carrchaintestnet', 'chronicleyellowstone', 'citreatestnet', @@ -24,9 +25,11 @@ export const testnet4SupportedChainNames = [ 'infinityvmmonza', 'inksepolia', 'kyvetestnet', + 'megaethtestnet', 'milkywaytestnet', 'modetestnet', 'monadtestnet', + 'nobletestnet', 'odysseytestnet', 'optimismsepolia', 'plumetestnet2', @@ -41,7 +44,6 @@ export const testnet4SupportedChainNames = [ 'suavetoliman', 'subtensortestnet', 'superpositiontestnet', - 'treasuretopaz', 'unichaintestnet', 'weavevmtestnet', ] as const; diff --git a/typescript/infra/config/environments/testnet4/tokenPrices.json b/typescript/infra/config/environments/testnet4/tokenPrices.json index 0974b0496e3..dff8ad9a8aa 100644 --- a/typescript/infra/config/environments/testnet4/tokenPrices.json +++ b/typescript/infra/config/environments/testnet4/tokenPrices.json @@ -5,9 +5,10 @@ "arbitrumsepolia": "10", "arcadiatestnet2": "10", "auroratestnet": "10", + "basecamptestnet": "10", "basesepolia": "10", + "bepolia": "10", "bsctestnet": "10", - "camptestnet": "10", "carrchaintestnet": "10", "chronicleyellowstone": "10", "citreatestnet": "10", @@ -23,9 +24,11 @@ "infinityvmmonza": "10", "inksepolia": "10", "kyvetestnet": "10", + "megaethtestnet": "10", "milkywaytestnet": "10", "modetestnet": "10", "monadtestnet": "10", + "nobletestnet": "10", "odysseytestnet": "10", "optimismsepolia": "10", "plumetestnet2": "10", @@ -40,7 +43,6 @@ "suavetoliman": "10", "subtensortestnet": "10", "superpositiontestnet": "10", - "treasuretopaz": "10", "unichaintestnet": "10", "weavevmtestnet": "10" } diff --git a/typescript/infra/config/environments/testnet4/validators.ts b/typescript/infra/config/environments/testnet4/validators.ts index bf641a876d4..d07ad5662b3 100644 --- a/typescript/infra/config/environments/testnet4/validators.ts +++ b/typescript/infra/config/environments/testnet4/validators.ts @@ -221,18 +221,6 @@ export const validatorChainConfig = ( 'citreatestnet', ), }, - camptestnet: { - interval: 5, - reorgPeriod: getReorgPeriod('camptestnet'), - validators: validatorsConfig( - { - [Contexts.Hyperlane]: ['0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e'], - [Contexts.ReleaseCandidate]: [], - [Contexts.Neutron]: [], - }, - 'camptestnet', - ), - }, formtestnet: { interval: 5, reorgPeriod: getReorgPeriod('formtestnet'), @@ -369,18 +357,6 @@ export const validatorChainConfig = ( 'abstracttestnet', ), }, - treasuretopaz: { - interval: 5, - reorgPeriod: getReorgPeriod('treasuretopaz'), - validators: validatorsConfig( - { - [Contexts.Hyperlane]: ['0x9750849beda0a7870462d4685f953fe39033a5ae'], - [Contexts.ReleaseCandidate]: [], - [Contexts.Neutron]: [], - }, - 'treasuretopaz', - ), - }, hyperliquidevmtestnet: { interval: 5, @@ -554,5 +530,46 @@ export const validatorChainConfig = ( 'milkywaytestnet', ), }, + + nobletestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('nobletestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xc30427bd74fdcf179a15b9a6e3c4e1d66104726a'], + }, + 'nobletestnet', + ), + }, + megaethtestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('megaethtestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xf5c8a82f966d2ec8563a2012ccf556ee3f4b25ef'], + }, + 'megaethtestnet', + ), + }, + basecamptestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('basecamptestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x84441e39ed5251410aa2baa72e7747c46d1e5e9d'], + }, + 'basecamptestnet', + ), + }, + bepolia: { + interval: 5, + reorgPeriod: getReorgPeriod('bepolia'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xdb0128bb3d3f204eb18de7e8268e94fde0382daf'], + }, + 'bepolia', + ), + }, }; }; diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index f0f5f5c54b9..85df79c4f05 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -37,7 +37,6 @@ export const chainsToSkip: ChainName[] = [ // testnets 'abstracttestnet', - 'treasuretopaz', // Oct 16 batch 'lumia', diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index 05a6555d7b4..df817e58a13 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -319,6 +319,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + basecamptestnet: { + threshold: 1, + validators: [ + { + address: '0x84441e39ed5251410aa2baa72e7747c46d1e5e9d', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + basesepolia: { threshold: 1, validators: [ @@ -329,6 +339,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + bepolia: { + threshold: 1, + validators: [ + { + address: '0xdb0128bb3d3f204eb18de7e8268e94fde0382daf', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + berachain: { threshold: 3, validators: [ @@ -461,16 +481,6 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - camptestnet: { - threshold: 1, - validators: [ - { - address: '0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e', - alias: AW_VALIDATOR_ALIAS, - }, - ], - }, - carrchaintestnet: { threshold: 1, validators: [ @@ -1390,6 +1400,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + megaethtestnet: { + threshold: 1, + validators: [ + { + address: '0xf5c8a82f966d2ec8563a2012ccf556ee3f4b25ef', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + merlin: { threshold: 2, validators: [ @@ -1604,6 +1624,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + nobletestnet: { + threshold: 1, + validators: [ + { + address: '0xc30427bd74fdcf179a15b9a6e3c4e1d66104726a', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + odysseytestnet: { threshold: 1, validators: [ @@ -2402,16 +2432,6 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - treasuretopaz: { - threshold: 1, - validators: [ - { - address: '0x9750849beda0a7870462d4685f953fe39033a5ae', - alias: AW_VALIDATOR_ALIAS, - }, - ], - }, - trumpchain: { threshold: 2, validators: [ diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 9df383cc757..b50704af348 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -552,14 +552,18 @@ export function buildAgentConfig( const chainConfigs: ChainMap = {}; for (const chain of [...chains].sort()) { const metadata = multiProvider.tryGetChainMetadata(chain); - if ( - metadata?.protocol === ProtocolType.Cosmos || - metadata?.protocol === ProtocolType.CosmosNative - ) { + // Cosmos Native chains have the correct gRPC URL format in the registry. So only delete the gRPC URL for legacy Cosmos chains. + 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; + } + // Delete transaction overrides for all Cosmos chains. + if ( + metadata?.protocol === ProtocolType.Cosmos || + metadata?.protocol === ProtocolType.CosmosNative + ) { // 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; From 5281378b0c4c4a5e4e796da5c0919fc1c15d5256 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 7 May 2025 12:22:31 -0400 Subject: [PATCH 149/223] feat: fix message status not being stored correctly (#6153) ### Description Fix message status not being stored correctly. Turns out, instead of storing the new message status, we store the old message status, but set it to the new message status in memory. ### Related issues - fixes: https://linear.app/hyperlane-xyz/issue/ENG-1024/bug-relayer-messages-are-loaded-with-the-wrong-operation-status-from ### Drive-bys - move some dummy test code to be public so other tests can also use them ### Backward compatibility Yes ### Testing - added a test with a temp dir rocksdb to make sure `PendingMessage.set_status()` works as expected --- rust/main/agents/relayer/src/msg/op_batch.rs | 5 +- .../agents/relayer/src/msg/pending_message.rs | 54 +++++++- rust/main/agents/relayer/src/msg/processor.rs | 105 ++-------------- .../relayer/src/test_utils/dummy_data.rs | 116 ++++++++++++++++++ .../main/agents/relayer/src/test_utils/mod.rs | 1 + 5 files changed, 175 insertions(+), 106 deletions(-) create mode 100644 rust/main/agents/relayer/src/test_utils/dummy_data.rs diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index f1cf4440c03..4d2b5abeed1 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -169,13 +169,12 @@ mod tests { }, op_queue::test::MockPendingOperation, pending_message::{MessageContext, PendingMessage}, - processor::test::{ - dummy_cache_metrics, dummy_submission_metrics, DummyApplicationOperationVerifier, - }, + processor::test::{dummy_cache_metrics, DummyApplicationOperationVerifier}, }, settings::{ matching_list::MatchingList, GasPaymentEnforcementConf, GasPaymentEnforcementPolicy, }, + test_utils::dummy_data::dummy_submission_metrics, }; use ethers::utils::hex; use hyperlane_base::{ diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index ff7ea82b2d3..aabd5d0d1e2 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -177,9 +177,9 @@ impl PendingOperation for PendingMessage { if let Err(e) = self .ctx .origin_db - .store_status_by_message_id(&self.message.id(), &self.status) + .store_status_by_message_id(&self.message.id(), &status) { - warn!(message_id = ?self.message.id(), err = %e, status = %self.status, "Persisting `status` failed for message"); + warn!(message_id = ?self.message.id(), err = %e, status = %status, "Persisting `status` failed for message"); } self.status = status; } @@ -1051,12 +1051,12 @@ mod test { }; use chrono::TimeDelta; - use hyperlane_base::db::*; + use hyperlane_base::{cache::OptionalCache, db::*}; use hyperlane_core::{identifiers::UniqueIdentifier, *}; - use crate::msg::pending_message::DEFAULT_MAX_MESSAGE_RETRIES; + use crate::test_utils::dummy_data::{dummy_message_context, dummy_metadata_builder}; - use super::PendingMessage; + use super::{PendingMessage, DEFAULT_MAX_MESSAGE_RETRIES}; mockall::mock! { pub Db { @@ -1337,4 +1337,48 @@ mod test { assert_eq!(num_retries_in_range, 2); } + + #[tokio::test] + async fn check_stored_status() { + let origin_domain = HyperlaneDomain::Known(hyperlane_core::KnownHyperlaneDomain::Arbitrum); + let destination_domain = + HyperlaneDomain::Known(hyperlane_core::KnownHyperlaneDomain::Arbitrum); + let cache = OptionalCache::new(None); + + let temp_dir = tempfile::tempdir().unwrap(); + let db = DB::from_path(temp_dir.path()).unwrap(); + let base_db = HyperlaneRocksDB::new(&origin_domain, db); + + let message = HyperlaneMessage { + nonce: 0, + origin: KnownHyperlaneDomain::Arbitrum as u32, + destination: KnownHyperlaneDomain::Arbitrum as u32, + ..Default::default() + }; + + let base_metadata_builder = + dummy_metadata_builder(&origin_domain, &destination_domain, &base_db, cache.clone()); + let message_context = + dummy_message_context(Arc::new(base_metadata_builder), &base_db, cache); + + let mut pending_message = PendingMessage::new( + message.clone(), + Arc::new(message_context), + PendingOperationStatus::FirstPrepareAttempt, + Some(format!("test-{}", 0)), + 2, + ); + + let expected_status = PendingOperationStatus::ReadyToSubmit; + pending_message.set_status(expected_status.clone()); + + let db_status = pending_message + .ctx + .origin_db + .retrieve_status_by_message_id(&pending_message.id()) + .expect("Failed to fetch message status") + .expect("Message status not found"); + + assert_eq!(db_status, expected_status); + } } diff --git a/rust/main/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs index f874818101b..37674a3fc06 100644 --- a/rust/main/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -407,12 +407,9 @@ impl MessageProcessorMetrics { pub mod test { use std::time::Instant; - use prometheus::{CounterVec, IntCounter, IntCounterVec, Opts, Registry}; + use prometheus::IntCounterVec; use tokio::{ - sync::{ - mpsc::{self, UnboundedReceiver}, - RwLock, - }, + sync::mpsc::{self, UnboundedReceiver}, time::sleep, }; use tokio_metrics::TaskMonitor; @@ -423,7 +420,6 @@ pub mod test { test_utils, DbResult, HyperlaneRocksDB, InterchainGasExpenditureData, InterchainGasPaymentData, }, - settings::{ChainConf, ChainConnectionConf, Settings}, }; use hyperlane_core::{ identifiers::UniqueIdentifier, test_utils::dummy_domain, GasPaymentKey, @@ -433,20 +429,11 @@ pub mod test { use hyperlane_operation_verifier::{ ApplicationOperationVerifier, ApplicationOperationVerifierReport, }; - use hyperlane_test::mocks::{MockMailboxContract, MockValidatorAnnounceContract}; use tracing::info_span; use crate::{ - merkle_tree::builder::MerkleTreeBuilder, - metrics::message_submission::MessageSubmissionMetrics, - msg::{ - gas_payment::GasPaymentEnforcer, - metadata::{ - BaseMetadataBuilder, DefaultIsmCache, IsmAwareAppContextClassifier, - IsmCachePolicyClassifier, - }, - }, processor::Processor, + test_utils::dummy_data::{dummy_message_context, dummy_metadata_builder}, }; use super::*; @@ -493,79 +480,6 @@ pub mod test { } } - pub fn dummy_submission_metrics() -> MessageSubmissionMetrics { - MessageSubmissionMetrics { - origin: "".to_string(), - destination: "".to_string(), - last_known_nonce: IntGauge::new("last_known_nonce_gauge", "help string").unwrap(), - messages_processed: IntCounter::new("message_processed_gauge", "help string").unwrap(), - metadata_build_count: IntCounterVec::new( - Opts::new("metadata_build_count", "help string"), - &["app_context", "origin", "remote", "status"], - ) - .unwrap(), - metadata_build_duration: CounterVec::new( - Opts::new("metadata_build_duration", "help string"), - &["app_context", "origin", "remote", "status"], - ) - .unwrap(), - } - } - - fn dummy_chain_conf(domain: &HyperlaneDomain) -> ChainConf { - ChainConf { - domain: domain.clone(), - signer: Default::default(), - submitter: Default::default(), - estimated_block_time: Duration::from_secs_f64(1.1), - reorg_period: Default::default(), - addresses: Default::default(), - connection: ChainConnectionConf::Ethereum(hyperlane_ethereum::ConnectionConf { - rpc_connection: hyperlane_ethereum::RpcConnectionConf::Http { - url: "http://example.com".parse().unwrap(), - }, - transaction_overrides: Default::default(), - op_submission_config: Default::default(), - }), - metrics_conf: Default::default(), - index: Default::default(), - } - } - - fn dummy_metadata_builder( - origin_domain: &HyperlaneDomain, - destination_domain: &HyperlaneDomain, - db: &HyperlaneRocksDB, - cache: OptionalCache>, - ) -> BaseMetadataBuilder { - let mut settings = Settings::default(); - settings.chains.insert( - origin_domain.name().to_owned(), - dummy_chain_conf(origin_domain), - ); - settings.chains.insert( - destination_domain.name().to_owned(), - dummy_chain_conf(destination_domain), - ); - let destination_chain_conf = settings.chain_setup(destination_domain).unwrap(); - let core_metrics = CoreMetrics::new("dummy_relayer", 37582, Registry::new()).unwrap(); - let default_ism_getter = DefaultIsmCache::new(Arc::new( - MockMailboxContract::new_with_default_ism(H256::zero()), - )); - BaseMetadataBuilder::new( - origin_domain.clone(), - destination_chain_conf.clone(), - Arc::new(RwLock::new(MerkleTreeBuilder::new())), - Arc::new(MockValidatorAnnounceContract::default()), - false, - Arc::new(core_metrics), - cache, - db.clone(), - IsmAwareAppContextClassifier::new(default_ism_getter.clone(), vec![]), - IsmCachePolicyClassifier::new(default_ism_getter, Default::default()), - ) - } - fn dummy_message_processor( origin_domain: &HyperlaneDomain, destination_domain: &HyperlaneDomain, @@ -574,16 +488,11 @@ pub mod test { ) -> (MessageProcessor, UnboundedReceiver) { let base_metadata_builder = dummy_metadata_builder(origin_domain, destination_domain, db, cache.clone()); - let message_context = Arc::new(MessageContext { - destination_mailbox: Arc::new(MockMailboxContract::new_with_default_ism(H256::zero())), - origin_db: Arc::new(db.clone()), + let message_context = Arc::new(dummy_message_context( + Arc::new(base_metadata_builder), + db, cache, - metadata_builder: Arc::new(base_metadata_builder), - origin_gas_payment_enforcer: Arc::new(GasPaymentEnforcer::new([], db.clone())), - transaction_gas_limit: Default::default(), - metrics: dummy_submission_metrics(), - application_operation_verifier: Some(Arc::new(DummyApplicationOperationVerifier {})), - }); + )); let (send_channel, receive_channel) = mpsc::unbounded_channel::(); ( diff --git a/rust/main/agents/relayer/src/test_utils/dummy_data.rs b/rust/main/agents/relayer/src/test_utils/dummy_data.rs new file mode 100644 index 00000000000..d8962f44fcf --- /dev/null +++ b/rust/main/agents/relayer/src/test_utils/dummy_data.rs @@ -0,0 +1,116 @@ +use std::{sync::Arc, time::Duration}; + +use hyperlane_base::{ + cache::{LocalCache, MeteredCache, OptionalCache}, + db::HyperlaneRocksDB, + settings::{ChainConf, ChainConnectionConf, Settings}, + CoreMetrics, +}; +use hyperlane_core::{HyperlaneDomain, H256}; +use hyperlane_test::mocks::{MockMailboxContract, MockValidatorAnnounceContract}; +use prometheus::{CounterVec, IntCounter, IntCounterVec, IntGauge, Opts, Registry}; +use tokio::sync::RwLock; + +use crate::{ + merkle_tree::builder::MerkleTreeBuilder, + metrics::message_submission::MessageSubmissionMetrics, + msg::{ + gas_payment::GasPaymentEnforcer, + metadata::{ + BaseMetadataBuilder, DefaultIsmCache, IsmAwareAppContextClassifier, + IsmCachePolicyClassifier, + }, + pending_message::MessageContext, + processor::test::DummyApplicationOperationVerifier, + }, +}; + +pub fn dummy_chain_conf(domain: &HyperlaneDomain) -> ChainConf { + ChainConf { + domain: domain.clone(), + signer: Default::default(), + submitter: Default::default(), + estimated_block_time: Duration::from_secs_f64(1.1), + reorg_period: Default::default(), + addresses: Default::default(), + connection: ChainConnectionConf::Ethereum(hyperlane_ethereum::ConnectionConf { + rpc_connection: hyperlane_ethereum::RpcConnectionConf::Http { + url: "http://example.com".parse().unwrap(), + }, + transaction_overrides: Default::default(), + op_submission_config: Default::default(), + }), + metrics_conf: Default::default(), + index: Default::default(), + } +} + +pub fn dummy_metadata_builder( + origin_domain: &HyperlaneDomain, + destination_domain: &HyperlaneDomain, + db: &HyperlaneRocksDB, + cache: OptionalCache>, +) -> BaseMetadataBuilder { + let mut settings = Settings::default(); + settings.chains.insert( + origin_domain.name().to_owned(), + dummy_chain_conf(origin_domain), + ); + settings.chains.insert( + destination_domain.name().to_owned(), + dummy_chain_conf(destination_domain), + ); + let destination_chain_conf = settings.chain_setup(destination_domain).unwrap(); + let core_metrics = CoreMetrics::new("dummy_relayer", 37582, Registry::new()).unwrap(); + let default_ism_getter = DefaultIsmCache::new(Arc::new( + MockMailboxContract::new_with_default_ism(H256::zero()), + )); + BaseMetadataBuilder::new( + origin_domain.clone(), + destination_chain_conf.clone(), + Arc::new(RwLock::new(MerkleTreeBuilder::new())), + Arc::new(MockValidatorAnnounceContract::default()), + false, + Arc::new(core_metrics), + cache, + db.clone(), + IsmAwareAppContextClassifier::new(default_ism_getter.clone(), vec![]), + IsmCachePolicyClassifier::new(default_ism_getter, Default::default()), + ) +} + +pub fn dummy_submission_metrics() -> MessageSubmissionMetrics { + MessageSubmissionMetrics { + origin: "".to_string(), + destination: "".to_string(), + last_known_nonce: IntGauge::new("last_known_nonce_gauge", "help string").unwrap(), + messages_processed: IntCounter::new("message_processed_gauge", "help string").unwrap(), + metadata_build_count: IntCounterVec::new( + Opts::new("metadata_build_count", "help string"), + &["app_context", "origin", "remote", "status"], + ) + .unwrap(), + metadata_build_duration: CounterVec::new( + Opts::new("metadata_build_duration", "help string"), + &["app_context", "origin", "remote", "status"], + ) + .unwrap(), + } +} + +pub fn dummy_message_context( + base_metadata_builder: Arc, + db: &HyperlaneRocksDB, + cache: OptionalCache>, +) -> MessageContext { + MessageContext { + destination_mailbox: Arc::new(MockMailboxContract::new_with_default_ism(H256::zero())), + origin_db: Arc::new(db.clone()), + cache, + metadata_builder: base_metadata_builder, + origin_gas_payment_enforcer: Arc::new(GasPaymentEnforcer::new([], db.clone())), + transaction_gas_limit: Default::default(), + metrics: dummy_submission_metrics(), + application_operation_verifier: Some(Arc::new(DummyApplicationOperationVerifier {})), + } +} diff --git a/rust/main/agents/relayer/src/test_utils/mod.rs b/rust/main/agents/relayer/src/test_utils/mod.rs index 5a047976ce5..44f8d8eb82b 100644 --- a/rust/main/agents/relayer/src/test_utils/mod.rs +++ b/rust/main/agents/relayer/src/test_utils/mod.rs @@ -1,3 +1,4 @@ +pub mod dummy_data; pub mod mock_aggregation_ism; pub mod mock_base_builder; pub mod mock_ism; From 55fbe7e257c51e450fdaf39d01521f84c1b800b8 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Thu, 8 May 2025 13:44:34 +0100 Subject: [PATCH 150/223] fix: Scraper - Use previous compact mode version in Cosmos HttpClient to fix issue with Neutron chain (#6157) ### Description Scraper is not indexing payments and delivered messages for Neutron chain since it gets serde parsing error when it attempts to request a block by height. The issue was reproduced using unit tests with Neutron RPC and particular block. The issue was fixed by downgrading `CompactMode` from latest version (0.38 at the time of this commit) to version 0.37. HttpClient fails to get block 22488720 from Neutron when `CompactMode::latest()` is used. Latest compact mode at the moment is `V0_38`. It succeeds with `CompactMode::V0.38`. HttpClient succeeds to get block 15317185 from Osmosis when `CompactMode::latest()` is used. Actually, any compact mode works for Osmosis. ### Backward compatibility Yes ### Testing - Unit test of pure parsing of JSON - Unit test of HttpClient to request a block from Neutron - Unit test of CosmosFallbackProvider to request a block from Neutron. --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../src/providers/rpc/client.rs | 5 +- .../src/providers/rpc/client/tests.rs | 1298 +++++++++++++++++ .../src/providers/rpc/provider.rs | 3 + .../src/providers/rpc/provider/tests.rs | 54 + 4 files changed, 1359 insertions(+), 1 deletion(-) create mode 100644 rust/main/chains/hyperlane-cosmos/src/providers/rpc/client/tests.rs create mode 100644 rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider/tests.rs diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs index b97954c5ecd..efb1bf38a32 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs @@ -58,7 +58,7 @@ impl CosmosRpcClient { let client = HttpClient::builder(url) // Consider supporting different compatibility modes. - .compat_mode(CompatMode::latest()) + .compat_mode(CompatMode::V0_37) .build() .map_err(Box::new) .map_err(Into::::into)?; @@ -171,3 +171,6 @@ impl BlockNumberGetter for CosmosRpcClient { .map(|block| block.block.header.height.value()) } } + +#[cfg(test)] +mod tests; diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client/tests.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client/tests.rs new file mode 100644 index 00000000000..34f1475192d --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client/tests.rs @@ -0,0 +1,1298 @@ +use crate::HyperlaneCosmosError; +use std::str::FromStr; +use tendermint_rpc::client::CompatMode; +use tendermint_rpc::endpoint::block::Response; +use tendermint_rpc::{HttpClient, Url}; + +#[test] +fn test_deserialize_neutron_block_22488720() { + let json = r#"{ + "block_id": { + "hash": "9E947DB9A8B4C7DF627133BA3E63524A1FDA37569B8C3EF4BA565B298D67D932", + "parts": { + "total": 1, + "hash": "62443A9D4BDC9F2D36173869CED69117837DA650CFB3ABFDC7D9A82FF5886071" + } + }, + "block": { + "header": { + "version": { + "block": "11" + }, + "chain_id": "neutron-1", + "height": "22488720", + "time": "2025-04-17T08:53:58.591125912Z", + "last_block_id": { + "hash": "80210BCF50B6452AADF54E4DFDEA4E046118F4E160C7E950ABFF272F7EF652F1", + "parts": { + "total": 1, + "hash": "B679BCE5E8EB186266BA1EF1A2E473D93308A17FFA2EF9EA914FFD3E96F96A8C" + } + }, + "last_commit_hash": "F01A0742B4F967C2AA4400146B99402C76BC91B5204D85B7306E78D5119B9D8F", + "data_hash": "82084E4AEC2799CDEC4A28F046F4CAC1C9854A6C928AF75AACECFE8523306BF4", + "validators_hash": "DB26D5945D09EB22F6CAF73322BEB18C5610425B1383DFB3D3122342762BF3FE", + "next_validators_hash": "DB26D5945D09EB22F6CAF73322BEB18C5610425B1383DFB3D3122342762BF3FE", + "consensus_hash": "490140191D4EE09CBB314A48DC7D7CDC4BC021E6466F53AC18F2317296A29CD1", + "app_hash": "64ED13188CF9AA7694DE8B550F19AE87FCD259A96CD6B86D556D0BECB7F4E0F9", + "last_results_hash": "697AC0DA637A4D63975EEEC4D0114CA918111C81B7B2C0DD7F9BE63F9EC40B40", + "evidence_hash": "5553C83F99ED89CA98824AD26B6D6E407D86E77B3D2CB7DD36567543DC46135B", + "proposer_address": "C3CE921ADDCF756F31CB2D4B64FB0233011FF95B" + }, + "data": { + "txs": [ + "KLUv/WTMHt35ANztARLLAgobChRt/8gQdYvD7mnhVOamrbJgrceUEBjur7IDGucBeJw80GdKQ1EQR3FvGTOOsY3G3mKJ2LF3jS0aC2YVbkRwMW8bLsGduAHlccBPPzjwvzDXRJc8RBPdg36Ygzd4KVFteE7p+91EK/QZOIY7eIQqCGxDhEu44tkhzynWf0z0jN6GALMlFR30lL46JvpEbsEz3MIUnPDwsOdYazVNdIF+DuOwCkcwCg5NmIZ1eIVduAeFfeiAwTWcQoY1WIYx2OTISU8pNEx0hAsGPMfwWZjoDqH7P/QynIAtmIcLqLGpl//b/jXRPvoG3EAXLMIKPDA98JyqxYeJHtJ7PMS/AAAA//9UxSJ/IkAgNTOBcuy2Nl5V5Y49tqHJKsxU19zQcMYhbK2eXX3c0Ve9ctbiFrEAaxW5i33RmQgzu73xCrc4t1TFXZWfEkkFKAIS/AIKGwoU5mSbP1ahOuLQKJ3VWac/QvykSDAY8+aqAxqYAnicJNBHTiNREMbx6fdejWtqPKmGnE0wwiSRs0kGY4LwKTgACy6AxAXYsmbhBQfwliP4Hiw4AKDWf/WTvv6qWvVMdMezYAUNHsNTzVTnPcVYfzPRZv5FdAl6oAhbMAl98Bv6IYMhGMxR/eMpZA8tE10kP4BLGIc5mIAGKDicQwH2ocZPhj2FcNc20VnyE24c8BizsqnOeIrp8cNUN/NqtWOix3T+eoyvTRNdZ/SUlSVPMTQ+TXSDvBeWYQGu4QxWYQS6oALG2nL+2J0bE00Ea55isXVvoj8prsAYXMA3qMM27IGwaMpTkHbFVL97CvbybKLdNI5gGg5hFK6Y/pefW3o30R/ku/ALqrT+ewpeu/0KAAD//62+KdgiQAP1+qWllu4WCKDGejG9W2lN+T3V5qJEnW+ErMY0KvfPWFYYj45AwL03SosmK9LwKoItN81cLTLh9zUBCqq9DQwoAhKFAwobChSKoP8GYV0G31WWTCQ68ziG+ML/rBi766gDGqECeJws0UdOI1EQxvHpF9w1NQOYIudsRDZgcs7YgEAcwltuwSWQkNjiE8ANWLFgiTgAR/ACgVp/Vj/p66+q9VQapd0Sp1FmwWWINFlwye2jirRZ8OG9olEijUUYhgv6SxZcWn3SKAn5WEYqe+ZdsaZRRon/wgyksAHTcAU9LN614MLdq0a5JO+CBjiFEuzAAfSzocWCK748a5RG8i3y3O8TU8mb958ljVKhcAzj9HotuNzHl0Y5JF+GKZiAfZgHgxMYAIVNKMM/fjJowSf1exUpWPD+rapR1mn0wSocQStjzdlYvq5RFsjnoAPW4Az+gDA6kl3s4VqjbJP/hxU+D1nwrvytUc65ZLd5nxRUpDO7SO1GowQmJi1xPwEAAP//BDYsriJA4qD2ctTBqeEtzT9+jUWDuczwkTwtsDCH0pPuvR3FnTx8UHseV1mSuBdlInB2JX3RdGaGA41zXIYhyDzwWSZDAigCEmEKGwoUcRiqifFx4yyvVWD/ZHVI1Olg1fAYqtioAyJA8Z7xrqob4pdomfP3gtj4ENmX11tv30wXGg24r4N/AD4W0v/DkGo0jSRZOdV+bDhPSGmGmnwxxmrI97u61QHVDygCEtsCChsKFFsasDM4HWn9WKCdQLWqtAGacM0aGJvIpgMa9wF4nDzRR05DMRDGcZ7tIcMQAgy9E0oQXUDovSeQiHcKLoLEYXINjsBN2LAEPf2lrH7SZ4/tGZvojmfBRFcLVA89xXLnw0SPyR1m4Ra2QcEghyc4gDKUCko67TFmNRMV4gEuHfUUK78/JvpIfgn7sAEPsAlNaMM9B1U9xdD8M9E5gpqnGL/fTXSZjc8wBuewCHWYorTXU8g+OybaIr+CdXiFLWjACazQ7bzHmDdM9IL4FPbgBsZhASYhgzs4gyXYhWvohwlYgwRHMMNrhjzGr9xEe+hvxFNotesmWiEYLoZXLX4hUPgGLywPdufR51n4DwAA//9Z8yVMIkAcjwOBgNXMtPWDjPQFh3joDNhtJxszJreXATJC2GkQRHPUsBKs7mgtLS0SlO+Z3UQ5rqu/FQs5B/qpYGPqldQBKAIS/AIKGwoUFfzmQ088yt5mPw7zFIEJJvUW9B4Y6JemAxqYAnicJM/HUSNREMbxnfde7/T2WvUivJEwovCF9yCckEAUKoIgBMiAA1noSghcCYETaUAAUFP/06/mm+6u95nonmfBVP96CtnDk4luFIHoDFzAOST4zU7JUwzVdxNtkB9And8rnmLWfjPRU/Ir6AeHfZiCVZiDIQjQLMh10GPMaiba4fvIY6x0TPSasT5Yg2UY40nrRcvSvYkeElSLCq1PU/3mKchHZqILrEzADoM1TzG+3proNMF/T6HcqJuoMfgH2rALCluwBGVocWjEUwh3zyZ6SV4h/+4p5t0by/WXx/jYNdFhGv/zGF+KxrMs9MIZZLANiyAwzoFRj7HTNNGfxDn0wDEMwCZMwg+YhxPPwlcAAAD//wwoJz4iQBgZo3VPjVceIuIWsLU9I8ehIolFnWBlX3LReg5S3bYRYAzzzfNuVUHgzMPWphdah/8AO5ibeDywlJWV/8vRFgcoAhLLAgobChRlHKVEjdHfWQkmyxpF9qUtWLZlrBjl0KVAYV8fHOYOlzFR9RBN9AleYBv64AxuYQSqcAfjsAf9sAQGE7AA63BSolr3nGL710RnCAeeU7X4MNEag9ewBQKXDDc8p/T9bqLH9H16t+cYPgsTXaNPwylEaMN8SUUHPaWvjomekzfhkI0D/xt76R2owArsQuDNij4Gb/AMLTiCZeiCUdiADHNwA7PwCo9wBT2wCE24hx3+OFTev/5joqucYdJTCg0TdeYe4MJD/AsAAP//SI8ifyJAKxpRv8SdaNS6sK8d1GVcBGjqyzFR8iyIHpJFfGu5uFkMhl+njiQBxGSZXXYl/mdvaONhT7g7bPzkomXILmWXBigCEtnDzpIa3c91bzHLLUtk+wIzAR/5WxjFm6UDGvUBeJws0FdOK0EQRuE7Xd3XRWEMFDnbBCOyAJNzxmALr4KNILEYb4MlsBNeeAS1Dk+fdDTz90xb0ksvgqnWPYp8vlrSSg5JI5ThAdowAluZkk66SFG3pIfkbSjBKDQ55r/HULx3Lek5vQUdOIBVmIULWIEeOIV/zPb/zaoOeQytdsNUBz1KqH1ZUuOhWg7NH0u6z8tVuIVreIIjWIIzOIFxuIFneIQGjHHksEepfOdvCPRjKMBBYQ0WYZPLvXKRaseSbpB74QX6YAGmYAKmYZ6dARf5yDuJvAv3cAfrMAPLMMev7HmUcvfNku54EX4DAAD//56wJSQiQNeGgJP13cpxDakPQJlg/tM4HlqqQ4UyD8rbrYdEQpkdU425bYMVfQREe59wxNI9UQDoa8qK35tFNvFngVTUJgYoAhLL1m69+M7H0/UimjKNkINCcffKDBwYus+kxfEHP/3gcLicOyZ66CGa6BK8wT1MwyM8wx2cwRrUIMIsPIDCeYnqgedULT5M9JbcoQXL0AcG/dCEHh4a8BzDZ2Gqdc8ptn9N9JrGXG4QV2EK2tCBeXiCUdiFY9iGVdiHCcY0J+SncAQjtLr/Jw97jrVW00Q3aezAIsxAgC54hTEYhy14gXW44f+TnlJomGiFeAEEemEPViDDFVwwe6i8b/3HRC89m6hAv4TUP+TtfIYsap19q7cKuOwcgOr4fq8eKF35cw/vCt4B/Kqecko4nGAOHIVtaywws3WChVQSL8eZ8txTYoIPKAIS/gCVQl5myDZMqZqX12jG9Nux/9SSGKbJpAMamgJ4nDzQyUpjvuecSqqr01N1O49xiDijRuM8a4wmKD6DiG/hQvAZxJ3brH0T1+LarXtRwh9c/eCj6qt7j4n+8iyY6CnswS5UYbSFas5TyG6bJvqfoMdTzD+cm+qip1ho3phqyVOMT5cmesJyJwzCGFSgC/ahDFMg3PjrKYbim4lOkK9DR4u85j2GStVEvxMH6GP5n6dQb5RNNXmK2cWLibYxscXE769/mqPwj8f4WjbRYebqsAJLsAwFOIN5GIcaGPygvNtjzEom2iDehlW+pddTyD2/m+gCeT8MwDEcUrXjMTxem2g7scIQbMARjIBDxrVi62FrHyb6k3wWpuEbrMEkHLA64yne3V+Z6KZn4TMAAP//bKoqRyJA5G3v4FvQ9XCbm1PLaamQ0wg1+uf/i+6SNL9ezavqVbzIUU7vtqneAWrc/VOCcJ5KdQ8U0Ajy8u0A5Go+FQaUDSgCEtACChsKFBGPI8DiwcAFzR+wonZZlx9QYU9lGPXBpAMa7AF4nCzO500DQRBHcW53hxsGkwZMTiYYkUXOYJKxCeKqoBEkinEblEAnNAA6PX/6SX+91ayJVjwLJroJj1DAFpxBH0zACjzDDZyUqA55CtlXx0QX2behH24hJ+7txqojnmKo/ZqoUcyCwBRkcAgzcAlrsA5VOIclztU9xfjzYbnOe4xFy0RfCFahCQEOeFUrv9b+M9EL9mN4B4d72IM2LJTkOuwxfhcm+sR8CncwDmNcG/UUqs2G5TrtMWZ1E30juIJ9SHDNqyNPsdL5NNFd9gHogWXYgUF4hRYoPMAGTEID5jwL/wEAAP//LkQjHCJAMyfIYmASvINxs5WWefhbm2kWWDQAITAHP31eboCfdS4m02CVcWrf1ZF2L9YmqvPp4fzN6I3iB3X+/FxOl4MWACgCEr0DChsKFKSaisWP13zHXAwzedTpmUwBHMiRGIe4pAMa2QJ4nDzQOW5TYRQFYN4d/I4vZrrMZjSjsBkEGGwmMw8GGywEW6ChZwNIiJ4SUVAlcpE0qdxmASm8kywgiV5OlOr7/+Pr837dAC6nic/a4XidhQRwOE1G4244jlRBiX2p8rsTjrvV3bGXvCMXyWkyJnvIU/KAXGN/P01kMA/gSXUaboZjxIkeJx6lSf1LK4BnaWJ/18IRfEszVZYWwnGfd0/V1a9RAqmyfTjGg0PY+ZCDF1L143I43jJu81O1NCl+TcPRYf6Y3OHPrTStXliikSr9lXAMOfCSvCDXyYScJe/JJ1bdS9PG9Gc4zjE4VHW31sNxgoN15pfSpNZcDMcNBoM0tT8bAZypFvZjFkB352GO8/xzklvkA9lPTpKjpCBXWX47Tcr/n8Nxk3nJ/Eqa6vxbAKfStJoADuwuy7jU56nSm4TjOO8HU/Xf93C8YderLGQrAAD//8EBNqAiQHqzy7XQM2SSf82w+azn++TCANTc6Lzf8UZLM5rNZYte3XvCRdakP83MLQrk1UkF1FzCDLx/wuS8jA1suEQjgQUoAhLhAgobChRRNz3gxtvQwp6GD4aQmYvnwbXBBRiUtqQDGv0BeJw80ElOK0EMgOHXVeUXYyAQM8+EIYhZQJghzJBAInIbbsBhOAM7jsBN2LAElX6J1Sf9ltxdNtEJL4Kp/vcUitc3Ew05iF7AHONhT7H8/WWid/Rd6GFc8RRDNY9H6SUYgzOYh2ampIMe40fXRPvIU+RJj7Gomegi+ZEvDHkK7U7dRNfoBiNwCf+gDA/wxIb9/MLKi4nu0HthGzaggD1owTUbqvmVrR8TXaZf0Y88hdD4NNEF+hJ04RBuoQH9cM+Ggb/rK/0UtqDOaWY9xm7TRDtkgXG4YVUt/2T73USP6SfgcACbMAPrsAIJpuEcVuEZ2l6E3wAAAP//0pklgiJA6O22JfCbiSBOztnJ4NbOdSEMbsxttYIZe3794uViCmeO1ywmYZ+67nMfXGMAwY0lTewqKOY6THJqSNvPN7ssCigCEuYCChsKFI11helj602/R3dygFTQQ4f9IEavGIi2pAMaggJ4nDzRWUorURCA4dvnnLopyziVxnmKIxo1OM8ap2g0YnARbkQQt+FrlqFL8MltuAGl+cGnD/4+VBW0iV55Fkz1v6eQvbRNtJYH0Sn6Rt6bXya6Q2+C8bnPUwzlbxO9pi9DB9RzCtrlMbxWTLXnb0s3D1qwxrRtT7HYfjbRM/o6TMMpDME4NFgx6jG8V030gHwDCotwCEewCUUYhhJMwi04PHBlv6dQqtdM9JIw5ikW3h5NtMrDQRBYgRPYgnvO7vUYP1omukfeh3mYg1nohHOYgQm4gwpccNeCpxg/n0x0l74EAxAggxFIUGBCOf+9jR8TPab/g1XPwm+63SYsIkCTgmWTOTeJxI9CU+TYEy9q0T7H1RfJsAR6131GFc6P/3/aw/zKfrF1GALQKr17djtgS3uBUwK/BBDqeUsQXCHQCh1y7PJWKSuGBmJ4SBalZJZoihiwtTzQWS6DcRSGcf/h6HEUdah5HirmmGdqKjVEV2EbLiQW02s7sASrcUW+PImrX/JcvBeviU56iCZ6CC8Q4AS2oRe6ClRrnlP6fjXRQfozNOAYjmAOWnANKwUlrXhKXy0TnWB41nOKzV8TdcKe51Ruv5tomdDnOYaPtomesrTB0pinFGomWiKfwwxk2Ic6XMEF7MINrMIjdEAP3IPAOhxAFc7gFhZhBKbhCSJMwRAYrMEyDHNA5/8B3fQtaMISbMIdzMMojMMDewOeY7VRN9EF+g6HXnqKn2+m2u85hcqPiaqH+BcAAP//qRwjqyJArBlZ7nRjsq9QuyxEvOs/FGmg5tyHLKz8FubtmtH8i+DW82chnTb5qI4aO2Z6Tj/e+bii2ZNk4il8/S7e37rJBCgCEuUC43sOgvk7uDoJUwc+AP1HleLS29AYp7WkAxqB500zQRCA4e92dz4Pg0kDJicTjMgiZzDJYII4iR90gOgDiRoQJbgNSqATGgCdXn490quZu12tiT54Fkx0H2ZhG67hEhw6C1T/ewrZW8tEj+kbMAptsAgK7TAFFViHAViDMkxDH8zACZzCIAQYgRyGYIkTr3iK7x8vJpoVoaTDHmNWM9E9Bno9hUqjbqLnhJqnGL+eTfSCjW6P8TM30Xu+/A/uGK96iqH5Y6KH9AZswhnUYRI6WN3yFMutVxO9oU/APMxBgl12eorfVb9NdIzeD8tQggPYgVsQWIArLjfuMeRNU+36e1pV8xTl6dFEVxltwpFn4TcAAP//TBglcyJABprSCvMxChOyrg7ikqIPRvLzfLVCIXKzpOd0MHA4bhWAmL38kXWkHZN1klPIguPNu0JfvlEtuceZ72QVaUBHDygCEtMCChsKFIbOys53FB4AZGyk7ULIn4ocA4d3GPe0pAMa7wF4nDzR500DQRBAYTYMHgaTBkwONsGILHIGkwwmCFdBI0gU4zYogU5oAHR6kn990tvTzo3WRK89RBO9KFAd9RwrzYaJ3tIHYQmWwSHAGexBmYtqnlNs/ZnoFn0ezjmue07p58NEd+kleIM+UNiHd3iABkzCHEzARkFJpz2lUDfRZ/Ir3EMLTmAdBCKMQ4XrbjylattEj8jGKkOeY/jqmOgTfRWuYBG2+fjQcyp3Pk30kn4Hx1CFMXhk7rCn9F3M3SE3YQUy9EMPvMACrMEpDEAbNvmvkeK5ar8mOkufghmOe7s7HniI/wEAAP//vOAi9CJAkGiLKyJEg3Cjm3uDO1dwH4Dx03fHlPnBP9El8hosBs/tsAG6PinA4+byhRR6xTd30MRu6tvtBO/OO/f/9Ha3BSgCEsoDChsKFJnLaPK/jQQnr3Ss+EcljCkpuqADGPiypAMa5gJ4nDyROU/bQRDF89/Z9b5MfCSTOHfiOIejXI5yH9zmMpewAAsaWsT3QOIL0CAkCiQq19Q0rqj9QZAQDQUI/Xm2q580781bvR0FfpmXbGtLA4YtcRrwKUXEKxPXbiqQMy9+raMBP2gYIfLEsxTArdS1c6URr01kf10Bb17cybECQ+YdxYaJtJsaoNyeIsCQinmRzoYG3OE8IZ5QzqRybkUjImMinppIUtGA53QaMUFMc69gXpLtlgbc5nyAmCEG6Zoz7/SopBEvTaRRV+CFecfFGj3v05bFjEZ86BVZYMh34i0/z/XUEsf/ib9Uf1MF3pl3hdqhBsxTGTNx+aoCn/svf+TL5X6HWVq/mMjlqQZMMvsnfY/NSzxYUmDUvAurywrcSw9RPtOAPzRVzbvdvU0F7t6c7UKBN93FgEUGfiOyxCPiIfGPKBJfmXq/2yYi9M4znp7nXIEHXQ2os8h1AAAA//90CD/+IkAnThs872fqXFX/J8sCZSJC6FSZRT/OklCvcqbJY01qy2Rg6lTjYm/tXL2y6bWHR/o/77+pupRUnIR446+posUCKAISywIKGwoUJl4UAe/SH1OefvAaRQbS/+BbMe4YlLKkwQThKmgEiWKunsSvT3o3uk0muuEhmmrDc0rf76Y64DmGz8JEY/lJdAa64AmmIMM6eQL64ASOoMK6vggjcAwvDHf/b+2O3oJHGIMVWIAlmIZeWIU92IEzMNiHU9gGhWcQ2IQeuIJ5eIUAHbiAew514DlViw8TXSYMHxhcg34YhRrcwg00eYedvAXj/H+ovPL6j4m26VW4hF04hDc4h1kP8S8AAP//eeAifyJAwSet++QVfUBlpQKH50RjTm3xVLXEbuaCjfZ2ndcN24pC+qnNQQgzGn5U8rnkI1go0nYyKwI+Una9/XIrzaA9ASgCEtsEChoKFMlMX4tUntD5HqROD4jJaAII5WYZGJSaAxr4A3icFJBLSBVaFIbv2Wsf9+fv867r8ar3Hh89rNTe7zIrS02scNgDewkh0SCSgiYNJRCMIHDWKJrkQGhQgyioBk0kskY2ahBEFARBUTSIPV77+/a3lhLJzTq6BZs9hupj15TY42Z3zysR3Gx+QolRtxBGBLs9hjR1U9Dh0QoP2wU1HkM4MiXY4dFC36Kg06OVxs4IKjwGO/lSsD9PZ9sEazxa8fU3QYNHq7j/XEVKXgiCgSwrP1Oi0s16awXr80891YK+zJVKSvS62acLgp15+Pi24B+Poe76hOA/j1b1cUmwyaPVK0euzOijTkGtx1DxJOcOZrbhlKAnp7WeEDR6NJsZEox4tJrWS4JCZu81C7Z6tIa3PYK9ea2up4KYLXODgi3Z8u6DYMijVb8/JzjkMRQ7J5XodrN/ywLlvPF86Fa3MDos+D+TGyqV2O5mN6YFzVl7elywy6M1jl8UlD2G+OWKoN1jKPy8I6jK6NdXShx0C8MDgn0erWlpVomiWzh7XNCUXy0cFazzaP3fHwj+8mh1Lb8Fwzm39Etw2KO118wr0eVmG+cEa/MGM7eUaHELYzn372yb6BfUZfLHaiUOuFlhUYl+N7MOJdrcrPxC4HmVhW2CFdl19bIS9W42/VmwPB987I1gVbZO1qrIMi+EPwEAAP//JJtWXyJAIXPaTZw40rNUS+R65PhmNDexjZQnAh4hstU5hjK2Fb0zICsh3RgVd3btdLw4Thr/7cHOlzj46Vqz31DmFAhjBigCEt0CChkKFEkEBnybE6hEyiJAGExNOtDuBNEhGKhiGvsBeJw80UdOA0EQhWGmu8tTFJhQYILJyYgscgaTjA1GWIgzsOFMLNhxH1acBjT6JVaf9PS6qjRjom3PgokewSX0QAW2YBSuClRrnmL8fjPVBU8h/3wx0YxGAoEuOIVzOIMG7DNyyFOoNOom2ks+DutwAhOwDPewCQp3zOsvTiy/mugUeQf2YBee4REuYAU24AYCXMMhSxY9hVL1y0QHyY8Lch3wGD/eTXSV3o6nmLV/TLXkKfK9bnlhMALDsARNGIMZmAeHPmjBA+urHmNWM9FZ4hzqHDPnKYbWr4kekJdhksfTHmOnaaJr1Lf///ATvW7Pwl8AAAD//6D+I64iQOvg0jD0i94SAz9RJsvvtlHhM+9/DZ490er7Vj5c1nH2tr8plxbFC4unID4ykt7dv/VvKg3xZVgufvlODzlnNAUoAhLTAwoZChSQ2iFKurSwaz5rJbrhdx1leMcugxjdHxrxAnicPNHLckxhFAXgnL3/nbOyiCSbuMS1XaIkQbkHEUEkQtCl2isob+ANujyCUiYMDHrAgJFpP0KPTD2AuYGi/lpVGX1V6+y1//Ofw8CxbIyBFXGqApzP4r75m4Hnyu+LJ3p8OYs12z8ZOKf8kvILWfzd+9cM7FPeiouVFpPp9naRLSzdPqwzsKleJ4vb1j8GFlVYE+tiSzwQEAdFI27qhJPp/uwrW8ylezNPYDyL8ctnAld3DjmkU2ezmNk3Bk4rmMnizfQfBo5oa1db76V75xeBK1lsfGmFgTtqHM7i7ccXBJZqtz9g4Ja610WIh2KPatfq95t5w8AN5QeU385iE70OW+xOt+XvDGxr4Km4K45qfKqu6Q8InMli8WOBwEL9c5M9Bh5raLnecnVEYCyLTc39ZeC41hRN7M1isxtrDJzVffen2/AlgcxiPvaJgVU1dokJ8UicUGs63YddAvP1FUavGNjQALOx/wEAAP//FFs1NiJAScgGGM77JsQsOlrP7/7+fts6fOMJG7iYMI7hU8/BymA1Q/lbl6ohF1Vshi/L36Z38B1q7T/46TLZ/AifrrwtDygCEhwKGAoUxdAZMeN6ht64JTSkGMGi3xSCvrgYASgBGwDreIb6SsMBEQ0JMptheYfkfMCDBJgUHRjkGU/S7+L0GAPWNF52DfAKYKe5ZOemA+sJIAaEJggJ5riaGL10EoQBuEBIcOPVF71pMAdsQgOf3YN5HlvaMT8yFcGZ5Gg=" + ] + }, + "evidence": { + "evidence": [ + { + "type": "tendermint/DuplicateVoteEvidence", + "value": { + "vote_a": { + "type": 2, + "height": "22488718", + "round": 0, + "block_id": { + "hash": "", + "parts": { + "total": 0, + "hash": "" + } + }, + "timestamp": "2025-04-17T08:53:57.716439461Z", + "validator_address": "265E1401EFD21F539E7EF01A4506D2FFE05B31EE", + "validator_index": 18, + "signature": "I+qbH7GdPlJnH3+Xp318rEOM5JhSojSTflnZfbkpPzz5XpTJe7onsdzn+OeslmaIKygfZytf1dogYoshYB0SDg==", + "extension": null, + "extension_signature": null + }, + "vote_b": { + "type": 2, + "height": "22488718", + "round": 0, + "block_id": { + "hash": "212EA5BE06883493255C622C8D6C7E1C6A2B51DCB56787882939C3CFE4E850CC", + "parts": { + "total": 1, + "hash": "0618E356928A3526278747C52C7975C6C466B78EB52EA78A66BDC3CDD44D3594" + } + }, + "timestamp": "2025-04-17T08:53:57.514531594Z", + "validator_address": "265E1401EFD21F539E7EF01A4506D2FFE05B31EE", + "validator_index": 18, + "signature": "Hatd0nyvmqZNRQGqkysUVMc2SpTuxKI6eYcozShryvog335IRSSaOg3KmLtyYf9YdDL1ht7MXzEe5hk7Og+9Dw==", + "extension": "eJw80EdKRFEQRmFvqH5l2aYyt1lbxYw5P3NWFMHFuA5X4Mx9uBPX4FjkccDRB4cL/6VMdM1DNNEBOIAHGIdaRaFXntLXq4l2kUehH3rhAhzmYBUmK1S3PKd6+W2i8/ROuIEmtMMULEEPtPChU0+x+WaiBVlBmDnynPL7r4ne0o/hEkZgg8ebnmOt8WmibfQ+qMM+dMMddEADSsjwCPdwDdNgEFle8ByLjxcTXaEPwTY8wQScwSzswAmsc5RzTyn8mOgw+ZmZ3f+ZVvoMDMIi7EGAQxiDZQ/xLwAA//+pdCGR", + "extension_signature": "bQrSKBwhRe+6NEXT3xMrrSZiOSrAHWlwZQFCb5bwOB6/6qYK3Lwigq2cfd+R39go0srLl9Gkw5bhs85AzqE3AQ==" + }, + "TotalVotingPower": "131523423", + "ValidatorPower": "6887700", + "Timestamp": "2025-04-17T08:53:56.247834789Z" + } + } + ] + }, + "last_commit": { + "height": "22488719", + "round": 0, + "block_id": { + "hash": "80210BCF50B6452AADF54E4DFDEA4E046118F4E160C7E950ABFF272F7EF652F1", + "parts": { + "total": 1, + "hash": "B679BCE5E8EB186266BA1EF1A2E473D93308A17FFA2EF9EA914FFD3E96F96A8C" + } + }, + "signatures": [ + { + "block_id_flag": 2, + "validator_address": "6DFFC810758BC3EE69E154E6A6ADB260ADC79410", + "timestamp": "2025-04-17T08:53:58.556581704Z", + "signature": "Hc0QfEI1IWkS2bdzNmivJ+kTl3jbEvgqDNXXId1fWY9Mu4EnQlFIxdUWKPcd9AeUPkEWfqUPW2KymOItI3hMAA==" + }, + { + "block_id_flag": 2, + "validator_address": "E6649B3F56A13AE2D0289DD559A73F42FCA44830", + "timestamp": "2025-04-17T08:53:58.5490068Z", + "signature": "gDWLIF1OqR9lK/rPaq4Y4Vgd2GPFU3bh8BWqpijJ3tJYVouWzxREh0ADTPGu4+6paki/oh2AI19c6PFHgEYMAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "8AA0FF06615D06DF55964C243AF33886F8C2FFAC", + "timestamp": "2025-04-17T08:53:58.556747783Z", + "signature": "qyRb/mOpKSd4e0AGQ4X47WR3m6pODSSFpBBeR6Do4e+AVCakYo2C2yvUYMZ2axg60zWsWqwhOAlxrpjKcDYSDw==" + }, + { + "block_id_flag": 2, + "validator_address": "7118AA89F171E32CAF5560FF647548D4E960D5F0", + "timestamp": "2025-04-17T08:53:58.602528025Z", + "signature": "sAKH12lIUFUVbpt8j0jX8g1w4IWtoWDRX64XprLtTE7pZKijLgdBbBsVIJgBra2OazGAJpHXGBnGjG+PhSg0DQ==" + }, + { + "block_id_flag": 2, + "validator_address": "5B1AB033381D69FD58A09D40B5AAB4019A70CD1A", + "timestamp": "2025-04-17T08:53:58.642984979Z", + "signature": "PnbxqeFjQSWWDPA9j5DjyHJEt7V1upxSZmCWv1nbWgQBioBC/A8VtLHPZt/MJ9YHPbq0+BnkoK3uyAYxxq7EDA==" + }, + { + "block_id_flag": 2, + "validator_address": "15FCE6434F3CCADE663F0EF314810926F516F41E", + "timestamp": "2025-04-17T08:53:58.592487318Z", + "signature": "dOkAZD4zM9gJIlyGWw4Qh8LlCP0MQLcStLztOya8v5hBBRfA0+F4cQ+z988x9ntYuM+j/Nnag1i2ohjQgNR/AQ==" + }, + { + "block_id_flag": 2, + "validator_address": "651CA5448DD1DF590926CB1A45F6A52D58B665AC", + "timestamp": "2025-04-17T08:53:58.573635927Z", + "signature": "4om9s8g0nKYBQRp4gBmp3LNZkJX7gykm8W/yAWy/uU3rrS/kMWsl8qcAOq8A3KD07bVvRpEmEWR/iRX/1TJrCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "C3CE921ADDCF756F31CB2D4B64FB0233011FF95B", + "timestamp": "2025-04-17T08:53:58.537157461Z", + "signature": "ZqXKFGo70QPf7EGrllT92svJUxxRhtJ3nDs3eFpnmYakJodq/Rz0Hy36Uwjr84029W/knAXRexD/wPQiaHutBg==" + }, + { + "block_id_flag": 2, + "validator_address": "D66EBDF8CEC7D3F5229A328D90834271F7CA0C1C", + "timestamp": "2025-04-17T08:53:58.591125912Z", + "signature": "ZkO7fS3dYnN+fEwLFpO8oUy5B1OPkhgFz3zh46VQLfZ9T/MQokSWt1++WQv9OTC5KQSVydxIHqAdck95g9noCw==" + }, + { + "block_id_flag": 2, + "validator_address": "0095425E66C8364CA99A97D768C6F4DBB1FFD492", + "timestamp": "2025-04-17T08:53:58.556862295Z", + "signature": "dgLyzrvVRA/eM37XSi/qMCjhYOavkhKDxb3ixRGYa3MBPzQBkj0UyjhK3nFLq7ylT74VIlK9SKSI5S+vAiU5AQ==" + }, + { + "block_id_flag": 2, + "validator_address": "118F23C0E2C1C005CD1FB0A27659971F50614F65", + "timestamp": "2025-04-17T08:53:58.559131497Z", + "signature": "/xch/GLr4okY8qRUVihR03v0myuQmBsgzVY7SMB/sgNSIbICdPM03HE36wk3apJr0qEdsbA1TFfaGGLBbxE3BA==" + }, + { + "block_id_flag": 2, + "validator_address": "A49A8AC58FD77CC75C0C3379D4E9994C011CC891", + "timestamp": "2025-04-17T08:53:58.614730941Z", + "signature": "vrO0pOpUc+8BtoEWDtI6ziWEM/ykWRoVcRQOzEnpdMRqBpNewP1CGHzViPDUv5rIij8MXhBE1O/Oy5xOjBiaBA==" + }, + { + "block_id_flag": 2, + "validator_address": "51373DE0C6DBD0C29E860F8690998BE7C1B5C105", + "timestamp": "2025-04-17T08:53:58.593287089Z", + "signature": "Sf4Xo5jkK83A4EHajpsYYmGdnoOUnzgKy7DD0wjBVrXtfk6gDwZkVWqKqLML/hYFV+B98ezghmHwBtq/VJ2vCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "8D7585E963EB4DBF4777728054D04387FD2046AF", + "timestamp": "2025-04-17T08:53:58.565929356Z", + "signature": "1c0FyEUMDwBaLu7GufhNkfydWOm2Db7AemE0fAPbsR55f7/TLnhI+uw07dSrO6atuqVK5sOWxSx/yxbmkl4lAg==" + }, + { + "block_id_flag": 2, + "validator_address": "000A1D72ECF256292B860662784816A56496688A", + "timestamp": "2025-04-17T08:53:58.669114621Z", + "signature": "RrFQ4CkBli//iA1BYBL+NpefNJwnQ5seC6nLq0DaolKL+nrtdvfOg2iKyJA368nwgEDoo5N+3gaXL2ET6AwkBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "E37B0E82F93BB83A0953073E00FD4795E2D2DBD0", + "timestamp": "2025-04-17T08:53:58.593416597Z", + "signature": "2MCBqs6h8rU+FgYoJmOhtlo8Lc86vdztzEBBakBa1Z5ANVeNrijxojI1mR+XsE0IXRePruHT3W2E7HLJffA3Bw==" + }, + { + "block_id_flag": 2, + "validator_address": "86CECACE77141E00646CA4ED42C89F8A1C038777", + "timestamp": "2025-04-17T08:53:58.576751191Z", + "signature": "jK0qOWZQnjIvn0XmfeZ+WUDOEq+6qRAVKSbFCRjnM12J/qMtRwroTnZrZ7ilPbN220hqmPcqxl2apWwc5qXnAA==" + }, + { + "block_id_flag": 2, + "validator_address": "99CB68F2BF8D0427AF74ACF847258C2929BAA003", + "timestamp": "2025-04-17T08:53:58.592170694Z", + "signature": "RksBE9dlBXmm1s4A9PhwutJfG5Cmv6iVzE1vouYtDKdecfMn1ZV2oI1RLdvbaAskNMvIcdke4jLoEzgCxIMgDg==" + }, + { + "block_id_flag": 2, + "validator_address": "265E1401EFD21F539E7EF01A4506D2FFE05B31EE", + "timestamp": "2025-04-17T08:53:58.681007582Z", + "signature": "e+WpEStAWAbm5ogXavm8AF7YYGRy1nOkCx8CXjlvCFl+nMIj0KEAb8HgCW+/u12yf+/lLf1WT0C7LqdL+cJ0Dg==" + }, + { + "block_id_flag": 2, + "validator_address": "C94C5F8B549ED0F91EA44E0F88C9680208E56619", + "timestamp": "2025-04-17T08:53:58.584024745Z", + "signature": "YnoARqLmdLU8ajdpdBQTy+/i+MYNQQaKGt1OCzVVnmZRXslO8W0be9trhLWz7NFEdukvrawdS5WZRnPW7d6ZDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "4904067C9B13A844CA2240184C4D3AD0EE04D121", + "timestamp": "2025-04-17T08:53:58.567811321Z", + "signature": "O+652gTf6rGJ4ySTPXPNzSVbASD06V1u55GfLhksTzY0paW/D0Ni1ye/FXLiKPlf37v4fE24go7at2r49dsEBA==" + }, + { + "block_id_flag": 2, + "validator_address": "90DA214ABAB4B06B3E6B25BAE1771D6578C72E83", + "timestamp": "2025-04-17T08:53:58.580797282Z", + "signature": "kAVlAyJ08Sg2C0C+K4UtQ/SzZ/rXU+IaP1CFEBpQG4DM1ZI/gtV49awSjtxbLJfwBXD555SxWwqAgNdPAbgnDw==" + }, + { + "block_id_flag": 1, + "validator_address": "", + "timestamp": "0001-01-01T00:00:00Z", + "signature": null + } + ] + } + } + } + "#; + + let response: Response = serde_json::from_str(json).unwrap(); + + println!("Response: {:?}", response); +} + +#[test] +fn test_deserialize_osmosis_block_15317185() { + let json = r#"{ + "block_id": { + "hash": "EB414B8669FB413809EBA38BC6D14B9637082CA7D3ED9DAD8565F99C43FD299D", + "parts": { + "total": 1, + "hash": "1DE10A287D6BB70561A6BB8F252C91CB6DD7623EE14093A74776C5A8FA4799CC" + } + }, + "block": { + "header": { + "version": { + "block": "11" + }, + "chain_id": "osmosis-1", + "height": "15317185", + "time": "2024-04-29T14:54:38.821378833Z", + "last_block_id": { + "hash": "FE84EB267D13053EAFAA221CBB3B2354E0C87729F4A141D7E70BEFE4585AEF7F", + "parts": { + "total": 3, + "hash": "F104A0A55835F01CE7C98245FCC32BF7799F3E8708BAE729C51457F4141DCB73" + } + }, + "last_commit_hash": "861C3C6571069AAD9DAAD8510032BDCEBBBF8EEDE6E81EFFC99236D96509AD6E", + "data_hash": "52D05CBF8C18FC590F1885BE2282B4F01A707E7E75FEDC379EA6E7F9817ED960", + "validators_hash": "B084C1C1C85540EDE74C76ECA80BF765EF57FAE0E6DE01197515120C8AF25B93", + "next_validators_hash": "B084C1C1C85540EDE74C76ECA80BF765EF57FAE0E6DE01197515120C8AF25B93", + "consensus_hash": "7186B4DD67B243E05E4FFDF3C07923AB7E244FCFFA09739F6F4F7D965DC47EA9", + "app_hash": "3E75E5A680B2E8E43AE2E8CAA87CE1A2DBAE83D3BA91FFF91B0491B2573653B6", + "last_results_hash": "32F9AB287631D4EFD1EAFA323D9D6E7B4A5D227F58C2A90BE4D1600D6C98ABBB", + "evidence_hash": "1A83532623E064DA8FB06AD0529888FE7D0DB2580A9973CF10673241CB6ECB9F", + "proposer_address": "F3F55DA24BB47DA60B0FB71EC1A9C9274BCEEDB2" + }, + "data": { + "txs": [ + "CrKDAQqabAojL2liYy5jb3JlLmNsaWVudC52MS5Nc2dVcGRhdGVDbGllbnQS8msKEjA3LXRlbmRlcm1pbnQtMTcwMxKuawomL2liYy5saWdodGNsaWVudHMudGVuZGVybWludC52MS5IZWFkZXISg2sKmiYKkwMKAggLEgtpbmplY3RpdmUtMRiXy7MgIgsIrOm+sQYQsLOgRCpICiAHa2Zu1/9LvyGjYqfjvORI8O3f/USB34PU8qqRhdKiVRIkCAESIGR3a9UxFW5PhsNxiENFeyLE8dFjyXtPt6HuXpHsT3frMiD6WQQWhu2vig3Ig5qpBNeDDdy1tTCtc5Tl8+jnVjrZAzoguw2IsME0EmO+0pB0FQhpI99EcOeEdEY0yh3eLMGx6NFCIFEidrwgpcc/2GndLJNtgr37MQMJS55gYBLZM6NppyOCSiBRIna8IKXHP9hp3SyTbYK9+zEDCUueYGAS2TOjaacjglIg5bupI5wNP5Z/jvh5UG/269+5QPiQTXKRNRpGHwCqrU1aICxNj3aF+SnboN/EpjSBfaTC4vlB6tpZQdalC7tWyPfoYiAWd+motcnNNf/GNUoBGdTvvbDvituZuoBahCaCk3FGn2og47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFVyFFRXaPHE8e4+fvk96OWf1XpQbO6SEoEjCJfLsyAaSAog0gj0tb5Y2UQU0/M0bssmaUIJr453qU9goS5KmVSOGqISJAgBEiCks4zsDXnEkbQ+FqLJGf0N4fI4ruFPRqG00b/bmXfu8yIPCAEaCwiAkrjDmP7///8BImgIAhIUVFdo8cTx7j5++T3o5Z/VelBs7pIaDAis6b6xBhCFxZK0AiJAtMOq4SaAG+dDlDq0fTKkoDc3FMdFfkBbE0Tc14PnkS+vh0SaC5lLYD7Ix1dgxp1cjzH/qLFPvJRX5dzeS/V7AyJoCAISFGxWCE4c+jMWKMjrHab4ZZIkV6l1GgwIrOm+sQYQ98qGtAIiQFYGGfUqN+sK2u+raaQsB2k9jMIUDMg8xi7Vchj62TzGIS6Qr3ufOsvY4yHXTKc4eTDITHhHbQNPdIKkA6rMTQUiaAgCEhQz7TlskE3Y6aHCpD+zfBT139tuihoMCKzpvrEGEPGChLMCIkD4Ezf+155HNbbt+LmwpJGrOY47XSB+bSfg5YKCog1zTrNlUntDKUzcyZ5el6b88eZ3Q0Di9WtdBJgPlrGhlZsCImgIAhIU3SyYiyeOUmhw3ZfXunXuqi/r1V0aDAis6b6xBhD1kLeyAiJAbibCevRk75BP9/zziuU4D5A2m6OEJAGFzpRLXxl+7gB2pG/0eiGF1fMaMtDzjSiVP/ThwQMdVRAVqvnxFBwABCJoCAISFPOb+TFsB+TyfuMZUfcgeDxY0JwRGgwIrOm+sQYQivytugIiQDBFZfIat3dYZxW0ALDO+jT0PDoGKZQMOir1+bzt9p9I8AEAKWDhDFSM4dmcXHS98ueHFoa1v1jxNUy1RBsYUAQiaAgCEhT1ML3bHPgTZwpvbx5XRHTUyROF9RoMCKzpvrEGELP/qbcCIkCOtCdG5AUcSOpWsOBrnX6WwFKc7kOGYrtQ852LEzUridAKdWHbId1dSSYee8X6bbr4bfqa9zy2QTcNH6e+6R0PImgIAhIUR3hY5r9+Nq22Bi0iSgsGt7Tlwm4aDAis6b6xBhDH2bS0AiJA1QW5q2lXbyJIEwOQar4qrglREiMBZLXI42162kJlThSVnoBe8j17RYocg6YUI7+mbACoE1Pu4k0fGH5uj5ERAyJoCAISFOlykBWDzAA8TDiO8ub1Vwq/DVt1GgwIrOm+sQYQmqXCtwIiQAxLBGxpuCom1WCQx5xhoY1hsWwNiJZe+0T/FOTtSMmNMH09DVCxCdZUjSbCKK8Fvrj6c15v4AtS+Sjg3mhecQ0iaAgCEhTb3qDkPJ6wN0ItDifD/+34aS6clxoMCKzpvrEGELOBibgCIkAUDciTPriKtDYPJkuOe3k6I65hyv7I/Sh3dYfSOq3zyHiB3qb0ebNESH6De4uSGqwYFHVxjd2r5NmatXn8JdwIImgIAhIU0c6Hu+ibc3SVQW6MRLn1jSNy1dEaDAis6b6xBhCN2Ji4AiJAYhwbHf/h70acjnzfr1hoTrFVnWgI3NXbaqNJM29z9gXq3bFF4ojGWH0lJUbC3KD9iYdLLGhcdYiVSTms0NjeBiIPCAEaCwiAkrjDmP7///8BIg8IARoLCICSuMOY/v///wEiDwgBGgsIgJK4w5j+////ASJoCAISFOQRJ33Nje8j7VvKPx/sFp/bEO97GgwIrOm+sQYQ4pjSsQIiQPyyZB+PkBgRURLhCoAJspYmdhJeknY0vIEtsOu/277S+GRbFZ87F4G6hiXCDR/4+1cbu1UacEwJow5EW/IpywEiaAgCEhTN9VrNl7MaZ2Yr0L68Zz5bX6rsBBoMCKzpvrEGEPvYtrwCIkBmiGKCygF7WkuxegYgTwbpzDJKlxZdUdrlVzAwzdqxTlwSpYjWWLdSQ2s+cZajLJGe0n3u/NqJ8g9Zl6Nw30gBImgIAhIUN7P55GKWfxUPW/mKejVfNTG1jRIaDAis6b6xBhCWp/y7AiJA5iIA+SwBv5DSnesIYJVFnMZoYtCOUe0EpjT8lQn4tHKIiFE9jan1PS43Vh2v4DCXgz2jy0Zd34MY//YNDy3SCCJoCAISFLeFxzCNVRvRsieYG/k4/orT0+OvGgwIrOm+sQYQyPentQIiQApsiHB4hogjB2/WGpaBDU950gVHs8+spmUIPbv0yad0YyFDT3GOJUwPFYeU/lSR2bysT9etEt3/wCF76MPBhgQiDwgBGgsIgJK4w5j+////ASIPCAEaCwiAkrjDmP7///8BImgIAhIUk3OSWMViaQO6REEoz7dDmqMaqT8aDAis6b6xBhDE+e+5AiJA07rMwUhQ2cIxKvLEKM9RCCZmpAc6r0VbxESq6GOe/HifX2DtGd8DG/uSoVA/isqYlGc+5yArbhM+gxfDlr9KBiJoCAISFNjanLmBYmdenes4juF8Ng1wLVuWGgwIrOm+sQYQ8/CyuQIiQLMr9LekpOBmMJKx9QdjO6t7MwgoNap48KZt8AWC2VOsoEHL8/z0vn9ccLa5CgksFVjhYFSlD64qr+27wpFHJw8iaAgCEhQDM9ItyjAlQ7zR9aFETMgDiNybphoMCKzpvrEGEMrOqrgCIkCwVYmUiPkK7eWK1zMd+HsHL9JqRzD3XDb4xhhrpIv6o7ZngcMBBTPF7ZknoH/jNPgR7r3GMnmQ/whiG2iaVYsCImgIAhIUZGCESyyB6H8+KhBmd4e7dgDPqXAaDAis6b6xBhC7oYC1AiJAAt7eOWod/CD6ruMHsJ9mGGB+itqT18QlZBaQOitpQdC+iy6x9ZRhXcRft4SV+9a1ESTsDHGkVgDokKXJ63RVCSJoCAISFDfbdQt0AmsS8NSWM/lYgajviuR8GgwIrOm+sQYQk4HNvAIiQHYyAbEeUlWa8hD1oAN/NzEcdf8fMS9zm5QmYjuUxLXo5aW/XkxgT0eARE9PDakk/i+d4I56Nks4bvBSALdB7wUiaAgCEhR1vCB4gfk59xLHPw3tXqqwKfGbLBoMCKzpvrEGEIPok7QCIkBEVdEmRa1m+eYNiXS+QU/Dy4jh/Mrsj8ACGL/WviiH8BQFJ0tsOB5vBTCy+tE2aNi8FlIgl1tbPuT0T0/PcuMAImgIAhIUN85xrYzU3AHYeY4QOnCXhZFwgWkaDAis6b6xBhCcsMSzAiJA1U5mRC0gBGlaLD+b713slw2yDyC6TCWP01siNGu9WHCX4YoyOhN8RTOFgh+NolD5XYIz46J5Sn2wy/m8yKvHBCIPCAEaCwiAkrjDmP7///8BIg8IARoLCICSuMOY/v///wEiaAgCEhTsl17dqSN1TGumBE3GBBs1162SzxoMCKzpvrEGEN7h7LECIkAO/ITc1Sa/Rko5DdWAwhDeAMAL2OrHmTHxt5EXsdEkd88mPCpqwwmYpX/nf+43egCyVkGDEa+6umMCuPIbSskIIg8IARoLCICSuMOY/v///wEiaAgCEhT+qkKww1gs+evkvVIIK5BjbIvzpRoMCKzpvrEGELON6LQCIkDuR19SMWN9ZcFiIYukFwUHdTOONCXdgqZ0Yccdv6/Uc6ixjUzR0yk4aS3oRxZ07TO5mj1u8NdqgU1qwKgQa4gPImgIAhIUd4H6Fq3FfhCfY70eKfFPnTgX4UoaDAis6b6xBhDv6om5AiJAkyb6HYiKKp0aAxTlHlg70frSwytJWq4ep0khoITXPYAGY8J6Od9QcIkaVCsEQu++zMtLU29F9ztLG+YJhc3XDiJoCAISFBfrewhzzNg+p3piw0/4SpEMXzzgGgwIrOm+sQYQhNHWtwIiQHUIBdqTNlyfeTzRtr1rkUvW7qBXxqH/uQsUQRAsyE7pD8dcTXOgIdzr2z64WE67x3bgw6NA8X8KSrMu5uflyQwiaAgCEhQkdtNsDJ4bUKZrjMjonXUYTzdN3xoMCKzpvrEGEImU/LcCIkDCig1Qk6i4YEH3bwqik4209gOwMI9+Rg3u431ihBCErHEcemUWEnbN7iRKlwuFmjhFr73//CKSXCTQrJBawEwIIg8IARoLCICSuMOY/v///wEiaAgCEhTLgHaRxQo7KNq7u+urFDovNsfG4xoMCKzpvrEGEPaw27ECIkBDBbxlD9j30wwWG5/+lB2akzdBWCv4IhblU5S5Tvefwu7BBuFTHJgM8/yKRvk3QIqLOUoyRy+7x8fj0y3N+lwFIg8IARoLCICSuMOY/v///wEiaAgCEhQMO6L4XJEBRsoTcWGL8VjGa1QhNBoMCKzpvrEGEN+5j7UCIkBK1Urp5MGSx6PEgB3BD71Z4Ac0GhDNX/4WHhaMFkdwBTn+8jZje59uutU3HouxUI11mblb9DCQaiods25bhooMIg8IARoLCICSuMOY/v///wEiaAgCEhQYNOii/eRkSZnu1b1LmbSpoixDURoMCKzpvrEGEK3C0rACIkAFHKkL/B1Cth5BTt4mneFt3jST2S348BHWepHhJtTaIXbMRW7jQiqjzfcX/FwBe3cSSQVP2DSCndke8wiCJOsLIg8IARoLCICSuMOY/v///wEiDwgBGgsIgJK4w5j+////ASIPCAEaCwiAkrjDmP7///8BIg8IARoLCICSuMOY/v///wEiDwgBGgsIgJK4w5j+////ASJoCAISFIPzfKxypBwnwwBbYj8NTeuc5aIOGgwIrOm+sQYQh4SJswIiQBCjZapZYmvvuBjd3EAlN3DE7KfyNQkKNHzzMuBUyV03uQODDMpmuJWzEfFTy/KSoPFROHtYR78PQ9EJmuEvgA0iDwgBGgsIgJK4w5j+////ASJoCAISFPiXFYr9XenRoYUhkwTiFv4OgSpGGgwIrOm+sQYQzp3VtwIiQH4XJS7OvzHk7MkAbDLJg7AnThdHW+n+C0WE5hY7mDdUNmCZ9zUbht51UIH9fiInZCnJ6tBs2Yj6PJ/vsP2+ZAMiaAgCEhS7trfrLXVOwpV8pWFd1at5QVsLGhoMCKzpvrEGEMzOvbYCIkAwmRcM92Yg/aTQSWAlJ77sk0GrSEhlY3FE+YSi2aVyq5jU6SwAMAw7Qlw6ce+yMRnQ7ImbBdj/tBOjNRQ1ey0AImgIAhIUJ9h6qhe4avVcfm7E+2N5N/7zn7IaDAis6b6xBhC1lse1AiJAF8Rx03tdABtoYSP5LNSwPsnlvbA6ui2L8ppX5AcZatdK9OnWjInY1HzwJutKxdyljX40icoSueUEmljf6xdeCiIPCAEaCwiAkrjDmP7///8BImgIAhIUG4Wl/9xloj37UaMtwQh9ylFepswaDAis6b6xBhDym963AiJAj7lMf34OVkGDgRgMet6le/M1NQF99hqCYhSn1xT8ilUB7X30a64DnAhbxzH50fMrPJF7aLCkoCKBfob/Ymi+CSIPCAEaCwiAkrjDmP7///8BIg8IARoLCICSuMOY/v///wEiDwgBGgsIgJK4w5j+////ASJoCAISFH3VGl2gZ+mpX7guAtWkGlGt0e9GGgwIrOm+sQYQqdbAtgIiQLCKn6bqsMeegKmyKeqeDRI9N0VRWpyVM2XhM6eVZECXNh9LjrNGJEDkoOhujcyAFXEavk6J+6kIm8ocYbD9RwgiaAgCEhRyO/hsKyC0z2/y6ZvmBx+PKbRldBoMCKzpvrEGEJT+pbMCIkBSITJfi7lSWa8QZpNaZwvJLmdBGWFmGIe849aLYg672uSbulzArA50JglEz1lNIkNzgBlmU2oL1SP5wqSjkuEOImgIAhIUGALEN9FeflSCO9gr9ZRJ8SReBj8aDAis6b6xBhDa4LK1AiJALZf6AYMT8LMBoel+zOHA7sQM05fKMLyQ97zYInRmZnGu1htQgVaX9LIeKxqqkNblPVTVp7Yzof1N5NkuHh+YByJoCAISFOZkPMfjdf+InqpZ9/y5P03jVAPbGgwIrOm+sQYQq86eswIiQC0nWtjv1Nk7mA+E7iNDgltr79BA+iA/arIJ8G7zQiP0dYoLtjzaXc+rOcyDfbQ/hvvlz7/nX4kRq9H2thCGqwcStSIKRAoUOWM90H+H4hbgytAeC+IW4i+Jqz8SIgogfk3eHNOJCT5zrJEiuI2TglSe/vZsV2Y/gMwl5JANQPMYyNjwAiDSnZcHCkoKFFRXaPHE8e4+fvk96OWf1XpQbO6SEiIKIEGOwoqtn/kfuLUH/ETNFyD7BTdxBzAh8XZf++YtopVdGMTKgAIgxZHC9f//////AQpKChRsVghOHPozFijI6x2m+GWSJFepdRIiCiBQZfy4O71yuqGMU70/tthBNWKUm4uITfwZioslmRrW6hjIx5wBIICjrvX//////wEKSgoUM+05bJBN2OmhwqQ/s3wU9d/bbooSIgogW7XNeQ0LX9klTb3fDlAMPZHe6t3VU7HxinKC7hdIIB0Y9pSMASDE8Zf3//////8BCkQKFN0smIsnjlJocN2X17p17qov69VdEiIKIJk/mdFE6vd4VpexYSUqWXHPm9OxdDlDHLHXUg4yb6i5GPWtigEgiu2mBQpJChTzm/kxbAfk8n7jGVH3IHg8WNCcERIiCiCL3uAh87cEK7ttK8Ioc0EF9vZRjN8/wj1U3HeJpaNR1xidqXogzMqi/f//////AQpDChT1ML3bHPgTZwpvbx5XRHTUyROF9RIiCiB9mpmpufDSdAEd7vq32ohy87LIeOgwfe6mgexSpG9wFRi7xHAg1NzqAQpDChRHeFjmv342rbYGLSJKCwa3tOXCbhIiCiB+R/QoaR9tJsfI19l7dY7QQcuEeFlGLt4XHo5VpxpJQRj8uGAgj9KDBwpDChTpcpAVg8wAPEw4jvLm9VcKvw1bdRIiCiAsfPIPNK1XpdwucTnBMrNC0kjkBfxksRHyxrne3rtFoRjSpV4g4/vYAgpJChTb3qDkPJ6wN0ItDifD/+34aS6clxIiCiA4YbdNu9zkneebgIoH7Vm9ERG4mKiMegFnFpg2fb5Kwxjv30cgvJz9+v//////AQpJChTRzoe76JtzdJVBboxEufWNI3LV0RIiCiDhsEVGxGyOOyAf8uIM3RxyDzsBWK+kjMGdN0DeHUEcpxievUcgodmu////////AQpDChSd/MLjREM8vy2lM+hx7fdl4o+3ThIiCiAIqmd+ur7i5jVzIX+All0AZyEeIq0CE/7g10jaEJ1vWxjf50Ygkpq3DApDChR9LZbBRjKGEzeqYsDvTjygls4WZRIiCiC89C16d/supX4b4wuGnb2gG2Pegak8HA/tuIXWfmA5lxjPz0IgirK3DQpJChSyPvDGjQp+Z58zSulBAv8ptIcgMRIiCiClTk6WiLOJ1AEY1gTfLzsBdds3owYT2xO/Y/NGQQP8Zxj/gEIgmKT/+f//////AQpDChTkESd9zY3vI+1byj8f7Baf2xDvexIiCiBJ6QZRrK7sIPFytRy/XRdzkwMpW44jjWB8syNWrV5RihiVqT8gh5abBgpJChTN9VrNl7MaZ2Yr0L68Zz5bX6rsBBIiCiCBYu2i6Tof7YQK4ncFAw4C8lTuEUyfDITmlbK9pwKt8RjOmT4gr/rg9f//////AQpJChQ3s/nkYpZ/FQ9b+Yp6NV81MbWNEhIiCiC699fjdfNJQWY8nBK/pKN0woFWfzvlkYLPEWYP4ra6WBjAyD0g3sOw/f//////AQpDChS3hccwjVUb0bInmBv5OP6K09PjrxIiCiAHdCxLtUy/AOpV8N2AbUkyvUiAmGTmc8Ip/2YLUT0GhhjmhD0gpKmjCgpDChTcZdStW3VNZDxzNHDcx6RLn7TcpRIiCiBzKUvyqN/w18umePodCx4Yc3TUd0r5wvKaUDQfuoy2hBjC/DwgrcCEBwpJChRgh2B+Hlb27nk0q69lg0yS1hgQTBIiCiDOfFaptSK3mPqX9uGAxT6kv69V9FmKCHIQasaovfm7JRiX4Tog8JbH+///////AQpJChSTc5JYxWJpA7pEQSjPt0OaoxqpPxIiCiCW0sZgAYZqUtwoRB4G5xfeWrwwho0ppZn3MJzUqhUPaRi22jogkbqT/P//////AQpDChTY2py5gWJnXp3rOI7hfDYNcC1blhIiCiCa69Pq0Nd00AaKkOgSstI8zwhW6KW54p5LZyq0/z7m+Rjihzog+cXcDgpDChQDM9ItyjAlQ7zR9aFETMgDiNybphIiCiAEulKe3OJKxJ2udX0BywcAKLxccy4DiyWYYN6WsbhuUxiuvTMgpJHTDQpJChRkYIRLLIHofz4qEGZ3h7t2AM+pcBIiCiBfjIPQnUn56Glsd5J/oLpvkDnVakl1g0xcYg1YPJM0XxispzIg6d/X+f//////AQpDChQ323ULdAJrEvDUljP5WIGo74rkfBIiCiA3iNzEXa96A8SopNcAyRNdQ6JvEUiZFq+XN+yUAA+oIBjKry0gsKfNDApDChR1vCB4gfk59xLHPw3tXqqwKfGbLBIiCiAICsSMrwgJdi/v8qVBUkSATK5+v+hVENj2GUeuH4dhXBjC6Swg/5jrBApDChQ3znGtjNTcAdh5jhA6cJeFkXCBaRIiCiCRhaPRQlVzxp8rpf79EkmrwOaxu3ZyTQmFr0ChfwQNtRi3rysglbzoCwpJChTQwUOiN6aVacx87LDawuqV+IsmsxIiCiBnvy60wWEyN57QdlVBKN+Yc5gVdOsaEhdVUEkrNNT6JxiQgSkg6MLG+///////AQpDChR30/8VrYhukwaMCfXlxkNTdRGjpRIiCiCNRPtKq3h6WO/ITVM+fILr+hr1lEq1E+pxK7SHvqqeBRjp8yUg5emsAwpDChTsl17dqSN1TGumBE3GBBs1162SzxIiCiDlGyUmQX982bg0FEmIVgrbN53c28TfLgTqFfdSbG1DdhjIgCIgt5zvBApJChQD4KEoeyVimzbWqini4fDOABFBYhIiCiArZW+2h5YOZ/rtlb74DRKZ3ADffi5JaxdD959zf8jM8BjX7yEg5uny+v//////AQpJChT+qkKww1gs+evkvVIIK5BjbIvzpRIiCiDPd2fORuPKh1XUbCFw0r4fVbST+npe1XjFvewYRiAuHBi+sSEgh7eo9///////AQpDChR3gfoWrcV+EJ9jvR4p8U+dOBfhShIiCiCETITi+FStiFkovfc+iBDbHYaS3BePlUYrYmJ5XO960Bil8yAg3sCIAgpJChQX63sIc8zYPqd6YsNP+EqRDF884BIiCiCvdSWI59GPo4IB20m+cAf9cXcpKtF6kQyooaUzQ9u0pBjb9x8gqJPZ+v//////AQpJChQkdtNsDJ4bUKZrjMjonXUYTzdN3xIiCiBS2fHTP6PE0oricO1S/+bSkspBaeak+gEfh0WAHJpmnxiz3R4g8JLb+P//////AQpJChTYV6S6l0U1TZ/B35GoLNuHonVjphIiCiBp8ntDAQLaQeSr25aLp6fvPA1OslGNtt6C/XdX9l0/vBjW1x4gwcaK+f//////AQpJChTLgHaRxQo7KNq7u+urFDovNsfG4xIiCiC4s3hKM139A4mihTQ+SUH3mcMhyWZ7JWwGo42Q0XXYiRiImx4g4OrB9P//////AQpJChQmIAZfrdkcXgavJaO3+GRIO6HDuRIiCiAwEUQZD3TPq2WJdKFZgNHmP0fl3uJU8KVAwW2nen7Qthja0h0g/9/X9f//////AQpJChQMO6L4XJEBRsoTcWGL8VjGa1QhNBIiCiBIBkj/FQT0xwiLQmZUOdRyWvPpiSS7YHqAso2/qWSpyBj6hRwgztTd9f//////AQpJChRqfuyGsy23mwGL5zWspjul5P1ymhIiCiASVRD3v31FNC3A3H2XAhDltcvT6dl82dECybzpIipwdBje2hsgqIfV/f//////AQpDChQYNOii/eRkSZnu1b1LmbSpoixDURIiCiBP1rNCM3ki6oWSwy+P+u2WKvFJ7NePDllWX2oaqPGFzBiKhxsglt/OAQpDChQEpDcGLECfpR7Jik3WSTlL0Yva1xIiCiASIfjPvUKxVfdF5LZJK70RJ4vaLx9/Axz3JUe83M+VrxiEgRkg9NrADQpDChTAfc07Wr6Fc7qmFvndDj/NjsH2cRIiCiCMkrrUk3pTuHY+ajY0DZYZFCTAnJm8ija/gIhEqschIBiw9hggrfOpDgpJChTXtpJp5FfnrxQ5xDlEqirIKf18uBIiCiC2ukUjgyDCVh1gigcPLmAs6CfHrPCL2+M2ZC7pJ/WORRih2RYgnY2j9v//////AQpDChS2zQlXtj23PorA5RzQ3lOvQKxH1xIiCiCobq9jdCvtmDoxSHwtbknnfFplZO7g5j1mL9j0rJxD9Bid1hQgjpDBDQpJChQl0b1u9NyIswpHEIIoHgWVEwNgbhIiCiBWokKve+NB199wsgbpEwGXYyBcMVg1jpvcmhprjeBXehj/hRQgws+h9///////AQpJChSD83yscqQcJ8MAW2I/DU3rnOWiDhIiCiCcqqPUAeT5vLasOxzFZmt8HHWDA0o2qTVeLhKLfJwSkBiM8xMgqaTF9///////AQpDChTjU8XIn7YNLr/C8jjxzBFnPJgORRIiCiAkXhl9OUAAB/DCoHSUQASkTX4zFX5wO5Sc/jaJ2YmutBid1hMg+anHAgpDChT4lxWK/V3p0aGFIZME4hb+DoEqRhIiCiCagorB0VNs+EQYqdhOzhvdojogL52FGRGGF6XshXi2YxidyBMggaitCgpDChS7trfrLXVOwpV8pWFd1at5QVsLGhIiCiCJ6/CovT5hVtpVjgSM8Sa3NDkTBpjkKGk7t49BGsHTqBitvBMg2c/AAgpDChQn2HqqF7hq9Vx+bsT7Y3k3/vOfshIiCiCAQI+hCrUY3V73Fp/3RjwPJQyBwKLVhTd4GAq+kQPNAxjquxIg+s/2CwpDChR6ap3ufY87qoPyw6AXnXTtFIeTLBIiCiBWwDuKmdzppoZO7OVWWsORqtwiJhHSXNydlogEgfB2Ghj9shIg4+TtBwpJChQbhaX/3GWiPftRoy3BCH3KUV6mzBIiCiBsOEFSDjyrWyVds42i38/CidDiVh4XCZazLuElbRVGwxjIrRIg1MXI+v//////AQpCChSUJUk0JSzI9V34+8rYQx6qiu9mXRIiCiDdMfet6yiQCwbguAn67oLe4uEZ/212l5uYCkOlyOR+UhjZvxEgs4BGCkkKFKFPv3bd4YoBajZXbX6YQUt4+esDEiIKIC4PciYBwzrFuXTRiE/cVUIX6tE0gg+Hxji7ezLvPIu4GLa/ESCA5NP3//////8BCkkKFHcI+OCqoQZYi2u6SpfAP+nua0KuEiIKIB1klaBMFnkFW0SNmgnhMKAiCrAG0Rz77ouw+AUm1gYMGNz8ECDdzOP1//////8BCkkKFH3VGl2gZ+mpX7guAtWkGlGt0e9GEiIKILFzTi+kAP3Ny0DHH7ldynpEm43DgheiytsgnYePRNz6GJLdECDO+6T0//////8BCkkKFHI7+GwrILTPb/Lpm+YHH48ptGV0EiIKIFTlIzYv0pGYq021Z4mns5flw7RGJ6xRrLqBdxiY4dV+GPLUECD4l5X1//////8BCkMKFBgCxDfRXn5UgjvYK/WUSfEkXgY/EiIKIKuHZY7zlp0dmxfbk2S6yJlxYtop1F09XBqY+9tsvEUcGOX3DyCNhroFCkkKFOZkPMfjdf+InqpZ9/y5P03jVAPbEiIKILK93GDeTed/aEH+EcJxtjVeJuViiAIIqPIp9N6dIDhJGNbmDyDPzvT3//////8BEkkKFH3VGl2gZ+mpX7guAtWkGlGt0e9GEiIKILFzTi+kAP3Ny0DHH7ldynpEm43DgheiytsgnYePRNz6GJLdECDO+6T0//////8BGgcIARDryrMgIqIiCkoKFDljPdB/h+IW4MrQHgviFuIvias/EiIKIH5N3hzTiQk+c6yRIriNk4JUnv72bFdmP4DMJeSQDUDzGMjY8AIgkq7B9P//////AQpEChRUV2jxxPHuPn75Pejln9V6UGzukhIiCiBBjsKKrZ/5H7i1B/xEzRcg+wU3cQcwIfF2X/vmLaKVXRjEyoACILH9wAgKSgoUbFYIThz6MxYoyOsdpvhlkiRXqXUSIgogUGX8uDu9crqhjFO9P7bYQTVilJuLiE38GYqLJZka1uoYyMecASCUlq71//////8BCkoKFDPtOWyQTdjpocKkP7N8FPXf226KEiIKIFu1zXkNC1/ZJU293w5QDD2R3urd1VOx8Ypygu4XSCAdGPaUjAEgnubY/P//////AQpEChTdLJiLJ45SaHDdl9e6de6qL+vVXRIiCiCZP5nRROr3eFaXsWElKllxz5vTsXQ5Qxyx11IOMm+ouRj1rYoBII+vtQsKQwoU85v5MWwH5PJ+4xlR9yB4PFjQnBESIgogi97gIfO3BCu7bSvCKHNBBfb2UYzfP8I9VNx3iaWjUdcYnal6IJnW4ggKSQoU9TC92xz4E2cKb28eV0R01MkThfUSIgogfZqZqbnw0nQBHe76t9qIcvOyyHjoMH3upoHsUqRvcBUYu8RwIIHZqfb//////wEKQgoUR3hY5r9+Nq22Bi0iSgsGt7Tlwm4SIgogfkf0KGkfbSbHyNfZe3WO0EHLhHhZRi7eFx6OVacaSUEY/LhgING8dgpJChTpcpAVg8wAPEw4jvLm9VcKvw1bdRIiCiAsfPIPNK1XpdwucTnBMrNC0kjkBfxksRHyxrne3rtFoRjSpV4gs6Wo/f//////AQpJChTb3qDkPJ6wN0ItDifD/+34aS6clxIiCiA4YbdNu9zkneebgIoH7Vm9ERG4mKiMegFnFpg2fb5Kwxjv30cgrf6V/f//////AQpDChTRzoe76JtzdJVBboxEufWNI3LV0RIiCiDhsEVGxGyOOyAf8uIM3RxyDzsBWK+kjMGdN0DeHUEcpxievUcgrYzTAQpJChSd/MLjREM8vy2lM+hx7fdl4o+3ThIiCiAIqmd+ur7i5jVzIX+All0AZyEeIq0CE/7g10jaEJ1vWxjf50Ygna3S9P//////AQpJChR9LZbBRjKGEzeqYsDvTjygls4WZRIiCiC89C16d/supX4b4wuGnb2gG2Pegak8HA/tuIXWfmA5lxjPz0IgxdKG9///////AQpJChSyPvDGjQp+Z58zSulBAv8ptIcgMRIiCiClTk6WiLOJ1AEY1gTfLzsBdds3owYT2xO/Y/NGQQP8Zxj/gEIg2fWO/v//////AQpDChTkESd9zY3vI+1byj8f7Baf2xDvexIiCiBJ6QZRrK7sIPFytRy/XRdzkwMpW44jjWB8syNWrV5RihiVqT8glqieCwpJChTN9VrNl7MaZ2Yr0L68Zz5bX6rsBBIiCiCBYu2i6Tof7YQK4ncFAw4C8lTuEUyfDITmlbK9pwKt8RjOmT4gq6mU+///////AQpDChQ3s/nkYpZ/FQ9b+Yp6NV81MbWNEhIiCiC699fjdfNJQWY8nBK/pKN0woFWfzvlkYLPEWYP4ra6WBjAyD0gtJL/AgpJChS3hccwjVUb0bInmBv5OP6K09PjrxIiCiAHdCxLtUy/AOpV8N2AbUkyvUiAmGTmc8Ip/2YLUT0GhhjmhD0ggtvi9f//////AQpJChTcZdStW3VNZDxzNHDcx6RLn7TcpRIiCiBzKUvyqN/w18umePodCx4Yc3TUd0r5wvKaUDQfuoy2hBjC/Dwgl9bG8v//////AQpDChRgh2B+Hlb27nk0q69lg0yS1hgQTBIiCiDOfFaptSK3mPqX9uGAxT6kv69V9FmKCHIQasaovfm7JRiX4TogqcCOAgpDChSTc5JYxWJpA7pEQSjPt0OaoxqpPxIiCiCW0sZgAYZqUtwoRB4G5xfeWrwwho0ppZn3MJzUqhUPaRi22joglYbdAgpJChTY2py5gWJnXp3rOI7hfDYNcC1blhIiCiCa69Pq0Nd00AaKkOgSstI8zwhW6KW54p5LZyq0/z7m+Rjihzogg/ib+///////AQpJChQDM9ItyjAlQ7zR9aFETMgDiNybphIiCiAEulKe3OJKxJ2udX0BywcAKLxccy4DiyWYYN6WsbhuUxiuvTMg6sKt/P//////AQpDChRkYIRLLIHofz4qEGZ3h7t2AM+pcBIiCiBfjIPQnUn56Glsd5J/oLpvkDnVakl1g0xcYg1YPJM0XxispzIgm8CKAwpJChQ323ULdAJrEvDUljP5WIGo74rkfBIiCiA3iNzEXa96A8SopNcAyRNdQ6JvEUiZFq+XN+yUAA+oIBjKry0gwqmu/f//////AQpJChR1vCB4gfk59xLHPw3tXqqwKfGbLBIiCiAICsSMrwgJdi/v8qVBUkSATK5+v+hVENj2GUeuH4dhXBjC6Swg6d/j9f//////AQpJChQ3znGtjNTcAdh5jhA6cJeFkXCBaRIiCiCRhaPRQlVzxp8rpf79EkmrwOaxu3ZyTQmFr0ChfwQNtRi3rysg2MSf/f//////AQpDChTQwUOiN6aVacx87LDawuqV+IsmsxIiCiBnvy60wWEyN57QdlVBKN+Yc5gVdOsaEhdVUEkrNNT6JxiQgSkgzo6JCApJChR30/8VrYhukwaMCfXlxkNTdRGjpRIiCiCNRPtKq3h6WO/ITVM+fILr+hr1lEq1E+pxK7SHvqqeBRjp8yUgwvXO9v//////AQpJChTsl17dqSN1TGumBE3GBBs1162SzxIiCiDlGyUmQX982bg0FEmIVgrbN53c28TfLgTqFfdSbG1DdhjIgCIgn4S5+f//////AQpDChQD4KEoeyVimzbWqini4fDOABFBYhIiCiArZW+2h5YOZ/rtlb74DRKZ3ADffi5JaxdD959zf8jM8BjX7yEg36PoCQpDChT+qkKww1gs+evkvVIIK5BjbIvzpRIiCiDPd2fORuPKh1XUbCFw0r4fVbST+npe1XjFvewYRiAuHBi+sSEgs+OyBgpJChR3gfoWrcV+EJ9jvR4p8U+dOBfhShIiCiCETITi+FStiFkovfc+iBDbHYaS3BePlUYrYmJ5XO960Bil8yAgp+OB9///////AQpDChQX63sIc8zYPqd6YsNP+EqRDF884BIiCiCvdSWI59GPo4IB20m+cAf9cXcpKtF6kQyooaUzQ9u0pBjb9x8g9fOhCgpDChQkdtNsDJ4bUKZrjMjonXUYTzdN3xIiCiBS2fHTP6PE0oricO1S/+bSkspBaeak+gEfh0WAHJpmnxiz3R4g9d7XCApDChTYV6S6l0U1TZ/B35GoLNuHonVjphIiCiBp8ntDAQLaQeSr25aLp6fvPA1OslGNtt6C/XdX9l0/vBjW1x4g5YiJCQpDChTLgHaRxQo7KNq7u+urFDovNsfG4xIiCiC4s3hKM139A4mihTQ+SUH3mcMhyWZ7JWwGo42Q0XXYiRiImx4gntvUBApDChQmIAZfrdkcXgavJaO3+GRIO6HDuRIiCiAwEUQZD3TPq2WJdKFZgNHmP0fl3uJU8KVAwW2nen7Qthja0h0g9/eCBgpDChQMO6L4XJEBRsoTcWGL8VjGa1QhNBIiCiBIBkj/FQT0xwiLQmZUOdRyWvPpiSS7YHqAso2/qWSpyBj6hRwg5tDNBgpJChRqfuyGsy23mwGL5zWspjul5P1ymhIiCiASVRD3v31FNC3A3H2XAhDltcvT6dl82dECybzpIipwdBje2hsg3smt9P//////AQpJChQYNOii/eRkSZnu1b1LmbSpoixDURIiCiBP1rNCM3ki6oWSwy+P+u2WKvFJ7NePDllWX2oaqPGFzBiKhxsg6K7D+P//////AQpDChQEpDcGLECfpR7Jik3WSTlL0Yva1xIiCiASIfjPvUKxVfdF5LZJK70RJ4vaLx9/Axz3JUe83M+VrxiEgRkgyK6NBQpDChTAfc07Wr6Fc7qmFvndDj/NjsH2cRIiCiCMkrrUk3pTuHY+ajY0DZYZFCTAnJm8ija/gIhEqschIBiw9hggnZH6BQpDChTXtpJp5FfnrxQ5xDlEqirIKf18uBIiCiC2ukUjgyDCVh1gigcPLmAs6CfHrPCL2+M2ZC7pJ/WORRih2RYgqIv5CApDChS2zQlXtj23PorA5RzQ3lOvQKxH1xIiCiCobq9jdCvtmDoxSHwtbknnfFplZO7g5j1mL9j0rJxD9Bid1hQgr5TIBgpDChQl0b1u9NyIswpHEIIoHgWVEwNgbhIiCiBWokKve+NB199wsgbpEwGXYyBcMVg1jpvcmhprjeBXehj/hRQgg8rpCgpDChSD83yscqQcJ8MAW2I/DU3rnOWiDhIiCiCcqqPUAeT5vLasOxzFZmt8HHWDA0o2qTVeLhKLfJwSkBiM8xMgu8uTCwpJChTjU8XIn7YNLr/C8jjxzBFnPJgORRIiCiAkXhl9OUAAB/DCoHSUQASkTX4zFX5wO5Sc/jaJ2YmutBid1hMgmq75+///////AQpDChT4lxWK/V3p0aGFIZME4hb+DoEqRhIiCiCagorB0VNs+EQYqdhOzhvdojogL52FGRGGF6XshXi2YxidyBMgoobkAwpJChS7trfrLXVOwpV8pWFd1at5QVsLGhIiCiCJ6/CovT5hVtpVjgSM8Sa3NDkTBpjkKGk7t49BGsHTqBitvBMgyqz7+///////AQpDChQn2HqqF7hq9Vx+bsT7Y3k3/vOfshIiCiCAQI+hCrUY3V73Fp/3RjwPJQyBwKLVhTd4GAq+kQPNAxjquxIgrMPcBQpDChR6ap3ufY87qoPyw6AXnXTtFIeTLBIiCiBWwDuKmdzppoZO7OVWWsORqtwiJhHSXNydlogEgfB2Ghj9shIg5NTWAQpJChQbhaX/3GWiPftRoy3BCH3KUV6mzBIiCiBsOEFSDjyrWyVds42i38/CidDiVh4XCZazLuElbRVGwxjIrRIgvJ6z9P//////AQpJChSUJUk0JSzI9V34+8rYQx6qiu9mXRIiCiDdMfet6yiQCwbguAn67oLe4uEZ/212l5uYCkOlyOR+UhjZvxEgwM3V+v//////AQpJChShT7923eGKAWo2V21+mEFLePnrAxIiCiAuD3ImAcM6xbl00YhP3FVCF+rRNIIPh8Y4u3sy7zyLuBi2vxEg7rzj8f//////AQpDChR3CPjgqqEGWItrukqXwD/p7mtCrhIiCiAdZJWgTBZ5BVtEjZoJ4TCgIgqwBtEc++6LsPgFJtYGDBjc/BAg/9WvCgpDChR91RpdoGfpqV+4LgLVpBpRrdHvRhIiCiCxc04vpAD9zctAxx+5Xcp6RJuNw4IXosrbIJ2Hj0Tc+hiS3RAg3tL7CApDChRyO/hsKyC0z2/y6ZvmBx+PKbRldBIiCiBU5SM2L9KRmKtNtWeJp7OX5cO0RiesUay6gXcYmOHVfhjy1BAg6NHuCQpCChQYAsQ30V5+VII72Cv1lEnxJF4GPxIiCiCrh2WO85adHZsX25NkusiZcWLaKdRdPVwamPvbbLxFHBjl9w8glucMCkkKFOZkPMfjdf+InqpZ9/y5P03jVAPbEiIKILK93GDeTed/aEH+EcJxtjVeJuViiAIIqPIp9N6dIDhJGNbmDyDdj83y//////8BEkkKFKFPv3bd4YoBajZXbX6YQUt4+esDEiIKIC4PciYBwzrFuXTRiE/cVUIX6tE0gg+Hxji7ezLvPIu4GLa/ESDuvOPx//////8BGitvc21vMTZwNmxybHhmN2YwM2Mwa2E4Y3Y0c3pucjI5cnltMjd1ZjA3NHE5Cu8WCiIvaWJjLmNvcmUuY2hhbm5lbC52MS5Nc2dSZWN2UGFja2V0EsgWCowKCIq1ERIIdHJhbnNmZXIaCWNoYW5uZWwtOCIIdHJhbnNmZXIqC2NoYW5uZWwtMTIyMs0JeyJhbW91bnQiOiI4MDAwMDAwMDAwMDAwMDAwIiwiZGVub20iOiJpbmoiLCJtZW1vIjoie1wid2FzbVwiOntcImNvbnRyYWN0XCI6XCJvc21vMXF4eWR6YTdjdHpoOWZuN3NxNWdjZjBydW44Yzc4d2gwamo0N2Y0dDVkd2M1MzAycjJkbnFzcDRodTZcIixcIm1zZ1wiOntcInN3YXBfYW5kX2FjdGlvblwiOntcInVzZXJfc3dhcFwiOntcInN3YXBfZXhhY3RfYXNzZXRfaW5cIjp7XCJzd2FwX3ZlbnVlX25hbWVcIjpcIm9zbW9zaXMtcG9vbG1hbmFnZXJcIixcIm9wZXJhdGlvbnNcIjpbe1wicG9vbFwiOlwiMTU2N1wiLFwiZGVub21faW5cIjpcImliYy82NEJBNkUzMUZFODg3RDY2QzZGOEYzMUM3QjFBODBDN0NBMTc5MjM5Njc3QjQwODhCQjU1RjVFQTA3REJFMjczXCIsXCJkZW5vbV9vdXRcIjpcImliYy80OThBMDc1MUM3OThBMEQ5QTM4OUFBMzY5MTEyM0RBREE1N0RBQTRGRTE2NUQ1Qzc1ODk0NTA1Qjg3NkJBNkU0XCJ9LHtcInBvb2xcIjpcIjE2MDVcIixcImRlbm9tX2luXCI6XCJpYmMvNDk4QTA3NTFDNzk4QTBEOUEzODlBQTM2OTExMjNEQURBNTdEQUE0RkUxNjVENUM3NTg5NDUwNUI4NzZCQTZFNFwiLFwiZGVub21fb3V0XCI6XCJpYmMvNDAxN0M2NUNFQTMzODE5NkVDQ0VDM0ZFM0ZFODI1OEYyM0QxREU4OEYxRDk1NzUwQ0M5MTJDN0ExQzEwMTZGRlwifV19fSxcIm1pbl9hc3NldFwiOntcIm5hdGl2ZVwiOntcImRlbm9tXCI6XCJpYmMvNDAxN0M2NUNFQTMzODE5NkVDQ0VDM0ZFM0ZFODI1OEYyM0QxREU4OEYxRDk1NzUwQ0M5MTJDN0ExQzEwMTZGRlwiLFwiYW1vdW50XCI6XCI2NTIyOTBcIn19LFwidGltZW91dF90aW1lc3RhbXBcIjoxNzE0NDAyNzY0NTgyMzE0NzA5LFwicG9zdF9zd2FwX2FjdGlvblwiOntcImliY190cmFuc2ZlclwiOntcImliY19pbmZvXCI6e1wic291cmNlX2NoYW5uZWxcIjpcImNoYW5uZWwtMjExMTNcIixcInJlY2VpdmVyXCI6XCJuaWJpMTIzYTd6d2ZsbXJ4Z3h5eXBhZnB3bnA5cHVxdGVqeG13Njl1cTBzXCIsXCJtZW1vXCI6XCJcIixcInJlY292ZXJfYWRkcmVzc1wiOlwib3NtbzEyM2E3endmbG1yeGd4eXlwYWZwd25wOXB1cXRlanhtdzltdGZzM1wifX19LFwiYWZmaWxpYXRlc1wiOltdfX19fSIsInJlY2VpdmVyIjoib3NtbzFxeHlkemE3Y3R6aDlmbjdzcTVnY2YwcnVuOGM3OHdoMGpqNDdmNHQ1ZHdjNTMwMnIyZG5xc3A0aHU2Iiwic2VuZGVyIjoiaW5qMWtqZXJ2ZjJqMDhrYXZxeDJyYWpyNXc5cHhsazNxcnc2dWQ2MGRzIn06AECA9oTHsoey5RcSgAwKgAoK/QkKPmNvbW1pdG1lbnRzL3BvcnRzL3RyYW5zZmVyL2NoYW5uZWxzL2NoYW5uZWwtOC9zZXF1ZW5jZXMvMjg1MzIyEiAMy3tZ51tzYfU5Vucff9OMk2d7+rgZEcOhxQXCUQ7U/hoOCAEYASABKgYAAqyW50AiLAgBEigCBKyW50AgUch7rLbICeXPcU4aYu4e5uQTRxf+VEfaNMlnS8jaVXggIiwIARIoBAisludAIJvVdr1dxgHs2X4MUrwnj759maj8p0JcfC0j+JEaRCLdICIsCAESKAYQrJbnQCAy5L1aS7W/81JtMWboXx1thpzgnJ22CWjVeflUAmH3OCAiLggBEgcIGqyW50AgGiEgLTIh/0JH9jhb0zEyEXbS21y7f+vGpkvFvyT4TMcGPPUiLAgBEigMSKyW50AgF5iNImuVdb8q/r6JL+bX+aidnTelCXa3yY00ACECEIcgIiwIARIoDmqsludAIC4t0orEwhbG519ah6BDeqFc4jLutociQ5M9S2rulQxmICItCAESKRDCAayW50AgXSIRDxVm1lB96+P1w3oIPj3XFiRhukfrKpYRTNKTn1YgIi0IARIpEvoDrJbnQCDowMb9tAGn/xoN7D7RlnEViOIQbEi49Wh0TbJTOtBKYCAiLQgBEikU4gesludAIDu1IwrvzbZG7/5nl6hsFaOIGhqvVG6dCGFKW9nuhXmvICIvCAESCBieEqyW50AgGiEgNTUBomRiMDo58U74EF1FRIU0kFLZHQXJRPi4Yhf44LYiLQgBEikajCqsludAIEPclH+S1kUyQvAggZpmS3sS1iTpOXi6YzAa+KYjFVzIICIvCAESCBzsSqyW50AgGiEgtzY9C/mG8WSTkasoRnkXPz+/dMHj1hmaIH79YtVHjYwiMAgBEgke7pEBrJbnQCAaISCc26yKFz9slMowv3IbsO6J1GDuNXuuXGLZkQJKoWSVDSIwCAESCSKCnQOsludAIBohIGT6A2brCvKI0aqsIAQA5++vTMyCzXw7Q0HginMcADCcIjAIARIJJLbMBqyW50AgGiEgNgsfjXh1YMTXK1RYr3o8msCvr6MPjIBbEeNBPVtf4rMiLggBEiomxMUKrJbnQCAYRc2aLVWklBeZAzHUOXUAl4dX2jsOvvjRK1oritEEkSAiLggBEioouIsjrJbnQCCpmFe19hmEQUvPalE6syJMwgau1Zd3gDcqtNfTaQvrKiAiMAgBEgkq8KY7rJbnQCAaISA4v3zZoLz6oMCACTPLS2LAHoDGOtrGa4bZifN+oOZD1iIwCAESCSzc4VmsludAIBohIBePh5kJ8EWvTYur0HeoBkihgQgidMyxLGyRdv40q23TIjEIARIKLuCZgQGsludAIBohIJBBtdC2SP86Mtu9yX435tt8VCxWLWncjm3Rk6EkWxYjIjEIARIKMJT19AGsludAIBohICJTlqNk+LjQy5iTmw35BCmHI4FJGOiomHGzTZRKzLiOIjEIARIKMobm+wOsludAIBohIGWMXM5xQlbMSrGfdMqSm2w0+TEIsnlnx1EcZNK19SyqIjEIARIKNq7cxwmsludAIBohIOHDop9KqYHt4k5t4Zn9Cy9X1NeHvksCUax0C2K/A6cEIi8IARIrOLy5khSsludAIF8bYWC3pe51jSN4IbJDIQTGB/MX6n/C6YLigD23VA/KIAr6AQr3AQoDaWJjEiCsxoOmfpHfQ6T2zYjpA6Pli3gBMLaVbznIXqj1FgAtoxoJCAEYASABKgEAIiUIARIhAbxGYGC3TnoUrJDiY3Nv0KLnuHbPs6Hh5QP9FlWOrPDvIiUIARIhAZhaEVHz6WXzsGZBadxdN5H9J9B7kAXl+ghRmoZHDclJIiUIARIhAX8wTUm4ve2IaUWMP8kS1K8xbQp5nyuhDUm5um4s9b6qIiUIARIhAYZPExauJVd96uGlYWMHNr165nY1EwkvAoVbWInYRyQYIicIARIBARogTj+WkQMbXO95yQWuXJzaNL76kklD+Gi8UCCip+E5Fn4aBwgBEJfLsyAiK29zbW8xNnA2bHJseGY3ZjAzYzBrYThjdjRzem5yMjlyeW0yN3VmMDc0cTkSIVlvdXJzIHRydWx5LCBWYWxpREFPIHwgcmx5KDIuNC4yKRJpClIKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECS83ARNVr5z0EWKS9XHSKAmDT2zGmNiOqAdDmtb5SlfESBAoCCAEY7vA8EhMKDQoFdW9zbW8SBDQ4NjAQpNF2GkAGe6BVjUdAPMxq5XH+vrSlinDZpRL8wz1a3fMx9LlyCzzppwFJoMD2ZsVvjqgMmF8/a1ACGydJpSKZ/wv4xcxg", + "Ct0CCtYCCjEvb3Ntb3Npcy5wb29sbWFuYWdlci52MWJldGExLk1zZ1N3YXBFeGFjdEFtb3VudEluEqACCitvc21vMXI2N2EwOGx2a3psZXVhY3JqcHc5MHludXczZzU5MnVqNWh3NmVyEkkIvAkSRGliYy9EMTg5MzM1QzZFNEE2OEI1MTNDMTBBQjIyN0JGMUMxRDM4Qzc0Njc2NjI3OEJBM0VFQjRGQjE0MTI0RjFEODU4EkkIpwUSRGliYy9CRTFCQjQyRDRCRTNDMzBENTBCNjhEN0M0MURCNERGQ0U5Njc4RThFRjhDNTM5RjZFNkE5MzQ1MDQ4ODk0RkNDGlAKRGliYy80OThBMDc1MUM3OThBMEQ5QTM4OUFBMzY5MTEyM0RBREE1N0RBQTRGRTE2NUQ1Qzc1ODk0NTA1Qjg3NkJBNkU0EggxODY2MTAwMCIJOTkwNjI5MTk4EgJGRRJoClEKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECRVJ71dxG7dQfOFeWOhuRpDoKf862o8rfDx8JF4imDEASBAoCCH8Y+AMSEwoNCgV1b3NtbxIEOTM3MRCAiXoaQC7hcb6lL44JnO4Y4pFbMZ9dDJll91O0A6cfEtVCfCvkCvjp5MhWPyLxtF7xdDTGcwxHsGQmhrZWs6+iORir2zM=", + "CpUBCpIBCjovb3Ntb3Npcy5jb25jZW50cmF0ZWRsaXF1aWRpdHkudjFiZXRhMS5Nc2dXaXRoZHJhd1Bvc2l0aW9uElQI2LfdAhIrb3NtbzFqZXJtcHI5eXVzdDdjeWhmam1lM2NyMDhrdDZuOGp2NnAzNWwzORogNzA3NTYxNjQ1NTQyMzQwNTI2NjY3ODg4NTUzODAzMjgSaQpSCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAqEXfKC5bq2Y0WSQ97Da9WPJNepqAUpY9bo5ZuC7NlpdEgQKAggBGKWhIRITCg0KBXVvc21vEgQyNzE2ENueNxpAaRWzwQ1ornBu+RiTLtr8b/viIya/qviZdFM33n5eIQc1tssTrl7I+5eN+vIT0iUhwVq3TfPf4RTtqVPj0qL9Uw==", + "Cq4BCqsBCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QSggEKK29zbW8xc3MyOWVudHJjNTJjcXR4OTkzNnJ6d2VrOTltbmt6OWY4bjgydXESP29zbW8xeDA5dHphZjducGxjaDR4am5mcDJnZmVjYXptYXlnODRkamM0bXF2bXFxeWdlZGR2bGNuc200bW51ZhoSeyJjbGFpbV95aWVsZCI6e319EmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQL2Azut5mmgHPCkFtlnZDPv1JTu6qVNWR2lpAelMUFYmxIECgIIfxgvEhIKDAoFdW9zbW8SAzc5MRCrpRMaQGwKUEn++a8J1mfCLwXH/iLlW2Q3B9Se59KjGnsQS3jXMYYgDJ1aD3xREySfCVv7L38Ibgk7+Iz4pSgAYM5s/Yw=", + "Ct1rCtpVCiMvaWJjLmNvcmUuY2xpZW50LnYxLk1zZ1VwZGF0ZUNsaWVudBKyVQoSMDctdGVuZGVybWludC0yODQ2Eu5UCiYvaWJjLmxpZ2h0Y2xpZW50cy50ZW5kZXJtaW50LnYxLkhlYWRlchLDVArDIgqTAwoCCAsSCmNlbnRhdXJpLTEY8M+tAiIMCKrpvrEGEICAgIABKkgKIClLH8coKQzvCv8E/F/f//vhjpD4+VVgWLJr9Nn1jkZUEiQIARIgEZ5EMk+XfAL6uaFOW2D/qzkXWUNWQmMjmGke/OvF4N8yIDLmnbXXgKPbNoKhLGPUWxcIeXYOPV9skcbP0XAQp4DvOiAMkbBcihb8JdCtijy0GML76/O2S6K99pY4Ad7L5H9YOkIg3XEH5ITSXySypTUGa5waumm8qycwSiPSLCRKpI/L1xhKIN1xB+SE0l8ksqU1BmucGrppvKsnMEoj0iwkSqSPy9cYUiAEgJG8fdwoP3e/v5HXPETaWMPfipy8hnQF2Lfz2q2iL1ogpVwm8KKYi26ie57yrd5Pm9Q+IRakMaZ5grkkcCpK4/ZiIEn5W/RiQ/WQeh4dC8eIhirifMIxn49wnrJqxegbNOT0aiDjsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VXIUFV6N2WKjr1RN8OVbztYyPd0SiewSqh8I8M+tAhpICiAf9Yop4fQpx8UtrntQdICKnaSMRC7MG/FhvGl+ebx2qhIkCAESID2uilDwrRjyWGFo2g4IuBD7ghKrfbuAdoiKzNLC5FqoImgIAhIUadP6hVzrmQLNyKV4a1aBEKclsxIaDAiv6b6xBhD5jdGTAyJAgNWm/JZLm3VlugqMZ5tRviAMSHkVrIfY0gSyee5aVy1+DEeDcMiJRu6exmGEdrXpqQHQUGNxXg8AaY82crN1AyJoCAISFJcmysTtesVvkVdY7jMeEtDJcfoLGgwIr+m+sQYQ/dzrrwMiQIERwB02hsYlyVmsYH6xQ1qfS10DL5joWw7KqQdhQ+VRzyXoDasbx/xWCaz8OSznoUaj0anrlIvnYcPX34kNkwUiDwgBGgsIgJK4w5j+////ASJoCAISFBKpb7OpWoulhy/5cJ0f2/l8HBuHGgwIr+m+sQYQmYj2lAMiQLInVv1VTas0zV94pTOp3FmaZR9UI2iy/qBI/+xAEqzKeF1PiQATh3pH+3sVrCqvpYqe9lGoa0T8szMDKCazdg0iaAgCEhRfFGu6Sri5gzktSpZCAjYhRqz3VxoMCK/pvrEGEIfota8DIkA6aQSc0Ua98pc9FrtdJgJDrn8FWaSoAtlZm4e7f8GHzwt7eLI1uc+j3txMmsaZ4XRpwP/bvDswIRRcQUPLYm4HImgIAhIUrQR3yaJfkI5VnUXLupYj3yDSEHQaDAiv6b6xBhCfuYmzAyJAebuWqUuBb3w0E9LqbP/a6DPYmJWkOPqeaalyUd/ZFLSufjTVlnkNCQFPh5Vqiidzv6I/t9EHV3Fi5h2X5C7lCCIPCAEaCwiAkrjDmP7///8BImgIAhIUzN1wpLhrvTJNkOddiuWtwU9GXsgaDAiv6b6xBhD0nLe1AyJAv6u+Kxf3ak7NNb1aRFvIwdcEHTSK22onHZIRgnD2LcqGCSgVoY9ByBbabwYNmPFCS+tTEv92j/gzWPmxHzz9BiIPCAEaCwiAkrjDmP7///8BImgIAhIUxQV5FLzyAhGv92zAhgQM8FYwDVkaDAiv6b6xBhClz4ilAyJAJn9CdzgV4GMRrsVEyA1vniK4rS4WwielsaZ/OYgpK2OnDxFGhGuBaoRs5gEMeagkZJD7jCqYutxp4X2P36GiCiJoCAISFBVejdlio69UTfDlW87WMj3dEonsGgwIr+m+sQYQ3oK5tAMiQAyBr0ZW6WbS3PqmYI5KY/qT3hzQIqKk4kf6GSyOtLkfh7hYlc0znImsRroC/ZbW3KaWh/r/P3BHyHTurRn/ZA8iDwgBGgsIgJK4w5j+////ASIPCAEaCwiAkrjDmP7///8BIg8IARoLCICSuMOY/v///wEiaAgCEhQ5ch6SKQedK9olncCbWqGEduhrkxoMCK/pvrEGEPL1zIcDIkBOJ25IcDvFwEIpL58FCEAreLvHjIYnyXB5k1jraEwX8FMxAmFQnTwDs1TXbGBVxC/vuDZRnxSMfGSdPC0ZscwGImgIAhIU4r6uxEGaeeNIdEuUWbdthD+fTrsaDAiv6b6xBhCm8KCkAyJAAi/1E55uLIgso6GlF4aHLruWK0mxqiAVR48Gv6ra/8fIrrbGNLlcB+NzOassgOq9JN6P8fwPuvuHtUcx0CesBCIPCAEaCwiAkrjDmP7///8BIg8IARoLCICSuMOY/v///wEiaAgCEhSutM/+3Pu522FbAojIqZHaSlgV4xoMCKrpvrEGEMCEvYABIkAordzuJDu3SVTxcqd5Z1fBk0Ysc8Nki2JAYx7N8XTiGsHDfytRqoovtGeg1HzJaWEecuuktL+HxhX8Um7iKxEFImgIAhIUXEUklWhRGELn8YfYa3NvR2SxlJgaDAiv6b6xBhDk+r6kAyJAmU642wuK+KkJSEmbGGx0tzJJb0Sb3qsQ8/QqoW992BZOWtVJeyOlysKxozgrrHcor3AO6qDw/xGfuUXU/WJpCyJoCAISFMxnGazMYptQsCxfCVYovUazdIb4GgwIr+m+sQYQw77VqQMiQIQxEd8r4niwZ2vEEM9cGWJ/NmDI/xnA7P4Y/LZADeVIkFsqSpzhO/lwQSLItJi7eCHzrqESG6D44wmgozPzZgwiDwgBGgsIgJK4w5j+////ASJoCAISFFu9Z3gUTHrRJ3NPYKuXyTNaoMs7GgwIr+m+sQYQ3JPpqQMiQBNQ/yOMsxu5qXDwxamt/U8HvS3f0zKfcboXDcmHSShyWxBEoE1/2PhOhiAZRvDINC6XGk7ifWh1GRb+DjDy5g0iaAgCEhRp+8/UgtGG+w2PAkjoJWmDH9PDExoMCK/pvrEGEK/5uaMDIkCLOPbWhTgXfADvVbxPTqEOFqV67Uzbo4pdk0pSJqc6cR5QJzkzL0FIrT7EtC9l8efoeUbdh+PAGA8wiWUMjC8BImgIAhIUglElaokUq9+3B33xgcm3VSI7JAUaDAiv6b6xBhDe3umkAyJAprIcZuEkKsE28xVX/Y+n2qlRtgrLCpBYjusLwDnvyMxQ7N05fi9DdqMR9vBYM2dogQENtMpMz9SSodEWGU9NDyJoCAISFBwQvJwFAqenLP9cCzsfiy68i73AGgwIr+m+sQYQyLzJvgMiQLIZTWYSLjozTOtREHkBc4N/btCeJLqKd9zsCvgrqtZymf2XYc67bMAn89kesRr/bqSToh8uHPBYziI64o8yFwgiaAgCEhRnvzJBcC9YG1z/4tgkbMVWwmbZihoMCK/pvrEGEMWE5aIDIkAZ6f8UX+pVzFY2b4hm8ZjfuByNzRFOp6CZNA8Av41+Zpe+IVXNgMrEZbcgKwPLUiBfyix3Brkw73rOmG7pjg0DImgIAhIUZNaluKqblYYcJx76xp8LNDjdInEaDAiv6b6xBhDdlcuIAyJAyofnH9ZplPb2O0MqI5U0PBcdScIh26RJHvqvMv+0QmVYJlYZjldDtXmHgUm49mcdPObhmZpYJPUDGPid6+IpDSIPCAEaCwiAkrjDmP7///8BImgIAhIUJrxiS4W8odkvpzlBtVjXgoHzUZ8aDAiv6b6xBhDA5KCUAyJAHjerELRFaBwvN0r40XHUpPlICKGUB+mwt3uYLW9+QxldxpyQ+EQzYTJRm3ZUEXLrIyDZNtfdbBg550S8eNzwCyIPCAEaCwiAkrjDmP7///8BImgIAhIUmssICgxH6zd4oz0hVL9WxMVIVhYaDAiv6b6xBhDA4LnAAyJAJBupI1mlpok08XlZZjQYFrgX+vKz1lxzDDgAAZ5GWJSXtAFXvwCeBX0iwcLZvHSQ0G8+wX4GBZ9O+qZM6W82BiJoCAISFE0PP+lYp3gV9RhsBDnMi5i4YG5kGgwIr+m+sQYQw7+HqwMiQLTuaxdbw9kp3ag2i2Y5JbX6dOKuZnl+gc+Hspt/J7kn5vMprOlPy82Rl4rMZsF2wQ1dfkFWe2B9TN66icgLNQ0iaAgCEhSLpBdeZ5Ol0K6tUKtfSiM9x3HRqhoMCK/pvrEGEK+J8qMDIkDZ6dd4x2q5NwKdZnaYJ89djKCSOX8nzDRqSn8PQOmy58EarLdORqpmjpE8vh8O+T6ebJ7XlW+avhoFtm09sFkMImgIAhIUeMtBmIFj812I8JUVfTs5Gk46KH8aDAiv6b6xBhDvncOoAyJAsI/bMFG4LHuUDRW4ax1IyKQNSqVIpv/fxXP1I0fg2Lqnjv9ugC1byo6L0g4YM6tWJzSHS5GCcL+I/DRWldu3DCJoCAISFJnJLT8njV9PLIsUYrapjz+aV/YjGgwIr+m+sQYQ/ZzRqQMiQO3vjLI9axHHoiN3tJ4qqchQuhsFxb1K8JzH3ZjUQgK4z68DctelRUx9pPgOXkaVbtIxsPLNzC7GOWEdy3ip9QEiDwgBGgsIgJK4w5j+////ASJoCAISFFrZXeZtbMAmq/WjPDsaz07yBnl4GgwIr+m+sQYQjovkwAMiQMgmOLByx1vbNfNHYdtciBrSMdQJmS0UOdwO50cKsRg+d/WXnY+wZsLJccUSgdOaIUJ/CMAXhQ/19ij3F3gncQwiaAgCEhQ5JXCjaaBt6gUQWDJRRNPqeP24YhoMCK/pvrEGEPrCzpYDIkD1Gg1pvJ9QAnXgMpSDg3uV789bjNjoEzKWwUWQBfbaIT12YWoRCEHBexskeWKEzwrCtObq2GRv9TbysPwaQDkIIg8IARoLCICSuMOY/v///wEiaAgCEhSypiFx7d/W+D9pN6OkhT5jsbz0DxoMCK/pvrEGEM7nvqsDIkDb2/mTF+zpCG9kB/5VP0c3Bi/ii2Uj0U4uYYu8Ooz56eWzDm9ybPPv5J9hxUexTJ/89dK5bMRR2pZonb4y5cUMImgIAhIUw28j7WvXLn3++gDsiuGDLbsi5cgaDAiv6b6xBhDUpYKjAyJA4UcoGj7+tuLradGK6IgkQ0G0qeYiu47wQ+HOAFU6m3kht+ije0OwW9UBcYU7GhWwRC6LTEdUc0LMabshNV7fCiJoCAISFKmn0A/5oLrq8kFmmqOvl2Y11RvbGgwIr+m+sQYQuqy/mwMiQB0qHmePhoNcdzixzNTgpx1L2/I7zkr2Ow9gYf03iP7QwpfmQO/UP0mEqlDG/S7d9XqAEZAmkkNVCp6xRPSejQkiaAgCEhTbtFzYXQs4FgnPcSPehXNoZWOATxoMCK/pvrEGENLw5qsDIkDvjVwGOwrwT08uqMwMeO7/iNbr3EduW02TsXbDnqLvo9DzQK9G/6BJTbG59VhtrNM1GYPr5pxijOnRZUW5CCELImgIAhIUnpQPUyHJ3P2urreET4qxAqNEc1AaDAiv6b6xBhCInJmtAyJAraz2Dd8I9X7NKWd+nAg4XxCDmQ2lYcCmrdJ/1ir2I+K3NaRK7ZG+yDcaklPQsUW+oT5vpPKuIz3DgfpoUDjICiJoCAISFIcuIFgrf3bxr9m2+I8MOGZAvZojGgwIr+m+sQYQ5sP6pQMiQLpxN94mm41zLf18jp0Uwi0gEoqSw1CNVvsXBp1Uex2OKpmUgYomGAJsTOoychGNSbHg3qqmpKEu5SoChQA1fQEiaAgCEhRB8nVaDtRAj/AzI7rKLDVQA+rOoxoMCK/pvrEGELqw/KUDIkAqKmHS4zV/KALiu7I8EmYDtmZFp1PBDgaQWxDXLB8iM4CjLn0JFB8MZo4/JhMpjNFLOYXM5tYZ0bzi2DBCg+UJImgIAhIUQV4OrHe5j8BOQee02gq12CZLCy8aDAiv6b6xBhDCtaGkAyJAT7y/YRnf9IpIUogcfBIug9fMXx6MLZiR3av+9vuKp9LihTRqO7nJgzIuCXTJf8TCCBrQHlOr55LbJJaqghhpBBL3GAo/ChRp0/qFXOuZAs3IpXhrVoEQpyWzEhIiCiD4ABaThflZT1EUpPvrkVbOPhQOGzp81isFLkcKySxsqBiCscMtCj8KFJcmysTtesVvkVdY7jMeEtDJcfoLEiIKIJwjRImheraWRCRupISqF+QsjYjpodeHCIfzULbuM10pGN73qyoKPwoUv7xir/Tq3a6eWwfjUk/SBryuovASIgogvKu8py+1euufrZOr4u3RXRfaAXQaTlMUlij8clKtPEUYwP+PIwo/ChQSqW+zqVqLpYcv+XCdH9v5fBwbhxIiCiCi8GwEN9j6ueIX0k0Ylhxy7HTHOEJVsRMTbsPvSYZfrhjgiaAgCj8KFF8Ua7pKuLmDOS1KlkICNiFGrPdXEiIKIMcOPrgRwXNSYFP/NlNdBKsEQqL4T6uYp7bwG7m99MJOGNO4jhoKPwoUrQR3yaJfkI5VnUXLupYj3yDSEHQSIgogmFYuUU+Sq81L9RM5fvemAseYayUQDbrb00YoGudYVIAY1MiMEwo/ChQiCPiC2E8EpI0RJThAqKh8zNQKJxIiCiCpTLc5uHN6Z5GzeL5nOE6s4QrGHtQRUKN6mcFtDZOMsBiM1cYOCj8KFMzdcKS4a70yTZDnXYrlrcFPRl7IEiIKIOOMNAJ24REKMMagxhBaHFyiNQAZux5iJFpo4GdUhrNiGMSS8AsKPwoUaKjxQNRD0+d5CxFTiNfLeHvs4wISIgog3Ql3jWgGO+D1r6T/xjHSdYhga69yC7b3PEJHUIj4OWgYhrDuCwo/ChTFBXkUvPICEa/3bMCGBAzwVjANWRIiCiB3S4sbqWHRnveEEDQgfQ8SKQQ2rtMMJ4ifBAtTJDNeqxiUtsoLCj8KFBVejdlio69UTfDlW87WMj3dEonsEiIKIOrB8Cx7PCSo3vN3amcOJARYumtUHRGQHQ7MqG8ZwcqoGIW+yQsKPwoURl0Hgtxp9xkjHKUktSWrB0mMNasSIgogLS77n5pnfiUCyx325bdR6yCJN8Qk1v4c2sEB49feFfIYoOD3Cgo/ChT9sZO1z0cKI+szx3j5ljtm0zgBqhIiCiB63uZt1y79weTq7YQox6Y9Ax4xdweYT8PahYTRXdLGeBjL7M0KCj8KFONyKbt7TaBiKm4nKrn8aPLWqSQaEiIKIKCXQQkxoCcMvPQ9ODBsxzOAkt0L5ZHUkCvggQJujgQpGM+RzAoKPwoUOXIekikHnSvaJZ3Am1qhhHboa5MSIgog/6Kgajz6LYJ2BR8JyJ4La1r3y+HIP+hv2xyvczyCxkIY296oCgo/ChTivq7EQZp540h0S5RZt22EP59OuxIiCiBw5TOgwg+/jzjEgdeNjDPsXVAf6CZtDCE7cYVQnZ6HlRjj8p8KCj8KFDquO3wa09mKwR3BHLEvXsxlAxt6EiIKIB59PhnIpx5E13lYTqFGdhTX+pJul2kxIcw/SsBJ319bGK7cpQkKPwoU05yeA+oTH/q/q0XttAjXyIjJYLYSIgogwRYuugekxWP4UQ+gkhNdKjG8ImUJWaetmTqEOZ3k1zQYx9/hCAo/ChSutM/+3Pu522FbAojIqZHaSlgV4xIiCiASZTPYr9BVGhQrB1YUILctyXXYqqJXRJs9rojefflRXhiYz9cICj8KFFxFJJVoURhC5/GH2Gtzb0dksZSYEiIKIH8nRXqwdAMSYhW+nTfqbW9Pd4eSwOW+D28uPINOAvNyGIPYvggKPwoUzGcZrMxim1CwLF8JVii9RrN0hvgSIgog4hJkHjxmFH9g83ibKPVQB1rG83phBgRPOZxWTXOpY14Yhd6uCAo/ChRLTGnIhZ5JkBmcePLH1n5tVdzZyhIiCiD3zHrBvN+978LGrZ7c4V/SDU0WiTWxlKj/p+cTuSTJehix0JsICj8KFFu9Z3gUTHrRJ3NPYKuXyTNaoMs7EiIKIA8sU8IxYMq483bYx/Kg3lyyQNJU+1Oqo/NrNYRDSJ3wGL/2gggKPwoUafvP1ILRhvsNjwJI6CVpgx/TwxMSIgog/c0gZGn6AeW7ZS2EWK6K8etGpYyD6B6YJEOleLmHDuUYm93hBgo/ChSCUSVqiRSr37cHffGBybdVIjskBRIiCiCzBkZcw7Sn70oi5Sqt/GE59Dsk7j8c7V5+KCqEOBM6nRjPxMcGCj8KFBwQvJwFAqenLP9cCzsfiy68i73AEiIKIAawN5Ft0PyR7LKBDUsIoCLTzz1BE7TJH3daIPxa3S3JGKT9xQYKPwoUZ78yQXAvWBtc/+LYJGzFVsJm2YoSIgogUwiStth0hT6mgSTOyehar4AIrHHgcS1KMDURsMDYUgAY+b+vBgo/ChRk1qW4qpuVhhwnHvrGnws0ON0icRIiCiD45PnhmTNG1VjLFnv2dfHrgcdOL27mEFjAVOOyPV9ZQhjYye8FCj8KFAB9epoM9ghB7SBWc5VYWI9As6YpEiIKIGnnj+TuCEQ++hXyk8bXkF1Vkpb45zwpjNlSuLART49DGKqD6AUKPwoUJrxiS4W8odkvpzlBtVjXgoHzUZ8SIgogXrTDIJW6G/Qhov2L6IPYsU9P2vPqAtB6iX1n7C2DSmcYlqvlBQo/ChRrpr/OHEKyLb+eQQgbVOM51ykG5BIiCiAHsuQfiNrA0WoDxmtVHZbQ0pDCCDUZ/cN6x/Y2RYKtDBij6tYFCj8KFJrLCAoMR+s3eKM9IVS/VsTFSFYWEiIKIKI+dy+DKNCw0wB6xq09g395OnCXCDhftdnpwbA4OewrGNaJ0QUKPwoUTQ8/6VineBX1GGwEOcyLmLhgbmQSIgogPjmc/xV5+S5ZdrbNnZEMcnU1QQQIN9C8OQbX4gNTzpcY963IBQo/ChSLpBdeZ5Ol0K6tUKtfSiM9x3HRqhIiCiCmMjS8zE9Y/REFiu0eioBY8aT5gyCKGVyx9+JrD62cnBjg/7cFCj8KFHjLQZiBY/NdiPCVFX07ORpOOih/EiIKIL9fGxcxDLK7ZuMgvJLkdf0tuIAes5j/2eqfcia4bO2tGOekqgUKPwoUmcktPyeNX08sixRitqmPP5pX9iMSIgog8OruWQzLtBOsoVvct7RrdSJ3deDJRQs5swiHt9Ke1i8YxZKNBQo/ChSpCSQKmfyfxTkow0P4EoqcU6pxUhIiCiC70ERpmAiYBM8tNaLewlm//kTIfqJlv9o+QUpu/NQK8RjZ2P4ECj8KFFrZXeZtbMAmq/WjPDsaz07yBnl4EiIKIKtabwKzLw+MnctrVRlVb5EKfeauVV1RIQd8iz20nPDiGOfL4wQKPwoUOSVwo2mgbeoFEFgyUUTT6nj9uGISIgoglspVwBs0+RbjCBbyTTHIbdoEXe0VGgH1xl+iSurZV4QYq7XcBAo/ChSMtijY8CAPcErst775wEsu9bZsChIiCiDeOuHmsv9w0aujFM+hmN/145vhbBYwzu5YSwTdCpMXxhjSwdMECj8KFLKmIXHt39b4P2k3o6SFPmOxvPQPEiIKID/4np5jL5ZXK8ShxZ0KPmSjE36DqWOjQ1OLPzIXhYXZGP7vzgQKPwoUw28j7WvXLn3++gDsiuGDLbsi5cgSIgogYEUw7h2WO88rmogLj328W0W9WPq5aKuWC9lbrGTR9MoY6efEBAo/ChSpp9AP+aC66vJBZpqjr5dmNdUb2xIiCiDFeujO9ehsTgijkG+M19Y8EI/LD9srenhsJYSbRjcZ1xj32rwECj8KFNu0XNhdCzgWCc9xI96Fc2hlY4BPEiIKIIKH1H0w/Fin3n7B63Vk8hEn1sdJ3eaJ2kfaA7Z8cqNBGK/iuwQKPwoUnpQPUyHJ3P2urreET4qxAqNEc1ASIgogTZqzXv2HcfUFm7nCQ76x9iDTNGdJgxGf+CVcdShAmIQYp+G5BAo/ChSHLiBYK3928a/ZtviPDDhmQL2aIxIiCiCDHNDdHGQnBWMSTvJJQ0pQZNEgiqx3NT5DMJp2wom1sRi3yLQECj8KFEHydVoO1ECP8DMjusosNVAD6s6jEiIKIB4irBKkrfNDETZR1rQg9z0vO9nKpKAiu3xpGlKAaABjGOPkpQQKPwoUQV4OrHe5j8BOQee02gq12CZLCy8SIgogwqogUH941Un2hVCd0ItkiYBgWtKtPpCz6AtdJLIUInsY9qmcBBI/ChQVXo3ZYqOvVE3w5VvO1jI93RKJ7BIiCiDqwfAsezwkqN7zd2pnDiQEWLprVB0RkB0OzKhvGcHKqBiFvskLGK+Qw/kDGgcIARDsz60CIvcYCj8KFGnT+oVc65kCzcileGtWgRCnJbMSEiIKIPgAFpOF+VlPURSk++uRVs4+FA4bOnzWKwUuRwrJLGyoGIKxwy0KPwoUlybKxO16xW+RV1juMx4S0Mlx+gsSIgognCNEiaF6tpZEJG6khKoX5CyNiOmh14cIh/NQtu4zXSkY3verKgo/ChS/vGKv9Ordrp5bB+NST9IGvK6i8BIiCiC8q7ynL7V665+tk6vi7dFdF9oBdBpOUxSWKPxyUq08RRjA/48jCj8KFBKpb7OpWoulhy/5cJ0f2/l8HBuHEiIKIKLwbAQ32Pq54hfSTRiWHHLsdMc4QlWxExNuw+9Jhl+uGOCJoCAKPwoUXxRrukq4uYM5LUqWQgI2IUas91cSIgogxw4+uBHBc1JgU/82U10EqwRCovhPq5intvAbub30wk4Y07iOGgo/ChStBHfJol+QjlWdRcu6liPfINIQdBIiCiCYVi5RT5KrzUv1Ezl+96YCx5hrJRANutvTRiga51hUgBjUyIwTCj8KFCII+ILYTwSkjRElOECoqHzM1AonEiIKIKlMtzm4c3pnkbN4vmc4TqzhCsYe1BFQo3qZwW0Nk4ywGIzVxg4KPwoUzN1wpLhrvTJNkOddiuWtwU9GXsgSIgog44w0AnbhEQowxqDGEFocXKI1ABm7HmIkWmjgZ1SGs2IYxJLwCwo/ChRoqPFA1EPT53kLEVOI18t4e+zjAhIiCiDdCXeNaAY74PWvpP/GMdJ1iGBrr3ILtvc8QkdQiPg5aBiGsO4LCj8KFMUFeRS88gIRr/dswIYEDPBWMA1ZEiIKIHdLixupYdGe94QQNCB9DxIpBDau0wwniJ8EC1MkM16rGJS2ygsKPwoUFV6N2WKjr1RN8OVbztYyPd0SiewSIgog6sHwLHs8JKje83dqZw4kBFi6a1QdEZAdDsyobxnByqgYhb7JCwo/ChRGXQeC3Gn3GSMcpSS1JasHSYw1qxIiCiAtLvufmmd+JQLLHfblt1HrIIk3xCTW/hzawQHj194V8hig4PcKCj8KFP2xk7XPRwoj6zPHePmWO2bTOAGqEiIKIHre5m3XLv3B5OrthCjHpj0DHjF3B5hPw9qFhNFd0sZ4GMvszQoKPwoU43Ipu3tNoGIqbicqufxo8tapJBoSIgogoJdBCTGgJwy89D04MGzHM4CS3QvlkdSQK+CBAm6OBCkYz5HMCgo/ChQ5ch6SKQedK9olncCbWqGEduhrkxIiCiD/oqBqPPotgnYFHwnIngtrWvfL4cg/6G/bHK9zPILGQhjb3qgKCj8KFOK+rsRBmnnjSHRLlFm3bYQ/n067EiIKIHDlM6DCD7+POMSB142MM+xdUB/oJm0MITtxhVCdnoeVGOPynwoKPwoUOq47fBrT2YrBHcEcsS9ezGUDG3oSIgogHn0+GcinHkTXeVhOoUZ2FNf6km6XaTEhzD9KwEnfX1sYrtylCQo/ChTTnJ4D6hMf+r+rRe20CNfIiMlgthIiCiDBFi66B6TFY/hRD6CSE10qMbwiZQlZp62ZOoQ5neTXNBjH3+EICj8KFK60z/7c+7nbYVsCiMipkdpKWBXjEiIKIBJlM9iv0FUaFCsHVhQgty3JddiqoldEmz2uiN59+VFeGJjP1wgKPwoUXEUklWhRGELn8YfYa3NvR2SxlJgSIgogfydFerB0AxJiFb6dN+ptb093h5LA5b4Pby48g04C83IYg9i+CAo/ChTMZxmszGKbULAsXwlWKL1Gs3SG+BIiCiDiEmQePGYUf2DzeJso9VAHWsbzemEGBE85nFZNc6ljXhiF3q4ICj8KFEtMaciFnkmQGZx48sfWfm1V3NnKEiIKIPfMesG8373vwsatntzhX9INTRaJNbGUqP+n5xO5JMl6GLHQmwgKPwoUW71neBRMetEnc09gq5fJM1qgyzsSIgogDyxTwjFgyrjzdtjH8qDeXLJA0lT7U6qj82s1hENInfAYv/aCCAo/ChRp+8/UgtGG+w2PAkjoJWmDH9PDExIiCiD9zSBkafoB5btlLYRYrorx60aljIPoHpgkQ6V4uYcO5Rib3eEGCj8KFIJRJWqJFKvftwd98YHJt1UiOyQFEiIKILMGRlzDtKfvSiLlKq38YTn0OyTuPxztXn4oKoQ4EzqdGM/ExwYKPwoUHBC8nAUCp6cs/1wLOx+LLryLvcASIgogBrA3kW3Q/JHssoENSwigItPPPUETtMkfd1og/FrdLckYpP3FBgo/ChRnvzJBcC9YG1z/4tgkbMVWwmbZihIiCiBTCJK22HSFPqaBJM7J6FqvgAisceBxLUowNRGwwNhSABj5v68GCj8KFGTWpbiqm5WGHCce+safCzQ43SJxEiIKIPjk+eGZM0bVWMsWe/Z18euBx04vbuYQWMBU47I9X1lCGNjJ7wUKPwoUAH16mgz2CEHtIFZzlVhYj0CzpikSIgogaeeP5O4IRD76FfKTxteQXVWSlvjnPCmM2VK4sBFPj0MYqoPoBQo/ChQmvGJLhbyh2S+nOUG1WNeCgfNRnxIiCiBetMMglbob9CGi/Yvog9ixT0/a8+oC0HqJfWfsLYNKZxiWq+UFCj8KFGumv84cQrItv55BCBtU4znXKQbkEiIKIAey5B+I2sDRagPGa1UdltDSkMIINRn9w3rH9jZFgq0MGKPq1gUKPwoUmssICgxH6zd4oz0hVL9WxMVIVhYSIgogoj53L4Mo0LDTAHrGrT2Df3k6cJcIOF+12enBsDg57CsY1onRBQo/ChRNDz/pWKd4FfUYbAQ5zIuYuGBuZBIiCiA+OZz/FXn5Lll2ts2dkQxydTVBBAg30Lw5BtfiA1POlxj3rcgFCj8KFIukF15nk6XQrq1Qq19KIz3HcdGqEiIKIKYyNLzMT1j9EQWK7R6KgFjxpPmDIIoZXLH34msPrZycGOD/twUKPwoUeMtBmIFj812I8JUVfTs5Gk46KH8SIgogv18bFzEMsrtm4yC8kuR1/S24gB6zmP/Z6p9yJrhs7a0Y56SqBQo/ChSZyS0/J41fTyyLFGK2qY8/mlf2IxIiCiDw6u5ZDMu0E6yhW9y3tGt1Ind14MlFCzmzCIe30p7WLxjFko0FCj8KFKkJJAqZ/J/FOSjDQ/gSipxTqnFSEiIKILvQRGmYCJgEzy01ot7CWb/+RMh+omW/2j5BSm781ArxGNnY/gQKPwoUWtld5m1swCar9aM8OxrPTvIGeXgSIgogq1pvArMvD4ydy2tVGVVvkQp95q5VXVEhB3yLPbSc8OIY58vjBAo/ChQ5JXCjaaBt6gUQWDJRRNPqeP24YhIiCiCWylXAGzT5FuMIFvJNMcht2gRd7RUaAfXGX6JK6tlXhBirtdwECj8KFIy2KNjwIA9wSuy3vvnASy71tmwKEiIKIN464eay/3DRq6MUz6GY3/Xjm+FsFjDO7lhLBN0KkxfGGNLB0wQKPwoUsqYhce3f1vg/aTejpIU+Y7G89A8SIgogP/ienmMvllcrxKHFnQo+ZKMTfoOpY6NDU4s/MheFhdkY/u/OBAo/ChTDbyPta9cuff76AOyK4YMtuyLlyBIiCiBgRTDuHZY7zyuaiAuPfbxbRb1Y+rloq5YL2VusZNH0yhjp58QECj8KFKmn0A/5oLrq8kFmmqOvl2Y11RvbEiIKIMV66M716GxOCKOQb4zX1jwQj8sP2yt6eGwlhJtGNxnXGPfavAQKPwoU27Rc2F0LOBYJz3Ej3oVzaGVjgE8SIgoggofUfTD8WKfefsHrdWTyESfWx0nd5onaR9oDtnxyo0EYr+K7BAo/ChSelA9TIcnc/a6ut4RPirECo0RzUBIiCiBNmrNe/Ydx9QWbucJDvrH2INM0Z0mDEZ/4JVx1KECYhBin4bkECj8KFIcuIFgrf3bxr9m2+I8MOGZAvZojEiIKIIMc0N0cZCcFYxJO8klDSlBk0SCKrHc1PkMwmnbCibWxGLfItAQKPwoUQfJ1Wg7UQI/wMyO6yiw1UAPqzqMSIgogHiKsEqSt80MRNlHWtCD3PS872cqkoCK7fGkaUoBoAGMY4+SlBAo/ChRBXg6sd7mPwE5B57TaCrXYJksLLxIiCiDCqiBQf3jVSfaFUJ3Qi2SJgGBa0q0+kLPoC10kshQiexj2qZwEEj8KFIcuIFgrf3bxr9m2+I8MOGZAvZojEiIKIIMc0N0cZCcFYxJO8klDSlBk0SCKrHc1PkMwmnbCibWxGLfItAQYr5DD+QMaK29zbW8xeHEybmo0ZXkzdWhzcGV2dTgzcmxobG41cXc1cHBoc3J6eHVxeTUKphUKJy9pYmMuY29yZS5jaGFubmVsLnYxLk1zZ0Fja25vd2xlZGdlbWVudBL6FAr6CAi5nAISCHRyYW5zZmVyGgxjaGFubmVsLTEyNzkiCHRyYW5zZmVyKgljaGFubmVsLTMyugh7ImFtb3VudCI6IjIwMDQiLCJkZW5vbSI6InRyYW5zZmVyL2NoYW5uZWwtMTQzL2VyYzIwL3RldGhlci91c2R0IiwibWVtbyI6IntcIndhc21cIjp7XCJjb250cmFjdFwiOlwiY2VudGF1cmkxOWR3N3c1Y200OGFlcXdzenZhOGt4bW5mbmZ0N3dwNHh0NHM3M2tzeWhkeWE3MDRyM2NkcTM4OXN6cVwiLFwibXNnXCI6e1wibWVzc2FnZV9ob29rXCI6e1wiZnJvbV9uZXR3b3JrX2lkXCI6MyxcInBhY2tldFwiOntcImFzc2V0c1wiOltbXCIxNTg0NTYzMjUwMjg1Mjg2NzUxODcwODc5MDA2ODFcIixcIjIwMDRcIl1dLFwiZXhlY3V0b3JcIjpcIjZmNzM2ZDZmMzE2MTZlNzMzMDc1NmMzMzMzMzI2ZTZiMzk3OTY2MzM2ZDY4Mzc3MzY2MzczMjM1N2E3Mjc2NzA2YzZjNzU2YTMzNzI2NTY0NjUzODcxMzc2NDcwMzQ3NTcxNjc3OTM1NzQ2NDZlNzg3MTc4NzgzNDZlNjQ3YVwiLFwicHJvZ3JhbVwiOntcImluc3RydWN0aW9uc1wiOlt7XCJ0cmFuc2ZlclwiOntcImFzc2V0c1wiOltbXCIxNTg0NTYzMjUwMjg1Mjg2NzUxODcwODc5MDA2ODFcIix7XCJzbG9wZVwiOlwiMTAwMDAwMDAwMDAwMDAwMDAwMFwifV1dLFwidG9cIjp7XCJhY2NvdW50XCI6XCJjZW50YXVyaTE0amN0Y3dzM3E5ajRqazU3eWd3NHBhMjBqdWozenJqNDN1eTRobFwifX19XSxcInRhZ1wiOlwiZDdjOGEyOGI3MGFjYmQ5YzNlZmFmZjQ3MGU1N2FmZmRcIn0sXCJzYWx0XCI6XCJkN2M4YTI4YjcwYWNiZDljM2VmYWZmNDcwZTU3YWZmZFwiLFwidXNlcl9vcmlnaW5cIjp7XCJuZXR3b3JrX2lkXCI6MixcInVzZXJfaWRcIjpcIjYzNjU2ZTc0NjE3NTcyNjkzMTY2MzA2MTM4NmQ2NDM2NzU2YjcyNjc2ZDcwNzk3MjcwNmE3NjM5NzA2ZDM1NzgzOTY0MzQ3Nzc4Mzc2ZDc1NmQ2MzY0NzIzMzcxNmE2MzM4NmQ2YzY3NzU3MTczNzM2YTc5Mzc3ODcxMzQ2NTY1NmU2ZTZiXCJ9fX19fX0iLCJyZWNlaXZlciI6ImNlbnRhdXJpMTlkdzd3NWNtNDhhZXF3c3p2YThreG1uZm5mdDd3cDR4dDRzNzNrc3loZHlhNzA0cjNjZHEzODlzenEiLCJzZW5kZXIiOiJvc21vMTVycXV4ZzN6dzh0Y2dqODJoa3oycXp5NGY2OW56enQ1eWwycWxncWt3Nmw5ZHJsaGZ2Y3NyMnljOHkifToAQIbE/ceXkLLlFxJheyJyZXN1bHQiOiJleUpqYjI1MGNtRmpkRjl5WlhOMWJIUWlPaUpCUVQwOUlpd2lhV0pqWDJGamF5STZJbVY1U25sYVdFNHhZa2hSYVU5cFNrSlZWREE1U1c0d1BTSjkifRrhCgrbCArYCAo2YWNrcy9wb3J0cy90cmFuc2Zlci9jaGFubmVscy9jaGFubmVsLTMvc2VxdWVuY2VzLzM2NDA5EiD1Z9CiuHoRnsUQGE596TM4We9R7OvxUipLrW5ucspFIxoOCAEYASABKgYAAt6f2wQiLAgBEigCBN6f2wQgA1KJUG5dcJaWcHkWjJO8f6vJ91aNhDRX6d67WDTJtbMgIiwIARIoBAjen9sEICDjj2tcSc5ebj2wH9yT7GXetpJPPJdfC+tnJc3a+RulICIsCAESKAYM3p/bBCDnZp7PDqd9wVdim1o35de37G9R2YCPpqlcEL6DITOdvyAiLAgBEigIFN6f2wQgGi2qzOBl3WUpTKaXPP/nQkQDyvbd4LxDMBvFal7n8GMgIiwIARIoCi7en9sEIGSJZ6BtB4YBDfUiTOPqlwPpZi777MSWlyTSHKLT4p5GICIsCAESKAxE3p/bBCA4aNKr91K/Ku8yJmisI5rFp7bHusf0q3C8J8bQbFpxEyAiLwgBEggOnAHen9sEIBohIE+nBqSvhdPYK9kMBB+3ZKQ9uzagBKVBjy6m4oHTtdfzIi8IARIIENwB3p/bBCAaISCgJ4VwQG87dbNmQnResdayCQEareIuA/jiIyIgsNxlkCItCAESKRL2At6f2wQgWSzFyyUcYk6S6g+W1/C8hNLEx309LoCidQ3jNwb1UH4gIi8IARIIFMgG3p/bBCAaISAXptK6MFa1rY+bBdvWmT7epM5ZjTVvdWc631l4LKwj4SItCAESKRb6C96f2wQgowOmWuTWOEhxRS1oqNJ4jizgGim0QZvYfr1/msen5dkgIi8IARIIGMQS3p/bBCAaISBS61V15kCJV1dqGEV1GSIiFpOx0reQiGeOGbfEd5DqMyIvCAESCBriIN6f2wQgGiEgKpRcxZFsuT/4hhTvDRF2NMAkSFBi71Ak+uyCVKVjtX4iLwgBEggc2jjen9sEIBohIB9UzFtPZf83aU5aaoLV2dpLDWU/Cuft/yV5muUkkTKnIi4IARIqHr6PAd6f2wQg3zq1+RbPKt9MSh0nKSIqhJrpQEQ/Vk20JKC6hXdOVaYgIi4IARIqIPqIAt6f2wQgyJS+/+6pE8zdm2afq6S8o85qAh+l4rxoVC2f+M7fJ0AgIi4IARIqIoy+BN6f2wQgel9C5agNCZcy22rK97g0l/MBT3CWzWxEAbAgMt+VtLYgIjAIARIJJJKDBt6f2wQgGiEg+RhRBolx2Vl2klDc/0+C6NlKeQdm7XMJB9R8d0In6KIiLggBEiomtoMK3p/bBCAmr+8eTNhBWgkOUrWLzSx69dbZvfvm9UOFGFj/6s4kyyAiMAgBEgko5PUp3p/bBCAaISB/YY64AGN9OtQNhIdDUY4108ydD9mflyoaSBBLbBifSCIwCAESCSzs+HXen9sEIBohIA48ctmff4/73eQAJ0+g8aCJd0PuA7Z9GTHT40WvfWmMCoACCv0BCgNpYmMSIILmEEQV0GouqYUF96SpDDVeY4m+KSrU2m/iViIuBuc0GgkIARgBIAEqAQAiJwgBEgEBGiAw/AEzTt64FfpSRZkFbLiBCui+iUgzI4dS7i3u/IbGDSInCAESAQEaICnaQIUXofOmx3w8lCMNv72LVnyy9Vx1zCTjO2AbS9B9IicIARIBARogjDayd623be8k1aZ9G9fFPmLVaRvueTryaMIJl0EiN5EiJwgBEgEBGiApLxZCchpi9wYCpOvI0DTQzvDRTf8PPMf5wUP7elwBRCIlCAESIQHx0biYTUyxSnVgFCugTNuUs/RpBzt9Ncfmuzzc828RwCIHCAEQ8M+tAiorb3NtbzF4cTJuajRleTN1aHNwZXZ1ODNybGhsbjVxdzVwcGhzcnp4dXF5NRJVcmVsYXllZCBieSBOb3Rpb25hbC5WZW50dXJlcyB8IGhlcm1lcyAxLjguMiswNmRmYmFmIChodHRwczovL2hlcm1lcy5pbmZvcm1hbC5zeXN0ZW1zKRJoClEKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECkmXu6D/mY16Z1kz6XsEboGVyZfpVLuS5PTOkn95iRHUSBAoCCAEYoDESEwoNCgV1b3NtbxIEMTI0MRDCpB4aQGdtBaf3cez1fOlvutEYwihaV8IUFr/j0ZtUTGzdoFT/bE1RG2FZhX8jGLgF23yFbz5GSNDn1MiK27k/ltEuFA8=", + "CpsBCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKK29zbW8xMDhlZHU0aDlnN3RjcDJ2cGdzcjR3anF1N2p6dGF1aDZobGp1enkSK29zbW8xZTl3a3UwYWhrd2ZhbmRnMHc4MjluM2F2NWNudHF6NWtkbmZoMmwaEAoFdW9zbW8SBzMzOTkxNTQSCjE0MzA1MTAwNDgSZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAxwEwdOylJD8I+sJinvKUXDCOr3jc5jrf9Tb8ArxBF8XEgQKAgh/GAMSEgoMCgV1b3NtbxIDMjMzEIHeBBpABn1i5Kx4PXT9Bxz3ugSegILQV/s6K1AlSy6WQFbA6359azg2uKXuLAiujdCyv+x4dXfiVGwo68YKUkEGkz+TVw==" + ] + }, + "evidence": { + "evidence": [ + { + "type": "tendermint/DuplicateVoteEvidence", + "value": { + "vote_a": { + "type": 1, + "height": "15317184", + "round": 0, + "block_id": { + "hash": "2F8994767F3DA1372DEE38C45889329FE223CB10107D5F9CB12B511034E3886E", + "parts": { + "total": 3, + "hash": "5986E916CEB8A9D36B9BD73E410271E6ABA91ACDCDC4DC251E321824B004D433" + } + }, + "timestamp": "2024-04-29T14:54:38.102040075Z", + "validator_address": "2D159B72D40C1C1DADDF24D2511200001B74ED84", + "validator_index": 105, + "signature": "cpUVsZ45yIRjiI0hfH67wqIifEZpZTwPpmKjU05P6DUhs3Uo+wKVXKSjBsd2puNiNtGrtZ1EO741IE8hKS1FAw==", + "extension": null, + "extension_signature": null + }, + "vote_b": { + "type": 1, + "height": "15317184", + "round": 0, + "block_id": { + "hash": "FE84EB267D13053EAFAA221CBB3B2354E0C87729F4A141D7E70BEFE4585AEF7F", + "parts": { + "total": 3, + "hash": "F104A0A55835F01CE7C98245FCC32BF7799F3E8708BAE729C51457F4141DCB73" + } + }, + "timestamp": "2024-04-29T14:54:38.098135152Z", + "validator_address": "2D159B72D40C1C1DADDF24D2511200001B74ED84", + "validator_index": 105, + "signature": "vwPLzk/EfYtfoLs7IHEwFxOIc7rKjnwPGXW06+a0j7Tpdk2zyxVZKVKjnRq30XuEut/g+32QZaDVg+v6Qud4AA==", + "extension": null, + "extension_signature": null + }, + "TotalVotingPower": "367532352", + "ValidatorPower": "737515", + "Timestamp": "2024-04-29T14:54:35.740273937Z" + } + } + ] + }, + "last_commit": { + "height": "15317184", + "round": 0, + "block_id": { + "hash": "FE84EB267D13053EAFAA221CBB3B2354E0C87729F4A141D7E70BEFE4585AEF7F", + "parts": { + "total": 3, + "hash": "F104A0A55835F01CE7C98245FCC32BF7799F3E8708BAE729C51457F4141DCB73" + } + }, + "signatures": [ + { + "block_id_flag": 2, + "validator_address": "CB5A63B91E8F4EE8DB935942CBE25724636479E0", + "timestamp": "2024-04-29T14:54:38.821378833Z", + "signature": "666Qvawt5E3wmdR+MTYQ3DuMcaH2GJh60f+uPi/7/iwK4+yiIQ7cUFps1rJTyH7C1Fe820bIZ31pHqx9Yk/CDw==" + }, + { + "block_id_flag": 2, + "validator_address": "1F7249F418B90714BF52797336B771B5AD467533", + "timestamp": "2024-04-29T14:54:38.829813884Z", + "signature": "HePJYzUE8nkMTsu0weuQdxsYkX5PNbwpY5ABZdaw+luKuuFcYfU/waQ/3gWV218I8pdL+12AQ65tEqkaWn6BAA==" + }, + { + "block_id_flag": 2, + "validator_address": "E08FBA0FE999707D1496BAAB743EAB27784DC1C5", + "timestamp": "2024-04-29T14:54:38.742390896Z", + "signature": "36BI4je3eY6mJKqiVzXwSWu488HeUqU+H0Wpim6q7Xz1Fd57+pTnJ6IKioe4hp+okFHJQL/LK/cM6zTnQMbDCg==" + }, + { + "block_id_flag": 2, + "validator_address": "765550228CF309BDD33F3F5E768350BA3D69C3B1", + "timestamp": "2024-04-29T14:54:38.814084977Z", + "signature": "IBgMywaqZIZhYVzowqS3yHutdh+putr/CdVrHN/U78fgeJTcmhyRhwp0NN9NrpAHEVejr5ABjfPUTw6iBW80CQ==" + }, + { + "block_id_flag": 2, + "validator_address": "40CC723314B6EBB93B49FBD9D330EEC8B4641CAB", + "timestamp": "2024-04-29T14:54:39.384784726Z", + "signature": "+Uw5bkp0Qlnnn3czabNMY0Wxo+g/Q90mGBiIl6QcYnNd32Gg/X6dEGNUOZEaSP6fVSufpOao43eaMnJJjva/Aw==" + }, + { + "block_id_flag": 2, + "validator_address": "9D0281786872D3BBE53C58FBECA118D86FA82177", + "timestamp": "2024-04-29T14:54:38.847790745Z", + "signature": "TcBJ+AGS9RcrUtlnsxz1Z4ZO/7zSyMUI5MgPyOhX+M6jezinV7fj0BW6mTMJS8UD7oRrWVV+rzFc+15h98MuCA==" + }, + { + "block_id_flag": 2, + "validator_address": "D82343FCD5A74969C0B48457D70E8D55D8F6B801", + "timestamp": "2024-04-29T14:54:38.798664499Z", + "signature": "XeYdeZRkY9siRDVqHel5gJM9zLusInxIw3PMZvS5zXvxgD+2N1LhRL4fFnLQw1ywOfj5UhQCagGKStIu7sn7Ag==" + }, + { + "block_id_flag": 2, + "validator_address": "66B69666EBF776E7EBCBE197ABA466A712E27076", + "timestamp": "2024-04-29T14:54:38.831569254Z", + "signature": "9Ir/0Bn6RVHXRkktjZ0SGTVF87w3hiIXJLNY6ys4bAKMi1FDIsx5AZLFLUiJsNjsUuofU5l3rXBUomK536TuDg==" + }, + { + "block_id_flag": 2, + "validator_address": "A16E480524D636B2DA2AD18483327C2E10A5E8A0", + "timestamp": "2024-04-29T14:54:38.805294553Z", + "signature": "g5MxVuM1dJXiQRhyw3rjd+ONs7RztwSxjbXtQw2U/BsszN1Uo3FfIKzz2ZaFA6cMuOeshfpMbVdmE4hml6ThDA==" + }, + { + "block_id_flag": 2, + "validator_address": "72B1489EFB57A680577A838A5BAAEBE162A7C802", + "timestamp": "2024-04-29T14:54:38.781286481Z", + "signature": "hm4V0uvcJCqjT6aE1/CDfA5fVTndwsc20YCVv1uNmrVQeGlNCMhvSsUPWxDxT4WPiiSNbnZGl3FxTGSl4qGnBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "51D7D05A659208A6576190AEBBE8F07603851515", + "timestamp": "2024-04-29T14:54:38.736177678Z", + "signature": "grhvvJuLIWzdwkQzpXX/dan+Uh8S9D6m2OfAsj+gLPC/t8QypMOAwJ7HjcYnQhlnm2UemcgvcUjpl9plZBGPAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "1B002B6EBEB8653C721301B1B56472B1B4DE7247", + "timestamp": "2024-04-29T14:54:38.821712544Z", + "signature": "4GotFkiaBv4o4CdMKEo/ZmY8qtTSa6vHWEdXCCwy8WvW6w49VgoG5EyOSJzkymIHipNmUxpEGUe6fgHxNnDYBg==" + }, + { + "block_id_flag": 1, + "validator_address": "", + "timestamp": "0001-01-01T00:00:00Z", + "signature": null + }, + { + "block_id_flag": 2, + "validator_address": "71DF8D9879C20563A4E2ABEDA95CD1FC57DBF6AA", + "timestamp": "2024-04-29T14:54:38.76399027Z", + "signature": "I3hzTO5HebX0teBicaCseZtH6U40WwYnjrhcPR2BjGLZ0n/5QdI7bOZ+bzVgCMv/FpKTTSV4gc67Y7GGvm/QCg==" + }, + { + "block_id_flag": 2, + "validator_address": "131FC79E7A012D9E7EEF21DE3BA5D5033FCDBC1F", + "timestamp": "2024-04-29T14:54:38.844436588Z", + "signature": "DqOd/D/9KJ7Jh8CY2Hc/R00+UMYVtit9Y/0nFT6qD0tGkE8+0+vpgd4rLLkaBUhQyg2YqF4Dv/EZPUBENb/KDg==" + }, + { + "block_id_flag": 2, + "validator_address": "39327692C258A57970EF53F0AA4D3C00F95988B8", + "timestamp": "2024-04-29T14:54:38.735514199Z", + "signature": "BNt8gD6uJCBemNZeXjUlmjBppR+QuM1AsizRP8BxXGL6KZE0phd41D+4naEi03nj2DCKEj/UHkrb1OfxJMJwDg==" + }, + { + "block_id_flag": 2, + "validator_address": "7341E970B9B3EFF82B2060D3469FC50D7AF04146", + "timestamp": "2024-04-29T14:54:38.89507441Z", + "signature": "IjQgVPcRbgBgiVs5lVuH4G6BaLjA5jkRA6R6xc/h+HI0yAK+dYEqz3OCSKEd3W4LH+e/gmxafjE2RjOO5HnkCg==" + }, + { + "block_id_flag": 2, + "validator_address": "03C016AB7EC32D9F8D77AFDB191FBF53EA08D917", + "timestamp": "2024-04-29T14:54:38.867968508Z", + "signature": "ZW4KCWIkq1Mr8UCXC8FnHh2TXdaTsgI3wkWmeJ+Uge2w4UsIiGUGLfiCMnHOejbkoBsR90OiFgIBFwlx16HNBA==" + }, + { + "block_id_flag": 2, + "validator_address": "2022FE8CC49E48630C76160E11A880459219D244", + "timestamp": "2024-04-29T14:54:38.768952465Z", + "signature": "Y6IBZMs7M9x1avSjRs2V26YFXqdC+8qJg4ImEJ/cTVHvxJ4JHxUM0KmJ/bmzvSZVLlYGXX8KnFjbwHFeRaF3CA==" + }, + { + "block_id_flag": 2, + "validator_address": "9E7CAE009EFFF4D163F3FB8781A07B25C2B10B32", + "timestamp": "2024-04-29T14:54:38.756127698Z", + "signature": "fq58KsK9vmNRlITHPv+2jQaICPdcalQpLwIwJzcmnKSTppG48qWlsL4klSWd9bbpa1dFPmcKQLBessZlhlZMDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "99063B919404B6950A79A6A31E370378FE07020D", + "timestamp": "2024-04-29T14:54:38.757105631Z", + "signature": "pj1f+ZaBabK8J9PL53tQySgIsRsJMGP4dY0q+1vlDxMG3GIgWGJJyMQLlLkJqt3F4thwccynmwSURYPOY6Z0DA==" + }, + { + "block_id_flag": 2, + "validator_address": "16A169951A878247DBE258FDDC71638F6606D156", + "timestamp": "2024-04-29T14:54:38.956619036Z", + "signature": "2tqQaJT66l49k3wyZtFIzCLEObHjH60nZy41AjB692ENRnhsizSfHSw2l23eFBbSdL5j6W9rCB8H57vGUL/zBA==" + }, + { + "block_id_flag": 2, + "validator_address": "768A82700E3046E6DAF849664579E649597CC1B4", + "timestamp": "2024-04-29T14:54:38.746480065Z", + "signature": "xBFoRKA41sH1b8i5Xx36WyQ+tlvd26YIgY0G6nxOQuA5hLZAvPQs2qKkG1TigreJebaHJfo5E04RSw5Hvg92BQ==" + }, + { + "block_id_flag": 2, + "validator_address": "7EDB006522610C58283E30644A14F27BCC0D32ED", + "timestamp": "2024-04-29T14:54:39.067946747Z", + "signature": "4CWSezCAx0AvOb++HUCfrUiJ9Q4KLjr1hJceh5BgQkoSG0ITkKq7/dscywCutm0pJfOsxdDxpW9H9gvhzMpuBg==" + }, + { + "block_id_flag": 2, + "validator_address": "04C83AA20F7563BBCBCF6AA150EF6B0C81808DAA", + "timestamp": "2024-04-29T14:54:38.879425524Z", + "signature": "O9FU+xNeIuj9ROQV+pGmR/c2pjSGs0V2MZccE+YhcLQ9ZHjiLnf+tg3mhFrtUE+HbA6lc/THLmPVp2LIav3DCg==" + }, + { + "block_id_flag": 2, + "validator_address": "A06B5B682B425AD206A35CAF246FD70DD098E506", + "timestamp": "2024-04-29T14:54:38.769990365Z", + "signature": "bQXg42v26HO0v6wqzrCv88puLGY/67cJ51BloSst+p2GyjPfkNfFgKfFYjI9oc5TNILaVAj2O5tts+73IZXHCw==" + }, + { + "block_id_flag": 2, + "validator_address": "97AFE45395B74E784C88D45E5CCA2995019FAE08", + "timestamp": "2024-04-29T14:54:38.760463467Z", + "signature": "4ajH039QMK+QhrtYPCaeB6NB0epRtrMpIjXiOtP0H/iEyfm7CBJslhoVpRuVM4/Bnpm569PHK923slSfNrMGBA==" + }, + { + "block_id_flag": 2, + "validator_address": "6239A498C22DF3EC3FB0CA2F96D15535F6F3387A", + "timestamp": "2024-04-29T14:54:38.776305699Z", + "signature": "dnInxHsyMeIs8ECy6PPo7yTVJSjTSfCorEvtpLpW+VganVQNrQXBqQXJIlF3SI3zkK1eFIqMDsHQHtD1Dh7ICA==" + }, + { + "block_id_flag": 2, + "validator_address": "F3F55DA24BB47DA60B0FB71EC1A9C9274BCEEDB2", + "timestamp": "2024-04-29T14:54:38.75022013Z", + "signature": "zzrhvBuYzkkngb6oTRhQjp+SqFcs7yaNMLIAE3fLcWRNhIlQxBhs82iVTs6y2zhah8m0c1CjGRzcZMnrZE/fCA==" + }, + { + "block_id_flag": 2, + "validator_address": "5F999A4BE254869925A7F2FEA04D7B3B836CFF0B", + "timestamp": "2024-04-29T14:54:38.869636917Z", + "signature": "bH53UBmWDNkAfUxZx9qxC+oYOe4B2m63uK6lJdhfBoHq67TQlTPW3ZKwBNTI/RX+DYWaulsXLwooI+GHVN2nDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "9CBEC8CBD4ED3AAD4BB2B0346EFC86A6C41F9160", + "timestamp": "2024-04-29T14:54:38.811449015Z", + "signature": "zVL1mS6WlpM5eo7o0CGDHXqXJfpPxvPqAs5uAxXGapEDEeRhMY2ZtK+wlapCtPN/y+Ooq95EIwZyjK+/3uXUDA==" + }, + { + "block_id_flag": 2, + "validator_address": "AF195943E44FE1D6250076B8BC1910EABC85F1F2", + "timestamp": "2024-04-29T14:54:38.746404918Z", + "signature": "PJsBFjrsBqioqVLuCzEsqyDzvheRhb9VcnD5D41Kty1jXZBASiX3QRK1iE47nXAXKajIWkdqDQNMg0cwMhxEDw==" + }, + { + "block_id_flag": 2, + "validator_address": "DBCD765DB2640631946C1393BA255876C76DA38E", + "timestamp": "2024-04-29T14:54:38.83260873Z", + "signature": "sV+bpHKQ+GJWwzeSZaJ/lVzsPsrs04672dNUXykq1e/Mqf4GM1CHQfq8AFYxevl9HQpDAJ/0aFu0TRF6r1NzCw==" + }, + { + "block_id_flag": 2, + "validator_address": "6912E0BA38CD00C9F2FC9E71E971ECED507B42FD", + "timestamp": "2024-04-29T14:54:38.758415826Z", + "signature": "PN6X+imrsQcMO9Pi+29BGEkYQ/uWffnDvLm478DbS45ogPz4sCJwtBnaU4Q2uo2gt+u+DEY6bzfIIgnK2uIxAg==" + }, + { + "block_id_flag": 2, + "validator_address": "8B1D5676F4C0C871A0C7864850D451D6A8AC8E3B", + "timestamp": "2024-04-29T14:54:38.862157543Z", + "signature": "vf7eSVyyLXqNVHqEsD+LuBGh1Vz6tbogtI59fSuJLN+VZ0Cee7xwtj64/HueNHVAfGgF9wpSpDl0MfmAlk7vCw==" + }, + { + "block_id_flag": 2, + "validator_address": "40C48839CD487D8A13D65955B7FC6C4F560D8F72", + "timestamp": "2024-04-29T14:54:38.828798497Z", + "signature": "QWSj+dHzeUCoPYxg4i8g/m/Y31NaFKc16jfP+iP/GwSsi4KRWPO2hfq7v410CYELD8LBPcGPsmtGIovZ8nmTCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "8E0545B1222E7B5C85CE69EDC78F280CB2B79D18", + "timestamp": "2024-04-29T14:54:38.808315193Z", + "signature": "HH1Q3I2R6yw8XN2BBPbVlhPWu7XSR0cnmyvLvO7laD+6AwVtGXdf3RO8hCH3OwWAPRdxW5prDOQy5hZOuUH+Bw==" + }, + { + "block_id_flag": 2, + "validator_address": "C02F531D9BBBA4907511EF2680421CE714A11E3B", + "timestamp": "2024-04-29T14:54:38.974510262Z", + "signature": "OXP1w1At4mxkn89TpzU4z1Sy+Pw/TuW00qjf69K00iakfdVYiGdRZ9phUJWVeYlBxApX8Vy5bEZtV+IoPuiKAg==" + }, + { + "block_id_flag": 2, + "validator_address": "B0B35FED40DAA5FF9D4BC685C75925187F622119", + "timestamp": "2024-04-29T14:54:38.762780341Z", + "signature": "pavfRcM8qw+pbZJHP/+TXsVdpjNDZpPBihHzRoPhRyGwrIKO63Als4U8TDMDVBY56J46nbtHAUQbbamslnYtCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "138FD9AB7ABE0BAED14CA7D41D885B78052A4AA1", + "timestamp": "2024-04-29T14:54:38.856850383Z", + "signature": "ZxeHOOkaW8AGomUVxlqMZuz/DwDwWYmv6nzNG3RDjgs++wc2leZIWpWkMyeIMKQd0vBa4rEmjG2pUtNPC9+sAA==" + }, + { + "block_id_flag": 1, + "validator_address": "", + "timestamp": "0001-01-01T00:00:00Z", + "signature": null + }, + { + "block_id_flag": 2, + "validator_address": "95B002DE67707313D123D06492F1A7A58478E546", + "timestamp": "2024-04-29T14:54:38.824305339Z", + "signature": "wo6Wb7ADF20tQA5dN1uu8jJLmQ/e/IKQEbW3GC9Ma2mU6HIzQWRIGSxfBVZdvXTSd/VTHCNbC8wwQLb4DtGIBg==" + }, + { + "block_id_flag": 2, + "validator_address": "F9A968A405FB0429410AE5165E5F2193271E678A", + "timestamp": "2024-04-29T14:54:38.826154893Z", + "signature": "e7QtYGrlBJRvxa0VjqjzpTSbzvxqljsyN3W9HGb+pNh5ym4W/UDakq6OZ195iw2X90LBd4aM8Hk8eejhjKUGAw==" + }, + { + "block_id_flag": 2, + "validator_address": "5E809E91EAB69D385784D191140E9C8CF6DD1037", + "timestamp": "2024-04-29T14:54:39.107315346Z", + "signature": "XDL5UTXk8AJbflrS9mKbEaN1zgQwbN8Du2p2eQGS+X7YIO4u404Pxn8BiD5vV2uouwK5y6jDhVHTC9DsJdDxDg==" + }, + { + "block_id_flag": 2, + "validator_address": "7EF244868C304AA5B34889372E2DF874AFD635CD", + "timestamp": "2024-04-29T14:54:38.925577789Z", + "signature": "CRjVsvFM+gIFMCElg5P5RDxuj1cmWq4r7sxYlTbUn1VAnZoU0j77OqOdM3Udl8LtmwSbYXUcCsjEUeOROdMNDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "E191E654D06B9F721568BB945B2EB51DDC1C8FDC", + "timestamp": "2024-04-29T14:54:38.738608243Z", + "signature": "KbWEEf2uPjqUx41rnAWN3hoHPYfNvtpryo7s6AO4hWBeCENF2D1dp2IgCktyEpPuIxq8P76x07N98f6T9G/lCA==" + }, + { + "block_id_flag": 2, + "validator_address": "7E0ED7689B65C345D1C817C5B0332FD132DE5875", + "timestamp": "2024-04-29T14:54:38.811400287Z", + "signature": "Oh5n3gl7U/Lp5nbrbPjGqojCpasNzNPfNHyI9iCgQV7NeA4IDAGm3bxFXRNTWCT0fq6vWeKEYBZW4IuqSg73DA==" + }, + { + "block_id_flag": 2, + "validator_address": "9F8EC2EF581CE25630C819F19B5484039E748D1A", + "timestamp": "2024-04-29T14:54:38.803252634Z", + "signature": "quUVG2NXXazLh8QFIcyWJ8U/j+Pcy/CW+vPg+hJavZAU5Gh7EiizYhy121YUxQRpd7O6ZsTVco+cZR2Jh4Q7BA==" + }, + { + "block_id_flag": 2, + "validator_address": "CA0F2A7121F86D3B6D91349730155B9A5A31C554", + "timestamp": "2024-04-29T14:54:52.752902374Z", + "signature": "YqJSrMT7+DJiPOzBIDwdksUf3afCuvs6CwETi9vZVfcZwMcAvb96wySNHq5sYoV22nZWO9egObOcEjvkGZxVAg==" + }, + { + "block_id_flag": 2, + "validator_address": "966FD89B1DB51535F2D898CF9B0F14DA374EFB96", + "timestamp": "2024-04-29T14:54:38.834191148Z", + "signature": "Fy5UeQkk+WBYbmHbjDplf5N8NAoRunF/bq5Ph10EdAT88Cjp01OCh7NteQh0+s3csL/NXIk5iNJ52jFGkOgXCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "76F706AE73A8251652BC72CB801E4294E2135AFB", + "timestamp": "2024-04-29T14:54:38.880044349Z", + "signature": "PC5QIig5TPbF/QPIs7OOrILZmMIFpzzEsCMDmAtdwr32CAi2OmG5/aLTReyXFdQgcYT97uNTfIApMANu4AixBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "06F45C36FCB957E55D923A6D4E905C2D715115AD", + "timestamp": "2024-04-29T14:54:38.839523573Z", + "signature": "Im2fFC0PIL81zRhQ1niQ3sJLv+e7d/zqwV2T2ip3d2+1TTKRgRm+1fI7OZNKmgZoOTyrLNgYGgbAZ0b4CgQBDA==" + }, + { + "block_id_flag": 2, + "validator_address": "E12CEF3871B9595EF15401EED2466E9310E4816B", + "timestamp": "2024-04-29T14:54:38.781144645Z", + "signature": "9HCsafQLqhK8x8Q63n0eiVGCJi5/Do80BJzmKlZAXHthQwTOIH7NCSg93r8NaKzp4D52lkjqDjnkN0wa0KcJAw==" + }, + { + "block_id_flag": 2, + "validator_address": "7D53D76F2DB86BE30A9B26CADEA69078531AB9BB", + "timestamp": "2024-04-29T14:54:38.758226893Z", + "signature": "3ceXgAFuaZD2z1XAxF/ZN8+/OFpcghqrOH7/gpnwGDo360If45DHKfmHp2DdmS1AoTd5+Z67i3+tfCBZVtxSBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "20EFE186DA91A00AC7F042CD6CB6A1E882C583C7", + "timestamp": "2024-04-29T14:54:38.925494733Z", + "signature": "0HcNf1/RUhWl2fX8AdUFpzhIk5lYfF2cMABnqhIS1gVo9OwkCjoITQRpKTZETkb9Ap7/ITLko7HfKREm7zvhBA==" + }, + { + "block_id_flag": 2, + "validator_address": "CEFE7D654B523DEA2A9ED718A591126C74171689", + "timestamp": "2024-04-29T14:54:38.914450944Z", + "signature": "ldgcwQGDzLUzHGtmccdvOJKSKUMikthGdVqyGQvU8HiXIouKczvvCR6i0YsB6EvGW84ZwvmODXcQQ/zZi4VvBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "D8A6C54C54A236D4843BA566520BA03F60F09E35", + "timestamp": "2024-04-29T14:54:38.772211059Z", + "signature": "tvyjmb3x9SgQXOUY+S1N6MzZpJqN6DAzTigscTVogJSXO1MJsGJhdc8wLSVskbSu1fFX+PPKsUmb5Afe4leNAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "17386B308EF9670CDD412FB2F3C09C5B875FB8B8", + "timestamp": "2024-04-29T14:54:38.881261736Z", + "signature": "kQ0sKP8SxoGFXFdIw5gGU86zlovJWAq59aaytGA0klJLJnsEh/HWv5YupyhD12rbO483tw/YVUvCUO1hrkn0Cg==" + }, + { + "block_id_flag": 2, + "validator_address": "E03B985E6C8905E184D88C995CC632AA278981EB", + "timestamp": "2024-04-29T14:54:38.831308474Z", + "signature": "6dmOMZiBglTL+BZfeo9Jj4acP7BIMuliyLlqpYPqHiVP1NlO+YruifWJkSwcn1IUS1gCS9mrYpZHhikx8ZexAg==" + }, + { + "block_id_flag": 2, + "validator_address": "C9E615289D1D92E50292C3B0BD8358D9B2E40290", + "timestamp": "2024-04-29T14:54:38.757215164Z", + "signature": "gFghmp2RNx4Wjf8f0w+hr2PJAAaoktAmglJ9krXMcQvYrYFiHRAqy9SJRSR6IW2o2EpYHBjHdquWjei364wlCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "F6C3F7872B046DA7198905E6CB58C1B775B48BEA", + "timestamp": "2024-04-29T14:54:38.785752572Z", + "signature": "cDSJ6GRAZk8Q8r/e1HBDCyxt2dpmbjjLpSYylYrwsBoKOZO2A4jdlEnJp95dSVQiqfOuoNyDlloiXlIjtHW7BQ==" + }, + { + "block_id_flag": 2, + "validator_address": "04446DA0BCC4310003F97B1BED07AB2ABEC6FEA7", + "timestamp": "2024-04-29T14:54:38.886053653Z", + "signature": "j5GuF+n+Y2l6xvDIAB63fy5mhVyRWn6+v5VUDFfygoGnf0rav6W7RObPSHwJffnlRT2+48GwQYn2WUWAxK2MCw==" + }, + { + "block_id_flag": 2, + "validator_address": "46DEA137CFB10BC419B2577AA9A58718680E18BA", + "timestamp": "2024-04-29T14:54:38.868260718Z", + "signature": "DwoyYkBQwQpS/xDuU9103li434Jo2n+Ifo/BlOsrfRXiUWxvGYFU/S8PogiPW72nNnote5v1NAuBBQhLKRzVDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "F194DD4A8AD83323C3E9C2A93DB25F049621C7B4", + "timestamp": "2024-04-29T14:54:38.995796337Z", + "signature": "9FIy9JEWFu5d/n4xoIDjsl8S0lvN3E5iPWuT7MG5EPukCWX9qJb6fsDy2meJzoK6sxCrvh1sQqERv0SqQORWBA==" + }, + { + "block_id_flag": 2, + "validator_address": "692174B3FFBBA80394A94DC92665DC0144FBA837", + "timestamp": "2024-04-29T14:54:38.730595835Z", + "signature": "AylLh7nigY3AXCj2ep6QfN/+zhnYs1f1WmFbWdSIdWBo2RUrLk9P2FQnT4/8lC3ScttiW0aa/WpvFY9srWo8Dg==" + }, + { + "block_id_flag": 2, + "validator_address": "8445CF55CB51278E63B2131ADB65A81DC2389D8E", + "timestamp": "2024-04-29T14:54:38.789337822Z", + "signature": "bnPYm1ucmUipELZHnD1cpcHoDgaYBwUa57bRqlZ0Jhm0ZQQVDgP6A0KLR6Kw9ZgcObSHQRPwRUow+I9KTHLHCw==" + }, + { + "block_id_flag": 2, + "validator_address": "2712CF68AF6982B4BD7536B94CDD0D104A0313F4", + "timestamp": "2024-04-29T14:54:38.942499622Z", + "signature": "qN1F5Rl/7ooPzm3fu2vo615rBCTtbMHymFAL4O/WYip6RMSgDy4N9KNviZkeSjWb3jfrQBMuXWKe4Fiob4fGDA==" + }, + { + "block_id_flag": 2, + "validator_address": "63481F6DCAAF733D2FC953A335C2200EE190862C", + "timestamp": "2024-04-29T14:54:38.830078128Z", + "signature": "EJaYlazyYjhSZEpG5SYfbae0e1uryG0QDcC5hnqaj8IjAdN3HXt0vfI9oE3hm5rWNGwTWgJXsvXAc5QK9fryCg==" + }, + { + "block_id_flag": 2, + "validator_address": "712BC891AEB721DA72732BC30D531E0C1EAEDAE0", + "timestamp": "2024-04-29T14:54:38.821687054Z", + "signature": "LkspxUlX9blOQ2pfi0mP6r/UBgXl7aDNg7u10nhbtReN0hkRqcvpesQ8eU5IlYVgWVJKimTX/K15Zg8pqFZUBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "BD4F80F0C1A67B4950772662F6EBCAD58A258933", + "timestamp": "2024-04-29T14:54:38.977232646Z", + "signature": "8uKvXp94OWc3EQx+3YhYfpZHi80Er5ktbTLhil6xPgVgcTJRUEwabCR0UU5V6KAwYKOmlXvv19b3xaMHwH2MBw==" + }, + { + "block_id_flag": 2, + "validator_address": "943547CACB29C55797E121ACB4E586C49D9D39FD", + "timestamp": "2024-04-29T14:54:38.736110158Z", + "signature": "Z0loNwmnPFU3FXZQY5WkLWcfhESTNPiB/CkDupFiCThGheE2qBtKNTAwxBnL8aR+gKx7pmE2MHYl2xcxVtSfBw==" + }, + { + "block_id_flag": 2, + "validator_address": "5FECEC9408A2710833F2F7D848339462C29C1444", + "timestamp": "2024-04-29T14:54:38.750534114Z", + "signature": "fpG1as9y3O8ZwILLP3qj5O+FdjUY0sgU0vqAhs8xGzVqevYo4KDzEo6HuErSCgk1i575HnO+h45NaVHP45J9AA==" + }, + { + "block_id_flag": 2, + "validator_address": "D5B93190771A50604A5F7849AAF067C4A9DAAF9C", + "timestamp": "2024-04-29T14:54:38.796078658Z", + "signature": "7J3ig5O7vyWnTh8QPNl/e5+EL3DYmK1WRhZJMdIS/KuHsLjQ9YygT4ezcoV3v1/ka2Tma15xw09OKtYxAUoOCg==" + }, + { + "block_id_flag": 2, + "validator_address": "844290531EE59B40FEEFDE5259857368BF7119EC", + "timestamp": "2024-04-29T14:54:38.774165682Z", + "signature": "3+HG3OG5yp42QCF7tbgyEt4Iv7oT+LCdz6xYCUs+DFS0/BHJZ+asSK6eJjwh9D+C5IVdBZyw4/0KJS19AZqWBA==" + }, + { + "block_id_flag": 2, + "validator_address": "4E154C9288E31436BA814DD92D17C4ED6CEFD3F1", + "timestamp": "2024-04-29T14:54:38.754752377Z", + "signature": "qlUnCZ1+NC76FZ/kGJ30Yj5MjNAZ8gyfotNUgD9Eait/WqH5W8R2GiQezNbm4Ha3N9g4sRt4v3kQrrSDIm8cAA==" + }, + { + "block_id_flag": 2, + "validator_address": "0A70912D18E13D78CB32E6322A4E57F861E6C3C8", + "timestamp": "2024-04-29T14:54:38.734331988Z", + "signature": "jKDWmNus36uILaSLA84+xZar0l8RXVK8OkDF3AwWj4Vel8Z0osjKlL5re1ppGjKz6z0Omkfo2+nFvIGbV3d6DA==" + }, + { + "block_id_flag": 2, + "validator_address": "E80D1F5519A5B3C9D290D3EA314FA05564535C1A", + "timestamp": "2024-04-29T14:54:39.104734383Z", + "signature": "lDickzlBIcpAtFvYEqQZm/l9Wog9t09fTwWPOXOftiD1Og9cdukR3cwU6JLQ4E2RX9qhWhbL+/O3Ovydzd+UAA==" + }, + { + "block_id_flag": 2, + "validator_address": "B5C33A409A589C094E89F77D24139F25C6A6DEE9", + "timestamp": "2024-04-29T14:54:38.785347956Z", + "signature": "5dLuEDjlQQfOLIcnSzHCvmCfWHC6a2NkVGAUu4PD17+TYyxnYH71b5j0tI1Xb64Ox+wO1sIx5LGQfh3QpkxtAg==" + }, + { + "block_id_flag": 2, + "validator_address": "69D0605229C665974EBB736FC77E16245C3F79AA", + "timestamp": "2024-04-29T14:54:38.768103599Z", + "signature": "LcQaWorqTqJg7IGuntghRuR6EsduHyGMieLpgR0Focm14+fYnt5I6cYRFbjscGOTuWVN2kLax804TpK7b5UbCg==" + }, + { + "block_id_flag": 2, + "validator_address": "7FC1DA40B2568DDBD53CFF3B76C49CE89AE28680", + "timestamp": "2024-04-29T14:54:38.805137163Z", + "signature": "HAUTXVJD244BE/jsEM7oo2Jez4Frzf2rhXCy5oDsmG7BSukz8CHtSfpQ/394iNZww3tnORi5uhj6IgkWCbxXCg==" + }, + { + "block_id_flag": 2, + "validator_address": "DA4AF19A378C09B54C26C3467CB0ADF889292954", + "timestamp": "2024-04-29T14:54:38.893642493Z", + "signature": "snk7ySg+EvHU+ffkkT14YUnJd2zS5/CAynqX7i/MtzyzxpuOZh7ABv9ZJTrR45a8eiga5crPUdW+cEjnlv40Cw==" + }, + { + "block_id_flag": 2, + "validator_address": "68A393C7ED496871150C0A7CAD0CAC09B8E458FB", + "timestamp": "2024-04-29T14:54:38.76659975Z", + "signature": "N9tKNk8077XM8wniWzx6lgczRS5NNNVRV28FjPghKnM6Vxd76npi2IKNw8kQNXlHxFU9c8XrGQ06hNxati11Bg==" + }, + { + "block_id_flag": 2, + "validator_address": "3E88E7C54F64642A98B2E1DDD5BDBA48794F06C7", + "timestamp": "2024-04-29T14:54:38.847854754Z", + "signature": "ruYWtdW400qosBOKa1EJhKekJ1n45fyIlSkIRZIhMA7qNRDLlklv1f9xOO1c3AJj5qUHy9p9OGekIT4ql4tKCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "2C2467180BBA84F2F1D4565E66F565A34003EE4F", + "timestamp": "2024-04-29T14:54:38.862909198Z", + "signature": "aIRMSXBLuCvkYqferO0GHVFntlb1gBzaQvXbjypI7U6Z/Il6vIxnjeopb8eaIHsMVEF14jMl7dFpGw22tb2eBg==" + }, + { + "block_id_flag": 2, + "validator_address": "E5CBA199E045E7036711D814E57E2B66C3CC0391", + "timestamp": "2024-04-29T14:54:38.829436505Z", + "signature": "0QhkRvUS8ntyw/UfvE8kJG/Z/xdCWy673zu9u+kT63fIvb+N0lmtY0fPNLahy6fhTioOXOMnlBQbB0ifpDgDAw==" + }, + { + "block_id_flag": 2, + "validator_address": "894C56D6CFC3A8E09EB6D1A2E33467C4CF77C0F5", + "timestamp": "2024-04-29T14:54:38.821511698Z", + "signature": "ZY8Wk6iWeoEvNvybnbPvX14JQTDflKyCil2ATEk+UMPWaQKEUKqeuOIAoMZFcnzx3P5aoNDjhEWMSB3TPkddDw==" + }, + { + "block_id_flag": 2, + "validator_address": "C8969171F9B5A3354C712A20F007CDE0648C990F", + "timestamp": "2024-04-29T14:54:38.755479707Z", + "signature": "XByfGb40UZiWDpSjwRNRqfc3Wi/8gL1jppTz9YuFWuUq+l+vWrmoWvHILoJKev7dogpcdHft9U8e5nDxNklqBw==" + }, + { + "block_id_flag": 2, + "validator_address": "9CBF2EFFD5570B3A9A41346244757CDA3E18D401", + "timestamp": "2024-04-29T14:54:38.958203711Z", + "signature": "8DC+JkMGCdjyKjAE0q6j6ej1JHhYt7C8MYjTO33AToCA3mvK8JxDdMPQUYPZHrLwya1v9YEbEvt/buC5y9k+DA==" + }, + { + "block_id_flag": 2, + "validator_address": "D4C41C1E17E9321D067BA4E3E476E766B2C2C2BD", + "timestamp": "2024-04-29T14:54:38.774745625Z", + "signature": "hm24xLI2u8mnm1Y2lHYA5qdsujLS0bezq+KTjKb/SpDSukZTzZ2Siy4GZig3cqdLp4rGAf1uw44A4DmSiXPvAw==" + }, + { + "block_id_flag": 2, + "validator_address": "26F7777BD52918AE71801022B0E2DEED97DDD504", + "timestamp": "2024-04-29T14:54:38.766747733Z", + "signature": "+/gcXFohA6/zuWjBrhD/UyCttGZA7w06MYEnKze08I5qKGIzcQDTfzumGDg9zqum/T3539p5zla0jes8dMnZCA==" + }, + { + "block_id_flag": 2, + "validator_address": "F233E036248A36FC73C154FFA79261BCBDC4BB76", + "timestamp": "2024-04-29T14:54:38.962125901Z", + "signature": "Mk0ssWl3Kh0YZ5G38CJ1qzDZNqtkF4oCHPAInDQGalWvlDixm8xp/0FjsESaqWQOmz0uRU37JeE1bsH5cfmfBg==" + }, + { + "block_id_flag": 2, + "validator_address": "15FEC10416E359CC1DDB424C69166B2671F25148", + "timestamp": "2024-04-29T14:54:38.854787571Z", + "signature": "hxEkFrb/JzVns6RpfiO3HkyPhrDwTi+A2SqA0YJM1FWV7VBbMxtvjbcpIiANODG28/HbzNTci9STVyXXewnNBw==" + }, + { + "block_id_flag": 2, + "validator_address": "000A5959634B4296E4DE536481DE00A8A0EB9A58", + "timestamp": "2024-04-29T14:54:38.748660432Z", + "signature": "I2FvWRahF9fYedgmVzeT0322fM1d+VDcepNjYruCh44iKh+n9ZOHVcF96ANoB+QlFbL6mE2eod01gyQ//xL2AA==" + }, + { + "block_id_flag": 2, + "validator_address": "4B65255857E4393754F049DBE945C5AC87F563D8", + "timestamp": "2024-04-29T14:54:38.963770367Z", + "signature": "HE8oCR9O4BTT+XjuoCgRAxxe/j5wz8MTmwXqb7zkDmUoEqXNoXCSRptHK/qg4H37D3nI23yQg7ELhQBThDSbCg==" + }, + { + "block_id_flag": 2, + "validator_address": "60A433D28B08788C72E2133554BD5CC68769DCEC", + "timestamp": "2024-04-29T14:54:38.852881536Z", + "signature": "YIk7di5Xxy75HS8mTYAHQFyRevvXCDap4te5r9oWvf6LtsCaVjCuVUTdaHQOHcYpOHlyouVNN7xTIbDP4uBxAw==" + }, + { + "block_id_flag": 2, + "validator_address": "06AA34BD6D1DD34119E3DC173EFAD94F430AB74E", + "timestamp": "2024-04-29T14:54:38.743152188Z", + "signature": "tpDFp5TzAQBhXmUUvTVQ9gp/oCS4TV0RdpiSnt4o8SuQITbwK1EXAlA51deHz11E8eDQ88hgdHt+TztXzAgyCg==" + }, + { + "block_id_flag": 2, + "validator_address": "C9B753ED297E5F9894D4A43149CFC9F7B207B6B2", + "timestamp": "2024-04-29T14:54:38.754081395Z", + "signature": "Vnvtn3ny3sEYE8Ob/Q5hlZZJa65WmiYpVHn7mKV5nt+XZAxOsEcC0BTQNZGtcG8UD3kW/Ky4WxjO957y8qtWAA==" + }, + { + "block_id_flag": 2, + "validator_address": "3FF6C988799C1ADF3ACA0DA56143C8163890859A", + "timestamp": "2024-04-29T14:54:38.79594451Z", + "signature": "loGpvTMCKlf6vX61fDYWTIyZkNd3hiDNMRLFZJuijreNF9n/B43jQEs2bbFXq/+4dIpPIzIQIYv1ec4yzybxBA==" + }, + { + "block_id_flag": 2, + "validator_address": "99938495407C09B343562AAEC3AB551A5C246232", + "timestamp": "2024-04-29T14:54:38.880226238Z", + "signature": "NfL1GFALt0cGvbt46I2WkwV+Eo8UjgeqfzSqVMz0C1wyot+2UHVDZrhzlT9JbZfjg4u3IFr+CqOBcoJpNlwJAg==" + }, + { + "block_id_flag": 2, + "validator_address": "901FD122CC512EF13DE8E1A3D7953BFDDC0786D6", + "timestamp": "2024-04-29T14:54:38.749799152Z", + "signature": "SMlXSxvvbKJ1x9SqkBGPEjhSD7bAtQUvQ9iU/7wlfFyA6vYITgpaMX9eCieXk52TJaiCrl4yCwUlBwdOrDdMAw==" + }, + { + "block_id_flag": 2, + "validator_address": "19EC0A155A5BE755E76D0059EF730EBCA122B4F1", + "timestamp": "2024-04-29T14:54:38.858488466Z", + "signature": "Q6qdHD0jL+LnhlyzPVi10hpt2K5sdiVUYaGm/cGKkBoMbTi+9eiHsgYrYAX5lEgNH69DZyglA5dfuakAxrcuBw==" + }, + { + "block_id_flag": 2, + "validator_address": "F6783D8FB30E283081C16398293F482DCA0E912D", + "timestamp": "2024-04-29T14:54:38.821670946Z", + "signature": "8gcWDi1HfOHdaBAQUz30BqHNwfHdjStLZuDrY1bVXLECrC3lTzKZwlNqqDkiFHwHfVdjFqlKFn1AWcpsHXwtAA==" + }, + { + "block_id_flag": 2, + "validator_address": "A9C4E0E2AF00183DA11434ED413219905E9A868D", + "timestamp": "2024-04-29T14:54:38.76973855Z", + "signature": "rPXCQcMoDoHZ5Qf8Lzj0jQg+EAsqE0AwyTQFdvGBgWhH2buY1UA0xH1KS79OxXomI1ndnVGkkCGrgECl8EIkDg==" + }, + { + "block_id_flag": 2, + "validator_address": "3749086B6D85BDE3DACFBE4485E3DF95E709B6DB", + "timestamp": "2024-04-29T14:54:38.952607414Z", + "signature": "UZJ4FQDlI69vy0XrpbzuZp+fOJyDv2U81TKF3P8iF0WpZM2WPT5QiaZnrfx28YfPbSWW64K2RS5FfPgL+fkVBg==" + }, + { + "block_id_flag": 2, + "validator_address": "E06DADEB413829558F7C95339FFB61499C5A1BB9", + "timestamp": "2024-04-29T14:54:38.806015604Z", + "signature": "q08IWafHCPPsxldx08KcazYqKoYKN13iZpoGc51aFHo4Gw/gOQ1cM2/rWuaLpTDKkSl966bu+N8aNRo8Ld4RBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "2D159B72D40C1C1DADDF24D2511200001B74ED84", + "timestamp": "2024-04-29T14:54:38.783933684Z", + "signature": "AJ+ej+oNe1/sVwXLsZSbltn1Y6CBO0fRHo/xjIUtHQdOQmW6fv6UUa0srcIWQY7zM7luFrvyr5fL6bTi5NJvDg==" + }, + { + "block_id_flag": 2, + "validator_address": "0CEB917DE4DF1C4B4F8EDFC4ACE6FD6D39F1E61E", + "timestamp": "2024-04-29T14:54:38.888825855Z", + "signature": "VORDz/0zE5mRVEVMqmk6E5AGpC2QV7/YFdMihVULb59eDznRs7HgSswDm9FAakt6c+gdFdsYyW6hbrWpMpUKBw==" + }, + { + "block_id_flag": 1, + "validator_address": "", + "timestamp": "0001-01-01T00:00:00Z", + "signature": null + }, + { + "block_id_flag": 2, + "validator_address": "CE485517649E4F8C71469EF7DAFCF9A558BF167F", + "timestamp": "2024-04-29T14:54:38.782557633Z", + "signature": "FFBXyFKrEsopETQrZWO6TEcEzu45AjePDtI3sPc8LZq3GJ/fTTqohciDVi8/QOmBxGqqBNFsUxLVYDmsDgEUBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "68275C37CFF86BB53D29D6237AD370E8FD5097FC", + "timestamp": "2024-04-29T14:54:38.904736039Z", + "signature": "xfgCAUgWWIx6w0mqz3043fH9kZwRkVuC4H8KGsE6DfAg9725tZVAaxhMTTlRjxDvlYJ+CmB9fDRvtRHhubIbAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "E23AFCF0035FB01ACD02FE96F680066974D7072B", + "timestamp": "2024-04-29T14:54:38.958040197Z", + "signature": "iYjUCpRsDz2HgATE6q8Rh+JLvwfxqIhqXEUi4uN+824gwE4acEBDT2n/H2iusID25tkWkFLBaKUzYvz+NHD+Cg==" + }, + { + "block_id_flag": 2, + "validator_address": "E242DB2CB929D6F44A1A2FE485CC7D3F620FFAEB", + "timestamp": "2024-04-29T14:54:38.873405008Z", + "signature": "46uJlZKx5lEjtkekAjU2Qm1iMdt/AVYRVay03+IFxXzr9uUYnyRbdFFANcUEC13M9CjEdqKu7nZE68E1bX5zDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "6E705424231DCEEC337EB451BC4C1D2C5FBA48C9", + "timestamp": "2024-04-29T14:54:38.912880315Z", + "signature": "RzNzkZIwe+D75kdaoFcaD7PBUd7Vqf22AowN6ljwKBAXFuN6BN3+GxUOW55Etq1oPeTMummGihxc9h/TWY6pBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "41B543E91479A95CD5CA9F109C26DFAC149126FA", + "timestamp": "2024-04-29T14:54:38.899198785Z", + "signature": "dJQGqyYaNVPrKsl0iieJnJHGG98T2NidTPcqRbfWOKsmMR8NL5P2g8ac+wmuy+nuT/sUAub7AJycSfR6UJxQBw==" + }, + { + "block_id_flag": 2, + "validator_address": "7C5AA87E5203C66EA35C64262F576EDD29BAD980", + "timestamp": "2024-04-29T14:54:38.829991612Z", + "signature": "fcBClRX9ZVmw2vtiP9deavpvLee3PCyE2c2p3xgHd2zE5T/MYsnAkpvCNJpPPNlJU1KYEFrY9yyF91/uSfYHCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "451ACECAA7DC4CCE6E0B7CDE02F455DF973535E5", + "timestamp": "2024-04-29T14:54:38.827110959Z", + "signature": "O0iEc8dZTLr5WyR0yh6lpcw1IuvuvOLIC3sAp2Fs2huRkV3bQFfNWsolcmVSjScs57TPTpm3hT8cpUEyheRkAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "9127DFA61750DD1D56CB1D2A88F8831A2B3F9B0E", + "timestamp": "2024-04-29T14:54:38.776642123Z", + "signature": "QreBCVAnAL56xKa1yRL78/eFE3CYwW4uxxWnkrnK2EH1RiPZ00+wFMuOkUPJ/iqQeb4lUvApmo2Vjqg95r0PCw==" + }, + { + "block_id_flag": 2, + "validator_address": "583AE736E67DF9D72FE87B9AA7D3210D3B4B0E5A", + "timestamp": "2024-04-29T14:54:38.741625224Z", + "signature": "S4EXXySzSbq1npjPAXTB6kZ2u+wiDowvjkb7TCIGKyDXtkTmLC2dJLS2pMd6m8RFdNTdyZ+M7i/lnu6RW+XoAg==" + }, + { + "block_id_flag": 2, + "validator_address": "C5ED122E511FF9D7DEA986FD7423C61AEB139D34", + "timestamp": "2024-04-29T14:54:38.798614847Z", + "signature": "5b6ZG0aNPmF/YUIeMCzqAlA6AmvczGrXyVz83XgHXFYnjQvx9n9JFrM7B68JNltnH1CZi86ncycuX+nD17W6CQ==" + }, + { + "block_id_flag": 2, + "validator_address": "3519556AE84C5DCBCF6FDAF05FB644E40FF93C37", + "timestamp": "2024-04-29T14:54:38.885214041Z", + "signature": "BtQDXbPI8fpCAaJC26ZhqUzf69NTDhCpF4xi1EWMSL7bOmfSy3uDSgyucWDsFwsuvWAsaSRld0yfkFNyKv1XDg==" + }, + { + "block_id_flag": 2, + "validator_address": "1FADA14DEE843B733ECD5DE2E74552AD234A5451", + "timestamp": "2024-04-29T14:54:38.752438269Z", + "signature": "LKdQSmm3fOPK58IZerASYcETIJT1kyZ6/D3FTSTWOC/+WZ8FciXNo1o003xsWToN6ryZQaiM2CWb7TZ4QVfwCg==" + }, + { + "block_id_flag": 2, + "validator_address": "1571038B5AAAB431EC011F6AB1094463C6ED9842", + "timestamp": "2024-04-29T14:54:38.871920808Z", + "signature": "Irajsw0pu5jtzRtZBw8BOvo3HPDw6ksU3fwgdKLTDU/g6+xNL5rxkgR2qKAR6IZiyyVJhSDhvQLpLiwo6aXhDQ==" + }, + { + "block_id_flag": 2, + "validator_address": "2D387D95E13F681D33122E4475F9B7DFC2A68F64", + "timestamp": "2024-04-29T14:54:38.828853438Z", + "signature": "mcq0j6AKSh7wtvEealZ9uy0Pw1DsxfOm8zIDQ1WUmkrRNwreIoycxVBufTzw9G8S3MUQvkB3Tk5b6p8pPVpkBA==" + }, + { + "block_id_flag": 2, + "validator_address": "3FF719F1664BEE93D482B480677C03A47EC0B643", + "timestamp": "2024-04-29T14:54:38.824915834Z", + "signature": "NfywwXBDb8b5/zoY8C5H2V+HYa0ld2CkC/xykXKALEFty8yzO+7U5sZNQI13lArpTtP8kNyGrJVAjM8OcqBuAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "C02ACBA7653AC3782750B53D03A672E191F00361", + "timestamp": "2024-04-29T14:54:38.810934096Z", + "signature": "j5Hvin3vxWHtE1vuTBiNGwmVrTb6ALGMH2apmWTdJmstt1dILW3kcL2Ec/Oy7WMhZRNTET85ZRYFnT5LfTaOAA==" + }, + { + "block_id_flag": 2, + "validator_address": "37714C4DA407C9D13CDA424AAE78C3B28515A15C", + "timestamp": "2024-04-29T14:54:38.849764588Z", + "signature": "LJBvpKnkJZOVKMrO6C6IsObYQj43rq7l4BIT1lUgtLFpl/E1MeR0bOD9xXqUNurQZNDKuqYrVMGM+pVQu3HTDg==" + }, + { + "block_id_flag": 2, + "validator_address": "0614088C41E6A85FB5BF344552A5120E5A0139FC", + "timestamp": "2024-04-29T14:54:38.751970604Z", + "signature": "DaDZvN2z/lx+5q7++vqduTyXDW8H0KVCg+cIa9Aak+Rq8uFLxczNoHX7En2/gRTODR+Cyf8s95Q0ELI8i3IbDA==" + }, + { + "block_id_flag": 2, + "validator_address": "CDC018822747024BEAFD10A45ABECC7AC19CBAB0", + "timestamp": "2024-04-29T14:54:38.831940962Z", + "signature": "W/U6Pd0YvLGtAAf6zotX5u6OYfKA/5w55TAGtnWYooeYAi/k/CkSRQUlp71lnYxfgwrgwNkp8fUzcMZtmlPcBg==" + }, + { + "block_id_flag": 2, + "validator_address": "4146FD7A1AB8B861B7018978BCD13D2D1FA63EBE", + "timestamp": "2024-04-29T14:54:38.828202785Z", + "signature": "hJeuqlnbyQPcLVrYJQiXaP92KBvrggo/PZotrenH+vBrTh21ZFbhiqsM8nwNPYhyJ3Rmwlk8DSe34pvvlJY3CQ==" + }, + { + "block_id_flag": 2, + "validator_address": "8014BA212ED388597510D064258F5E30AA30D591", + "timestamp": "2024-04-29T14:54:38.759866725Z", + "signature": "cOg/stOX8uH62rqDN8W/cb4tKsn6yGGrY4QdsSMYrs8VmGgJf2Jy4vDUiK0F3znmRUrUfLpKvV2ozHgJAutUBA==" + }, + { + "block_id_flag": 2, + "validator_address": "373F86CB3755A1DE78CC69D3E5F7AD5D7615B85D", + "timestamp": "2024-04-29T14:54:38.893243715Z", + "signature": "6cGi9bEG4LsGdbdFAcsi7Rq7XX2Si4N6wg+o1CvCrb6vD4E1uztXE2d4RHKNyx64lSWWb9/AX6kT5C7ZUYE1AA==" + }, + { + "block_id_flag": 2, + "validator_address": "22BA59AC2918AFA4C1B56D3E6F86083E470CD8CB", + "timestamp": "2024-04-29T14:54:38.924952862Z", + "signature": "wlNELZTrgJOyHjLWtjxA68BT+ynFp3r4XFy3xlTtc2izUDAHUAUmfFGDmUlABH6qDnDmoU1k/HYjpFc1xui2Dw==" + }, + { + "block_id_flag": 2, + "validator_address": "7364BE6CC7B6E404BD1C2050CCB6A7472786E3B6", + "timestamp": "2024-04-29T14:54:38.771764133Z", + "signature": "Zrov+pxM2XTSbvIWkO626H709NaS13FN9iTZp1hHcudN2c4/cCBK+yBDQI5RGhWCGLHHkPeDjdBckLFq3jGIDA==" + }, + { + "block_id_flag": 2, + "validator_address": "972A684F364CCE31469B37A9D439985115EB5A40", + "timestamp": "2024-04-29T14:54:38.753927775Z", + "signature": "Dx9+CwwEcJSeqQrCr8b7EnX6hpVkj0xRO+KjmJxea8KA8tKuEJrvLlV0KORUZLELzsv/oTDw2IZyA/SObB23AA==" + }, + { + "block_id_flag": 2, + "validator_address": "E20004515311B205618FAD504FB529A3DEEE2E71", + "timestamp": "2024-04-29T14:54:38.802557285Z", + "signature": "gXhXm6JtYSaaGZOSClY8I+/dTrmGzIoCld33nlQL3uHqUCAd/bgFRugb+qEQrePl099R/yQZRZ2TruWL4AWHBQ==" + }, + { + "block_id_flag": 2, + "validator_address": "2F89D7D3D1E1478F88EF3AD8AAD76A88189F6124", + "timestamp": "2024-04-29T14:54:38.837165466Z", + "signature": "tBUIuki8Lf3RAJRM4MsRwYm3fO/kaPPKe/jcacHKUQ9Lya2WHDiO/MQAU8XXs/Y875GNNw7iTeCVKWGEkUnHCg==" + }, + { + "block_id_flag": 2, + "validator_address": "D807A55C7D69A84FB759FB0BD96BB4DA50ADBA27", + "timestamp": "2024-04-29T14:54:38.989099532Z", + "signature": "1CP4Uc2nIs8ERiOSAwlYSMtSKzTkJsKkK7Ntzb+5a/VFxHVEgs2+9nx1tSbjCBnMQH3rvJZcPPhUpkccff57BQ==" + }, + { + "block_id_flag": 2, + "validator_address": "7E11ED7DD06FAE7B0BEDB469721151F2F31CBB6A", + "timestamp": "2024-04-29T14:54:38.92626009Z", + "signature": "DOlcL5u6XGZI3MIc3a/diAwQ6Sc9xsoKN80SN8dn4nyRHdW7+SkHyRnFN2vOHtmnf1L5wpt+h68AfBLZ58weAg==" + }, + { + "block_id_flag": 2, + "validator_address": "2DD6D22969EE7C2CA1F8B428D13A8995C043044C", + "timestamp": "2024-04-29T14:54:38.904102054Z", + "signature": "QxaGdzaH9UWcqNvb/262tKJ7cxWbVfZlrkY5X/I+GtioQj8G2dYHQ0xOwpEdh425LZ+v8grGbkhZh8lCjG9nBw==" + }, + { + "block_id_flag": 2, + "validator_address": "634D2F5ED7C9D82F42298D8922304D1B4ADB20F7", + "timestamp": "2024-04-29T14:54:38.826929439Z", + "signature": "aotloGT4VgbTuaUUpVNAH820EhLlqgBT2fgeyNLqhtGf+YfY2PjRF7PAJ/WezogyFCmx5mYDocg+sXF1KFG9DQ==" + }, + { + "block_id_flag": 2, + "validator_address": "9496535A8F2945BDB60572015D2D6F721AB6FED9", + "timestamp": "2024-04-29T14:54:38.754602224Z", + "signature": "hD45jrh+ZWfW+lTe5rv/WT/JYh/hfQQZCRrqSppkTwykVuol/bzeqCDTzdFgpMrrRDZUZvdI3K8OVW113QQBCQ==" + }, + { + "block_id_flag": 2, + "validator_address": "DA96564D2379ACEE00DD9FAA558681BB499757FD", + "timestamp": "2024-04-29T14:54:38.955971489Z", + "signature": "4/n7/p7APfnGO7D8t4pyPiKC4NoRcYE29fzRGx6hluLcofxZdPx9/4zUA0ZEgx1OaERO0/ABHP3Um8RkohN4AA==" + }, + { + "block_id_flag": 2, + "validator_address": "B15069E41B1A60FF03AE8D8F741F78C7B1144FBE", + "timestamp": "2024-04-29T14:54:38.731700236Z", + "signature": "dzdOmHCJ/YvMSxns+92e9XqRM+ehQbXXDQAZinT0/ATSa3QQ74L0i2g8JBIWaxM49BYhwWkVRec3LastPmdfCw==" + }, + { + "block_id_flag": 2, + "validator_address": "D0C2071D6F2FEF021DBD3F5F1F72F1BF30A467B9", + "timestamp": "2024-04-29T14:54:38.72163783Z", + "signature": "1PT527dzKNf9cTTITOV/m2HRYWvehjYnPT5wXWJjOcyofqoLzmSfTnYw6CqZ8h3oJ2jQmfPlXIFToVtK+nM8Dw==" + }, + { + "block_id_flag": 2, + "validator_address": "5D564F844D411694B131B1C4A4FD3B389494F48F", + "timestamp": "2024-04-29T14:54:38.762599438Z", + "signature": "/tn/JrwXkYDiTSl7mHwFD/zpBwqAfo29BWM5LjM+72mMzOOgC5YFPxd0DcNvVNbfAiTENRNo79psK5ti9gB3Cg==" + }, + { + "block_id_flag": 2, + "validator_address": "191E896A11C0A77A96A99ABEE986A2A40355C044", + "timestamp": "2024-04-29T14:54:38.747075254Z", + "signature": "tfO7r2A5Dgcz7aN1IofdiRStVe6/c+GjxzodDz/owfFWLQWBShaC6CfxcAnFpjzA7NlUqd12CU6ARbXoRi3tCA==" + }, + { + "block_id_flag": 2, + "validator_address": "47C89621F47BA7FF2362C1B2F97A4F6311B646F9", + "timestamp": "2024-04-29T14:54:38.780378685Z", + "signature": "GRRau9drNkDhyZgu8eouPTrDSczWEldNf/PKroAzIti+KRIX3xXs/rN0gREY2smoMsoVz/jR20gtYeY5YAH/Ag==" + }, + { + "block_id_flag": 2, + "validator_address": "F0C8B6ADDAF7CC4ECE57086607A9A0C7EA6275E0", + "timestamp": "2024-04-29T14:54:38.807776052Z", + "signature": "92HNueLZsDLZCNvOHzV2h25Zlf/Wq05mbl8/PwGQRcEeeAIwwcmBQDpxSDpwEnpXdKJ3Ut9AA1ZxzkpDKvf9Dg==" + }, + { + "block_id_flag": 2, + "validator_address": "20658BF40ED48ED01A2D087C7FF7874F21A56333", + "timestamp": "2024-04-29T14:54:38.806281884Z", + "signature": "N0tYbHufXaN/QZfiBIK3uk7GAvyySZEkHDuRiMWgv0Loz3ftJmWM13+Rw8vwKFSgxy7NFyAIzYymqvzIJJIqDw==" + }, + { + "block_id_flag": 2, + "validator_address": "2335465B27B9548313AAF465217787FD8E6113D3", + "timestamp": "2024-04-29T14:54:38.724508271Z", + "signature": "+kBy7QjvwSe86iV0ki/wZqOo0HTNwFUr2eqTUy2zuEwOxXRcVFeM9UWFHk4MpczQUFNiitpqLgu8L79B1YAnBA==" + } + ] + } + } + } + "#; + + let response: Response = serde_json::from_str(json).unwrap(); + + println!("Response: {:?}", response); +} + +/// This test passes when HttpClient is initialised with `CompactMode::V0_37`. +/// This fails when `CompactMode::V0_38` is used with Neutron url and block height. +/// This test passes with Osmosis url and block height and any compact mode. +#[tokio::test] +#[ignore] +async fn test_http_client() { + use tendermint_rpc::Client; + + // Neutron + let url = ""; + let height = 22488720u32; + + // Osmosis + // let url = ""; + // let height = 15317185u32; + + let url = Url::from_str(url).unwrap(); + let tendermint_url = tendermint_rpc::Url::try_from(url).unwrap(); + let url = tendermint_rpc::HttpClientUrl::try_from(tendermint_url).unwrap(); + + let client = HttpClient::builder(url) + .compat_mode(CompatMode::V0_37) + .build() + .unwrap(); + + let response = client.block(height).await.unwrap(); + + println!("Response: {:?}", response); +} diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs index 3dcda49f025..6fde035c7e1 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs @@ -310,3 +310,6 @@ impl WasmRpcProvider for CosmosWasmRpcProvider { Ok(self.handle_tx(tx, block_hash, parser).collect()) } } + +#[cfg(test)] +mod tests; diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider/tests.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider/tests.rs new file mode 100644 index 00000000000..09d6b775a49 --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider/tests.rs @@ -0,0 +1,54 @@ +use std::str::FromStr; + +use tendermint_rpc::client::CompatMode; +use tendermint_rpc::HttpClient; +use url::Url; + +use hyperlane_core::rpc_clients::FallbackProvider; +use hyperlane_metric::prometheus_metric::{ + ClientConnectionType, PrometheusClientMetrics, PrometheusConfig, +}; + +use crate::rpc::CosmosRpcClient; +use crate::rpc_clients::CosmosFallbackProvider; + +/// This test passes when HttpClient is initialised with `CompactMode::V0_37` (done in prod code). +/// This test fails when `CompactMode::V0_38` is used with Neutron url and block height. +/// This test passes with Osmosis url and block height and any compact mode. +#[tokio::test] +#[ignore] +async fn test_fallback_provider() { + use tendermint_rpc::Client; + + // Neutron + let url = ""; + let height = 22488720u32; + + // Osmosis + // let url = ""; + // let height = 15317185u32; + + let url = Url::from_str(url).unwrap(); + + let metrics = PrometheusClientMetrics::default(); + + let metrics_config = PrometheusConfig { + connection_type: ClientConnectionType::Rpc, + node: None, + chain: None, + }; + let rpc_client = CosmosRpcClient::from_url(&url, metrics.clone(), metrics_config).unwrap(); + let providers = [rpc_client]; + + let mut builder = FallbackProvider::builder(); + builder = builder.add_providers(providers); + let fallback_provider = builder.build(); + let provider = CosmosFallbackProvider::new(fallback_provider); + + let response = provider + .call(|provider| Box::pin(async move { provider.get_block(height).await })) + .await + .unwrap(); + + println!("{:?}", response); +} From 17703182350324a9d654f865b18c04e8e4dda7fe Mon Sep 17 00:00:00 2001 From: mshojaei-txfusion <138107084+mshojaei-txfusion@users.noreply.github.com> Date: Thu, 8 May 2025 18:27:48 +0330 Subject: [PATCH 151/223] feat: update registry to 14.0.0 (#6171) ### Description Upgraded `@hyperlane-xyz/registry` from v11.1.0 to v14.0.0 across multiple packages and updated warp route config API usage to match the new version requirements. ### Drive-by changes None ### Related issues https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5244 ### Backward compatibility Yes ### Testing Manual --- .changeset/loose-numbers-tease.md | 8 ++++++++ typescript/cli/package.json | 2 +- typescript/helloworld/package.json | 2 +- typescript/infra/package.json | 2 +- .../scripts/warp-routes/export-warp-configs.ts | 5 ++--- typescript/widgets/package.json | 2 +- yarn.lock | 16 ++++++++-------- 7 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 .changeset/loose-numbers-tease.md diff --git a/.changeset/loose-numbers-tease.md b/.changeset/loose-numbers-tease.md new file mode 100644 index 00000000000..76e8b46367e --- /dev/null +++ b/.changeset/loose-numbers-tease.md @@ -0,0 +1,8 @@ +--- +'@hyperlane-xyz/helloworld': minor +'@hyperlane-xyz/widgets': minor +'@hyperlane-xyz/infra': minor +'@hyperlane-xyz/cli': minor +--- + +Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 278d34bc822..7a4bb5bd019 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -8,7 +8,7 @@ "@eslint/js": "^9.15.0", "@ethersproject/abi": "*", "@ethersproject/providers": "*", - "@hyperlane-xyz/registry": "11.1.0", + "@hyperlane-xyz/registry": "14.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@hyperlane-xyz/utils": "12.5.0", "@inquirer/core": "9.0.10", diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index ddf204a2661..fb23990eeb7 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -4,7 +4,7 @@ "version": "12.5.0", "dependencies": { "@hyperlane-xyz/core": "7.1.4", - "@hyperlane-xyz/registry": "11.1.0", + "@hyperlane-xyz/registry": "14.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 33d1f41a54d..2f316fbbb0a 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -14,7 +14,7 @@ "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", "@hyperlane-xyz/helloworld": "12.5.0", - "@hyperlane-xyz/registry": "11.1.0", + "@hyperlane-xyz/registry": "14.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@hyperlane-xyz/utils": "12.5.0", "@inquirer/prompts": "3.3.2", diff --git a/typescript/infra/scripts/warp-routes/export-warp-configs.ts b/typescript/infra/scripts/warp-routes/export-warp-configs.ts index 642790bd2a4..0aafc8ac69c 100644 --- a/typescript/infra/scripts/warp-routes/export-warp-configs.ts +++ b/typescript/infra/scripts/warp-routes/export-warp-configs.ts @@ -31,12 +31,11 @@ async function main() { }, ); - const configFileName = `${warpRouteId}-deploy.yaml`; - registry.addWarpRouteConfig(registryConfig, configFileName); + registry.addWarpRouteConfig(registryConfig, { warpRouteId }); // TODO: Use registry.getWarpRoutesPath() to dynamically generate path by removing "protected" console.log( - `Warp config successfully created at ${registry.getUri()}/deployments/warp_routes/${configFileName}`, + `Warp config successfully created at ${registry.getUri()}/deployments/warp_routes/${warpRouteId}-deploy.yaml`, ); } } diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 403d26f443d..73149af3210 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -31,7 +31,7 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@eslint/js": "^9.15.0", - "@hyperlane-xyz/registry": "11.1.0", + "@hyperlane-xyz/registry": "14.0.0", "@storybook/addon-essentials": "^7.6.14", "@storybook/addon-interactions": "^7.6.14", "@storybook/addon-links": "^7.6.14", diff --git a/yarn.lock b/yarn.lock index 504d506fbd3..23ac5646250 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7644,7 +7644,7 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" - "@hyperlane-xyz/registry": "npm:11.1.0" + "@hyperlane-xyz/registry": "npm:14.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" "@inquirer/core": "npm:9.0.10" @@ -7795,7 +7795,7 @@ __metadata: dependencies: "@eslint/js": "npm:^9.15.0" "@hyperlane-xyz/core": "npm:7.1.4" - "@hyperlane-xyz/registry": "npm:11.1.0" + "@hyperlane-xyz/registry": "npm:14.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -7846,7 +7846,7 @@ __metadata: "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" "@hyperlane-xyz/helloworld": "npm:12.5.0" - "@hyperlane-xyz/registry": "npm:11.1.0" + "@hyperlane-xyz/registry": "npm:14.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" "@inquirer/prompts": "npm:3.3.2" @@ -7910,13 +7910,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/registry@npm:11.1.0": - version: 11.1.0 - resolution: "@hyperlane-xyz/registry@npm:11.1.0" +"@hyperlane-xyz/registry@npm:14.0.0": + version: 14.0.0 + resolution: "@hyperlane-xyz/registry@npm:14.0.0" dependencies: yaml: "npm:2.4.5" zod: "npm:^3.21.2" - checksum: 10/4acb717a1f2d5b2c93237be6cd24406ae6f52eb86a4e5246eee919d5f95866ebdb7d4ce0fff2b193f6f4e29da2bcbe48e525ac958afaaa523e8260863069295d + checksum: 10/893c802f8f8206d5a314e8d729ae1f9bf7ae5e66c6034beb0a56e3dcfdb15a532e97f1b8c3ffe394970ccccf3d9129f340d4ad47c27102dafcfcc1c6321ea57b languageName: node linkType: hard @@ -8020,7 +8020,7 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" - "@hyperlane-xyz/registry": "npm:11.1.0" + "@hyperlane-xyz/registry": "npm:14.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" "@interchain-ui/react": "npm:^1.23.28" From ce0b173b6aea6b0b319a81b581cff400dd22b1b5 Mon Sep 17 00:00:00 2001 From: mshojaei-txfusion <138107084+mshojaei-txfusion@users.noreply.github.com> Date: Thu, 8 May 2025 19:21:34 +0330 Subject: [PATCH 152/223] refactor: warp config loading (#6173) ### Description This PR refactors the warp route configuration functions to use object parameters instead of positional parameters, improving API clarity and flexibility. The main changes include: - Modified `readWarpRouteDeployConfig` to accept an object parameter with named properties - Updated `readWarpCoreConfig` with similar parameter style changes - Added utility functions for handling warp route configuration in various scenarios - Enhanced context types with warp configuration properties ### Drive-by changes - Added new `getWarpRouteDeployConfig` utility function - Added `useProvidedWarpRouteIdOrPrompt` helper function - Improved error messaging and assertions Diff with last PR: https://github.com/hyperlane-xyz/hyperlane-monorepo/compare/upgrade/registry-14.0.0...refactor/warp-config-loading ### Related issues https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5244 https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5922 ### Backward compatibility Yes - Internal refactoring only, existing CLI commands work the same way ### Testing Manual testing of CLI commands --- .changeset/little-houses-crash.md | 5 + typescript/cli/src/commands/config.ts | 2 +- typescript/cli/src/commands/warp.ts | 5 +- typescript/cli/src/config/warp.ts | 112 ++++++++++++++++--- typescript/cli/src/context/types.ts | 4 + typescript/cli/src/deploy/warp.ts | 6 +- typescript/cli/src/utils/warp.ts | 148 +++++++++++++++++--------- 7 files changed, 215 insertions(+), 67 deletions(-) create mode 100644 .changeset/little-houses-crash.md diff --git a/.changeset/little-houses-crash.md b/.changeset/little-houses-crash.md new file mode 100644 index 00000000000..2f2a858863e --- /dev/null +++ b/.changeset/little-houses-crash.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Refactored warp route configuration functions to use object parameters instead of positional parameters for improved clarity and flexibility. diff --git a/typescript/cli/src/commands/config.ts b/typescript/cli/src/commands/config.ts index 65490cade68..541b113ae2a 100644 --- a/typescript/cli/src/commands/config.ts +++ b/typescript/cli/src/commands/config.ts @@ -98,7 +98,7 @@ const validateWarpCommand: CommandModuleWithContext<{ path: string }> = { path: inputFileCommandOption(), }, handler: async ({ path, context }) => { - await readWarpRouteDeployConfig(path, context); + await readWarpRouteDeployConfig({ context, filePath: path }); logGreen('Config is valid'); process.exit(0); }, diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index 33f2e0993a6..66784c76a12 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -120,7 +120,10 @@ export const apply: CommandModuleWithWriteContext<{ if (strategyUrl) ChainSubmissionStrategySchema.parse(readYamlOrJson(strategyUrl)); - const warpDeployConfig = await readWarpRouteDeployConfig(config, context); + const warpDeployConfig = await readWarpRouteDeployConfig({ + filePath: config, + context, + }); await runWarpRouteApply({ context, diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 91942b0e5fa..750ef93458a 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -4,6 +4,7 @@ import { stringify as yamlStringify } from 'yaml'; import { ChainMap, DeployedOwnableConfig, + HypERC20Deployer, IsmConfig, IsmType, MailboxClientConfig, @@ -22,6 +23,7 @@ import { errorRed, log, logBlue, logGreen } from '../logger.js'; import { runMultiChainSelectionStep } from '../utils/chains.js'; import { indentYamlOrJson, + isFile, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js'; @@ -29,6 +31,7 @@ import { detectAndConfirmOrPrompt, setProxyAdminConfig, } from '../utils/input.js'; +import { useProvidedWarpRouteIdOrPrompt } from '../utils/warp.js'; import { createAdvancedIsmConfig } from './ism.js'; @@ -88,13 +91,24 @@ export async function fillDefaults( ); } -export async function readWarpRouteDeployConfig( - filePath: string, - context: CommandContext, -): Promise { - let config = readYamlOrJson(filePath); - if (!config) - throw new Error(`No warp route deploy config found at ${filePath}`); +export async function readWarpRouteDeployConfig({ + context, + ...args +}: + | { + context: CommandContext; + warpRouteId: string; + } + | { + context: CommandContext; + filePath: string; + }): Promise { + let config = + 'filePath' in args + ? readYamlOrJson(args.filePath) + : await context.registry.getWarpDeployConfig(args.warpRouteId); + + assert(config, `No warp route deploy config found!`); config = await fillDefaults(context, config as any); @@ -112,7 +126,7 @@ export async function createWarpRouteDeployConfig({ advanced = false, }: { context: CommandContext; - outPath: string; + outPath?: string; advanced: boolean; }) { logBlue('Creating a new warp route deployment config...'); @@ -253,7 +267,21 @@ export async function createWarpRouteDeployConfig({ const warpRouteDeployConfig = WarpRouteDeployConfigSchema.parse(result); logBlue(`Warp Route config is valid, writing to file ${outPath}:\n`); log(indentYamlOrJson(yamlStringify(warpRouteDeployConfig, null, 2), 4)); - writeYamlOrJson(outPath, warpRouteDeployConfig, 'yaml'); + if (outPath) { + writeYamlOrJson(outPath, warpRouteDeployConfig, 'yaml'); + } else { + const tokenMetadata = await HypERC20Deployer.deriveTokenMetadata( + context.multiProvider, + warpRouteDeployConfig, + ); + assert( + tokenMetadata?.symbol, + 'Error deriving token metadata, please check the provided token addresses', + ); + await context.registry.addWarpRouteConfig(warpRouteDeployConfig, { + symbol: tokenMetadata.symbol, + }); + } logGreen('✅ Successfully created new warp route deployment config.'); } catch (e) { errorRed( @@ -269,9 +297,29 @@ function restrictChoices(typeChoices: TokenType[]) { // Note, this is different than the function above which reads a config // for a DEPLOYMENT. This gets a config for using a warp route (aka WarpCoreConfig) -export function readWarpCoreConfig(filePath: string): WarpCoreConfig { - const config = readYamlOrJson(filePath); - if (!config) throw new Error(`No warp route config found at ${filePath}`); +export async function readWarpCoreConfig( + args: + | { + context: CommandContext; + warpRouteId: string; + } + | { + filePath: string; + }, +): Promise { + let config: WarpCoreConfig | null = null; + const readWithFilePath = 'filePath' in args; + if (readWithFilePath) { + config = readYamlOrJson(args.filePath); + } else { + config = await args.context.registry.getWarpRoute(args.warpRouteId); + } + assert( + config, + `No warp route config found for warp route ${ + readWithFilePath ? args.filePath : args.warpRouteId + }`, + ); return WarpCoreConfigSchema.parse(config); } @@ -310,3 +358,43 @@ function createFallbackRoutingConfig(owner: Address): IsmConfig { owner, }; } + +export async function getWarpRouteDeployConfig({ + context, + warpRouteDeployConfigPath, + warpRouteId: providedWarpRouteId, + symbol, +}: { + context: CommandContext; + warpRouteDeployConfigPath?: string; + warpRouteId?: string; + symbol?: string; +}): Promise { + let warpDeployConfig: WarpRouteDeployConfigMailboxRequired; + + if (warpRouteDeployConfigPath) { + assert( + isFile(warpRouteDeployConfigPath), + `Warp route deployment config file not found at ${warpRouteDeployConfigPath}`, + ); + log(`Using warp route deployment config at ${warpRouteDeployConfigPath}`); + + warpDeployConfig = await readWarpRouteDeployConfig({ + context, + filePath: warpRouteDeployConfigPath, + }); + } else { + const warpRouteId = await useProvidedWarpRouteIdOrPrompt({ + warpRouteId: providedWarpRouteId, + context, + symbol, + }); + + warpDeployConfig = await readWarpRouteDeployConfig({ + context, + warpRouteId, + }); + } + + return warpDeployConfig; +} diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index a99a08802a6..3b02c9e31f6 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -6,6 +6,8 @@ import type { ChainMap, ChainMetadata, MultiProvider, + WarpCoreConfig, + WarpRouteDeployConfigMailboxRequired, } from '@hyperlane-xyz/sdk'; export interface ContextSettings { @@ -35,6 +37,8 @@ export interface WriteCommandContext extends CommandContext { signer: ethers.Signer; isDryRun?: boolean; dryRunChain?: string; + warpDeployConfig?: WarpRouteDeployConfigMailboxRequired; + warpCoreConfig?: WarpCoreConfig; } export type CommandModuleWithContext = CommandModule< diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index a094877c2ed..c58cb52876e 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -120,10 +120,10 @@ export async function runWarpRouteDeploy({ `Using warp route deployment config at ${warpRouteDeploymentConfigPath}`, ); } - const warpRouteConfig = await readWarpRouteDeployConfig( - warpRouteDeploymentConfigPath, + const warpRouteConfig = await readWarpRouteDeployConfig({ + filePath: warpRouteDeploymentConfigPath, context, - ); + }); const chains = Object.keys(warpRouteConfig); diff --git a/typescript/cli/src/utils/warp.ts b/typescript/cli/src/utils/warp.ts index 310e96c633d..0dbb9378f78 100644 --- a/typescript/cli/src/utils/warp.ts +++ b/typescript/cli/src/utils/warp.ts @@ -4,7 +4,6 @@ import { filterWarpRoutesIds } from '@hyperlane-xyz/registry'; import { WarpCoreConfig, WarpRouteDeployConfigMailboxRequired, - WarpRouteDeployConfigMailboxRequiredSchema, } from '@hyperlane-xyz/sdk'; import { assert, @@ -14,11 +13,10 @@ import { } from '@hyperlane-xyz/utils'; import { - fillDefaults, readWarpCoreConfig, readWarpRouteDeployConfig, } from '../config/warp.js'; -import { CommandContext } from '../context/types.js'; +import { CommandContext, WriteCommandContext } from '../context/types.js'; import { logRed } from '../logger.js'; import { selectRegistryWarpRoute } from './tokens.js'; @@ -42,7 +40,7 @@ export async function getWarpCoreConfigOrExit({ if (symbol) { warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); } else if (warp) { - warpCoreConfig = readWarpCoreConfig(warp); + warpCoreConfig = await readWarpCoreConfig({ filePath: warp }); } else { logRed(`Please specify either a symbol or warp config`); process.exit(0); @@ -51,6 +49,64 @@ export async function getWarpCoreConfigOrExit({ return warpCoreConfig; } +/** + * Gets or prompts user selection for a warp route ID. + * Uses provided ID or filters by symbol and prompts if multiple options exist. + */ +export async function useProvidedWarpRouteIdOrPrompt({ + context, + warpRouteId, + symbol, +}: { + context: CommandContext; + warpRouteId?: string; + symbol?: string; +}): Promise { + if (warpRouteId) return warpRouteId; + assert(!context.skipConfirmation, 'Warp route ID is required'); + + const { ids: routeIds } = filterWarpRoutesIds( + (await context.registry.listRegistryContent()).deployments.warpRoutes, + symbol ? { symbol } : undefined, + ); + + assert(routeIds.length !== 0, 'No valid warp routes found in registry'); + + return routeIds.length === 1 + ? routeIds[0] + : ((await search({ + message: 'Select a warp route:', + source: (term) => { + return routeIds.filter((id) => + id.toLowerCase().includes(term?.toLowerCase() || ''), + ); + }, + pageSize: 20, + })) as string); +} + +async function loadWarpConfigsFromFiles({ + warpDeployConfigPath, + warpCoreConfigPath, + context, +}: { + warpDeployConfigPath: string; + warpCoreConfigPath: string; + context: CommandContext; +}): Promise<{ + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; + warpCoreConfig: WarpCoreConfig; +}> { + const warpDeployConfig = await readWarpRouteDeployConfig({ + filePath: warpDeployConfigPath, + context, + }); + const warpCoreConfig = await readWarpCoreConfig({ + filePath: warpCoreConfigPath, + }); + return { warpDeployConfig, warpCoreConfig }; +} + /** * Gets both warp configs based on the provided inputs. Handles all cases: * - warpRouteId: gets configs directly from registry @@ -65,7 +121,7 @@ export async function getWarpConfigs({ warpCoreConfigPath, symbol, }: { - context: CommandContext; + context: CommandContext | WriteCommandContext; warpRouteId?: string; warpDeployConfigPath?: string; warpCoreConfigPath?: string; @@ -74,58 +130,50 @@ export async function getWarpConfigs({ warpDeployConfig: WarpRouteDeployConfigMailboxRequired; warpCoreConfig: WarpCoreConfig; }> { - if (warpDeployConfigPath || warpCoreConfigPath) { - if (!warpDeployConfigPath || !warpCoreConfigPath) { - throw new Error( - 'Both --config/-wd and --warp/-wc must be provided together when using individual file paths', - ); - } - const warpDeployConfig = await readWarpRouteDeployConfig( - warpDeployConfigPath, - context, - ); - const warpCoreConfig = readWarpCoreConfig(warpCoreConfigPath); - return { warpDeployConfig, warpCoreConfig }; - } - - let selectedId = warpRouteId; - if (!selectedId) { - const { ids: routeIds } = filterWarpRoutesIds( - (await context.registry.listRegistryContent()).deployments.warpRoutes, - symbol ? { symbol } : undefined, - ); - - assert(routeIds.length !== 0, 'No valid warp routes found in registry'); - - selectedId = - routeIds.length === 1 - ? routeIds[0] - : ((await search({ - message: 'Select a warp route:', - source: (term) => { - return routeIds.filter((id) => - id.toLowerCase().includes(term?.toLowerCase() || ''), - ); - }, - pageSize: 20, - })) as string); + if ( + 'warpCoreConfig' in context && + 'warpDeployConfig' in context && + context.warpCoreConfig && + context.warpDeployConfig + ) { + return { + warpDeployConfig: context.warpDeployConfig, + warpCoreConfig: context.warpCoreConfig, + }; } - const warpCoreConfig = await context.registry.getWarpRoute(selectedId); - assert(warpCoreConfig, `Missing warp config for warp route ${selectedId}.`); - const warpDeployConfig = - await context.registry.getWarpDeployConfig(selectedId); + const hasDeployConfigFilePath = !!warpDeployConfigPath; + const hasCoreConfigFilePath = !!warpCoreConfigPath; assert( - warpDeployConfig, - `Missing warp deploy config for warp route ${selectedId}.`, + hasDeployConfigFilePath === hasCoreConfigFilePath, + 'Both --config/-wd and --warp/-wc must be provided together when using individual file paths', ); - const filledConfig = await fillDefaults(context, warpDeployConfig); - const validatedConfig = - WarpRouteDeployConfigMailboxRequiredSchema.parse(filledConfig); + if (hasDeployConfigFilePath && hasCoreConfigFilePath) { + return loadWarpConfigsFromFiles({ + warpDeployConfigPath, + warpCoreConfigPath, + context, + }); + } + + const selectedId = await useProvidedWarpRouteIdOrPrompt({ + context, + warpRouteId, + symbol, + }); + + const warpCoreConfig = await readWarpCoreConfig({ + context, + warpRouteId: selectedId, + }); + const warpDeployConfig = await readWarpRouteDeployConfig({ + warpRouteId: selectedId, + context, + }); return { - warpDeployConfig: validatedConfig, + warpDeployConfig, warpCoreConfig, }; } From a539ecccdc5156df4df9823e9bbe835236017105 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Thu, 8 May 2025 17:01:38 +0100 Subject: [PATCH 153/223] fix: Add rule for CryptoCrew (#6176) ### Description Add rule for CryptoCrew ### Backward compatibility Yes ### Testing Manual, locally --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .gitleaks.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitleaks.toml b/.gitleaks.toml index e065f2ee1be..55023f074f5 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -86,3 +86,13 @@ keywords = [ "grove.city" ] tags = ["key", "Grove City"] + +[[rules]] +id = "ccvalidators-api-key" +description = "CryptoCrew API Key" +regex = '''https://(rpc|grpc|rest)\.[a-zA-Z0-9-]+\.ccvalidators\.com:?[0-9]*/?[a-zA-Z0-9-]*''' +keywords = [ + "ccvalidators", + "ccvalidators.com" +] +tags = ["key", "Crypto Crew"] From c517e5c8863373a078c25eff37115d1aa16fb0d5 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Thu, 8 May 2025 17:47:29 +0100 Subject: [PATCH 154/223] feat(submitter): Report reverted payload with success criteria (#6170) ### Description - Generate contract calls to check if a particular message was delivered - Populate payload success criteria with serialized contract calls - Check if success criteria is met at finality stage of Lander ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/agents/relayer/src/msg/op_queue.rs | 4 ++ .../agents/relayer/src/msg/op_submitter.rs | 18 +++++++- .../agents/relayer/src/msg/pending_message.rs | 6 +++ .../hyperlane-cosmos-native/src/mailbox.rs | 4 ++ .../hyperlane-cosmos/src/mailbox/contract.rs | 7 ++- .../src/contracts/mailbox.rs | 18 +++++--- .../src/rpc_clients/provider.rs | 23 +++++++--- .../main/chains/hyperlane-fuel/src/mailbox.rs | 4 ++ .../chains/hyperlane-sealevel/src/mailbox.rs | 4 ++ .../main/hyperlane-core/src/traits/mailbox.rs | 3 ++ .../src/traits/pending_operation.rs | 6 +++ .../src/traits/pending_operation/tests.rs | 3 ++ rust/main/hyperlane-test/src/mocks/mailbox.rs | 4 ++ .../submitter/src/chain_tx_adapter/adapter.rs | 10 +++-- .../src/chain_tx_adapter/chains/cosmos.rs | 14 +++--- .../chains/ethereum/adapter.rs | 28 +++++++++--- .../chains/ethereum/payload.rs | 9 +--- .../chains/ethereum/payload/parse.rs | 27 ++++++++++++ .../chains/ethereum/precursor.rs | 10 ++++- .../chains/sealevel/adapter.rs | 44 ++++--------------- rust/main/submitter/src/payload/types.rs | 22 +++++++--- 21 files changed, 186 insertions(+), 82 deletions(-) create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload/parse.rs diff --git a/rust/main/agents/relayer/src/msg/op_queue.rs b/rust/main/agents/relayer/src/msg/op_queue.rs index 9cdabfc8eb8..464fd203056 100644 --- a/rust/main/agents/relayer/src/msg/op_queue.rs +++ b/rust/main/agents/relayer/src/msg/op_queue.rs @@ -384,6 +384,10 @@ pub mod test { todo!() } + fn success_criteria(&self) -> ChainResult>> { + todo!() + } + fn on_reprepare( &mut self, _err_msg: Option, diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index 6d76632f7b3..fa2c4b5788e 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -661,6 +661,16 @@ async fn submit_via_lander( } }; + let operation_success_criteria = match op.success_criteria() { + Ok(s) => s, + Err(e) => { + let reason = ReprepareReason::ErrorCreatingPayloadSuccessCriteria; + let msg = "Error creating payload success criteria"; + prepare_op(op, prepare_queue, e, msg, reason).await; + return; + } + }; + let message_id = op.id(); let metadata = format!("{message_id:?}"); let mailbox = op @@ -668,7 +678,13 @@ async fn submit_via_lander( .expect("Operation should contain Mailbox address") .address(); let payload_id = PayloadId::random(); - let payload = FullPayload::new(payload_id, metadata, operation_payload, mailbox); + let payload = FullPayload::new( + payload_id, + metadata, + operation_payload, + operation_success_criteria, + mailbox, + ); if let Err(e) = entrypoint.send_payload(&payload).await { let reason = ReprepareReason::ErrorSubmitting; diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index aabd5d0d1e2..574e7b25915 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -552,6 +552,12 @@ impl PendingOperation for PendingMessage { Ok(payload) } + fn success_criteria(&self) -> ChainResult>> { + let mailbox = &self.ctx.destination_mailbox; + let message = &self.message; + mailbox.delivered_calldata(message.id()) + } + fn on_reprepare( &mut self, err: Option, diff --git a/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs b/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs index e1b4ad34f49..7c2fd9903ad 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs @@ -182,4 +182,8 @@ impl Mailbox for CosmosNativeMailbox { ) -> ChainResult> { todo!() // we dont need this for now } + + fn delivered_calldata(&self, _message_id: H256) -> ChainResult>> { + todo!() + } } diff --git a/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs b/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs index 7bb17989729..501e3a693f2 100644 --- a/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs +++ b/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU64; use std::str::FromStr; use async_trait::async_trait; @@ -18,7 +17,7 @@ use crate::payloads::mailbox::{ }; use crate::types::tx_response_to_outcome; use crate::utils::get_block_height_for_reorg_period; -use crate::{payloads, ConnectionConf, CosmosAddress, CosmosProvider, Signer}; +use crate::{payloads, ConnectionConf, CosmosAddress, CosmosProvider}; #[derive(Clone, Debug)] /// A reference to a Mailbox contract on some Cosmos chain @@ -206,6 +205,10 @@ impl Mailbox for CosmosMailbox { ) -> ChainResult> { todo!() // not required } + + fn delivered_calldata(&self, message_id: H256) -> ChainResult>> { + todo!() + } } impl CosmosMailbox { diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index 72a27a2ef3e..b11133660fd 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -14,17 +14,16 @@ use ethers_contract::builders::ContractCall; use ethers_contract::{Multicall, MulticallResult}; use ethers_core::utils::WEI_IN_ETHER; use futures_util::future::join_all; -use hyperlane_core::rpc_clients::call_and_retry_indefinitely; -use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512}; use tokio::join; use tokio::sync::Mutex; use tracing::instrument; use hyperlane_core::{ - utils::bytes_to_hex, BatchItem, ChainCommunicationError, ChainResult, ContractLocator, - HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, - HyperlaneProtocolError, HyperlaneProvider, Indexed, Indexer, LogMeta, Mailbox, - RawHyperlaneMessage, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H160, H256, U256, + rpc_clients::call_and_retry_indefinitely, utils::bytes_to_hex, BatchItem, BatchResult, + ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, + HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProtocolError, + HyperlaneProvider, Indexed, Indexer, LogMeta, Mailbox, QueueOperation, RawHyperlaneMessage, + ReorgPeriod, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H160, H256, H512, U256, }; use crate::error::HyperlaneEthereumError; @@ -693,6 +692,13 @@ where let data = (contract_call.tx, contract_call.function); serde_json::to_vec(&data).map_err(Into::into) } + + fn delivered_calldata(&self, message_id: H256) -> ChainResult>> { + let call = self.contract.delivered(message_id.into()); + + let data = (call.tx, call.function); + serde_json::to_vec(&data).map(Some).map_err(Into::into) + } } pub struct EthereumMailboxAbi; diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index 58ea8a08d09..98f1fa310d6 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -35,17 +35,17 @@ pub struct EthereumProvider { impl EthereumProvider { /// Create a ContractCall object for a given transaction and function. - pub fn build_contract_call( + pub fn build_contract_call( &self, tx: TypedTransaction, function: Function, - ) -> ContractCall { + ) -> ContractCall { ContractCall { tx, function, block: None, client: self.provider.clone(), - datatype: PhantomData::<()>, + datatype: PhantomData::, } } } @@ -93,6 +93,9 @@ pub trait EvmProviderForSubmitter: Send + Sync { /// Send transaction into blockchain async fn send(&self, tx: &TypedTransaction, function: &Function) -> ChainResult; + + /// Read-only call into blockchain which returns a boolean + async fn check(&self, tx: &TypedTransaction, function: &Function) -> ChainResult; } #[async_trait] @@ -133,13 +136,13 @@ where tx: &TypedTransaction, function: &Function, ) -> Result { - let contract_call = self.build_contract_call(tx.clone(), function.clone()); + let contract_call = self.build_contract_call::<()>(tx.clone(), function.clone()); let gas_limit = contract_call.estimate_gas().await?.into(); Ok(gas_limit) } async fn send(&self, tx: &TypedTransaction, function: &Function) -> ChainResult { - let contract_call = self.build_contract_call(tx.clone(), function.clone()); + let contract_call = self.build_contract_call::<()>(tx.clone(), function.clone()); let pending = contract_call .send() .await @@ -147,6 +150,16 @@ where Ok(pending.tx_hash().into()) } + + async fn check(&self, tx: &TypedTransaction, function: &Function) -> ChainResult { + let contract_call = self.build_contract_call::(tx.clone(), function.clone()); + let success = contract_call + .call() + .await + .map_err(|e| ChainCommunicationError::CustomError(e.to_string()))?; + + Ok(success) + } } #[async_trait] diff --git a/rust/main/chains/hyperlane-fuel/src/mailbox.rs b/rust/main/chains/hyperlane-fuel/src/mailbox.rs index 7c2342e5ae9..56511f010fe 100644 --- a/rust/main/chains/hyperlane-fuel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-fuel/src/mailbox.rs @@ -220,6 +220,10 @@ impl Mailbox for FuelMailbox { // Seems like this is not needed for Fuel, as it's only used in mocks todo!() } + + fn delivered_calldata(&self, message_id: H256) -> ChainResult>> { + todo!() + } } /// Struct that retrieves event data for a Fuel Mailbox contract diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 69020d2fced..6764e8471db 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -552,4 +552,8 @@ impl Mailbox for SealevelMailbox { let process_instruction = self.get_process_instruction(message, metadata).await?; serde_json::to_vec(&process_instruction).map_err(Into::into) } + + fn delivered_calldata(&self, _message_id: H256) -> ChainResult>> { + Ok(None) + } } diff --git a/rust/main/hyperlane-core/src/traits/mailbox.rs b/rust/main/hyperlane-core/src/traits/mailbox.rs index e7f97238f02..afffc69f988 100644 --- a/rust/main/hyperlane-core/src/traits/mailbox.rs +++ b/rust/main/hyperlane-core/src/traits/mailbox.rs @@ -71,6 +71,9 @@ pub trait Mailbox: HyperlaneContract + Send + Sync + Debug { message: &HyperlaneMessage, metadata: &[u8], ) -> ChainResult>; + + /// Get the calldata for a call which allows to check if a particular messages was delivered + fn delivered_calldata(&self, message_id: H256) -> ChainResult>>; } /// The result of processing a batch of messages diff --git a/rust/main/hyperlane-core/src/traits/pending_operation.rs b/rust/main/hyperlane-core/src/traits/pending_operation.rs index ebb20e16089..c9f48fc9201 100644 --- a/rust/main/hyperlane-core/src/traits/pending_operation.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation.rs @@ -171,6 +171,9 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { /// Creates payload for the operation async fn payload(&self) -> ChainResult>; + /// Creates success criteria for the operation + fn success_criteria(&self) -> ChainResult>>; + /// Public version of on_reprepare method fn on_reprepare( &mut self, @@ -285,6 +288,9 @@ pub enum ReprepareReason { #[strum(to_string = "Failed to retrieve payload id status by message id")] /// Failed to retrieve payload id status by message id ErrorRetrievingPayloadStatus, + #[strum(to_string = "Failed to create payload success criteria")] + /// Failed to create payload success criteria + ErrorCreatingPayloadSuccessCriteria, } #[derive(Display, Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs b/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs index 1428ef7c47e..5ef6602681c 100644 --- a/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation/tests.rs @@ -77,6 +77,9 @@ impl PendingOperation for MockQueueOperation { async fn payload(&self) -> ChainResult> { unimplemented!() } + fn success_criteria(&self) -> ChainResult>> { + unimplemented!() + } fn on_reprepare( &mut self, _err_msg: Option, diff --git a/rust/main/hyperlane-test/src/mocks/mailbox.rs b/rust/main/hyperlane-test/src/mocks/mailbox.rs index dc6b9bb00e2..50a775eeffe 100644 --- a/rust/main/hyperlane-test/src/mocks/mailbox.rs +++ b/rust/main/hyperlane-test/src/mocks/mailbox.rs @@ -115,6 +115,10 @@ impl Mailbox for MockMailboxContract { Ok(self.process_calldata(message, metadata)) } + fn delivered_calldata(&self, _message_id: H256) -> ChainResult>> { + Ok(None) + } + async fn process_batch<'a>(&self, ops: Vec<&'a QueueOperation>) -> ChainResult { self.process_batch(ops) } diff --git a/rust/main/submitter/src/chain_tx_adapter/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/adapter.rs index b1d99b02864..4676f3bb202 100644 --- a/rust/main/submitter/src/chain_tx_adapter/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/adapter.rs @@ -5,7 +5,6 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; -use eyre::Report; use futures_util::future::join_all; use tokio::sync::mpsc::error::SendError; use tracing::{info, instrument}; @@ -81,11 +80,14 @@ pub trait AdaptsChain: Send + Sync { true } - /// uses BatchManager, returns any reverted Payload IDs sent in a Transaction. Called in the Finality Stage (PayloadDispatcher) + /// Uses BatchManager, returns any reverted Payload IDs sent in a Transaction. + /// Called in the Finality Stage (PayloadDispatcher). async fn reverted_payloads( &self, - tx: &Transaction, - ) -> Result, SubmitterError>; + _tx: &Transaction, + ) -> Result, SubmitterError> { + Ok(Vec::new()) + } /// Returns the estimated block time of the chain. Used for polling pending transactions. Called in the Inclusion and Finality Stages of the PayloadDispatcher fn estimated_block_time(&self) -> &Duration; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs b/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs index 676f587ed8e..7030da6b26a 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/cosmos.rs @@ -50,6 +50,13 @@ impl AdaptsChain for CosmosTxAdapter { todo!() } + async fn get_tx_hash_status( + &self, + _hash: hyperlane_core::H512, + ) -> std::result::Result { + todo!() + } + async fn reverted_payloads( &self, _tx: &Transaction, @@ -64,11 +71,4 @@ impl AdaptsChain for CosmosTxAdapter { fn max_batch_size(&self) -> u32 { todo!() } - - async fn get_tx_hash_status( - &self, - _hash: hyperlane_core::H512, - ) -> std::result::Result { - todo!() - } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs index e431a8f1a3f..2542633df4a 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -6,7 +6,6 @@ use ethers::contract::builders::ContractCall; use ethers::prelude::U64; use ethers::providers::Middleware; use ethers::types::H256; -use eyre::Result; use tracing::{info, warn}; use uuid::Uuid; @@ -16,7 +15,6 @@ use hyperlane_base::CoreMetrics; use hyperlane_core::ContractLocator; use hyperlane_ethereum::{EthereumReorgPeriod, EvmProviderForSubmitter, SubmitterProviderBuilder}; -use crate::chain_tx_adapter::chains::ethereum::transaction::Precursor; use crate::{ chain_tx_adapter::{adapter::TxBuildingResult, AdaptsChain, EthereumTxPrecursor, GasLimit}, error::SubmitterError, @@ -24,6 +22,8 @@ use crate::{ transaction::{Transaction, TransactionStatus}, }; +use super::transaction::Precursor; + mod gas_limit_estimator; mod tx_status_checker; @@ -101,7 +101,7 @@ impl AdaptsChain for EthereumTxAdapter { todo!() } - async fn estimate_tx(&self, tx: &mut Transaction) -> std::result::Result<(), SubmitterError> { + async fn estimate_tx(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { let precursor = tx.precursor_mut(); gas_limit_estimator::estimate_tx( &self.provider, @@ -140,9 +140,27 @@ impl AdaptsChain for EthereumTxAdapter { async fn reverted_payloads( &self, - _tx: &Transaction, + tx: &Transaction, ) -> Result, SubmitterError> { - todo!() + let payload_details_and_precursors = tx + .payload_details + .iter() + .filter_map(|d| EthereumTxPrecursor::from_success_criteria(d).map(|p| (d, p))) + .collect::>(); + + let mut reverted = Vec::new(); + for (detail, precursor) in payload_details_and_precursors { + let success = self + .provider + .check(&precursor.tx, &precursor.function) + .await + .unwrap_or(true); + if !success { + reverted.push(detail.clone()); + } + } + + Ok(reverted) } fn estimated_block_time(&self) -> &std::time::Duration { diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs index fbe4e8faa4c..a196f3fb595 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload.rs @@ -1,8 +1,3 @@ -use crate::FullPayload; -use ethers::abi::Function; -use ethers::types::transaction::eip2718::TypedTransaction; +pub use parse::{parse_data, parse_success_criteria}; -pub fn parse_data(payload: &FullPayload) -> (TypedTransaction, Function) { - serde_json::from_slice::<(TypedTransaction, Function)>(&payload.data) - .expect("Payload should contain tuple of TypedTransaction and Function for Ethereum") -} +mod parse; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload/parse.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload/parse.rs new file mode 100644 index 00000000000..ea7cc98bf8c --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/payload/parse.rs @@ -0,0 +1,27 @@ +use ethers::abi::Function; +use ethers::types::transaction::eip2718::TypedTransaction; + +use crate::payload::PayloadDetails; +use crate::FullPayload; + +pub fn parse_data(payload: &FullPayload) -> (TypedTransaction, Function) { + parse(&payload.data).expect( + "Payload should contain tuple of TypedTransaction and Function for Ethereum as data", + ) +} + +pub fn parse_success_criteria( + payload_details: &PayloadDetails, +) -> Option<(TypedTransaction, Function)> { + payload_details + .success_criteria + .as_ref() + .map(|data| parse(data)) + .map(|r| { + r.expect("Payload should contain tuple of TypedTransaction and Function for Ethereum as success criteria") + }) +} + +fn parse(data: &[u8]) -> serde_json::Result<(TypedTransaction, Function)> { + serde_json::from_slice::<(TypedTransaction, Function)>(data) +} diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs index 5a6d3cbc2ae..9dc68b9f9d6 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use ethers::abi::Function; use ethers::types::transaction::eip2718::TypedTransaction; -use crate::payload::FullPayload; +use crate::payload::{FullPayload, PayloadDetails}; #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct EthereumTxPrecursor { @@ -29,9 +29,15 @@ impl EthereumTxPrecursor { } pub fn from_payload(payload: &FullPayload) -> Self { - use crate::chain_tx_adapter::chains::ethereum::payload::parse_data; + use super::payload::parse_data; let (tx, function) = parse_data(payload); EthereumTxPrecursor::new(tx, function) } + + pub fn from_success_criteria(details: &PayloadDetails) -> Option { + use super::payload::parse_success_criteria; + + parse_success_criteria(details).map(|(tx, function)| EthereumTxPrecursor::new(tx, function)) + } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs index d87ae8ce69f..3c944be1f5c 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/sealevel/adapter.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use chrono::Utc; -use eyre::{bail, eyre, ContextCompat, Report}; +use eyre::eyre; use futures_util::future::join_all; use serde_json::json; use solana_client::rpc_response::{Response, RpcSimulateTransactionResult}; @@ -376,26 +376,6 @@ impl AdaptsChain for SealevelTxAdapter { Ok(()) } - #[instrument(skip(self))] - async fn tx_status(&self, tx: &Transaction) -> Result { - info!(?tx, "checking status of transaction"); - - if tx.tx_hashes.is_empty() { - return Ok(TransactionStatus::PendingInclusion); - } - - let hash_status_futures = tx - .tx_hashes - .iter() - .map(|tx_hash| self.get_tx_hash_status(*tx_hash)) - .collect::>(); - // this may lead to rate limiting if too many hashes build up. Consider querying from most recent to oldest - let hash_status_results = join_all(hash_status_futures).await; - Ok(TransactionStatus::classify_tx_status_from_hash_statuses( - hash_status_results, - )) - } - async fn get_tx_hash_status(&self, hash: H512) -> Result { info!(?hash, "getting transaction hash status"); let status = self.get_tx_hash_status(hash).await?; @@ -403,12 +383,13 @@ impl AdaptsChain for SealevelTxAdapter { Ok(status) } - async fn reverted_payloads( - &self, - _tx: &Transaction, - ) -> Result, SubmitterError> { - // Dummy implementation of reverted payloads for Sealevel since we don't have batching for Sealevel - Ok(Vec::new()) + async fn tx_ready_for_resubmission(&self, tx: &Transaction) -> bool { + if let Some(ref last_submission_time) = tx.last_submission_attempt { + let seconds_since_last_submission = + (Utc::now() - last_submission_time).num_seconds() as u64; + return seconds_since_last_submission >= TX_RESUBMISSION_MIN_DELAY_SECS; + } + true } fn estimated_block_time(&self) -> &Duration { @@ -418,15 +399,6 @@ impl AdaptsChain for SealevelTxAdapter { fn max_batch_size(&self) -> u32 { self.max_batch_size } - - async fn tx_ready_for_resubmission(&self, tx: &Transaction) -> bool { - if let Some(ref last_submission_time) = tx.last_submission_attempt { - let seconds_since_last_submission = - (Utc::now() - last_submission_time).num_seconds() as u64; - return seconds_since_last_submission >= TX_RESUBMISSION_MIN_DELAY_SECS; - } - true - } } #[cfg(test)] diff --git a/rust/main/submitter/src/payload/types.rs b/rust/main/submitter/src/payload/types.rs index df12123f315..72c7b7220ee 100644 --- a/rust/main/submitter/src/payload/types.rs +++ b/rust/main/submitter/src/payload/types.rs @@ -2,10 +2,8 @@ #![allow(dead_code)] use std::fmt::Debug; -use std::ops::Deref; use chrono::{DateTime, Utc}; -use uuid::Uuid; use hyperlane_core::{identifiers::UniqueIdentifier, H256, U256}; @@ -25,15 +23,19 @@ pub struct PayloadDetails { // unused field in MVP /// view calls for checking if batch subcalls reverted. EVM-specific for now. - pub success_criteria: Option<(Vec, Address)>, + pub success_criteria: Option>, } impl PayloadDetails { - pub fn new(id: PayloadId, metadata: impl Into) -> Self { + pub fn new( + id: PayloadId, + metadata: impl Into, + success_criteria: Option>, + ) -> Self { Self { id, metadata: metadata.into(), - success_criteria: None, + success_criteria, } } } @@ -71,9 +73,15 @@ impl Debug for FullPayload { } impl FullPayload { - pub fn new(id: PayloadId, metadata: impl Into, data: Vec, to: Address) -> Self { + pub fn new( + id: PayloadId, + metadata: impl Into, + data: Vec, + success_criteria: Option>, + to: Address, + ) -> Self { Self { - details: PayloadDetails::new(id, metadata), + details: PayloadDetails::new(id, metadata, success_criteria), data, to, status: Default::default(), From ce95ffc52c2fcdc6953ec845b46d527aa3e01db5 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 9 May 2025 10:24:39 +0100 Subject: [PATCH 155/223] feat: fixes to SVM warp deploy tooling (#6178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description Two things: 1. Updates the commit for our fork of the spl lib that's used to include https://github.com/hyperlane-xyz/solana-program-library/pull/4 which fixes compilation 2. Our tooling generally requires Solana 1.18.18 to be used when running our SVM tooling to perform the program deploy. Eclipse used to allow you to deploy programs just fine with this version but Christopher and I experienced this regression recently where the command just hangs indefinitely. The “workaround” seems to be to downgrade to 1.17.31, but this comes at the cost of not being able to use a CLI flag to set the compute unit price when deploying a program As a temporary fix, bc the compute unit price is originally set to 0 on Eclipse anyways, I'm changing the tooling to omit this compute unit price flag if it’s set to 0. But it’s still possible for us to hit issues here because we bid up compute unit prices if there are issues deploying the program (not a rare occurrence). If you do a deploy to Eclipse and hit issues, I’d recommend just commenting out the compute unit price flag at least for the eclipse leg ### Drive-by changes ### Related issues ### Backward compatibility ### Testing - `cargo +1.76.0 install spl-token-cli --git https://github.com/hyperlane-xyz/solana-program-library --branch dan/create-token-for-mint --rev e101cca --force` works - confirmed not providing the flag works --- rust/sealevel/client/src/cmd_utils.rs | 13 ++++++++++++- rust/sealevel/client/src/warp_route.rs | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rust/sealevel/client/src/cmd_utils.rs b/rust/sealevel/client/src/cmd_utils.rs index f8e20b4d641..dd95a1147f6 100644 --- a/rust/sealevel/client/src/cmd_utils.rs +++ b/rust/sealevel/client/src/cmd_utils.rs @@ -18,6 +18,8 @@ use solana_sdk::{ signature::{Keypair, Signer}, }; +use crate::ECLIPSE_DOMAIN; + const SOLANA_DOMAIN: u32 = 1399811149; pub(crate) fn get_compute_unit_price_micro_lamports_for_id(domain: u32) -> u64 { @@ -84,6 +86,13 @@ pub(crate) fn deploy_program( ); } + // Temporary measure for Eclipse due to incompatibility with Solana CLI 1.18.18 + // to avoid setting a non-zero compute unit price, which is not supported + // by earlier versions of the Solana CLI. + if local_domain == ECLIPSE_DOMAIN { + compute_unit_price = 0; + } + if attempt_program_deploy( payer_keypair_path, program_name, @@ -150,7 +159,9 @@ fn attempt_program_deploy( ]; let compute_unit_price_str = compute_unit_price.to_string(); - command.extend(vec!["--with-compute-unit-price", &compute_unit_price_str]); + if compute_unit_price > 0 { + command.extend(vec!["--with-compute-unit-price", &compute_unit_price_str]); + } // Success! if let Ok(true) = run_cmd(command.as_slice(), None, None) { diff --git a/rust/sealevel/client/src/warp_route.rs b/rust/sealevel/client/src/warp_route.rs index 60843741f74..7b98145b958 100644 --- a/rust/sealevel/client/src/warp_route.rs +++ b/rust/sealevel/client/src/warp_route.rs @@ -746,7 +746,7 @@ pub fn install_spl_token_cli() { "--branch", "dan/create-token-for-mint", "--rev", - "ae4c8ac46", + "e101cca", "--locked", ]) .stdout(Stdio::inherit()) From 98209a2463d9a862a357062cb31149f2d086364b Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 May 2025 05:48:18 -0400 Subject: [PATCH 156/223] feat: chunk message db insertions (#6114) ### Description Instead of inserting all the messages we receive in a single db transaction, we chunk it into smaller sizes. This prevents us from running into `Too many arguments` errors from Postgres for exceeding their `u16::MAX` parameter count. ### Related issues - fixes https://linear.app/hyperlane-xyz/issue/ENG-1390/scraper-cannot-insert-too-many-records ### Backward compatibility Yes ### Testing Manual. From Jeff: Tried it locally with a postgres instance with 100k messages and it failed without chunking ``` running 1 test RES: Err(Query Error: encountered unexpected or invalid data: PgConnection::run(): too many arguments for query: 1000000 (sqlx_postgres::connection::executor:215) Caused by: 0: encountered unexpected or invalid data: PgConnection::run(): too many arguments for query: 1000000 (sqlx_postgres::connection::executor:215) 1: encountered unexpected or invalid data: PgConnection::run(): too many arguments for query: 1000000 (sqlx_postgres::connection::executor:215) Location: agents/scraper/src/db/message.rs:320:9) test db::message::tests::test_store_dispatched_messages_real_postgres ... ok ``` Tried it again with chunking + transaction and it didn't have any errors :+1: So we can use the transaction if we want the action to be atomic, but the important part is the chunking. --- rust/main/agents/scraper/src/db/message.rs | 229 ++++++++++++++++-- .../templates/relayer-statefulset.yaml | 4 +- .../templates/scraper-statefulset.yaml | 4 +- .../templates/validator-statefulset.yaml | 2 +- 4 files changed, 215 insertions(+), 24 deletions(-) diff --git a/rust/main/agents/scraper/src/db/message.rs b/rust/main/agents/scraper/src/db/message.rs index fc39049049c..714cdd32f35 100644 --- a/rust/main/agents/scraper/src/db/message.rs +++ b/rust/main/agents/scraper/src/db/message.rs @@ -2,7 +2,9 @@ use eyre::Result; use itertools::Itertools; -use sea_orm::{prelude::*, ActiveValue::*, DeriveColumn, EnumIter, Insert, QuerySelect}; +use sea_orm::{ + prelude::*, ActiveValue::*, DeriveColumn, EnumIter, Insert, QuerySelect, TransactionTrait, +}; use tracing::{debug, instrument, trace}; use hyperlane_core::{ @@ -32,6 +34,14 @@ pub struct StorableMessage<'a> { } impl ScraperDb { + /// Used for store_dispatched_messages() + /// message::ActiveModel has 11 fields, on conflict has 3, + /// update has 6. So there should be about a maximum of 20 sql + /// parameters per ActiveModel + /// u16::MAX (65_535u16) is the maximum amount of parameters we can + /// have for Postgres. So 65000 / 20 = 3250 + const STORE_MESSAGE_CHUNK_SIZE: usize = 3250; + /// Get the delivered message associated with a sequence. #[instrument(skip(self))] pub async fn retrieve_delivery_by_sequence( @@ -274,9 +284,11 @@ impl ScraperDb { messages: impl Iterator>, ) -> Result { let origin_mailbox = address_to_bytes(origin_mailbox); + let latest_id_before = self .latest_dispatched_id(domain, origin_mailbox.clone()) .await?; + // we have a race condition where a message may not have been scraped yet even let models = messages .map(|storable| message::ActiveModel { @@ -305,24 +317,36 @@ impl ScraperDb { return Ok(0); } - Insert::many(models) - .on_conflict( - OnConflict::columns([ - message::Column::OriginMailbox, - message::Column::Origin, - message::Column::Nonce, - ]) - .update_columns([ - message::Column::TimeCreated, - message::Column::Destination, - message::Column::Sender, - message::Column::Recipient, - message::Column::MsgBody, - message::Column::OriginTxId, - ]) - .to_owned(), - ) - .exec(&self.0) + // ensure all chunks are inserted or none at all + self.0 + .transaction::<_, (), DbErr>(|txn| { + Box::pin(async move { + // insert messages in chunks, to not run into + // "Too many arguments" error + for chunk in models.chunks(Self::STORE_MESSAGE_CHUNK_SIZE) { + Insert::many(chunk.to_vec()) + .on_conflict( + OnConflict::columns([ + message::Column::OriginMailbox, + message::Column::Origin, + message::Column::Nonce, + ]) + .update_columns([ + message::Column::TimeCreated, + message::Column::Destination, + message::Column::Sender, + message::Column::Recipient, + message::Column::MsgBody, + message::Column::OriginTxId, + ]) + .to_owned(), + ) + .exec(txn) + .await?; + } + Ok(()) + }) + }) .await?; let new_dispatch_count = self @@ -336,3 +360,170 @@ impl ScraperDb { Ok(new_dispatch_count) } } + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use time::macros::*; + + use hyperlane_core::{HyperlaneMessage, LogMeta, H256}; + use sea_orm::{Database, DatabaseBackend, DbErr, MockDatabase, RuntimeErr, Value}; + use time::PrimitiveDateTime; + + use crate::db::{generated::message, ScraperDb, StorableMessage}; + + /// Tests store_dispatched_messages() a transaction works + #[tokio::test] + async fn test_store_dispatched_messages_transaction() { + const MESSAGE_AMOUNT: usize = 10000; + + let query_results: Vec> = (0..10000) + .map(|i| message::Model { + id: i as i64, + time_created: PrimitiveDateTime::new(date!(2019 - 01 - 01), time!(0:00)), + msg_id: vec![], + origin: 0, + destination: 0, + nonce: 0, + sender: vec![], + recipient: vec![], + msg_body: None, + origin_mailbox: vec![], + origin_tx_id: 0, + }) + .collect::>() + .chunks(ScraperDb::STORE_MESSAGE_CHUNK_SIZE) + .map(|v| v.to_vec()) + .collect(); + + let mock_result: BTreeMap<&str, _> = [ + ("num_items", Into::::into(10000i64)), + ("last_insert_id", Into::::into(10000i64)), + ] + .into_iter() + .collect(); + let mock_db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([[message::Model { + id: 0, + time_created: PrimitiveDateTime::new(date!(2019 - 01 - 01), time!(0:00)), + msg_id: vec![], + origin: 0, + destination: 0, + nonce: 0, + sender: vec![], + recipient: vec![], + msg_body: None, + origin_mailbox: vec![], + origin_tx_id: 0, + }]]) + .append_query_results(query_results) + .append_query_results([[mock_result.clone()]]) + .into_connection(); + let scraper_db = ScraperDb::with_connection(mock_db); + + let logs_meta: Vec<_> = (0..MESSAGE_AMOUNT).map(|_| LogMeta::default()).collect(); + let messages: Vec<_> = (0..MESSAGE_AMOUNT) + .map(|i| StorableMessage { + msg: HyperlaneMessage::default(), + meta: &logs_meta[i], + txn_id: i as i64, + }) + .collect(); + let res = scraper_db + .store_dispatched_messages(0, &H256::zero(), messages.into_iter()) + .await; + assert!(res.is_ok()); + } + + /// Tests store_dispatched_messages() fails if one of the queries + /// within the transaction fails + #[tokio::test] + async fn test_store_dispatched_messages_fail() { + const MESSAGE_AMOUNT: usize = 5000; + + let query_results: Vec> = (0..MESSAGE_AMOUNT) + .map(|i| message::Model { + id: i as i64, + time_created: PrimitiveDateTime::new(date!(2019 - 01 - 01), time!(0:00)), + msg_id: vec![], + origin: 0, + destination: 0, + nonce: 0, + sender: vec![], + recipient: vec![], + msg_body: None, + origin_mailbox: vec![], + origin_tx_id: 0, + }) + .collect::>() + .chunks(ScraperDb::STORE_MESSAGE_CHUNK_SIZE) + .map(|v| v.to_vec()) + .collect(); + + let mock_db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([[message::Model { + id: 0, + time_created: PrimitiveDateTime::new(date!(2019 - 01 - 01), time!(0:00)), + msg_id: vec![], + origin: 0, + destination: 0, + nonce: 0, + sender: vec![], + recipient: vec![], + msg_body: None, + origin_mailbox: vec![], + origin_tx_id: 0, + }]]) + .append_query_results(query_results) + // fail halfway through the transaction + .append_query_errors([DbErr::Exec(RuntimeErr::Internal( + "Unknown error".to_string(), + ))]) + .into_connection(); + let scraper_db = ScraperDb::with_connection(mock_db); + + let logs_meta: Vec<_> = (0..MESSAGE_AMOUNT).map(|_| LogMeta::default()).collect(); + let messages: Vec<_> = (0..MESSAGE_AMOUNT) + .map(|i| StorableMessage { + msg: HyperlaneMessage::default(), + meta: &logs_meta[i], + txn_id: i as i64, + }) + .collect(); + let res = scraper_db + .store_dispatched_messages(0, &H256::zero(), messages.into_iter()) + .await; + assert!(res.is_err()); + } + + /// Tests store_dispatched_messages() with a real postgres instance + #[ignore] + #[tokio::test] + async fn test_store_dispatched_messages_real_postgres() { + const POSTGRES_URL: &str = "postgresql://postgres:password@localhost:5432"; + const MESSAGE_AMOUNT: usize = 100000; + + let db = Database::connect(POSTGRES_URL).await.unwrap(); + let scraper_db = ScraperDb::with_connection(db); + + let logs_meta: Vec<_> = (0..MESSAGE_AMOUNT).map(|_| LogMeta::default()).collect(); + let messages: Vec<_> = (0..MESSAGE_AMOUNT) + .map(|i| { + let mut msg = HyperlaneMessage::default(); + msg.nonce = i as u32; + msg.origin = 1; + msg.destination = 1; + StorableMessage { + msg, + meta: &logs_meta[i], + txn_id: 0 as i64, + } + }) + .collect(); + let res = scraper_db + .store_dispatched_messages(0, &H256::zero(), messages.into_iter()) + .await; + assert!(res.is_ok()); + } +} diff --git a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml index 4dcf1a258c8..59448b681b1 100644 --- a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml @@ -11,7 +11,7 @@ spec: matchLabels: {{- include "agent-common.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: relayer - replicas: 1 + replicas: 1 serviceName: {{ include "agent-common.fullname" . }}-relayer template: metadata: @@ -119,7 +119,7 @@ spec: mountPath: {{ .Values.hyperlane.dbPath }} - name: relayer-configmap mountPath: /relayer-configmap - ports: + ports: - name: metrics containerPort: {{ .Values.hyperlane.metrics.port }} volumes: diff --git a/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml index 0ca8c184b7a..9afe1e01428 100644 --- a/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml @@ -11,7 +11,7 @@ spec: matchLabels: {{- include "agent-common.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: scraper3 - replicas: 1 + replicas: 1 serviceName: {{ include "agent-common.fullname" . }}-scraper3 template: metadata: @@ -60,7 +60,7 @@ spec: {{- include "agent-common.config-env-vars" (dict "config" .Values.hyperlane.scraper.config) | nindent 8 }} resources: {{- toYaml .Values.hyperlane.scraper.resources | nindent 10 }} - ports: + ports: - name: metrics containerPort: {{ .Values.hyperlane.metrics.port }} {{- with .Values.nodeSelector }} diff --git a/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml index b5929bfd99f..f4df1ce54a3 100644 --- a/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml @@ -75,7 +75,7 @@ spec: mountPath: /config-env-vars - name: secret-env-vars mountPath: /secret-env-vars - ports: + ports: - name: metrics containerPort: {{ .Values.hyperlane.metrics.port }} volumes: From 6c9b5594114bca3b78cde6026cfaae5a6a78ea0a Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 9 May 2025 10:51:22 +0100 Subject: [PATCH 157/223] fix(submitter): don't use ethers submission middleware (#6177) ### Description **When reviewing, make double sure the changes in `build_with_signer` make sense, otherwise this can break prod** Since the new submitter has its own logic for setting tx parameters, so ethers middleware like the gas escalator, nonce manager, etc isn't required. This PR makes the usage of middleware configurable, via an associated const in `BuildableWithProvider` that defaults to `true`. This const is only ever used if `NEEDS_SIGNER` is `true` ### Drive-by changes Cleans up the provider-building logic in `build_with_signer` ### Related issues - Fixes: https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6163 ### Backward compatibility **Yes - but make double sure the changes in `build_with_signer` make sense, otherwise this can break prod** ### Testing EVM E2E --- .../src/rpc_clients/provider.rs | 7 +++ .../src/rpc_clients/trait_builder.rs | 48 ++++++++++++------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index 98f1fa310d6..15d93cdff1b 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -323,6 +323,13 @@ impl BuildableWithProvider for SubmitterProviderBuilder { type Output = Box; const NEEDS_SIGNER: bool = true; + // the submitter does not use the ethers submission middleware. + // it uses its own logic for setting transaction parameters + // and landing them onchain + fn uses_ethers_submission_middleware(&self) -> bool { + false + } + async fn build_with_provider( &self, provider: M, diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs index e3156df4568..f2ffcfa8223 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs @@ -65,6 +65,13 @@ pub trait BuildableWithProvider { /// Whether this provider requires a signer const NEEDS_SIGNER: bool; + /// Whether this provider requires submission middleware such as gas oracle, + /// gas escalator, nonce manager. It defaults to true, since it's only Lander + /// that doesn't require it. + fn uses_ethers_submission_middleware(&self) -> bool { + true + } + /// Construct a new instance of the associated trait using a connection /// config. This is the first step and will wrap the provider with /// metrics and a signer as needed. @@ -200,25 +207,32 @@ pub trait BuildableWithProvider { where M: Middleware + 'static, { - Ok(if let Some(signer) = signer { - // The signing provider is used for sending txs, which may end up stuck in the mempool due to - // gas pricing issues. We first wrap the provider in a signer middleware, to sign any new txs sent by the gas escalator middleware. - // We keep nonce manager as the outermost middleware, so that every new tx with a higher gas price reuses the same nonce. - let signing_provider = wrap_with_signer(provider, signer.clone()) - .await - .map_err(ChainCommunicationError::from_other)?; - let gas_escalator_provider = wrap_with_gas_escalator(signing_provider); - let gas_oracle_provider = wrap_with_gas_oracle(gas_escalator_provider, locator.domain)?; - let nonce_manager_provider = - wrap_with_nonce_manager(gas_oracle_provider, signer.address()) - .await - .map_err(ChainCommunicationError::from_other)?; + let Some(signer) = signer else { + return Ok(self.build_with_provider(provider, conn, locator).await); + }; + let signing_provider = wrap_with_signer(provider, signer.clone()) + .await + .map_err(ChainCommunicationError::from_other)?; - self.build_with_provider(nonce_manager_provider, conn, locator) - } else { - self.build_with_provider(provider, conn, locator) + if !self.uses_ethers_submission_middleware() { + // don't wrap the signing provider in any middlewares + return Ok(self + .build_with_provider(signing_provider, conn, locator) + .await); } - .await) + + // The signing provider is used for sending txs, which may end up stuck in the mempool due to + // gas pricing issues. We first wrap the provider in a signer middleware, to sign any new txs sent by the gas escalator middleware. + // We keep nonce manager as the outermost middleware, so that resubmitting a tx with a higher gas price reuses its initial nonce. + let gas_escalator_provider = wrap_with_gas_escalator(signing_provider); + let gas_oracle_provider = wrap_with_gas_oracle(gas_escalator_provider, locator.domain)?; + let nonce_manager_provider = wrap_with_nonce_manager(gas_oracle_provider, signer.address()) + .await + .map_err(ChainCommunicationError::from_other)?; + + Ok(self + .build_with_provider(nonce_manager_provider, conn, locator) + .await) } /// Construct a new instance of the associated trait using a provider. From 46eeda6c3a7110520f4b671a16ed775922d49983 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 9 May 2025 11:24:01 +0100 Subject: [PATCH 158/223] feat(submitter): Add naive nonce manager (#6179) ### Description Add naive nonce manager: get current nonce of the account and add the current number of transactions in finality stage. ### Drive-by changes Minor rename and imports improvements ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../src/rpc_clients/provider.rs | 12 +++++ .../submitter/src/chain_tx_adapter/adapter.rs | 4 ++ .../src/chain_tx_adapter/chains/ethereum.rs | 1 + .../chains/ethereum/adapter.rs | 20 +++++++- .../chain_tx_adapter/chains/ethereum/nonce.rs | 3 ++ .../chains/ethereum/nonce/manager.rs | 47 +++++++++++++++++++ .../src/payload_dispatcher/dispatcher.rs | 4 +- .../stages/finality_stage.rs | 26 ++++++---- .../stages/finality_stage/pool.rs | 46 ++++++++++++++++++ .../src/payload_dispatcher/stages/state.rs | 8 ++-- 10 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce.rs create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs create mode 100644 rust/main/submitter/src/payload_dispatcher/stages/finality_stage/pool.rs diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index 15d93cdff1b..da8e717810f 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -11,6 +11,7 @@ use ethers::{prelude::Middleware, types::TransactionReceipt}; use ethers_contract::builders::ContractCall; use ethers_core::abi::Function; use ethers_core::types::transaction::eip2718::TypedTransaction; +use ethers_core::types::BlockId; use ethers_core::{abi::Address, types::BlockNumber}; use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, H512, U256}; use tokio::time::sleep; @@ -96,6 +97,9 @@ pub trait EvmProviderForSubmitter: Send + Sync { /// Read-only call into blockchain which returns a boolean async fn check(&self, tx: &TypedTransaction, function: &Function) -> ChainResult; + + /// Get the next nonce to use for a given address (using the finalized block) + async fn get_next_nonce_on_finalized_block(&self, address: &Address) -> ChainResult; } #[async_trait] @@ -160,6 +164,14 @@ where Ok(success) } + + async fn get_next_nonce_on_finalized_block(&self, address: &Address) -> ChainResult { + self.provider + .get_transaction_count(*address, Some(BlockId::Number(BlockNumber::Finalized))) + .await + .map_err(ChainCommunicationError::from_other) + .map(Into::into) + } } #[async_trait] diff --git a/rust/main/submitter/src/chain_tx_adapter/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/adapter.rs index 4676f3bb202..9f3ddec1189 100644 --- a/rust/main/submitter/src/chain_tx_adapter/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/adapter.rs @@ -107,4 +107,8 @@ pub trait AdaptsChain: Send + Sync { async fn replace_tx(&self, _tx: &Transaction) -> Result<(), SubmitterError> { todo!() } + + async fn set_unfinalized_tx_count(&self, _count: usize) { + // nothing as default implementation + } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs index 63081ffe292..b856999110f 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum.rs @@ -2,6 +2,7 @@ pub use adapter::EthereumTxAdapter; pub use precursor::EthereumTxPrecursor; mod adapter; +mod nonce; mod payload; mod precursor; mod transaction; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs index 2542633df4a..89d7835f668 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -6,6 +6,7 @@ use ethers::contract::builders::ContractCall; use ethers::prelude::U64; use ethers::providers::Middleware; use ethers::types::H256; +use tokio::sync::Mutex; use tracing::{info, warn}; use uuid::Uuid; @@ -16,13 +17,15 @@ use hyperlane_core::ContractLocator; use hyperlane_ethereum::{EthereumReorgPeriod, EvmProviderForSubmitter, SubmitterProviderBuilder}; use crate::{ - chain_tx_adapter::{adapter::TxBuildingResult, AdaptsChain, EthereumTxPrecursor, GasLimit}, - error::SubmitterError, + chain_tx_adapter::{adapter::TxBuildingResult, AdaptsChain, GasLimit}, payload::{FullPayload, PayloadDetails}, transaction::{Transaction, TransactionStatus}, + SubmitterError, }; +use super::nonce::NonceManager; use super::transaction::Precursor; +use super::EthereumTxPrecursor; mod gas_limit_estimator; mod tx_status_checker; @@ -33,6 +36,7 @@ pub struct EthereumTxAdapter { _raw_conf: RawChainConf, provider: Box, reorg_period: EthereumReorgPeriod, + nonce_manager: Arc>, } impl EthereumTxAdapter { @@ -55,6 +59,7 @@ impl EthereumTxAdapter { ) .await?; let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; + let nonce_manager = Arc::new(Mutex::new(NonceManager::new())); Ok(Self { conf, @@ -62,6 +67,7 @@ impl EthereumTxAdapter { _raw_conf: raw_conf, provider, reorg_period, + nonce_manager, }) } } @@ -118,6 +124,12 @@ impl AdaptsChain for EthereumTxAdapter { info!(?tx, "submitting transaction"); + self.nonce_manager + .lock() + .await + .set_nonce(tx, &self.provider) + .await?; + let precursor = tx.precursor(); let hash = self .provider @@ -170,4 +182,8 @@ impl AdaptsChain for EthereumTxAdapter { fn max_batch_size(&self) -> u32 { todo!() } + + async fn set_unfinalized_tx_count(&self, count: usize) { + self.nonce_manager.lock().await.tx_in_finality_count = count; + } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce.rs new file mode 100644 index 00000000000..c743ac83c84 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce.rs @@ -0,0 +1,3 @@ +pub use manager::NonceManager; + +mod manager; diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs new file mode 100644 index 00000000000..037371f8d02 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs @@ -0,0 +1,47 @@ +// the evm provider-building logic returns a box. `EvmProviderForSubmitter` is only implemented for the underlying type rather than the boxed type. +// implementing the trait for the boxed type would require a lot of boilerplate code. +#![allow(clippy::borrowed_box)] + +use hyperlane_ethereum::EvmProviderForSubmitter; + +use crate::transaction::Transaction; +use crate::SubmitterError; + +use super::super::transaction::Precursor; + +pub struct NonceManager { + pub tx_in_finality_count: usize, +} + +impl NonceManager { + pub fn new() -> Self { + Self { + tx_in_finality_count: 0, + } + } + + pub async fn set_nonce( + &self, + tx: &mut Transaction, + provider: &Box, + ) -> Result<(), SubmitterError> { + let precursor = tx.precursor_mut(); + + if precursor.tx.nonce().is_some() { + return Ok(()); + } + + let address = precursor + .tx + .from() + .ok_or(SubmitterError::TxSubmissionError( + "Transaction missing address".to_string(), + ))?; + let nonce = provider.get_next_nonce_on_finalized_block(address).await?; + let next_nonce = nonce + self.tx_in_finality_count; + + precursor.tx.set_nonce(next_nonce); + + Ok(()) + } +} diff --git a/rust/main/submitter/src/payload_dispatcher/dispatcher.rs b/rust/main/submitter/src/payload_dispatcher/dispatcher.rs index 55441f247d2..6f2f6a8d12e 100644 --- a/rust/main/submitter/src/payload_dispatcher/dispatcher.rs +++ b/rust/main/submitter/src/payload_dispatcher/dispatcher.rs @@ -111,7 +111,7 @@ impl PayloadDispatcher { .expect("spawning tokio task from Builder is infallible"); tasks.push(inclusion_task); - let finality_state = FinalityStage::new( + let finality_stage = FinalityStage::new( finality_stage_receiver, building_stage_queue.clone(), self.inner.clone(), @@ -121,7 +121,7 @@ impl PayloadDispatcher { .name("finality_stage") .spawn( async move { - finality_state.run().await; + finality_stage.run().await; } .instrument(tracing::info_span!("finality_stage")), ) diff --git a/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs b/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs index 8bf8761d99a..c9a22498db7 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/finality_stage.rs @@ -29,7 +29,9 @@ use super::{ PayloadDispatcherState, }; -pub type FinalityStagePool = Arc>>; +use pool::FinalityStagePool; + +mod pool; pub const STAGE_NAME: &str = "FinalityStage"; @@ -49,7 +51,7 @@ impl FinalityStage { domain: String, ) -> Self { Self { - pool: Arc::new(Mutex::new(HashMap::new())), + pool: FinalityStagePool::new(), tx_receiver, building_stage_queue, state, @@ -94,7 +96,8 @@ impl FinalityStage { .metrics .update_liveness_metric(format!("{}::receive_txs", STAGE_NAME).as_str(), &domain); if let Some(tx) = tx_receiver.recv().await { - pool.lock().await.insert(tx.id.clone(), tx.clone()); + let pool_len = pool.insert(tx.clone()).await; + state.adapter.set_unfinalized_tx_count(pool_len).await; info!(?tx, "Received transaction"); } else { error!("Inclusion stage channel closed"); @@ -117,7 +120,11 @@ impl FinalityStage { // evaluate the pool every block sleep(*estimated_block_time).await; - let pool_snapshot = pool.lock().await.clone(); + let pool_snapshot = pool.snapshot().await; + state + .adapter + .set_unfinalized_tx_count(pool_snapshot.len()) + .await; state.metrics.update_queue_length_metric( STAGE_NAME, pool_snapshot.len() as u64, @@ -187,7 +194,9 @@ impl FinalityStage { update_tx_status(state, &mut tx, tx_status).await?; let tx_id = tx.id.clone(); info!(?tx_id, "Transaction is finalized"); - pool.lock().await.remove(&tx_id); + + let pool_len = pool.remove(&tx_id).await; + state.adapter.set_unfinalized_tx_count(pool_len).await; } TransactionStatus::Dropped(drop_reason) => { Self::handle_dropped_transaction( @@ -247,7 +256,8 @@ impl FinalityStage { building_stage_queue.lock().await.push_front(full_payload); } } - pool.lock().await.remove(&tx.id); + let pool_len = pool.remove(&tx.id).await; + state.adapter.set_unfinalized_tx_count(pool_len).await; Ok(()) } } @@ -527,7 +537,7 @@ mod tests { async fn run_stage(stage: FinalityStage) -> Vec { let pool = stage.pool.clone(); - let pool_before = pool.lock().await.clone(); + let pool_before = pool.snapshot().await; let stage_task = tokio::spawn(async move { stage.run().await }); // give the building stage 100ms to send the transaction(s) to the receiver let _ = tokio::select! { @@ -537,7 +547,7 @@ mod tests { _ = tokio::time::sleep(tokio::time::Duration::from_millis(100)) => { }, }; - let pool_after = pool.lock().await.clone(); + let pool_after = pool.snapshot().await; pool_before .iter() .filter(|(id, _)| !pool_after.contains_key(id)) diff --git a/rust/main/submitter/src/payload_dispatcher/stages/finality_stage/pool.rs b/rust/main/submitter/src/payload_dispatcher/stages/finality_stage/pool.rs new file mode 100644 index 00000000000..eb801118f8a --- /dev/null +++ b/rust/main/submitter/src/payload_dispatcher/stages/finality_stage/pool.rs @@ -0,0 +1,46 @@ +use std::collections::HashMap; +use std::ops::Deref; +use std::sync::Arc; + +use tokio::sync::Mutex; + +use crate::transaction::{Transaction, TransactionId}; + +#[derive(Debug, Clone)] +pub struct FinalityStagePool { + pool: Arc>>, +} + +impl FinalityStagePool { + pub fn new() -> Self { + Self { + pool: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub async fn insert(&self, transaction: Transaction) -> usize { + let mut pool = self.pool.lock().await; + pool.insert(transaction.id.clone(), transaction); + pool.len() + } + + pub async fn remove(&self, id: &TransactionId) -> usize { + let mut pool = self.pool.lock().await; + pool.remove(id); + pool.len() + } + + pub async fn snapshot(&self) -> HashMap { + let pool = self.pool.lock().await; + pool.clone() + } +} + +#[cfg(test)] +impl Deref for FinalityStagePool { + type Target = Arc>>; + + fn deref(&self) -> &Self::Target { + &self.pool + } +} diff --git a/rust/main/submitter/src/payload_dispatcher/stages/state.rs b/rust/main/submitter/src/payload_dispatcher/stages/state.rs index dcc442a3017..4e843054caa 100644 --- a/rust/main/submitter/src/payload_dispatcher/stages/state.rs +++ b/rust/main/submitter/src/payload_dispatcher/stages/state.rs @@ -1,10 +1,14 @@ // TODO: re-enable clippy warnings #![allow(dead_code)] +use std::sync::Arc; + use chrono::format; use derive_new::new; use eyre::Result; -use std::{path::PathBuf, sync::Arc}; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; +use tracing::{error, info, instrument::Instrumented, warn}; use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, @@ -12,8 +16,6 @@ use hyperlane_base::{ settings::{ChainConf, RawChainConf}, }; use hyperlane_core::HyperlaneDomain; -use tokio::task::JoinHandle; -use tracing::{error, info, instrument::Instrumented, warn}; use crate::{ chain_tx_adapter::{AdaptsChain, ChainTxAdapterFactory}, From e381a8dddab6f7a67e4b15481b6314a8a00ff334 Mon Sep 17 00:00:00 2001 From: Jason Guo <33064781+Xaroz@users.noreply.github.com> Date: Fri, 9 May 2025 10:46:46 -0400 Subject: [PATCH 159/223] chore: bump registry to 15.0.0 and update monitor image (#6187) ### Description Update registry version to 15.0.0 ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .changeset/upset-humans-spend.md | 7 +++++++ typescript/cli/package.json | 2 +- typescript/helloworld/package.json | 2 +- typescript/infra/package.json | 2 +- typescript/infra/src/warp/helm.ts | 2 +- typescript/widgets/package.json | 2 +- yarn.lock | 16 ++++++++-------- 7 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 .changeset/upset-humans-spend.md diff --git a/.changeset/upset-humans-spend.md b/.changeset/upset-humans-spend.md new file mode 100644 index 00000000000..dd85b208b63 --- /dev/null +++ b/.changeset/upset-humans-spend.md @@ -0,0 +1,7 @@ +--- +'@hyperlane-xyz/helloworld': minor +'@hyperlane-xyz/widgets': minor +'@hyperlane-xyz/cli': minor +--- + +Update Registry version to 15.0.0 diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 7a4bb5bd019..24e13a6e588 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -8,7 +8,7 @@ "@eslint/js": "^9.15.0", "@ethersproject/abi": "*", "@ethersproject/providers": "*", - "@hyperlane-xyz/registry": "14.0.0", + "@hyperlane-xyz/registry": "15.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@hyperlane-xyz/utils": "12.5.0", "@inquirer/core": "9.0.10", diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index fb23990eeb7..57cb8a7ff00 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -4,7 +4,7 @@ "version": "12.5.0", "dependencies": { "@hyperlane-xyz/core": "7.1.4", - "@hyperlane-xyz/registry": "14.0.0", + "@hyperlane-xyz/registry": "15.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 2f316fbbb0a..2e3098c96ef 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -14,7 +14,7 @@ "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", "@hyperlane-xyz/helloworld": "12.5.0", - "@hyperlane-xyz/registry": "14.0.0", + "@hyperlane-xyz/registry": "15.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@hyperlane-xyz/utils": "12.5.0", "@inquirer/prompts": "3.3.2", diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 792aeaec3da..7d04a32b080 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -73,7 +73,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '8b985a4-20250421-152953', + tag: '5ad1aed-20250509-141037', }, warpRouteId: this.warpRouteId, fullnameOverride: this.helmReleaseName, diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 73149af3210..6c1bec042ba 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -31,7 +31,7 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@eslint/js": "^9.15.0", - "@hyperlane-xyz/registry": "14.0.0", + "@hyperlane-xyz/registry": "15.0.0", "@storybook/addon-essentials": "^7.6.14", "@storybook/addon-interactions": "^7.6.14", "@storybook/addon-links": "^7.6.14", diff --git a/yarn.lock b/yarn.lock index 23ac5646250..741f1df3003 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7644,7 +7644,7 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" - "@hyperlane-xyz/registry": "npm:14.0.0" + "@hyperlane-xyz/registry": "npm:15.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" "@inquirer/core": "npm:9.0.10" @@ -7795,7 +7795,7 @@ __metadata: dependencies: "@eslint/js": "npm:^9.15.0" "@hyperlane-xyz/core": "npm:7.1.4" - "@hyperlane-xyz/registry": "npm:14.0.0" + "@hyperlane-xyz/registry": "npm:15.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -7846,7 +7846,7 @@ __metadata: "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" "@hyperlane-xyz/helloworld": "npm:12.5.0" - "@hyperlane-xyz/registry": "npm:14.0.0" + "@hyperlane-xyz/registry": "npm:15.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" "@inquirer/prompts": "npm:3.3.2" @@ -7910,13 +7910,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/registry@npm:14.0.0": - version: 14.0.0 - resolution: "@hyperlane-xyz/registry@npm:14.0.0" +"@hyperlane-xyz/registry@npm:15.0.0": + version: 15.0.0 + resolution: "@hyperlane-xyz/registry@npm:15.0.0" dependencies: yaml: "npm:2.4.5" zod: "npm:^3.21.2" - checksum: 10/893c802f8f8206d5a314e8d729ae1f9bf7ae5e66c6034beb0a56e3dcfdb15a532e97f1b8c3ffe394970ccccf3d9129f340d4ad47c27102dafcfcc1c6321ea57b + checksum: 10/41cc4635697447ae16a65fe2b5553054a205f37a17d81892d0767f049abbc8abce6ff5cd744a9ea2df32c816a22cab19ec469307c4b62550d91c5cba33f955e0 languageName: node linkType: hard @@ -8020,7 +8020,7 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" - "@hyperlane-xyz/registry": "npm:14.0.0" + "@hyperlane-xyz/registry": "npm:15.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" "@interchain-ui/react": "npm:^1.23.28" From de7c6ae4fb1534e1da9f57d63c854e5b219f98bf Mon Sep 17 00:00:00 2001 From: mshojaei-txfusion <138107084+mshojaei-txfusion@users.noreply.github.com> Date: Fri, 9 May 2025 18:33:04 +0330 Subject: [PATCH 160/223] refactor: registry based warp config (#6174) ### Description This PR refactors warp command options to improve flexibility and configuration handling: - Makes warp deployment config optional instead of requiring a default path - Introduces `getWarpConfigs` utility to centralize configuration handling - Adds support for more optional parameters (symbol, warpRouteId) in warp commands - Improves chain resolution logic for warp operations ### Drive-by changes - Rename `readWarpRouteDeployConfig` to `getWarpRouteDeployConfig` - Remove redundant chain resolution logic from MultiChainResolver - Remove hardcoded default config paths Diff with last PR: https://github.com/hyperlane-xyz/hyperlane-monorepo/compare/refactor/warp-config-loading...refactor/registry-based-warp-config ### Related issues https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5244 https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5922 ### Backward compatibility Yes - This PR improves flexibility without breaking existing functionality. ### Testing Manual --- .changeset/thirty-chicken-smash.md | 5 ++ typescript/cli/src/commands/options.ts | 2 +- typescript/cli/src/commands/warp.ts | 65 +++++++-------- .../strategies/chain/ChainResolverFactory.ts | 2 +- .../strategies/chain/MultiChainResolver.ts | 81 +++++++++---------- typescript/cli/src/context/types.ts | 20 ++++- typescript/cli/src/deploy/warp.ts | 33 +------- typescript/cli/src/utils/warp.ts | 12 --- 8 files changed, 98 insertions(+), 122 deletions(-) create mode 100644 .changeset/thirty-chicken-smash.md diff --git a/.changeset/thirty-chicken-smash.md b/.changeset/thirty-chicken-smash.md new file mode 100644 index 00000000000..e43082852f0 --- /dev/null +++ b/.changeset/thirty-chicken-smash.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Added registry based warp configs support diff --git a/typescript/cli/src/commands/options.ts b/typescript/cli/src/commands/options.ts index e2e67139165..9e934fc6e51 100644 --- a/typescript/cli/src/commands/options.ts +++ b/typescript/cli/src/commands/options.ts @@ -113,7 +113,7 @@ export const warpDeploymentConfigCommandOption: Options = { type: 'string', description: 'A path to a JSON or YAML file with a warp route deployment config.', - default: DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, + demandOption: false, alias: 'wd', }; diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index 66784c76a12..a8e1290cd9c 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -10,12 +10,11 @@ import { import { ProtocolType, assert, objFilter } from '@hyperlane-xyz/utils'; import { runWarpRouteCheck } from '../check/warp.js'; -import { - createWarpRouteDeployConfig, - readWarpRouteDeployConfig, -} from '../config/warp.js'; +import { createWarpRouteDeployConfig } from '../config/warp.js'; import { CommandModuleWithContext, + CommandModuleWithWarpApplyContext, + CommandModuleWithWarpDeployContext, CommandModuleWithWriteContext, } from '../context/types.js'; import { evaluateIfDryRunFailure } from '../deploy/dry-run.js'; @@ -75,10 +74,11 @@ export const warpCommand: CommandModule = { handler: () => log('Command required'), }; -export const apply: CommandModuleWithWriteContext<{ - config: string; +export const apply: CommandModuleWithWarpApplyContext<{ + config?: string; + warp?: string; symbol?: string; - warp: string; + warpRouteId?: string; strategy?: string; receiptsDir: string; }> = { @@ -86,14 +86,15 @@ export const apply: CommandModuleWithWriteContext<{ describe: 'Update Warp Route contracts', builder: { config: warpDeploymentConfigCommandOption, - symbol: { - ...symbolCommandOption, - demandOption: false, - }, + warpRouteId: warpRouteIdCommandOption, warp: { ...warpCoreConfigCommandOption, demandOption: false, }, + symbol: { + ...symbolCommandOption, + demandOption: false, + }, strategy: { ...strategyCommandOption, demandOption: false }, 'receipts-dir': { type: 'string', @@ -102,33 +103,17 @@ export const apply: CommandModuleWithWriteContext<{ coerce: (dir) => removeEndingSlash(dir), }, }, - handler: async ({ - context, - config, - symbol, - warp, - strategy: strategyUrl, - receiptsDir, - }) => { + handler: async ({ context, strategy: strategyUrl, receiptsDir }) => { logCommandHeader('Hyperlane Warp Apply'); - const warpCoreConfig = await getWarpCoreConfigOrExit({ - symbol, - warp, - context, - }); - if (strategyUrl) ChainSubmissionStrategySchema.parse(readYamlOrJson(strategyUrl)); - const warpDeployConfig = await readWarpRouteDeployConfig({ - filePath: config, - context, - }); await runWarpRouteApply({ context, - warpDeployConfig, - warpCoreConfig, + // Already fetched in the resolveWarpApplyChains + warpDeployConfig: context.warpDeployConfig, + warpCoreConfig: context.warpCoreConfig, strategyUrl, receiptsDir, }); @@ -136,10 +121,12 @@ export const apply: CommandModuleWithWriteContext<{ }, }; -export const deploy: CommandModuleWithWriteContext<{ - config: string; +export const deploy: CommandModuleWithWarpDeployContext<{ + config?: string; 'dry-run': string; 'from-address': string; + symbol?: string; + warpRouteId?: string; }> = { command: 'deploy', describe: 'Deploy Warp Route contracts', @@ -147,8 +134,13 @@ export const deploy: CommandModuleWithWriteContext<{ config: warpDeploymentConfigCommandOption, 'dry-run': dryRunCommandOption, 'from-address': fromAddressCommandOption, + symbol: { + ...symbolCommandOption, + demandOption: false, + }, + warpRouteId: warpRouteIdCommandOption, }, - handler: async ({ context, config, dryRun }) => { + handler: async ({ context, dryRun }) => { logCommandHeader( `Hyperlane Warp Route Deployment${dryRun ? ' Dry-Run' : ''}`, ); @@ -156,7 +148,8 @@ export const deploy: CommandModuleWithWriteContext<{ try { await runWarpRouteDeploy({ context, - warpRouteDeploymentConfigPath: config, + // Already fetched in the resolveWarpRouteConfigChains + warpDeployConfig: context.warpDeployConfig, }); } catch (error: any) { evaluateIfDryRunFailure(error, dryRun); @@ -178,7 +171,7 @@ export const init: CommandModuleWithContext<{ describe: 'Create an advanced ISM', default: false, }, - out: outputFileCommandOption(DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH), + out: outputFileCommandOption(), }, handler: async ({ context, advanced, out }) => { logCommandHeader('Hyperlane Warp Configure'); diff --git a/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts b/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts index ec70ac02a0c..61ccd980a30 100644 --- a/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts +++ b/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts @@ -12,7 +12,7 @@ export class ChainResolverFactory { [CommandType.WARP_DEPLOY, () => MultiChainResolver.forWarpRouteConfig()], // Using the forRelayer resolver because warp send allows the user to self relay the tx [CommandType.WARP_SEND, () => MultiChainResolver.forRelayer()], - [CommandType.WARP_APPLY, () => MultiChainResolver.forWarpRouteConfig()], + [CommandType.WARP_APPLY, () => MultiChainResolver.forWarpApply()], // Using the forRelayer resolver because send allows the user to self relay the tx [CommandType.SEND_MESSAGE, () => MultiChainResolver.forRelayer()], [CommandType.AGENT_KURTOSIS, () => MultiChainResolver.forAgentKurtosis()], diff --git a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts index aac6581cb4b..f2305dc908a 100644 --- a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts +++ b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts @@ -8,26 +8,22 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH } from '../../../commands/options.js'; import { readCoreDeployConfigs } from '../../../config/core.js'; import { readChainSubmissionStrategyConfig } from '../../../config/strategy.js'; -import { log } from '../../../logger.js'; +import { getWarpRouteDeployConfig } from '../../../config/warp.js'; import { extractChainsFromObj, runMultiChainSelectionStep, runSingleChainSelectionStep, } from '../../../utils/chains.js'; -import { - isFile, - readYamlOrJson, - runFileSelectionStep, -} from '../../../utils/files.js'; +import { getWarpConfigs } from '../../../utils/warp.js'; import { ChainResolver } from './types.js'; enum ChainSelectionMode { AGENT_KURTOSIS, WARP_CONFIG, + WARP_APPLY, STRATEGY, CORE_APPLY, DEFAULT, @@ -46,6 +42,8 @@ export class MultiChainResolver implements ChainResolver { switch (this.mode) { case ChainSelectionMode.WARP_CONFIG: return this.resolveWarpRouteConfigChains(argv); + case ChainSelectionMode.WARP_APPLY: + return this.resolveWarpApplyChains(argv); case ChainSelectionMode.AGENT_KURTOSIS: return this.resolveAgentChains(argv); case ChainSelectionMode.STRATEGY: @@ -61,10 +59,38 @@ export class MultiChainResolver implements ChainResolver { private async resolveWarpRouteConfigChains( argv: Record, ): Promise { - argv.config ||= DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH; - argv.context.chains = await this.getWarpRouteConfigChains( - argv.config.trim(), - argv.context.skipConfirmation, + const warpDeployConfig = await getWarpRouteDeployConfig({ + context: argv.context, + warpRouteDeployConfigPath: argv.config, + warpRouteId: argv.warpRouteId, + symbol: argv.symbol, + }); + argv.context.warpDeployConfig = warpDeployConfig; + argv.context.chains = Object.keys(warpDeployConfig); + assert( + argv.context.chains.length !== 0, + 'No chains found in warp route deployment config', + ); + return argv.context.chains; + } + + private async resolveWarpApplyChains( + argv: Record, + ): Promise { + const { warpCoreConfig, warpDeployConfig } = await getWarpConfigs({ + context: argv.context, + warpRouteId: argv.warpRouteId, + symbol: argv.symbol, + warpDeployConfigPath: argv.config, + warpCoreConfigPath: argv.warp, + }); + argv.context.warpCoreConfig = warpCoreConfig; + argv.context.warpDeployConfig = warpDeployConfig; + argv.context.chains = Object.keys(warpDeployConfig); + + assert( + argv.context.chains.length !== 0, + 'No chains found in warp route deployment config', ); return argv.context.chains; } @@ -129,36 +155,6 @@ export class MultiChainResolver implements ChainResolver { return Array.from(chains); } - private async getWarpRouteConfigChains( - configPath: string, - skipConfirmation: boolean, - ): Promise { - if (!configPath || !isFile(configPath)) { - assert(!skipConfirmation, 'Warp route deployment config is required'); - configPath = await runFileSelectionStep( - './configs', - 'Warp route deployment config', - 'warp', - ); - } else { - log(`Using warp route deployment config at ${configPath}`); - } - - // Alternative to readWarpRouteDeployConfig that doesn't use context for signer and zod validation - const warpRouteConfig = (await readYamlOrJson(configPath)) as Record< - string, - any - >; - - const chains = Object.keys(warpRouteConfig) as ChainName[]; - assert( - chains.length !== 0, - 'No chains found in warp route deployment config', - ); - - return chains; - } - private async resolveCoreApplyChains( argv: Record, ): Promise { @@ -217,6 +213,9 @@ export class MultiChainResolver implements ChainResolver { static forWarpRouteConfig(): MultiChainResolver { return new MultiChainResolver(ChainSelectionMode.WARP_CONFIG); } + static forWarpApply(): MultiChainResolver { + return new MultiChainResolver(ChainSelectionMode.WARP_APPLY); + } static forCoreApply(): MultiChainResolver { return new MultiChainResolver(ChainSelectionMode.CORE_APPLY); diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index 3b02c9e31f6..68858724957 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -37,8 +37,14 @@ export interface WriteCommandContext extends CommandContext { signer: ethers.Signer; isDryRun?: boolean; dryRunChain?: string; - warpDeployConfig?: WarpRouteDeployConfigMailboxRequired; - warpCoreConfig?: WarpCoreConfig; +} + +export interface WarpDeployCommandContext extends WriteCommandContext { + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; +} +export interface WarpApplyCommandContext extends WriteCommandContext { + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; + warpCoreConfig: WarpCoreConfig; } export type CommandModuleWithContext = CommandModule< @@ -50,3 +56,13 @@ export type CommandModuleWithWriteContext = CommandModule< {}, Args & { context: WriteCommandContext } >; + +export type CommandModuleWithWarpApplyContext = CommandModule< + {}, + Args & { context: WarpApplyCommandContext } +>; + +export type CommandModuleWithWarpDeployContext = CommandModule< + {}, + Args & { context: WarpDeployCommandContext } +>; diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index c58cb52876e..ce598a350b4 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -64,7 +64,6 @@ import { retryAsync, } from '@hyperlane-xyz/utils'; -import { readWarpRouteDeployConfig } from '../config/warp.js'; import { MINIMUM_WARP_DEPLOY_GAS } from '../consts.js'; import { requestAndSaveApiKeys } from '../context/context.js'; import { WriteCommandContext } from '../context/types.js'; @@ -72,9 +71,7 @@ import { log, logBlue, logGray, logGreen, logTable } from '../logger.js'; import { getSubmitterBuilder } from '../submit/submit.js'; import { indentYamlOrJson, - isFile, readYamlOrJson, - runFileSelectionStep, writeYamlOrJson, } from '../utils/files.js'; @@ -97,35 +94,13 @@ interface WarpApplyParams extends DeployParams { export async function runWarpRouteDeploy({ context, - warpRouteDeploymentConfigPath, + warpDeployConfig, }: { context: WriteCommandContext; - warpRouteDeploymentConfigPath?: string; + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; }) { const { skipConfirmation, chainMetadata, registry } = context; - - if ( - !warpRouteDeploymentConfigPath || - !isFile(warpRouteDeploymentConfigPath) - ) { - if (skipConfirmation) - throw new Error('Warp route deployment config required'); - warpRouteDeploymentConfigPath = await runFileSelectionStep( - './configs', - 'Warp route deployment config', - 'warp', - ); - } else { - log( - `Using warp route deployment config at ${warpRouteDeploymentConfigPath}`, - ); - } - const warpRouteConfig = await readWarpRouteDeployConfig({ - filePath: warpRouteDeploymentConfigPath, - context, - }); - - const chains = Object.keys(warpRouteConfig); + const chains = Object.keys(warpDeployConfig); let apiKeys: ChainMap = {}; if (!skipConfirmation) @@ -133,7 +108,7 @@ export async function runWarpRouteDeploy({ const deploymentParams = { context, - warpDeployConfig: warpRouteConfig, + warpDeployConfig, }; await runDeployPlanStep(deploymentParams); diff --git a/typescript/cli/src/utils/warp.ts b/typescript/cli/src/utils/warp.ts index 0dbb9378f78..f5023e38110 100644 --- a/typescript/cli/src/utils/warp.ts +++ b/typescript/cli/src/utils/warp.ts @@ -130,18 +130,6 @@ export async function getWarpConfigs({ warpDeployConfig: WarpRouteDeployConfigMailboxRequired; warpCoreConfig: WarpCoreConfig; }> { - if ( - 'warpCoreConfig' in context && - 'warpDeployConfig' in context && - context.warpCoreConfig && - context.warpDeployConfig - ) { - return { - warpDeployConfig: context.warpDeployConfig, - warpCoreConfig: context.warpCoreConfig, - }; - } - const hasDeployConfigFilePath = !!warpDeployConfigPath; const hasCoreConfigFilePath = !!warpCoreConfigPath; assert( From d3f8da9432e32a0dc1defc3de185e175bdc72416 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 9 May 2025 16:56:12 +0100 Subject: [PATCH 161/223] feat: add svmBNB (#6172) ### Description ### Drive-by changes ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/839 ### Backward compatibility ### Testing --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 47 +- .../environments/mainnet3/chain-config.json | 672 +++++++- .../mainnet3/gas-oracle-configs.json | 52 +- .../svmbnb/hyperlane/multisig-config.json | 1377 +++++++++++++++++ .../mainnet3/svmbnb/core/program-ids.json | 8 + .../config/environments/mainnet3/agent.ts | 5 +- .../mainnet3/aw-validators/hyperlane.json | 3 + .../mainnet3/balances/dailyRelayerBurn.json | 1 + .../balances/desiredRelayerBalances.json | 1 + .../balances/highUrgencyRelayerBalance.json | 1 + .../lowUrgencyEngKeyFunderBalance.json | 1 + .../balances/lowUrgencyKeyFunderBalance.json | 1 + .../environments/mainnet3/gasPrices.json | 4 + .../config/environments/mainnet3/owners.ts | 3 + .../mainnet3/supportedChainNames.ts | 1 + .../environments/mainnet3/tokenPrices.json | 1 + .../environments/mainnet3/validators.ts | 11 + .../sealevel-helpers/print-gas-oracles.ts | 6 +- typescript/sdk/src/consts/multisigIsm.ts | 10 + 20 files changed, 2115 insertions(+), 92 deletions(-) create mode 100644 rust/sealevel/environments/mainnet3/multisig-ism-message-id/svmbnb/hyperlane/multisig-config.json create mode 100644 rust/sealevel/environments/mainnet3/svmbnb/core/program-ids.json diff --git a/.registryrc b/.registryrc index 3201a719ef1..22e6d1bcf31 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -93f9850ce4b89f89b3d4ec7c3cc36500709e507f +5b610083b651afe4beb39848f87329cbe84ba348 diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 71812ec7fbd..d34dd7485ad 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -796,10 +796,10 @@ "eclipsemainnet": { "blockExplorers": [ { - "apiUrl": "https://explorer.eclipse.xyz/api", + "apiUrl": "https://api.eclipsescan.xyz/", "family": "other", "name": "Eclipse Explorer", - "url": "https://explorer.eclipse.xyz/" + "url": "https://eclipsescan.xyz/" } ], "blocks": { @@ -836,7 +836,7 @@ } ], "validatorAnnounce": "Hqnn593pqDZWLy6bKZ4NbY767wFhUNBShDrLktuQa3Q2", - "interchainSecurityModule": "BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V", + "interchainSecurityModule": "CMtST8GBWTTzBgYCdAm7Kfh4aVhM2KAeVvTo2gNoDgNU", "technicalStack": "other" }, "endurance": { @@ -9449,6 +9449,47 @@ "index": { "from": 4997878 } + }, + "svmbnb": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.svmbnbmainnet.soo.network", + "family": "other", + "name": "svmBNB Explorer", + "url": "https://explorer.svmbnbmainnet.soo.network" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.4, + "reorgPeriod": 0 + }, + "chainId": 574456, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "svmBNB", + "domainId": 574456, + "gasCurrencyCoinGeckoId": "binancecoin", + "name": "svmbnb", + "nativeToken": { + "decimals": 9, + "name": "BNB", + "symbol": "BNB" + }, + "protocol": "sealevel", + "rpcUrls": [ + { + "http": "https://rpc.svmbnbmainnet.soo.network/rpc" + } + ], + "technicalStack": "other", + "interchainGasPaymaster": "76JNRGvHbER5CL7X4Mw6qpzvvKDW3syyfZ5LNXfY53sC", + "interchainSecurityModule": "3aD11eiNt2ZjmgiQayNRyZfp7hXBRS9hbeyPbk7cmTyv", + "mailbox": "GzwNZJ2EXUWQCEoV6zoBGLxkdsnjkLvm6zHReMEC2JSA", + "merkleTreeHook": "GzwNZJ2EXUWQCEoV6zoBGLxkdsnjkLvm6zHReMEC2JSA", + "validatorAnnounce": "Gh4LUk91bj4QGYWsBY4QyDGhs68n8Maf8oeTRZoQZAEf" } }, "defaultRpcConsensusType": "fallback" diff --git a/rust/sealevel/environments/mainnet3/chain-config.json b/rust/sealevel/environments/mainnet3/chain-config.json index 4c4dec3ab77..6dc8d18ebbe 100644 --- a/rust/sealevel/environments/mainnet3/chain-config.json +++ b/rust/sealevel/environments/mainnet3/chain-config.json @@ -201,6 +201,7 @@ "arbitrum": { "blockExplorers": [ { + "apiKey": "QAI5SWBNHJSFAN6KMS9RC5JGFKV2IYD2Z5", "apiUrl": "https://api.arbiscan.io/api", "family": "etherscan", "name": "Arbiscan", @@ -233,13 +234,13 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://arbitrum.llamarpc.com" + "http": "https://arb1.arbitrum.io/rpc" }, { - "http": "https://rpc.ankr.com/arbitrum" + "http": "https://arbitrum.rpc.subquery.network/public" }, { - "http": "https://arb1.arbitrum.io/rpc" + "http": "https://arbitrum.drpc.org" } ], "technicalStack": "arbitrumnitro" @@ -433,45 +434,6 @@ ], "technicalStack": "polkadotsubstrate" }, - "astarzkevm": { - "blockExplorers": [ - { - "apiUrl": "https://astar-zkevm.explorer.startale.com/api", - "family": "blockscout", - "name": "Astar zkEVM Explorer", - "url": "https://astar-zkevm.explorer.startale.com" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 3, - "reorgPeriod": 5 - }, - "chainId": 3776, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Astar zkEVM", - "domainId": 3776, - "gasCurrencyCoinGeckoId": "ethereum", - "name": "astarzkevm", - "nativeToken": { - "decimals": 18, - "name": "Ethereum", - "symbol": "ETH" - }, - "protocol": "ethereum", - "rpcUrls": [ - { - "http": "https://rpc.startale.com/astar-zkevm" - }, - { - "http": "https://astar-zkevm-rpc.dwellir.com" - } - ], - "technicalStack": "polygoncdk" - }, "aurora": { "blockExplorers": [ { @@ -586,6 +548,7 @@ "avalanche": { "blockExplorers": [ { + "apiKey": "NIF3616T2AP6EHYWIHJEKR1HCMA2K7D96X", "apiUrl": "https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api", "family": "routescan", "name": "SnowTrace", @@ -615,7 +578,13 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://rpc.ankr.com/avalanche" + "http": "https://avalanche.drpc.org" + }, + { + "http": "https://1rpc.io/avax/c" + }, + { + "http": "https://avalanche-c-chain-rpc.publicnode.com" }, { "http": "https://api.avax.network/ext/bc/C/rpc", @@ -666,6 +635,7 @@ "base": { "blockExplorers": [ { + "apiKey": "R8CVTG7HDJYD5JDV2GSD5TGQH3J2KJDSSY", "apiUrl": "https://api.basescan.org/api", "family": "etherscan", "name": "BaseScan", @@ -702,6 +672,18 @@ }, { "http": "https://base.blockpi.network/v1/rpc/public" + }, + { + "http": "https://base.drpc.org" + }, + { + "http": "https://base.llamarpc.com" + }, + { + "http": "https://1rpc.io/base" + }, + { + "http": "https://base-pokt.nodies.app" } ], "technicalStack": "opstack" @@ -765,6 +747,7 @@ "displayName": "Bitlayer", "domainId": 200901, "gasCurrencyCoinGeckoId": "bitcoin", + "gnosisSafeTransactionServiceUrl": "https://multisign.bitlayer.org/txs/", "name": "bitlayer", "nativeToken": { "decimals": 18, @@ -901,6 +884,7 @@ "bsc": { "blockExplorers": [ { + "apiKey": "NXSXUUUAUDD5SYGQUIVS9TZTBCEG6X5THY", "apiUrl": "https://api.bscscan.com/api", "family": "etherscan", "name": "BscScan", @@ -931,13 +915,19 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://rpc.ankr.com/bsc" + "http": "https://bsc.drpc.org" }, { - "http": "https://bsc.drpc.org" + "http": "https://bnb.rpc.subquery.network/public" }, { - "http": "https://bscrpc.com" + "http": "https://binance.llamarpc.com" + }, + { + "http": "https://bsc.blockrazor.xyz" + }, + { + "http": "https://bsc-pokt.nodies.app" } ], "technicalStack": "other", @@ -993,6 +983,7 @@ "celo": { "blockExplorers": [ { + "apiKey": "IC1UCY8JWIYFWGPCEEE58HKFIFEVN73PVV", "apiUrl": "https://api.celoscan.io/api", "family": "etherscan", "name": "CeloScan", @@ -1229,15 +1220,9 @@ { "http": "https://rpc.coredao.org" }, - { - "http": "https://core.public.infstones.com" - }, { "http": "https://rpc.ankr.com/core" }, - { - "http": "https://core.drpc.org" - }, { "http": "https://rpc-core.icecreamswap.com" } @@ -1289,6 +1274,42 @@ ], "technicalStack": "arbitrumnitro" }, + "coti": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet.cotiscan.io/api", + "family": "blockscout", + "name": "Cotiscan", + "url": "https://mainnet.cotiscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 5 + }, + "chainId": 2632500, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Coti", + "domainId": 2632500, + "gasCurrencyCoinGeckoId": "coti", + "name": "coti", + "nativeToken": { + "decimals": 18, + "name": "COTI", + "symbol": "COTI" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.coti.io/rpc" + } + ], + "technicalStack": "other" + }, "cyber": { "blockExplorers": [ { @@ -1328,6 +1349,42 @@ ], "technicalStack": "opstack" }, + "deepbrainchain": { + "blockExplorers": [ + { + "apiUrl": "https://www.dbcscan.io/api", + "family": "blockscout", + "name": "dbcscan", + "url": "https://www.dbcscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": "finalized" + }, + "chainId": 19880818, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Deep Brain Chain", + "domainId": 19880818, + "gasCurrencyCoinGeckoId": "deepbrain-chain", + "name": "deepbrainchain", + "nativeToken": { + "decimals": 18, + "name": "DBC", + "symbol": "DBC" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.dbcwallet.io" + } + ], + "technicalStack": "polkadotsubstrate" + }, "degenchain": { "blockExplorers": [ { @@ -1521,6 +1578,7 @@ "ethereum": { "blockExplorers": [ { + "apiKey": "CYUPN3Q66JIMRGQWYUDXJKQH4SX8YIYZMW", "apiUrl": "https://api.etherscan.io/api", "family": "etherscan", "name": "Etherscan", @@ -1556,16 +1614,22 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://rpc.ankr.com/eth" + "http": "https://eth.llamarpc.com" }, { "http": "https://ethereum.publicnode.com" }, { - "http": "https://cloudflare-eth.com" + "http": "https://eth.drpc.org" }, { - "http": "https://eth.drpc.org" + "http": "https://rpc.flashbots.net" + }, + { + "http": "https://eth.blockrazor.xyz" + }, + { + "http": "https://eth-pokt.nodies.app" } ], "technicalStack": "other" @@ -1736,7 +1800,10 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://flare-api.flare.network/ext/C/rpc" + "http": "https://flare-api.flare.network/ext/C/rpc", + "pagination": { + "maxBlockRange": 30 + } }, { "http": "https://flare.solidifi.app/ext/C/rpc" @@ -1790,6 +1857,45 @@ "gasPrice": 100000000 } }, + "fluence": { + "blockExplorers": [ + { + "apiUrl": "https://blockscout.mainnet.fluence.dev/api", + "family": "blockscout", + "name": "Fluence explorer", + "url": "https://blockscout.mainnet.fluence.dev" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 9999999, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Fluence", + "domainId": 9999999, + "gasCurrencyCoinGeckoId": "fluence", + "index": { + "from": 10997212 + }, + "name": "fluence", + "nativeToken": { + "decimals": 18, + "name": "fluence", + "symbol": "FLT" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.mainnet.fluence.dev" + } + ], + "technicalStack": "arbitrumnitro" + }, "form": { "blockExplorers": [ { @@ -1830,6 +1936,7 @@ "fraxtal": { "blockExplorers": [ { + "apiKey": "X3G6FVJU5VEZXVNQFRX52EQ5FEVP8IPR6F", "apiUrl": "https://api.fraxscan.com/api", "family": "etherscan", "name": "Fraxscan", @@ -1913,6 +2020,46 @@ ], "technicalStack": "other" }, + "game7": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet.game7.io/api", + "family": "blockscout", + "name": "Game7 Explorer", + "url": "https://mainnet.game7.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, + "chainId": 2187, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Game7", + "domainId": 2187, + "gasCurrencyCoinGeckoId": "game7", + "index": { + "from": 1635677 + }, + "isTestnet": false, + "name": "game7", + "nativeToken": { + "decimals": 18, + "name": "Game7", + "symbol": "G7" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet-rpc.game7.build" + } + ], + "technicalStack": "arbitrumnitro" + }, "glue": { "blockExplorers": [ { @@ -1952,6 +2099,7 @@ "gnosis": { "blockExplorers": [ { + "apiKey": "98A32SWJE876CHG95TMFRC5H5SBBX9GHAG", "apiUrl": "https://api.gnosisscan.io/api", "family": "etherscan", "name": "GnosisScan", @@ -2104,9 +2252,43 @@ }, { "http": "https://1rpc.io/one" - }, + } + ], + "technicalStack": "other" + }, + "hashkey": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.hsk.xyz/api", + "family": "blockscout", + "name": "Hashkey Explorer", + "url": "https://explorer.hsk.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 177, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Hashkey", + "domainId": 177, + "gasCurrencyCoinGeckoId": "hashkey-ecopoints", + "isTestnet": false, + "name": "hashkey", + "nativeToken": { + "decimals": 18, + "name": "HashKey Platform Token", + "symbol": "HSK" + }, + "protocol": "ethereum", + "rpcUrls": [ { - "http": "https://rpc.ankr.com/harmony" + "http": "https://mainnet.hsk.xyz" } ], "technicalStack": "other" @@ -2270,6 +2452,37 @@ ], "technicalStack": "arbitrumnitro" }, + "infinityvmmainnet": { + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 1032009, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Infinity VM", + "domainId": 1001032009, + "gasCurrencyCoinGeckoId": "infinityvm", + "index": { + "from": 157889 + }, + "name": "infinityvmmainnet", + "nativeToken": { + "decimals": 18, + "name": "INF", + "symbol": "INF" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.infinityvm.xyz" + } + ], + "technicalStack": "other" + }, "ink": { "blockExplorers": [ { @@ -2450,6 +2663,7 @@ "linea": { "blockExplorers": [ { + "apiKey": "ZCA9836XUX1XPAACHK4BMMYTIFMHIGU8VN", "apiUrl": "https://api.lineascan.build/api", "family": "etherscan", "name": "LineaScan", @@ -2481,9 +2695,6 @@ { "http": "https://rpc.linea.build" }, - { - "http": "https://linea.blockpi.network/v1/rpc/public" - }, { "http": "https://1rpc.io/linea" }, @@ -2823,6 +3034,7 @@ "displayName": "Metal L2", "domainId": 1000001750, "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://txs.safe.metall2.com/", "isTestnet": false, "name": "metal", "nativeToken": { @@ -2877,6 +3089,60 @@ ], "technicalStack": "opstack" }, + "milkyway": { + "bech32Prefix": "milk", + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": 1 + }, + "canonicalAsset": "umilk", + "chainId": "milkyway", + "contractAddressBytes": 32, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "MilkyWay", + "domainId": 1835625579, + "gasCurrencyCoinGeckoId": "milkyway-2", + "gasPrice": { + "amount": "0.03", + "denom": "umilk" + }, + "grpcUrls": [ + { + "http": "https://grpc.mainnet.milkyway.zone/" + } + ], + "index": { + "chunk": 10, + "from": 2249100 + }, + "name": "milkyway", + "nativeToken": { + "decimals": 6, + "denom": "umilk", + "name": "MILK", + "symbol": "MILK" + }, + "protocol": "cosmosnative", + "restUrls": [ + { + "http": "https://lcd.mainnet.milkyway.zone/" + } + ], + "rpcUrls": [ + { + "http": "https://rpc.mainnet.milkyway.zone/" + } + ], + "slip44": 118, + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": "0.03" + } + }, "mint": { "blockExplorers": [ { @@ -2996,6 +3262,7 @@ "moonbeam": { "blockExplorers": [ { + "apiKey": "DDAT4TGIUSSV8MR489UJ21R34WGA1MM2GG", "apiUrl": "https://api-moonbeam.moonscan.io/api", "family": "etherscan", "name": "MoonScan", @@ -3179,6 +3446,42 @@ "gasPrice": "0.0075" } }, + "nibiru": { + "blockExplorers": [ + { + "apiUrl": "https://api.routescan.io/v2/network/mainnet/evm/6900/etherscan/api", + "family": "routescan", + "name": "nibiscan", + "url": "https://nibiscan.io" + } + ], + "blocks": { + "confirmations": 7, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 6900, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Nibiru", + "domainId": 6900, + "gasCurrencyCoinGeckoId": "nibiru", + "name": "nibiru", + "nativeToken": { + "decimals": 18, + "name": "NIBI", + "symbol": "NIBI" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://evm-rpc.nibiru.fi" + } + ], + "technicalStack": "other" + }, "oortmainnet": { "blockExplorers": [ { @@ -3215,9 +3518,93 @@ ], "technicalStack": "other" }, + "ontology": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.ont.io/api", + "family": "other", + "name": "Ontology Explorer", + "url": "https://explorer.ont.io/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 58, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Ontology", + "domainId": 58, + "gasCurrencyCoinGeckoId": "ong", + "isTestnet": false, + "name": "ontology", + "nativeToken": { + "decimals": 18, + "name": "Ontology Gas", + "symbol": "ONG" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://dappnode4.ont.io:10339" + } + ], + "technicalStack": "other" + }, + "opbnb": { + "blockExplorers": [ + { + "apiKey": "GK47JUUDX17PHP8XQ947IYTTPHP6FTAQMK", + "apiUrl": "https://api-opbnb.bscscan.com/api", + "family": "etherscan", + "name": "opBNB Scan", + "url": "https://opbnb.bscscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 204, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "opBNB", + "domainId": 204, + "gasCurrencyCoinGeckoId": "binancecoin", + "name": "opbnb", + "nativeToken": { + "decimals": 18, + "name": "BNB", + "symbol": "BNB" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://opbnb-mainnet-rpc.bnbchain.org" + }, + { + "http": "https://opbnb-mainnet.nodereal.io/v1/64a9df0874fb4a93b9d0a3849de012d3" + }, + { + "http": "https://opbnb-mainnet.nodereal.io/v1/e9a36765eb8a40b9bd12e680a1fd2bc5" + }, + { + "http": "https://opbnb.publicnode.com" + } + ], + "technicalStack": "opstack" + }, "optimism": { "blockExplorers": [ { + "apiKey": "JMYR3W6HHVPQ1HH8T6W8HSZVG88IH3MHRU", "apiUrl": "https://api-optimistic.etherscan.io/api", "family": "etherscan", "name": "Etherscan", @@ -3248,6 +3635,15 @@ "rpcUrls": [ { "http": "https://mainnet.optimism.io" + }, + { + "http": "https://optimism.drpc.org" + }, + { + "http": "https://optimism-rpc.publicnode.com" + }, + { + "http": "https://op-pokt.nodies.app" } ], "technicalStack": "opstack" @@ -3284,9 +3680,6 @@ "rpcUrls": [ { "http": "https://rpc.orderly.network" - }, - { - "http": "https://l2-orderly-mainnet-0.t.conduit.xyz" } ], "technicalStack": "opstack" @@ -3354,6 +3747,43 @@ "gasPrice": "0.025" } }, + "peaq": { + "blockExplorers": [ + { + "apiUrl": "https://peaq.subscan.io/api", + "family": "other", + "name": "Peaq Explorer", + "url": "https://peaq.subscan.io/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": "finalized" + }, + "chainId": 3338, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Peaq", + "domainId": 3338, + "gasCurrencyCoinGeckoId": "peaq-2", + "isTestnet": false, + "name": "peaq", + "nativeToken": { + "decimals": 18, + "name": "peaq", + "symbol": "PEAQ" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://peaq.api.onfinality.io/public" + } + ], + "technicalStack": "polkadotsubstrate" + }, "plume": { "blockExplorers": [ { @@ -3396,6 +3826,7 @@ "polygon": { "blockExplorers": [ { + "apiKey": "ZPAHJ5A73MC45WJED98YJX4MKJE1UGN8D1", "apiUrl": "https://api.polygonscan.com/api", "family": "etherscan", "name": "PolygonScan", @@ -3431,7 +3862,10 @@ "http": "https://polygon-rpc.com" }, { - "http": "https://rpc.ankr.com/polygon" + "http": "https://polygon.drpc.org" + }, + { + "http": "https://polygon-pokt.nodies.app" } ], "technicalStack": "other" @@ -3439,6 +3873,7 @@ "polygonzkevm": { "blockExplorers": [ { + "apiKey": "P8765WMUM4KAM9NPNAY49EACATYC4927HK", "apiUrl": "https://api-zkevm.polygonscan.com/api", "family": "etherscan", "name": "PolygonScan", @@ -3630,6 +4065,42 @@ ], "technicalStack": "arbitrumnitro" }, + "reactive": { + "blockExplorers": [ + { + "apiUrl": "https://reactscan.net/api", + "family": "other", + "name": "Reactscan", + "url": "https://reactscan.net" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 7, + "reorgPeriod": 5 + }, + "chainId": 1597, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Reactive Mainnet", + "domainId": 1597, + "gasCurrencyCoinGeckoId": "reactive-network", + "name": "reactive", + "nativeToken": { + "decimals": 18, + "name": "REACT", + "symbol": "REACT" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet-rpc.rnk.dev" + } + ], + "technicalStack": "other" + }, "real": { "blockExplorers": [ { @@ -3664,7 +4135,10 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://real.drpc.org" + "http": "https://rpc.realforreal.gelato.digital" + }, + { + "http": "https://tangible-real.gateway.tenderly.co" } ], "technicalStack": "arbitrumnitro" @@ -3767,6 +4241,7 @@ "displayName": "Ronin", "domainId": 2020, "gasCurrencyCoinGeckoId": "ronin", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-ronin.safe.onchainden.com", "name": "ronin", "nativeToken": { "decimals": 18, @@ -3816,12 +4291,6 @@ "rpcUrls": [ { "http": "https://rpc.mainnet.rootstock.io/kXhXHf6TnnfW1POvr4UT0YUvujmuju-M" - }, - { - "http": "https://public-node.rsk.co" - }, - { - "http": "https://mycrypto.rsk.co" } ], "technicalStack": "other", @@ -3871,6 +4340,7 @@ "scroll": { "blockExplorers": [ { + "apiKey": "8MU9QJVN429TEYSCMB68NC5MG4VM542CFW", "apiUrl": "https://api.scrollscan.com/api", "family": "etherscan", "name": "Scroll Explorer", @@ -3923,6 +4393,7 @@ "sei": { "blockExplorers": [ { + "apiKey": "487e1ac5-03f4-4473-9e73-cee721b782b5", "apiUrl": "https://seitrace.com/pacific-1/api", "family": "etherscan", "name": "Seitrace", @@ -4093,7 +4564,7 @@ "displayName": "Soneium", "domainId": 1868, "gasCurrencyCoinGeckoId": "ethereum", - "gnosisSafeTransactionServiceUrl": "https://trx-soneium-stg.safe.protofire.io", + "gnosisSafeTransactionServiceUrl": "https://trx-soneium.safe.protofire.io", "name": "soneium", "nativeToken": { "decimals": 18, @@ -4111,6 +4582,7 @@ "sonic": { "blockExplorers": [ { + "apiKey": "V9PP7AFQF5Q6GJSQQ5YS2UBN7GI8QCA443", "apiUrl": "https://api.sonicscan.org/api", "family": "etherscan", "name": "Sonic Explorer", @@ -4130,6 +4602,7 @@ "displayName": "Sonic", "domainId": 146, "gasCurrencyCoinGeckoId": "fantom", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-sonic.safe.global/", "name": "sonic", "nativeToken": { "decimals": 18, @@ -4465,6 +4938,42 @@ ], "technicalStack": "arbitrumnitro" }, + "svmbnb": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.svmbnbmainnet.soo.network/", + "family": "other", + "name": "svmBNB Explorer", + "url": "https://explorer.svmbnbmainnet.soo.network/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.05, + "reorgPeriod": 0 + }, + "chainId": 574456, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "svmBNB", + "domainId": 574456, + "gasCurrencyCoinGeckoId": "binancecoin", + "name": "svmbnb", + "nativeToken": { + "decimals": 9, + "name": "BNB", + "symbol": "BNB" + }, + "protocol": "sealevel", + "rpcUrls": [ + { + "http": "https://rpc.svmbnbmainnet.soo.network/rpc" + } + ], + "technicalStack": "other" + }, "swell": { "blockExplorers": [ { @@ -4505,6 +5014,7 @@ "taiko": { "blockExplorers": [ { + "apiKey": "1CDVSH9KFUVPSFGD1EUFNHD18FUH484IEK", "apiUrl": "https://api.taikoscan.io/api", "family": "etherscan", "name": "Taikoscan", @@ -4733,6 +5243,7 @@ "unichain": { "blockExplorers": [ { + "apiKey": "MUPHYQXB8A6GEEKKYA95N7WENSHYTU1UQQ", "apiUrl": "https://api.uniscan.xyz/api", "family": "etherscan", "name": "Unichain Explorer", @@ -4870,7 +5381,10 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://rpc.tomochain.com" + "http": "https://rpc.viction.xyz" + }, + { + "http": "https://viction.drpc.org" }, { "http": "https://viction.blockpi.network/v1/rpc/public" @@ -4881,6 +5395,7 @@ "worldchain": { "blockExplorers": [ { + "apiKey": "BA89EI7MB9Z88UBURAYPRRRUKCQJB96KAE", "apiUrl": "https://api.worldscan.org/api", "family": "etherscan", "name": "Worldscan", @@ -4909,6 +5424,12 @@ }, "protocol": "ethereum", "rpcUrls": [ + { + "http": "https://worldchain.drpc.org" + }, + { + "http": "https://worldchain-mainnet.gateway.tenderly.co" + }, { "http": "https://worldchain-mainnet.g.alchemy.com/public" } @@ -5185,9 +5706,6 @@ "rpcUrls": [ { "http": "https://rpc.zklink.io" - }, - { - "http": "https://rpc.zklink.network" } ], "technicalStack": "zksync" diff --git a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json index cca3285cc97..1a462112e2e 100644 --- a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json +++ b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json @@ -16,7 +16,7 @@ }, "overhead": 166887 }, - "infinityvm": { + "infinityvmmainnet": { "oracleConfig": { "tokenExchangeRate": "91246426181641219", "gasPrice": "0", @@ -32,6 +32,14 @@ }, "overhead": 333774 }, + "svmbnb": { + "oracleConfig": { + "tokenExchangeRate": "6211087198000555", + "gasPrice": "8599", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, "artela": { "oracleConfig": { "tokenExchangeRate": "1026217531480017", @@ -160,14 +168,6 @@ }, "overhead": 333774 }, - "sophon": { - "oracleConfig": { - "tokenExchangeRate": "91246426181641219", - "gasPrice": "2261877890384", - "tokenDecimals": 18 - }, - "overhead": 333774 - }, "hyperevm": { "oracleConfig": { "tokenExchangeRate": "2108704909057728572", @@ -212,6 +212,14 @@ } }, "soon": { + "svmbnb": { + "oracleConfig": { + "tokenExchangeRate": "498065403286865", + "gasPrice": "8599", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, "solanamainnet": { "oracleConfig": { "tokenExchangeRate": "93995867894608", @@ -230,5 +238,31 @@ }, "overhead": 600000 } + }, + "svmbnb": { + "solanamainnet": { + "oracleConfig": { + "tokenExchangeRate": "362255419747497", + "gasPrice": "85447", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, + "bsc": { + "oracleConfig": { + "tokenExchangeRate": "15000000000000000000", + "gasPrice": "2767201741", + "tokenDecimals": 18 + }, + "overhead": 151966 + }, + "soon": { + "oracleConfig": { + "tokenExchangeRate": "4517479000050299", + "gasPrice": "2855", + "tokenDecimals": 9 + }, + "overhead": 600000 + } } } diff --git a/rust/sealevel/environments/mainnet3/multisig-ism-message-id/svmbnb/hyperlane/multisig-config.json b/rust/sealevel/environments/mainnet3/multisig-ism-message-id/svmbnb/hyperlane/multisig-config.json new file mode 100644 index 00000000000..dccc85a9857 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/multisig-ism-message-id/svmbnb/hyperlane/multisig-config.json @@ -0,0 +1,1377 @@ +{ + "abstract": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x2ef8ece5b51562e65970c7d36007baa43a1de685", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "alephzeroevmmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x33f20e6e775747d60301c6ea1c50e51f0389740c", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0xCbf382214825F8c2f347dd4f23F0aDFaFad55dAa" + ] + }, + "ancient8": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xbb5842ae0e05215b53df4787a29144efb7e67551", + "0xa5a56e97fb46f0ac3a3d261e404acb998d9a6969", + "0x95c7bf235837cb5a609fe6c95870410b9f68bcff" + ] + }, + "apechain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "appchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x0531251bbadc1f9f19ccce3ca6b3f79f08eae1be", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "arbitrum": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x4d966438fe9e2b1e7124c87bbb90cb4f0f6c59a1", + "0xec68258a7c882ac2fc46b81ce80380054ffb4ef2", + "0x5450447aee7b544c462c9352bef7cad049b0c2dc", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b" + ] + }, + "arbitrumnova": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xd2a5e9123308d187383c87053811a2c21bd8af1f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "arcadia": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xe16ee9618f138cc2dcf9f9a95462099a8bf33a38", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "artela": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x8fcc1ebd4c0b463618db13f83e4565af3e166b00", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "arthera": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x13710ac11c36c169f62fba95767ae59a1e57098d", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "astar": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x4d1b2cade01ee3493f44304653d8e352c66ec3e7", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "aurora": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x37105aec3ff37c7bb0abdb0b1d75112e1e69fa86", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "avalanche": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x3fb8263859843bffb02950c492d492cae169f4cf", + "0x402e0f8c6e4210d408b6ac00d197d4a099fcd25a", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8" + ] + }, + "b3": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xd77b516730a836fc41934e7d5864e72c165b934e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "base": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xb9453d675e0fa3c178a17b4ce1ad5b1a279b3af9", + "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xcff391b4e516452d424db66beb9052b041a9ed79", + "0x5450447aee7b544c462c9352bef7cad049b0c2dc", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f" + ] + }, + "berachain": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x0190915c55d9c7555e6d2cb838f04d18b5e2260e", + "0xa7341aa60faad0ce728aa9aeb67bb880f55e4392", + "0xae09cb3febc4cad59ef5a56c1df741df4eb1f4b6", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "bitlayer": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb" + ] + }, + "blast": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xf20c0b09f597597c8d2430d3d72dfddaf09177d1", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x1652d8ba766821cf01aeea34306dfc1cab964a32", + "0x54bb0036f777202371429e062fe6aee0d59442f9" + ] + }, + "bob": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x20f283be1eb0e81e22f51705dcb79883cfdd34aa", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "boba": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xebeb92c94ca8408e73aa16fd554cb3a7df075c59", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "bouncebit": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xaf38612d1e79ec67320d21c5f7e92419427cd154", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "bsc": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x570af9b7b36568c8877eebba6c6727aa9dab7268", + "0x8292b1a53907ece0f76af8a50724e9492bcdc8a3", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0x5450447aee7b544c462c9352bef7cad049b0c2dc", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "bsquared": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xcadc90933c9fbe843358a4e70e46ad2db78e28aa", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "celo": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x63478422679303c3e4fc611b771fa4a707ef7f4a", + "0xeb0c31e2f2671d724a2589d4a8eca91b97559148", + "0x033e391e9fc57a7b5dd6c91b69be9a1ed11c4986", + "0x4a2423ef982b186729e779b6e54b0e84efea7285", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "cheesechain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x478fb53c6860ae8fc35235ba0d38d49b13128226", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47" + ] + }, + "chilizmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x7403e5d58b48b0f5f715d9c78fbc581f01a625cb", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "conflux": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x113dfa1dc9b0a2efb6ad01981e2aad86d3658490", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "conwai": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x949e2cdd7e79f99ee9bbe549540370cdc62e73c3", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "coredao": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xbd6e158a3f5830d99d7d2bce192695bc4a148de2", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "corn": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xc80b2e3e38220e02d194a0effa9d5bfe89894c07", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "coti": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x3c89379537f8beafc54e7e8ab4f8a1cf7974b9f0", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "cyber": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x94d7119ceeb802173b6924e6cc8c4cd731089a27", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "deepbrainchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x3825ea1e0591b58461cc4aa34867668260c0e6a8", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "degenchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x433e311f19524cd64fb2123ad0aa1579a4e1fc83", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "dogechain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xe43f742c37858746e6d7e458bc591180d0cba440", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "duckchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x91d55fe6dac596a6735d96365e21ce4bca21d83c", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "eclipsemainnet": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0", + "0x3571223e745dc0fcbdefa164c9b826b90c0d2dac", + "0xea83086a62617a7228ce4206fae2ea8b0ab23513", + "0x4d4629f5bfeabe66edc7a78da26ef5273c266f97" + ] + }, + "endurance": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x28c5b322da06f184ebf68693c5d19df4d4af13e5", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x7419021c0de2772b763e554480158a82a291c1f2" + ] + }, + "ethereum": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x03c842db86a6a3e524d4a6615390c1ea8e2b9541", + "0x94438a7de38d4548ae54df5c6010c4ebc5239eae", + "0x5450447aee7b544c462c9352bef7cad049b0c2dc", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b", + "0xb683b742b378632a5f73a2a5a45801b3489bba44", + "0xbf1023eff3dba21263bf2db2add67a0d6bcda2de", + "0x5d7442439959af11172bf92d9a8d21cf88d136e3", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f" + ] + }, + "everclear": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0xD79DFbF56ee2268f061cc613027a44A880f61Ba2" + ] + }, + "evmos": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x8f82387ad8b7b13aa9e06ed3f77f78a77713afe0", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "fantom": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xa779572028e634e16f26af5dfd4fa685f619457d", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "flame": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x1fa928ce884fa16357d4b8866e096392d4d81f43", + "0xa6c998f0db2b56d7a63faf30a9b677c8b9b6faab", + "0x09f9de08f7570c4146caa708dc9f75b56958957f", + "0xf1f4ae9959490380ad7863e79c3faf118c1fbf77", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "flare": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xb65e52be342dba3ab2c088ceeb4290c744809134", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "flowmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xe132235c958ca1f3f24d772e5970dd58da4c0f6e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x14ADB9e3598c395Fe3290f3ba706C3816Aa78F59" + ] + }, + "fluence": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0xabc8dd7594783c90a3c0fb760943f78c37ea6d75" + ] + }, + "form": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x58554b2e76167993b5fc000d0070a2f883cd333a", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "fraxtal": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4", + "0x1c3C3013B863Cf666499Da1A61949AE396E3Ab82", + "0x573e960e07ad74ea2c5f1e3c31b2055994b12797", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x25b3a88f7cfd3c9f7d7e32b295673a16a6ddbd91" + ] + }, + "fusemainnet": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x770c8ec9aac8cec4b2ead583b49acfbc5a1cf8a9", + "0x1FE988A1A20cE4141B2081fF8446DA99e11D61d7", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + ] + }, + "game7": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0x691dc4e763514df883155df0952f847b539454c0" + ] + }, + "glue": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xbe2ded12f7b023916584836506677ea89a0b6924", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "gnosis": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xd4df66a859585678f2ea8357161d896be19cc1ca", + "0x19fb7e04a1be6b39b6966a0b0c60b929a93ed672", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0x5450447aee7b544c462c9352bef7cad049b0c2dc" + ] + }, + "gravity": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x23d549bf757a02a6f6068e9363196ecd958c974e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "guru": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x0d756d9051f12c4de6aee2ee972193a2adfe00ef", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "harmony": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xd677803a67651974b1c264171b5d7ca8838db8d5", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "hashkey": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0x55007cab8788cdba22844e7a2499cf43347f487a" + ] + }, + "hemi": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x312dc72c17d01f3fd0abd31dd9b569bc473266dd", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "hyperevm": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x01be14a9eceeca36c9c1d46c056ca8c87f77c26f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x36f2bd8200ede5f969d63a0a28e654392c51a193" + ] + }, + "immutablezkevmmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xbdda85b19a5efbe09e52a32db1a072f043dd66da", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "inevm": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xf9e35ee88e4448a3673b4676a4e153e3584a08eb", + "0x0d4e7e64f3a032db30b75fe7acae4d2c877883bc", + "0x9ab11f38a609940153850df611c9a2175dcffe0f" + ] + }, + "infinityvmmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0x777c19c87aaa625486dff5aab0a479100f4249ad" + ] + }, + "injective": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xbfb8911b72cfb138c7ce517c57d9c691535dc517", + "0xd6f6dee54443632490eddc82680d8917544bcb5a", + "0x9e551b6694bbd295d7d6e6a2540c7d41ce70a3b9" + ] + }, + "ink": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xb533b8b104522958b984fb258e0684dec0f1a6a5", + "0xd207a6dfd887d91648b672727ff1aef6223cb15a", + "0xa40203b5301659f1e201848d92f5e81f64f206f5", + "0xff9c1e7b266a36eda0d9177d4236994d94819dc0", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "kaia": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x9de0b3abb221d19719882fa4d61f769fdc2be9a4", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "kroma": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x71b83c21342787d758199e4b8634d3a15f02dc6e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "linea": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xf2d5409a59e0f5ae7635aff73685624904a77d94", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb" + ] + }, + "lisk": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xc0b282aa5bac43fee83cf71dc3dd1797c1090ea5", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4", + "0x3DA4ee2801Ec6CC5faD73DBb94B10A203ADb3d9e", + "0x4df6e8878992c300e7bfe98cac6bf7d3408b9cbf", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0xf0da628f3fb71652d48260bad4691054045832ce", + "0xead4141b6ea149901ce4f4b556953f66d04b1d0c" + ] + }, + "lukso": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xa5e953701dcddc5b958b5defb677a829d908df6d", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47" + ] + }, + "lumia": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "lumiaprism": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xb69731640ffd4338a2c9358a935b0274c6463f85", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "mantapacific": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x8e668c97ad76d0e28375275c41ece4972ab8a5bc", + "0x521a3e6bf8d24809fde1c1fd3494a859a16f132c", + "0x14025fe092f5f8a401dd9819704d9072196d2125", + "0x25b9a0961c51e74fd83295293bc029131bf1e05a", + "0xa0eE95e280D46C14921e524B075d0C341e7ad1C8", + "0xcc9a0b6de7fe314bd99223687d784730a75bb957", + "0x42b6de2edbaa62c2ea2309ad85d20b3e37d38acf" + ] + }, + "mantle": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xf930636c5a1a8bf9302405f72e3af3c96ebe4a52", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189" + ] + }, + "matchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x8a052f7934b0626105f34f980c875ec03aaf82e8", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "merlin": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xc1d6600cb9326ed2198cc8c4ba8d6668e8671247", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "metal": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xd9f7f1a05826197a93df51e86cefb41dfbfb896a", + "0x01e3909133d20c05bbc94247769235d30101f748", + "0xaba06266f47e3ef554d218b879bd86114a8dabd4", + "0x05d91f80377ff5e9c6174025ffaf094c57a4766a", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "metis": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xc4a3d25107060e800a43842964546db508092260", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb" + ] + }, + "milkyway": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0x9985e0c6df8e25b655b46a317af422f5e7756875" + ] + }, + "mint": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x0230505530b80186f8cdccfaf9993eb97aebe98a" + ] + }, + "mode": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4", + "0x65C140e3a05F33192384AffEF985696Fe3cDDE42", + "0x20eade18ea2af6dfd54d72b3b5366b40fcb47f4b", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x485a4f0009d9afbbf44521016f9b8cdd718e36ea" + ] + }, + "molten": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "moonbeam": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x2225e2f4e9221049456da93b71d2de41f3b6b2a8", + "0x645428d198d2e76cbd9c1647f5c80740bb750b97", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b" + ] + }, + "morph": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x4884535f393151ec419add872100d352f71af380", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "nero": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xb86f872df37f11f33acbe75b6ed208b872b57183", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "neutron": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0", + "0xb65438a014fb05fbadcfe35bc6e25d372b6ba460", + "0x42fa752defe92459370a052b6387a87f7de9b80c", + "0xc79503a3e3011535a9c60f6d21f76f59823a38bd", + "0x47aa126e05933b95c5eb90b26e6b668d84f4b25a", + "0x54b2cca5091b098a1a993dec03c4d1ee9af65999", + "0x42b6de2edbaa62c2ea2309ad85d20b3e37d38acf" + ] + }, + "nibiru": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xba9779d84a8efba1c6bc66326d875c3611a24b24", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "ontology": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0x2578b0a330c492e1a1682684e27e6a93649befd5" + ] + }, + "oortmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0xfa94a494f01d1034b8cea025ca4c2a7e31ca39a1" + ] + }, + "opbnb": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x1bdf52749ef2411ab9c28742dea92f209e96c9c4", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "optimism": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x20349eadc6c72e94ce38268b96692b1a5c20de4f", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4", + "0xd8c1cCbfF28413CE6c6ebe11A3e29B0D8384eDbB", + "0x1b9e5f36c4bfdb0e3f0df525ef5c888a4459ef99", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0xf9dfaa5c20ae1d84da4b2696b8dc80c919e48b12" + ] + }, + "orderly": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "osmosis": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0xea483af11c19fa41b16c31d1534c2a486a92bcac" + ] + }, + "peaq": { + "type": "messageIdMultisigIsm", + "threshold": 1, + "validators": [ + "0x7f7fe70b676f65097e2a1e2683d0fc96ea8fea49" + ] + }, + "plume": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x63c9b5ea28710d956a51f0f746ee8df81215663f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "polygon": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x12ecb319c7f4e8ac5eb5226662aeb8528c5cefac", + "0x008f24cbb1cc30ad0f19f2516ca75730e37efb5f", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0x5450447aee7b544c462c9352bef7cad049b0c2dc" + ] + }, + "polygonzkevm": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x86f2a44592bb98da766e880cfd70d3bbb295e61a", + "0x865818fe1db986036d5fd0466dcd462562436d1a", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8" + ] + }, + "polynomialfi": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x23d348c2d365040e56f3fee07e6897122915f513", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "prom": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xb0c4042b7c9a95345be8913f4cdbf4043b923d98", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "proofofplay": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xcda40baa71970a06e5f55e306474de5ca4e21c3b", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "rarichain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xeac012df7530720dd7d6f9b727e4fe39807d1516", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "reactive": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x45768525f6c5ca2e4e7cc50d405370eadee2d624", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "real": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xaebadd4998c70b05ce8715cf0c3cb8862fe0beec", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "redstone": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x1400b9737007f7978d8b4bbafb4a69c83f0641a7", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x101cE77261245140A0871f9407d6233C8230Ec47" + ] + }, + "rivalz": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xf87c3eb3dde972257b0d6d110bdadcda951c0dc1", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "ronin": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xa3e11929317e4a871c3d47445ea7bb8c4976fd8a", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb" + ] + }, + "rootstockmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x8675eb603d62ab64e3efe90df914e555966e04ac", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "sanko": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x795c37d5babbc44094b084b0c89ed9db9b5fae39", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "scroll": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xad557170a9f2f21c35e03de07cb30dcbcc3dff63", + "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xbac4ac39f1d8b5ef15f26fdb1294a7c9aba3f948" + ] + }, + "sei": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "shibarium": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xfa33391ee38597cbeef72ccde8c9e13e01e78521", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "snaxchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "solanamainnet": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x28464752829b3ea59a497fca0bdff575c534c3ff", + "0x2b7514a2f77bd86bbf093fe6bb67d8611f51c659", + "0xd90ea26ff731d967c5ea660851f7d63cb04ab820", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xcb6bcbd0de155072a7ff486d9d7286b0f71dcc2d" + ] + }, + "soneium": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xd4b7af853ed6a2bfc329ecef545df90c959cbee8", + "0x9f4fa50ce49815b0932428a0eb1988382cef4a97", + "0x8d2f8ebd61d055d58768cf3b07cb2fb565d87716", + "0x6c5f6ab7a369222e6691218ad981fe08a5def094", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "sonic": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xa313d72dbbd3fa51a2ed1611ea50c37946fa42f7", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb" + ] + }, + "sonicsvm": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xf21f46905d8d09f76bc8c503f856e5466bc5ffea", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x85c7a16790cfd9dad6d4abdd4e2d3f1d550c7606" + ] + }, + "soon": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x0E6723b3C1eD3Db0C24347AA2cf16D28BC2a1F76", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "sophon": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xb84c5d02120ed0b39d0f78bbc0e298d89ebcd10b", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "story": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x501eda013378c60557d763df98d617b6ba55447a", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "stride": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0x88f0E5528131b10e3463C4c68108217Dd33462ac", + "0xa3eaa1216827ad63dd9db43f6168258a89177990", + "0x3f869C36110F00D10dC74cca3ac1FB133cf019ad", + "0x502dC6135d16E74056f609FBAF76846814C197D3", + "0xc36979780c1aD43275182600a61Ce41f1C390FbE", + "0x87460dcEd16a75AECdBffD4189111d30B099f5b0", + "0xf54982134e52Eb7253236943FBffE0886C5bde0C", + "0x5937b7cE1029C3Ec4bD8e1AaCc0C0f9422654D7d", + "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b" + ] + }, + "subtensor": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xd5f8196d7060b85bea491f0b52a671e05f3d10a2", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "superpositionmainnet": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x3f489acdd341c6b4dd86293fa2cc5ecc8ccf4f84", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "superseed": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0xdc2b87cb555411bb138d3a4e5f7832c87fae2b88", + "0x68f3a3b244f6ddc135130200a6b8729e290b4240", + "0x6ff4554cffbc2e4e4230b78e526eab255101d05a", + "0x55880ac03fdf15fccff54ed6f8a83455033edd22", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "swell": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x4f51e4f4c7fb45d82f91568480a1a2cfb69216ed", + "0x9eadf9217be22d9878e0e464727a2176d5c69ff8", + "0xa5a23fa2a67782bbf1a540cb5ca6a47a0f3f66fb", + "0x3f707633ccab09d2978e29107c0bbef8a993e7a0", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "taiko": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xa930073c8f2d0b2f7423ea32293e0d1362e65d79", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x2F007c82672F2Bb97227D4e3F80Ac481bfB40A2a" + ] + }, + "tangle": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x1ee52cbbfacd7dcb0ba4e91efaa6fbc61602b15b", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0xe271ef9a6e312540f099a378865432fa73f26689" + ] + }, + "telos": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xcb08410b14d3adf0d0646f0c61cd07e0daba8e54", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "torus": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x96982a325c28a842bc8cf61b63000737bb9f1f7d", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "treasure": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x6ad994819185553e8baa01533f0cd2c7cadfe6cc", + "0x278460fa51ff448eb53ffa62951b4b8e3e8f74e3", + "0xe92ff70bb463e2aa93426fd2ba51afc39567d426", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb" + ] + }, + "trumpchain": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x3ada634c8dfa57a67f5f22ca757b35cde6cfab5e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0xcF8151b8aEFfF4e22F6B48fe2Ffe2d60F00C890C" + ] + }, + "unichain": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x9773a382342ebf604a2e5de0a1f462fb499e28b1", + "0xa2549be30fb852c210c2fe8e7639039dca779936", + "0xbcbed4d11e946844162cd92c6d09d1cf146b4006", + "0xa9d517776fe8beba7d67c21cac1e805bd609c08e", + "0x14d0B24d3a8F3aAD17DB4b62cBcEC12821c98Cb3", + "0x0d4c1394a255568ec0ecd11795b28d1bda183ca4" + ] + }, + "unitzero": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x18818e3ad2012728465d394f2e3c0ea2357ae9c5", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "vana": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xfdf3b0dfd4b822d10cacb15c8ae945ea269e7534", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0xba2f4f89cae6863d8b49e4ca0208ed48ad9ac354" + ] + }, + "viction": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189", + "0xa3f93fe365bf99f431d8fde740b140615e24f99b", + "0x1f87c368f8e05a85ef9126d984a980a20930cb9c" + ] + }, + "worldchain": { + "type": "messageIdMultisigIsm", + "threshold": 4, + "validators": [ + "0x31048785845325b22817448b68d08f8a8fe36854", + "0x11e2a683e83617f186614071e422b857256a9aae", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x761980c3debdc8ddb69a2713cf5126d4db900f0f", + "0x5aed2fd5cc5f9749c455646c86b0db6126cafcbb", + "0x6d113ae51bfea7b63a8828f97e9dce393b25c189" + ] + }, + "xai": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xe993f01fea86eb64cda45ae5af1d5be40ac0c7e9", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "xlayer": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xa2ae7c594703e988f23d97220717c513db638ea3", + "0xfed056cC0967F5BC9C6350F6C42eE97d3983394d", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + ] + }, + "xpla": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0xc11cba01d67f2b9f0288c4c8e8b23c0eca03f26e", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "zeronetwork": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "zetachain": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "zircuit": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x169ec400cc758fef3df6a0d6c51fbc6cdd1015bb", + "0x7aC6584c068eb2A72d4Db82A7B7cd5AB34044061", + "0x0180444c9342BD672867Df1432eb3dA354413a6E", + "0x1da9176C2CE5cC7115340496fa7D1800a98911CE" + ] + }, + "zklink": { + "type": "messageIdMultisigIsm", + "threshold": 2, + "validators": [ + "0x217a8cb4789fc45abf56cb6e2ca96f251a5ac181", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + }, + "zksync": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x75237d42ce8ea27349a0254ada265db94157e0c1" + ] + }, + "zoramainnet": { + "type": "messageIdMultisigIsm", + "threshold": 3, + "validators": [ + "0x35130945b625bb69b28aee902a3b9a76fa67125f", + "0x7089b6352d37d23fb05a7fee4229c78e038fba09", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ] + } +} diff --git a/rust/sealevel/environments/mainnet3/svmbnb/core/program-ids.json b/rust/sealevel/environments/mainnet3/svmbnb/core/program-ids.json new file mode 100644 index 00000000000..a2bb71de497 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/svmbnb/core/program-ids.json @@ -0,0 +1,8 @@ +{ + "mailbox": "GzwNZJ2EXUWQCEoV6zoBGLxkdsnjkLvm6zHReMEC2JSA", + "validator_announce": "Gh4LUk91bj4QGYWsBY4QyDGhs68n8Maf8oeTRZoQZAEf", + "multisig_ism_message_id": "3aD11eiNt2ZjmgiQayNRyZfp7hXBRS9hbeyPbk7cmTyv", + "igp_program_id": "ALUFQNqrKA9FgxvzkDvp2K5M94P3bvJYzPvrB8LSe6Ee", + "overhead_igp_account": "FcXbqeqf23ToXfFXKJisQvCnRu3GjCEunNtkqvxAcNWF", + "igp_account": "76JNRGvHbER5CL7X4Mw6qpzvvKDW3syyfZ5LNXfY53sC" +} \ No newline at end of file diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 23d3550bb3a..f9c93b8d32c 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -187,6 +187,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< subtensor: true, superseed: true, superpositionmainnet: true, + svmbnb: true, swell: true, taiko: true, tangle: true, @@ -333,6 +334,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< subtensor: true, superseed: true, superpositionmainnet: true, + svmbnb: true, swell: true, taiko: true, tangle: true, @@ -479,6 +481,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< subtensor: true, superseed: true, superpositionmainnet: true, + svmbnb: true, swell: true, taiko: true, tangle: true, @@ -860,7 +863,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: '673c6d2-20250502-141150', + tag: '6df66f8-20250508-104808', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index 859d8214e3f..76831191d7e 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -416,6 +416,9 @@ "superpositionmainnet": { "validators": ["0x3f489acdd341c6b4dd86293fa2cc5ecc8ccf4f84"] }, + "svmbnb": { + "validators": ["0xabcd4dac2d06ae30c011d25b0c2c193873116a14"] + }, "swell": { "validators": ["0x4f51e4f4c7fb45d82f91568480a1a2cfb69216ed"] }, diff --git a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json index c015cd6bd72..77b45aae3a7 100644 --- a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json +++ b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json @@ -120,6 +120,7 @@ "subtensor": 0.228, "superpositionmainnet": 0.0235, "superseed": 0.0126, + "svmbnb": 0.00524, "swell": 0.00856, "taiko": 0.005, "tangle": 3.13, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index 93a39b54ba8..a6d6a4677c5 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -120,6 +120,7 @@ "subtensor": 1.82, "superpositionmainnet": 0.188, "superseed": 0.101, + "svmbnb": 0.1, "swell": 0.0685, "taiko": 0.04, "tangle": 6, diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index b1223007c4e..c7ec29c7e3c 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -117,6 +117,7 @@ "subtensor": 0.456, "superpositionmainnet": 0.047, "superseed": 0.0252, + "svmbnb": 0.025, "swell": 0.0171, "taiko": 0.01, "tangle": 1.5, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index f2f2b49f836..0d2ca0aefac 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -117,6 +117,7 @@ "subtensor": 1.37, "superpositionmainnet": 0.141, "superseed": 0.0756, + "svmbnb": 0.075, "swell": 0.0514, "taiko": 0.03, "tangle": 4.5, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index f975d38e0e6..85fa481a86b 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -117,6 +117,7 @@ "subtensor": 2.74, "superpositionmainnet": 0.282, "superseed": 0.151, + "svmbnb": 0.15, "swell": 0.103, "taiko": 0.06, "tangle": 9, diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index 9ccc5b3533a..6d2d805240e 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -483,6 +483,10 @@ "amount": "0.2", "decimals": 9 }, + "svmbnb": { + "amount": "0.0000001", + "decimals": 1 + }, "swell": { "amount": "0.001000251", "decimals": 9 diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index 092b2378d7e..da1e4b73413 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -95,4 +95,7 @@ export const chainOwners: ChainMap = { // Will move to a Squads once it's live owner: '9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf', }, + svmbnb: { + owner: '9bRSUPjfS3xS6n5EfkJzHFTRDa4AHLda8BU2pP4HoWnf', + }, }; diff --git a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts index 1f28c86663f..f382f890173 100644 --- a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts +++ b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts @@ -124,6 +124,7 @@ export const mainnet3SupportedChainNames = [ 'subtensor', 'superseed', 'superpositionmainnet', + 'svmbnb', 'swell', 'taiko', 'tangle', diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index 1a37134e800..ffebf8bea9e 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -120,6 +120,7 @@ "subtensor": "364.88", "superseed": "1796.24", "superpositionmainnet": "1796.24", + "svmbnb": "596.43", "swell": "1796.24", "taiko": "1796.24", "tangle": "1", diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index d4a988210a5..6b373a7a28e 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -1765,5 +1765,16 @@ export const validatorChainConfig = ( 'peaq', ), }, + + svmbnb: { + interval: 5, + reorgPeriod: getReorgPeriod('svmbnb'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xabcd4dac2d06ae30c011d25b0c2c193873116a14'], + }, + 'svmbnb', + ), + }, }; }; diff --git a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts index 8128f556e37..c95d355abe8 100644 --- a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts +++ b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts @@ -101,8 +101,12 @@ function getChainConnections( // For the Rivalz team building out their own warp route ['solanamainnet', 'rivalz'], ['solanamainnet', 'everclear'], - ['solanamainnet', 'infinityvm'], + ['solanamainnet', 'infinityvmmainnet'], ['solanamainnet', 'sophon'], + // for svmBNB + ['svmbnb', 'solanamainnet'], + ['svmbnb', 'bsc'], + ['svmbnb', 'soon'], // All warp routes ...Object.values(WarpRouteIds).map(getWarpChains), ]; diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index df817e58a13..a46b0e51444 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -2333,6 +2333,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + svmbnb: { + threshold: 1, + validators: [ + { + address: '0xabcd4dac2d06ae30c011d25b0c2c193873116a14', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + swell: { threshold: 4, validators: [ From e35ad1af589d6af70d36087e129e78add0002dfb Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Fri, 9 May 2025 17:52:11 +0100 Subject: [PATCH 162/223] chore: Upgrade Relayer and Scraper to latest (#6188) ### Description Upgrade Relayer and Scraper to latest ### Drive-by changes - Update leak configuration ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .gitleaks.toml | 10 ++++++++++ typescript/infra/config/environments/mainnet3/agent.ts | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.gitleaks.toml b/.gitleaks.toml index 55023f074f5..5bc3fb67b40 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -96,3 +96,13 @@ keywords = [ "ccvalidators.com" ] tags = ["key", "Crypto Crew"] + +[[rules]] +id = "ccnodes-api-key" +description = "CryptoCrew Nodes API Key" +regex = '''https://[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+\.ccnodes\.com:?[0-9]*/?[a-zA-Z0-9-]*''' +keywords = [ + "ccnodes", + "ccnodes.com" +] +tags = ["key", "Crypto Crew Nodes"] diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index f9c93b8d32c..bb8d85bd373 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -849,7 +849,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'f05ebc4-20250502-225226', + tag: 'd3f8da9-20250509-160937', }, blacklist, gasPaymentEnforcement: gasPaymentEnforcement, @@ -874,7 +874,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '673c6d2-20250502-141150', + tag: 'd3f8da9-20250509-160937', }, resources: scraperResources, }, From df7251dac61363ed84cb01bb5d9e70166e471a80 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Mon, 12 May 2025 14:16:05 +0100 Subject: [PATCH 163/223] chore: Upgrade Relayer RC and Neutron to latest (#6197) ### Description Upgrade Relayer RC and Neutron to latest ### Backward compatibility Yes ### Testing Sync with Hyperlane context Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- typescript/infra/config/environments/mainnet3/agent.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index bb8d85bd373..b20fb98c6f1 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -889,7 +889,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'f05ebc4-20250502-225226', + tag: 'd3f8da9-20250509-160937', }, blacklist, // We're temporarily (ab)using the RC relayer as a way to increase @@ -927,7 +927,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'f05ebc4-20250502-225226', + tag: 'd3f8da9-20250509-160937', }, blacklist, gasPaymentEnforcement, From 6bf0df0947c724d427eac751f5958d55101ffb09 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 12 May 2025 16:48:10 +0100 Subject: [PATCH 164/223] chore: re-enable some testnets (#6184) ### Description - chore: re-enable arcadiatestnet2 scraping - chore: re-enable hyperliduidevmtestnet scraping + relaying ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/testnet4/agent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 1f41ee62455..600926f7215 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -115,7 +115,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< formtestnet: true, fuji: true, holesky: true, - hyperliquidevmtestnet: false, + hyperliquidevmtestnet: true, infinityvmmonza: true, inksepolia: true, kyvetestnet: true, @@ -146,7 +146,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< alephzeroevmtestnet: true, alfajores: true, arbitrumsepolia: true, - arcadiatestnet2: false, + arcadiatestnet2: true, auroratestnet: true, basecamptestnet: true, basesepolia: true, @@ -163,7 +163,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< formtestnet: true, fuji: true, holesky: true, - hyperliquidevmtestnet: false, + hyperliquidevmtestnet: true, infinityvmmonza: true, inksepolia: true, kyvetestnet: true, From d54435cf07f79288da8ce6a110ee3d4a16e1fd33 Mon Sep 17 00:00:00 2001 From: Jason Guo <33064781+Xaroz@users.noreply.github.com> Date: Mon, 12 May 2025 12:45:59 -0400 Subject: [PATCH 165/223] fix: re-add registry commit to docker entry (#6203) ### Description Re-add registry commit to `git fetch origin` in docker-entrypoint.sh ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- docker-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 801b0c035e8..e4253581edb 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -13,7 +13,7 @@ if [ -n "$REGISTRY_COMMIT" ]; then echo "Updating Hyperlane registry to: $REGISTRY_COMMIT" OLDPWD=$(pwd) cd "$REGISTRY_URI" - git fetch origin + git fetch origin "$REGISTRY_COMMIT" git checkout "$REGISTRY_COMMIT" # Only reset if it's a branch From 2ae0f72b9a8488192c9decc71b1cf7632aa489cc Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 12 May 2025 15:15:21 -0400 Subject: [PATCH 166/223] feat: cli warp check should assert that contracts are verified (#6167) ### Description This PR implements https://linear.app/hyperlane-xyz/issue/ENG-1031/cli-warp-check-should-assert-that-contracts-are-verified which allows `warp check` to determine if a contract has been verified. The details are in the Linear issue. In summary, it does this by expanding the onchain config with a VirtualConfig, which compares to a hardcoded ExpectedVirtualConfig. Example ```bash hl warp check --registry ~/Desktop/code/hyperlane/hyperlane-registry --warpRouteId LUMIA/arbitrum-avalanche-base-bsc-ethereum-lumiaprism-optimism-polygon Hyperlane CLI Hyperlane Warp Check ____________________ lumiaprism: contractVerificationStatus: TransparentUpgradeableProxy: true HypNative: true mailbox: "0x0df25a2d59f03f039b56e90edc5b89679ace28bc" owner: "0xa86c4af592ddaa676f53de278de9cfcd52ae6b39" ... ``` ### Drive-by changes - Add collateralUri and syntheticUrisyntheticUri to hypERC20contracts ### Related issues Resolves https://linear.app/hyperlane-xyz/issue/ENG-1031/cli-warp-check-should-assert-that-contracts-are-verified ### Backward compatibility Yes ### Testing Manual. --------- Co-authored-by: Le Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> --- .changeset/dark-socks-carry.md | 6 ++ typescript/cli/src/check/warp.ts | 4 +- typescript/cli/src/commands/warp.ts | 37 ++++++---- typescript/cli/src/deploy/warp.ts | 4 +- .../sdk/src/deploy/verify/ContractVerifier.ts | 51 +++++++++++-- typescript/sdk/src/deploy/verify/types.ts | 6 ++ typescript/sdk/src/index.ts | 2 + .../sdk/src/token/EvmERC20WarpRouteReader.ts | 53 +++++++++++++- typescript/sdk/src/token/configUtils.ts | 73 +++++++++++++++++-- typescript/sdk/src/token/contracts.ts | 2 + typescript/sdk/src/token/types.ts | 25 ++++++- 11 files changed, 228 insertions(+), 35 deletions(-) create mode 100644 .changeset/dark-socks-carry.md diff --git a/.changeset/dark-socks-carry.md b/.changeset/dark-socks-carry.md new file mode 100644 index 00000000000..a53c71c450d --- /dev/null +++ b/.changeset/dark-socks-carry.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +Add contract verification to CLI Warp Checker diff --git a/typescript/cli/src/check/warp.ts b/typescript/cli/src/check/warp.ts index 33b4e3d6538..a866cf4186a 100644 --- a/typescript/cli/src/check/warp.ts +++ b/typescript/cli/src/check/warp.ts @@ -1,6 +1,7 @@ import { stringify as yamlStringify } from 'yaml'; import { + HypTokenRouterVirtualConfig, WarpRouteDeployConfigMailboxRequired, transformConfigToCheck, } from '@hyperlane-xyz/sdk'; @@ -13,7 +14,8 @@ export async function runWarpRouteCheck({ warpRouteConfig, onChainWarpConfig, }: { - warpRouteConfig: WarpRouteDeployConfigMailboxRequired; + warpRouteConfig: WarpRouteDeployConfigMailboxRequired & + Record>; onChainWarpConfig: WarpRouteDeployConfigMailboxRequired; }): Promise { // Go through each chain and only add to the output the chains that have mismatches diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index a8e1290cd9c..df0d6225f94 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -4,6 +4,7 @@ import { CommandModule } from 'yargs'; import { ChainName, ChainSubmissionStrategySchema, + expandVirtualWarpDeployConfig, expandWarpDeployConfig, getRouterAddressesFromWarpCoreConfig, } from '@hyperlane-xyz/sdk'; @@ -382,13 +383,8 @@ export const check: CommandModuleWithContext<{ warpCoreConfig, )); - // Expand the config before removing non-EVM chain configs to correctly expand - // the remote routers - let expandedWarpDeployConfig = await expandWarpDeployConfig( - context.multiProvider, - warpDeployConfig, - getRouterAddressesFromWarpCoreConfig(warpCoreConfig), - ); + const deployedRoutersAddresses = + getRouterAddressesFromWarpCoreConfig(warpCoreConfig); // Remove any non EVM chain configs to avoid the checker crashing warpCoreConfig.tokens = warpCoreConfig.tokens.filter( @@ -397,20 +393,33 @@ export const check: CommandModuleWithContext<{ ProtocolType.Ethereum, ); - expandedWarpDeployConfig = objFilter( - expandedWarpDeployConfig, - (chain, _config): _config is any => - context.multiProvider.getProtocol(chain) === ProtocolType.Ethereum, - ); - // Get on-chain config const onChainWarpConfig = await getWarpRouteConfigsByCore({ context, warpCoreConfig, }); - await runWarpRouteCheck({ + // get virtual on-chain config + const expandedOnChainWarpConfig = await expandVirtualWarpDeployConfig({ + multiProvider: context.multiProvider, onChainWarpConfig, + deployedRoutersAddresses, + }); + + let expandedWarpDeployConfig = await expandWarpDeployConfig({ + multiProvider: context.multiProvider, + warpDeployConfig, + deployedRoutersAddresses, + expandedOnChainWarpConfig, + }); + expandedWarpDeployConfig = objFilter( + expandedWarpDeployConfig, + (chain, _config): _config is any => + context.multiProvider.getProtocol(chain) === ProtocolType.Ethereum, + ); + + await runWarpRouteCheck({ + onChainWarpConfig: expandedOnChainWarpConfig, warpRouteConfig: expandedWarpDeployConfig, }); diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index ce598a350b4..4878a1244c0 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -619,11 +619,11 @@ async function updateExistingWarpRoute( const deployedRoutersAddresses = getRouterAddressesFromWarpCoreConfig(warpCoreConfig); - const expandedWarpDeployConfig = await expandWarpDeployConfig( + const expandedWarpDeployConfig = await expandWarpDeployConfig({ multiProvider, warpDeployConfig, deployedRoutersAddresses, - ); + }); await promiseObjAll( objMap(expandedWarpDeployConfig, async (chain, config) => { diff --git a/typescript/sdk/src/deploy/verify/ContractVerifier.ts b/typescript/sdk/src/deploy/verify/ContractVerifier.ts index 49ecd30980f..497d0d9cfff 100644 --- a/typescript/sdk/src/deploy/verify/ContractVerifier.ts +++ b/typescript/sdk/src/deploy/verify/ContractVerifier.ts @@ -2,10 +2,11 @@ import fetch from 'cross-fetch'; import { ethers } from 'ethers'; import { Logger } from 'pino'; -import { rootLogger, sleep, strip0x } from '@hyperlane-xyz/utils'; +import { Address, rootLogger, sleep, strip0x } from '@hyperlane-xyz/utils'; import { ExplorerFamily } from '../../metadata/chainMetadataTypes.js'; import { MultiProvider } from '../../providers/MultiProvider.js'; +import { ContractVerificationStatus } from '../../token/types.js'; import { ChainMap, ChainName } from '../../types.js'; import { @@ -223,9 +224,9 @@ export class ContractVerifier { errorMessage = `${responseJson.message}: ${responseJson.result}`; break; default: - errorMessage = `Verification failed: ${ - JSON.stringify(responseJson.result) ?? response.statusText - }`; + errorMessage = `Verification failed: ${JSON.stringify( + responseJson.result ?? response.statusText, + )}`; break; } @@ -241,9 +242,9 @@ export class ContractVerifier { } if (responseJson.result === ExplorerApiErrors.UNABLE_TO_VERIFY) { - const errorMessage = `Verification failed. ${ - JSON.stringify(responseJson.result) ?? response.statusText - }`; + const errorMessage = `Verification failed. ${JSON.stringify( + responseJson.result ?? response.statusText, + )}`; verificationLogger.debug(errorMessage); throw new Error(`[${chain}] ${errorMessage}`); } @@ -335,6 +336,42 @@ export class ContractVerifier { ); } + async getContractVerificationStatus( + chain: ChainName, + address: Address, + verificationLogger: Logger = this.logger, + ): Promise { + try { + const metadata = this.multiProvider.tryGetChainMetadata(chain); + const rpcUrl = metadata?.rpcUrls[0].http ?? ''; + if (rpcUrl.includes('localhost') || rpcUrl.includes('127.0.0.1')) { + verificationLogger.debug('Skipping verification for local endpoints'); + return ContractVerificationStatus.Skipped; + } + verificationLogger.trace( + `Fetching contract ABI for ${chain} address ${address}`, + ); + const sourceCodeResults = ( + await this.submitForm( + chain, + ExplorerApiActions.GETSOURCECODE, + verificationLogger, + { address }, + ) + )[0]; // This specific query only returns 1 result + + // Explorer won't return ContractName if unverified + return sourceCodeResults.ContractName + ? ContractVerificationStatus.Verified + : ContractVerificationStatus.Unverified; + } catch (e) { + this.logger.info( + `Error fetching contract verification status for ${address} on chain ${chain}: ${e}`, + ); + return ContractVerificationStatus.Error; + } + } + private getProxyData(input: ContractVerificationInput) { return { address: input.address, diff --git a/typescript/sdk/src/deploy/verify/types.ts b/typescript/sdk/src/deploy/verify/types.ts index 469b44463cd..f7935cfc668 100644 --- a/typescript/sdk/src/deploy/verify/types.ts +++ b/typescript/sdk/src/deploy/verify/types.ts @@ -67,6 +67,12 @@ export const EXPLORER_GET_ACTIONS = [ ExplorerApiActions.GETSOURCECODE, ]; +export enum VerifyContractTypes { + Proxy = 'proxy', + ProxyAdmin = 'proxyAdmin', + Implementation = 'implementation', +} + export enum ExplorerApiErrors { ALREADY_VERIFIED = 'Contract source code already verified', ALREADY_VERIFIED_ALT = 'Already Verified', diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 131c7b4b44b..ad5cfdc233a 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -565,6 +565,7 @@ export { HypERC20Checker } from './token/checker.js'; export { TokenType } from './token/config.js'; export { expandWarpDeployConfig, + expandVirtualWarpDeployConfig, getRouterAddressesFromWarpCoreConfig, splitWarpCoreAndExtendedConfigs, transformConfigToCheck, @@ -633,6 +634,7 @@ export { TokenMetadataSchema, WarpRouteDeployConfig, WarpRouteDeployConfigMailboxRequired, + HypTokenRouterVirtualConfig, WarpRouteDeployConfigSchema, WarpRouteDeployConfigMailboxRequiredSchema, WarpRouteDeployConfigSchemaErrors, diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 78a5b9e25b4..655fe3e1719 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -13,6 +13,7 @@ import { ProxyAdmin__factory, TokenRouter__factory, } from '@hyperlane-xyz/core'; +import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; import { Address, assert, @@ -23,6 +24,9 @@ import { } from '@hyperlane-xyz/utils'; import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js'; +import { ContractVerifier } from '../deploy/verify/ContractVerifier.js'; +import { ExplorerLicenseType } from '../deploy/verify/types.js'; +import { VerifyContractTypes } from '../deploy/verify/types.js'; import { EvmHookReader } from '../hook/EvmHookReader.js'; import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { MultiProvider } from '../providers/MultiProvider.js'; @@ -32,16 +36,17 @@ import { RemoteRouters, RemoteRoutersSchema, } from '../router/types.js'; -import { ChainNameOrId, DeployedOwnableConfig } from '../types.js'; +import { ChainName, ChainNameOrId, DeployedOwnableConfig } from '../types.js'; import { HyperlaneReader } from '../utils/HyperlaneReader.js'; -import { isProxy, proxyAdmin } from './../deploy/proxy.js'; +import { isProxy, proxyAdmin, proxyImplementation } from './../deploy/proxy.js'; import { NON_ZERO_SENDER_ADDRESS, TokenType } from './config.js'; import { CollateralTokenConfig, DerivedTokenRouterConfig, HypTokenConfig, HypTokenConfigSchema, + HypTokenRouterVirtualConfig, TokenMetadata, XERC20TokenMetadata, } from './types.js'; @@ -60,11 +65,13 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { >; evmHookReader: EvmHookReader; evmIsmReader: EvmIsmReader; + contractVerifier: ContractVerifier; constructor( protected readonly multiProvider: MultiProvider, protected readonly chain: ChainNameOrId, protected readonly concurrency: number = DEFAULT_CONTRACT_READ_CONCURRENCY, + contractVerifier?: ContractVerifier, ) { super(multiProvider, chain); this.evmHookReader = new EvmHookReader(multiProvider, chain, concurrency); @@ -89,6 +96,15 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { [TokenType.collateralUri]: null, [TokenType.syntheticUri]: null, }; + + this.contractVerifier = + contractVerifier ?? + new ContractVerifier( + multiProvider, + {}, + coreBuildArtifact, + ExplorerLicenseType.MIT, + ); } /** @@ -123,6 +139,39 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { }; } + async deriveWarpRouteVirtualConfig( + chain: ChainName, + address: Address, + ): Promise { + const virtualConfig: HypTokenRouterVirtualConfig = { + contractVerificationStatus: {}, + }; + + const contractType = (await isProxy(this.provider, address)) + ? VerifyContractTypes.Proxy + : VerifyContractTypes.Implementation; + + virtualConfig.contractVerificationStatus[contractType] = + await this.contractVerifier.getContractVerificationStatus(chain, address); + + if (contractType === VerifyContractTypes.Proxy) { + virtualConfig.contractVerificationStatus.Implementation = + await this.contractVerifier.getContractVerificationStatus( + chain, + await proxyImplementation(this.provider, address), + ); + + // Derive ProxyAdmin status + virtualConfig.contractVerificationStatus.ProxyAdmin = + await this.contractVerifier.getContractVerificationStatus( + chain, + await proxyAdmin(this.provider, address), + ); + } + + return virtualConfig; + } + /** * Derives the token type for a given Warp Route address using specific methods * diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index b2fddf41c2f..716e49e0b0b 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -17,10 +17,13 @@ import { ChainMap } from '../types.js'; import { sortArraysInConfig } from '../utils/ism.js'; import { WarpCoreConfig } from '../warp/types.js'; +import { EvmERC20WarpRouteReader } from './EvmERC20WarpRouteReader.js'; import { gasOverhead } from './config.js'; import { HypERC20Deployer } from './deploy.js'; import { + ContractVerificationStatus, HypTokenRouterConfig, + HypTokenRouterVirtualConfig, WarpRouteDeployConfig, WarpRouteDeployConfigMailboxRequired, } from './types.js'; @@ -78,11 +81,28 @@ export function getRouterAddressesFromWarpCoreConfig( ) as ChainMap
; } -export async function expandWarpDeployConfig( - multiProvider: MultiProvider, - warpDeployConfig: WarpRouteDeployConfigMailboxRequired, - deployedRoutersAddresses: ChainMap
, -): Promise { +/** + * Expands a Warp deploy config with additional data + * + * @param multiProvider + * @param warpDeployConfig - The warp deployment config + * @param deployedRoutersAddresses - Addresses of deployed routers for each chain + * @param virtualConfig - Optional virtual config to include in the warpDeployConfig + * @returns A promise resolving to an expanded Warp deploy config with derived and virtual metadata + */ +export async function expandWarpDeployConfig(params: { + multiProvider: MultiProvider; + warpDeployConfig: WarpRouteDeployConfigMailboxRequired; + deployedRoutersAddresses: ChainMap
; + expandedOnChainWarpConfig?: WarpRouteDeployConfigMailboxRequired; +}): Promise { + const { + multiProvider, + warpDeployConfig, + deployedRoutersAddresses, + expandedOnChainWarpConfig, + } = params; + const derivedTokenMetadata = await HypERC20Deployer.deriveTokenMetadata( multiProvider, warpDeployConfig, @@ -109,7 +129,8 @@ export async function expandWarpDeployConfig( warpDeployConfig, ); - const chainConfig: WarpRouteDeployConfigMailboxRequired[string] = { + const chainConfig: WarpRouteDeployConfigMailboxRequired[string] & + Partial = { // Default Expansion ...derivedTokenMetadata, remoteRouters, @@ -120,11 +141,28 @@ export async function expandWarpDeployConfig( ? { owner: config.owner } : undefined, isNft: false, - // User-specified config takes precedence ...config, }; + // Expand EVM warpDeployConfig virtual to the control states + if ( + expandedOnChainWarpConfig?.[chain]?.contractVerificationStatus && + multiProvider.getProtocol(chain) === ProtocolType.Ethereum + ) { + // For most cases, we set to Verified + chainConfig.contractVerificationStatus = objMap( + expandedOnChainWarpConfig[chain].contractVerificationStatus ?? {}, + (_, status) => { + // Skipped for local e2e testing + if (status === ContractVerificationStatus.Skipped) + return ContractVerificationStatus.Skipped; + + return ContractVerificationStatus.Verified; + }, + ); + } + // Properly set the remote routers addresses to their 32 bytes representation // as that is how they are set on chain const formattedRemoteRouters = objMap( @@ -140,6 +178,27 @@ export async function expandWarpDeployConfig( }); } +export async function expandVirtualWarpDeployConfig(params: { + multiProvider: MultiProvider; + onChainWarpConfig: WarpRouteDeployConfigMailboxRequired; + deployedRoutersAddresses: ChainMap
; +}): Promise { + const { multiProvider, onChainWarpConfig, deployedRoutersAddresses } = params; + return promiseObjAll( + objMap(onChainWarpConfig, async (chain, config) => { + const warpReader = new EvmERC20WarpRouteReader(multiProvider, chain); + const warpVirtualConfig = await warpReader.deriveWarpRouteVirtualConfig( + chain, + deployedRoutersAddresses[chain], + ); + return { + ...warpVirtualConfig, + ...config, + }; + }), + ); +} + const transformWarpDeployConfigToCheck: TransformObjectTransformer = ( obj: any, propPath: ReadonlyArray, diff --git a/typescript/sdk/src/token/contracts.ts b/typescript/sdk/src/token/contracts.ts index a50740e66d2..d9baa81bce1 100644 --- a/typescript/sdk/src/token/contracts.ts +++ b/typescript/sdk/src/token/contracts.ts @@ -19,8 +19,10 @@ import { TokenType } from './config.js'; export const hypERC20contracts = { [TokenType.synthetic]: 'HypERC20', [TokenType.syntheticRebase]: 'HypERC4626', + [TokenType.syntheticUri]: 'HypERC721', [TokenType.collateral]: 'HypERC20Collateral', [TokenType.collateralFiat]: 'HypFiatToken', + [TokenType.collateralUri]: 'HypERC721Collateral', [TokenType.XERC20]: 'HypXERC20', [TokenType.XERC20Lockbox]: 'HypXERC20Lockbox', [TokenType.collateralVault]: 'HypERC4626OwnerCollateral', diff --git a/typescript/sdk/src/token/types.ts b/typescript/sdk/src/token/types.ts index e8c89dd321b..b0e21ca1daa 100644 --- a/typescript/sdk/src/token/types.ts +++ b/typescript/sdk/src/token/types.ts @@ -113,6 +113,26 @@ export const isSyntheticRebaseTokenConfig = isCompliant( SyntheticRebaseTokenConfigSchema, ); +export enum ContractVerificationStatus { + Verified = 'verified', + Unverified = 'unverified', + Error = 'error', + Skipped = 'skipped', +} +export const HypTokenRouterVirtualConfigSchema = z.object({ + contractVerificationStatus: z.record( + z.enum([ + ContractVerificationStatus.Verified, + ContractVerificationStatus.Unverified, + ContractVerificationStatus.Error, + ContractVerificationStatus.Skipped, + ]), + ), +}); +export type HypTokenRouterVirtualConfig = z.infer< + typeof HypTokenRouterVirtualConfigSchema +>; + /** * @remarks * The discriminatedUnion is basically a switch statement for zod schemas @@ -129,7 +149,8 @@ export type HypTokenConfig = z.infer; export const HypTokenRouterConfigSchema = HypTokenConfigSchema.and( GasRouterConfigSchema, -); +).and(HypTokenRouterVirtualConfigSchema.partial()); + export type HypTokenRouterConfig = z.infer; export type DerivedTokenRouterConfig = z.infer & @@ -154,7 +175,7 @@ export const HypTokenRouterConfigMailboxOptionalSchema = GasRouterConfigSchema.extend({ mailbox: z.string().optional(), }), - ); + ).and(HypTokenRouterVirtualConfigSchema.partial()); export type HypTokenRouterConfigMailboxOptional = z.infer< typeof HypTokenRouterConfigMailboxOptionalSchema From d182d7d3068268a0e4fa21a301afe55f60359372 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Mon, 12 May 2025 16:14:06 -0400 Subject: [PATCH 167/223] feat(sdk): fix out of order validators array in ism triggers check violation [eng-1555] (#6152) ### Description This PR updates the `transformConfigToCheck` the newly implemented `sortArraysInObject` function to reliably sort all the arrays in a given object recursively. Note that there is already an existing `sortArraysInConfig` function, but it is tightly tied to ism structure,s and updating it might have introduced unexpected bugs in the infra package ### Drive-by changes - None ### Related issues Fixes - [ENG-1555](https://linear.app/hyperlane-xyz/issue/ENG-1552/add-derivation-support-for-fiat-collateral-in-the-sdk) ### Backward compatibility - YES ### Testing - Manual - e2e - unit --- .changeset/slimy-ears-strive.md | 6 ++ typescript/sdk/src/token/configUtils.test.ts | 99 ++++++++++++++++++++ typescript/sdk/src/token/configUtils.ts | 21 ++++- typescript/utils/src/index.ts | 1 + typescript/utils/src/objects.test.ts | 43 +++++++++ typescript/utils/src/objects.ts | 23 +++++ 6 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 .changeset/slimy-ears-strive.md diff --git a/.changeset/slimy-ears-strive.md b/.changeset/slimy-ears-strive.md new file mode 100644 index 00000000000..61ec1965789 --- /dev/null +++ b/.changeset/slimy-ears-strive.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/utils': minor +'@hyperlane-xyz/sdk': minor +--- + +Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function diff --git a/typescript/sdk/src/token/configUtils.test.ts b/typescript/sdk/src/token/configUtils.test.ts index b8d606ccb56..f08adca3ed7 100644 --- a/typescript/sdk/src/token/configUtils.test.ts +++ b/typescript/sdk/src/token/configUtils.test.ts @@ -81,6 +81,105 @@ describe('configUtils', () => { }, }, }, + { + msg: 'It should sort out of order modules and validator arrays', + expected: { + bsc: { + decimals: 6, + interchainSecurityModule: { + type: 'defaultFallbackRoutingIsm', + owner: '0xe472f601aeeebeafbbd3a6fd9a788966011ad1df', + domains: { + milkyway: { + threshold: '1', + modules: [ + { + threshold: 3, + type: 'merkleRootMultisigIsm', + validators: [ + '0x55010624d5e239281d0850dc7915b78187e8bc0e', + '0x56fa9ac314ad49836ffb35918043d6b2dec304fb', + '0x9985e0c6df8e25b655b46a317af422f5e7756875', + '0x9ecf299947b030f9898faf328e5edbf77b13e974', + '0xb69c0d1aacd305edeca88b482b9dd9657f3a8b5c', + ], + }, + { + threshold: 3, + type: 'messageIdMultisigIsm', + validators: [ + '0x55010624d5e239281d0850dc7915b78187e8bc0e', + '0x56fa9ac314ad49836ffb35918043d6b2dec304fb', + '0x9985e0c6df8e25b655b46a317af422f5e7756875', + '0x9ecf299947b030f9898faf328e5edbf77b13e974', + '0xb69c0d1aacd305edeca88b482b9dd9657f3a8b5c', + ], + }, + ], + }, + }, + }, + name: 'MilkyWay', + owner: '0xe472f601aeeebeafbbd3a6fd9a788966011ad1df', + symbol: 'MILK', + type: 'synthetic', + }, + milkyway: { + foreignDeployment: + '0x726f757465725f61707000000000000000000000000000010000000000000000', + owner: 'milk169dcaz397j75tjfpl6ykm23dfrv39dqd58lsag', + type: 'native', + }, + }, + input: { + bsc: { + decimals: 6, + interchainSecurityModule: { + type: 'defaultFallbackRoutingIsm', + owner: '0xE472F601aeEeBEafbbd3a6FD9A788966011AD1Df', + domains: { + milkyway: { + threshold: '1', + modules: [ + { + threshold: 3, + type: 'messageIdMultisigIsm', + validators: [ + '0x9985e0c6df8e25b655b46a317af422f5e7756875', + '0x55010624d5e239281d0850dc7915b78187e8bc0e', + '0x9ecf299947b030f9898faf328e5edbf77b13e974', + '0x56fa9ac314ad49836ffb35918043d6b2dec304fb', + '0xb69c0d1aacd305edeca88b482b9dd9657f3a8b5c', + ], + }, + { + threshold: 3, + type: 'merkleRootMultisigIsm', + validators: [ + '0x9985e0c6df8e25b655b46a317af422f5e7756875', + '0x55010624d5e239281d0850dc7915b78187e8bc0e', + '0x9ecf299947b030f9898faf328e5edbf77b13e974', + '0x56fa9ac314ad49836ffb35918043d6b2dec304fb', + '0xb69c0d1aacd305edeca88b482b9dd9657f3a8b5c', + ], + }, + ], + }, + }, + }, + name: 'MilkyWay', + owner: '0xE472F601aeEeBEafbbd3a6FD9A788966011AD1Df', + symbol: 'MILK', + type: 'synthetic', + }, + milkyway: { + foreignDeployment: + '0x726f757465725f61707000000000000000000000000000010000000000000000', + owner: 'milk169dcaz397j75tjfpl6ykm23dfrv39dqd58lsag', + type: 'native', + }, + }, + }, ]; for (const { msg, input, expected } of testCases) { diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index 716e49e0b0b..b299c6d3f88 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -5,8 +5,10 @@ import { ProtocolType, TransformObjectTransformer, addressToBytes32, + isAddressEvm, objMap, promiseObjAll, + sortArraysInObject, transformObj, } from '@hyperlane-xyz/utils'; @@ -14,7 +16,6 @@ import { isProxy } from '../deploy/proxy.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { DestinationGas, RemoteRouters } from '../router/types.js'; import { ChainMap } from '../types.js'; -import { sortArraysInConfig } from '../utils/ism.js'; import { WarpCoreConfig } from '../warp/types.js'; import { EvmERC20WarpRouteReader } from './EvmERC20WarpRouteReader.js'; @@ -216,13 +217,26 @@ const transformWarpDeployConfigToCheck: TransformObjectTransformer = ( return undefined; } - if (typeof obj === 'string' && parentKey !== 'type') { + if (typeof obj === 'string' && parentKey !== 'type' && isAddressEvm(obj)) { return obj.toLowerCase(); } return obj; }; +const sortArraysInConfigToCheck = (a: any, b: any): number => { + if (a.type && b.type) { + if (a.type < b.type) return -1; + if (a.type > b.type) return 1; + return 0; + } + + if (a < b) return -1; + if (a > b) return 1; + + return 0; +}; + /** * transforms the provided {@link HypTokenRouterConfig}, removing the address, totalSupply and ownerOverrides * field where they are not required for the config comparison @@ -230,8 +244,9 @@ const transformWarpDeployConfigToCheck: TransformObjectTransformer = ( export function transformConfigToCheck( obj: HypTokenRouterConfig, ): HypTokenRouterConfig { - return sortArraysInConfig( + return sortArraysInObject( transformObj(obj, transformWarpDeployConfigToCheck), + sortArraysInConfigToCheck, ); } diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index 8e4880d6831..71527a6a601 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -130,6 +130,7 @@ export { stringifyObject, transformObj, TransformObjectTransformer, + sortArraysInObject, } from './objects.js'; export { Result, failure, success } from './result.js'; export { diff --git a/typescript/utils/src/objects.test.ts b/typescript/utils/src/objects.test.ts index 0b3b4c9b3d5..3be1a4bb5f9 100644 --- a/typescript/utils/src/objects.test.ts +++ b/typescript/utils/src/objects.test.ts @@ -20,6 +20,7 @@ import { objOmit, pick, promiseObjAll, + sortArraysInObject, stringifyObject, transformObj, } from './objects.js'; @@ -470,4 +471,46 @@ describe('Object utilities', () => { }); } }); + + describe(sortArraysInObject.name, () => { + [1, 'hello', true, null, undefined].map((value) => { + it(`should return the same primitive value if the input is a primitive ${value}`, () => { + expect(sortArraysInObject(value)).to.equal(value); + }); + }); + + it('should return an empty array if the input is an empty array', () => { + expect(sortArraysInObject([])).to.deep.equal([]); + }); + + it('should recursively sort arrays within an array', () => { + const input = [ + [3, 1, 2], + [6, 4, 5], + ]; + const expected = [ + [1, 2, 3], + [4, 5, 6], + ]; + + expect(sortArraysInObject(input)).to.deep.equal(expected); + }); + + it('should return an empty object if the input is an empty object', () => { + expect(sortArraysInObject({})).to.deep.equal({}); + }); + + it('should recursively sort arrays within an object', () => { + const input = { + a: [3, 1, 2], + b: { c: [6, 4, 5] }, + }; + const expected = { + a: [1, 2, 3], + b: { c: [4, 5, 6] }, + }; + + expect(sortArraysInObject(input)).to.deep.equal(expected); + }); + }); }); diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index 2e081acf8b6..cdb51ca262f 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -394,3 +394,26 @@ function internalTransformObj( return transformer(obj, propPath); } + +export function sortArraysInObject( + obj: any, + sortFunction?: (a: any, b: any) => number, +): any { + // Check if the current object is an array + if (Array.isArray(obj)) { + return obj + .sort(sortFunction) + .map((item) => sortArraysInObject(item, sortFunction)); + } + // Check if it's an object and not null or undefined + else if (isObject(obj)) { + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [ + key, + sortArraysInObject(value, sortFunction), + ]), + ); + } + + return obj; +} From 672d6d15944e6dae9059bbf27b71176e44d422d1 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Mon, 12 May 2025 19:56:42 -0400 Subject: [PATCH 168/223] feat(sdk,cli): expand ism and hook configs to compare on chain and artifacts configs [eng-1088] (#6155) ### Description This PR adds the logic to properly compare the hook and ism configuration depending on whether they are set as an address or a partial config. ### Drive-by changes - Exports the `DerivedWarpRouteDeployConfig` and `DerivedTokenRouterConfig` types from the SDK package - Exports the `derivedHookAddress` and `derivedIsmAddress` from the SDK package - Updates the `runWarpRouteCheck` function signature to have the `onChainConfig` to be of type `DerivedWarpRouteDeployConfig` - Updates the `transformConfigToCheck` function to remove the gas property from the config to check because it is not derived from on-chain config, and if specified in the deploy config, caused false positives (it is ok because the destinationGas field covers this) - Updates the EvmIsmReader to expand an ism config if it contains any addresses instead of the full config ### Related issues - Fixes #[ENG-1553](https://linear.app/hyperlane-xyz/issue/ENG-1553/non-expanded-hook-and-ism-configs-make-cli-checker-fail) ### Backward compatibility - YES ### Testing - Manual - e2e - unit --- .changeset/dry-rings-allow.md | 6 + typescript/cli/src/check/warp.ts | 25 ++- typescript/cli/src/read/warp.ts | 5 +- .../cli/src/tests/warp/warp-check.e2e-test.ts | 87 +++++++++- typescript/sdk/src/index.ts | 4 + typescript/sdk/src/ism/EvmIsmReader.ts | 38 ++++- typescript/sdk/src/token/configUtils.ts | 161 ++++++++++++------ typescript/sdk/src/token/types.ts | 2 + 8 files changed, 267 insertions(+), 61 deletions(-) create mode 100644 .changeset/dry-rings-allow.md diff --git a/.changeset/dry-rings-allow.md b/.changeset/dry-rings-allow.md new file mode 100644 index 00000000000..ab516ae9d30 --- /dev/null +++ b/.changeset/dry-rings-allow.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +adds logic to expand an ism or hook config if it is partially defined in the input file for the warp checker diff --git a/typescript/cli/src/check/warp.ts b/typescript/cli/src/check/warp.ts index a866cf4186a..92d13284b30 100644 --- a/typescript/cli/src/check/warp.ts +++ b/typescript/cli/src/check/warp.ts @@ -1,8 +1,11 @@ import { stringify as yamlStringify } from 'yaml'; import { + DerivedWarpRouteDeployConfig, HypTokenRouterVirtualConfig, WarpRouteDeployConfigMailboxRequired, + derivedHookAddress, + derivedIsmAddress, transformConfigToCheck, } from '@hyperlane-xyz/sdk'; import { ObjectDiff, diffObjMerge } from '@hyperlane-xyz/utils'; @@ -16,14 +19,30 @@ export async function runWarpRouteCheck({ }: { warpRouteConfig: WarpRouteDeployConfigMailboxRequired & Record>; - onChainWarpConfig: WarpRouteDeployConfigMailboxRequired; + onChainWarpConfig: DerivedWarpRouteDeployConfig & + Record>; }): Promise { // Go through each chain and only add to the output the chains that have mismatches const [violations, isInvalid] = Object.keys(warpRouteConfig).reduce( (acc, chain) => { + const expectedDeployedConfig = warpRouteConfig[chain]; + const currentDeployedConfig = onChainWarpConfig[chain]; + + // If the expected config specifies the hook or the ism as an address instead of the full config + // compare just the addresses + if (typeof expectedDeployedConfig.hook === 'string') { + currentDeployedConfig.hook = derivedHookAddress(currentDeployedConfig); + } + + if (typeof expectedDeployedConfig.interchainSecurityModule === 'string') { + currentDeployedConfig.interchainSecurityModule = derivedIsmAddress( + currentDeployedConfig, + ); + } + const { mergedObject, isInvalid } = diffObjMerge( - transformConfigToCheck(onChainWarpConfig[chain]), - transformConfigToCheck(warpRouteConfig[chain]), + transformConfigToCheck(currentDeployedConfig), + transformConfigToCheck(expectedDeployedConfig), ); if (isInvalid) { diff --git a/typescript/cli/src/read/warp.ts b/typescript/cli/src/read/warp.ts index c6bccd142fb..444032da2e0 100644 --- a/typescript/cli/src/read/warp.ts +++ b/typescript/cli/src/read/warp.ts @@ -8,6 +8,7 @@ import { import { ChainMap, ChainName, + DerivedWarpRouteDeployConfig, EvmERC20WarpRouteReader, HypTokenRouterConfig, MultiProvider, @@ -63,7 +64,7 @@ export async function getWarpRouteConfigsByCore({ }: { context: CommandContext; warpCoreConfig: WarpCoreConfig; -}): Promise> { +}): Promise { const addresses = Object.fromEntries( warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom!]), ); @@ -75,7 +76,7 @@ async function deriveWarpRouteConfigs( context: CommandContext, addresses: ChainMap, warpCoreConfig?: WarpCoreConfig, -): Promise> { +): Promise { const { multiProvider } = context; validateEvmCompatibility(addresses); diff --git a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts index ce80acab03d..1411b00bced 100644 --- a/typescript/cli/src/tests/warp/warp-check.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-check.e2e-test.ts @@ -2,7 +2,11 @@ import { expect } from 'chai'; import { Signer, Wallet, ethers } from 'ethers'; import { zeroAddress } from 'viem'; -import { ERC20Test, HypERC20Collateral__factory } from '@hyperlane-xyz/core'; +import { + ERC20Test, + HypERC20Collateral__factory, + Mailbox__factory, +} from '@hyperlane-xyz/core'; import { ChainAddresses, createWarpRouteConfigId, @@ -382,6 +386,87 @@ describe('hyperlane warp check e2e tests', async function () { expect(output.text()).to.includes(expectedDiffText); expect(output.text()).to.includes(expectedActualText); }); + + it(`should find differences in the hook config between the local and on chain config if it needs to be expanded`, async function () { + warpConfig[CHAIN_NAME_2].hook = { + type: HookType.MERKLE_TREE, + }; + + const mailboxInstance = Mailbox__factory.connect( + chain2Addresses.mailbox, + signer, + ); + const hookAddress = await mailboxInstance.callStatic.defaultHook(); + + writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); + writeYamlOrJson( + combinedWarpCoreConfigPath.replace('-config.yaml', '-deploy.yaml'), + warpConfig, + ); + + const warpDeployConfig = warpConfig; + await hyperlaneWarpDeploy(WARP_DEPLOY_OUTPUT_PATH); + + const expectedOwner = (await signer.getAddress()).toLowerCase(); + warpDeployConfig[CHAIN_NAME_2].hook = { + type: HookType.FALLBACK_ROUTING, + domains: {}, + fallback: hookAddress, + owner: expectedOwner, + }; + + writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig); + + const expectedActualText = `ACTUAL: ${HookType.MERKLE_TREE}\n`; + const expectedDiffText = `EXPECTED: ${HookType.FALLBACK_ROUTING}`; + + const expectedFallbackDiff = ` fallback: + ACTUAL: "" + EXPECTED: + owner: "${expectedOwner}" + type: protocolFee + maxProtocolFee: "1000000000000000000" + protocolFee: "200000000000000" + beneficiary: "${expectedOwner}"`; + + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }).nothrow(); + + expect(output.exitCode).to.equal(1); + expect(output.text()).to.includes(expectedDiffText); + expect(output.text()).to.includes(expectedActualText); + expect(output.text()).to.includes(expectedFallbackDiff); + }); + + it(`should find differences in the hook config between the local and on chain config if it compares the hook addresses`, async function () { + const mailboxInstance = Mailbox__factory.connect( + chain2Addresses.mailbox, + signer, + ); + const hookAddress = ( + await mailboxInstance.callStatic.defaultHook() + ).toLowerCase(); + + const warpDeployConfig = await deployAndExportWarpRoute(); + await hyperlaneWarpDeploy(WARP_DEPLOY_OUTPUT_PATH); + + warpDeployConfig[CHAIN_NAME_2].hook = hookAddress; + writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig); + + const expectedActualText = `ACTUAL: "${zeroAddress}"\n`; + const expectedDiffText = `EXPECTED: "${hookAddress}"`; + + const output = await hyperlaneWarpCheckRaw({ + warpDeployPath: WARP_DEPLOY_OUTPUT_PATH, + warpCoreConfigPath: combinedWarpCoreConfigPath, + }).nothrow(); + + expect(output.exitCode).to.equal(1); + expect(output.text()).to.includes(expectedDiffText); + expect(output.text()).to.includes(expectedActualText); + }); }); for (const hookType of MUTABLE_HOOK_TYPE) { diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index ad5cfdc233a..e116956ff86 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -634,6 +634,10 @@ export { TokenMetadataSchema, WarpRouteDeployConfig, WarpRouteDeployConfigMailboxRequired, + derivedHookAddress, + derivedIsmAddress, + DerivedTokenRouterConfig, + DerivedWarpRouteDeployConfig, HypTokenRouterVirtualConfig, WarpRouteDeployConfigSchema, WarpRouteDeployConfigMailboxRequiredSchema, diff --git a/typescript/sdk/src/ism/EvmIsmReader.ts b/typescript/sdk/src/ism/EvmIsmReader.ts index 8568fbaf39e..4749a7e47e9 100644 --- a/typescript/sdk/src/ism/EvmIsmReader.ts +++ b/typescript/sdk/src/ism/EvmIsmReader.ts @@ -20,6 +20,8 @@ import { assert, concurrentMap, getLogLevel, + objMap, + promiseObjAll, rootLogger, } from '@hyperlane-xyz/utils'; @@ -37,6 +39,7 @@ import { CCIPReadIsmConfig, DerivedIsmConfig, DomainRoutingIsmConfig, + IsmConfig, IsmType, ModuleType, MultisigIsmConfig, @@ -84,7 +87,9 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { this.isZkSyncChain = chainTechnicalStack === ChainTechnicalStack.ZkSync; } - async deriveIsmConfig(address: Address): Promise { + async deriveIsmConfigFromAddress( + address: Address, + ): Promise { let moduleType: ModuleType | undefined = undefined; let derivedIsmConfig: DerivedIsmConfig; try { @@ -140,6 +145,37 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { return derivedIsmConfig; } + // expands ISM configs that are set as addresses by deriving the config + // from the on-chain deployment + async deriveIsmConfig(config: IsmConfig): Promise { + if (typeof config === 'string') + return this.deriveIsmConfigFromAddress(config); + + // Extend the inner isms + switch (config.type) { + case IsmType.FALLBACK_ROUTING: + case IsmType.ROUTING: + config.domains = await promiseObjAll( + objMap(config.domains, async (_, ism) => this.deriveIsmConfig(ism)), + ); + break; + case IsmType.AGGREGATION: + case IsmType.STORAGE_AGGREGATION: + config.modules = await Promise.all( + config.modules.map(async (ism) => this.deriveIsmConfig(ism)), + ); + break; + case IsmType.AMOUNT_ROUTING: + [config.lowerIsm, config.upperIsm] = await Promise.all([ + this.deriveIsmConfig(config.lowerIsm), + this.deriveIsmConfig(config.upperIsm), + ]); + break; + } + + return config as DerivedIsmConfig; + } + async deriveRoutingConfig( address: Address, ): Promise> { diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index b299c6d3f88..19f000b311b 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -13,6 +13,8 @@ import { } from '@hyperlane-xyz/utils'; import { isProxy } from '../deploy/proxy.js'; +import { EvmHookReader } from '../hook/EvmHookReader.js'; +import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { DestinationGas, RemoteRouters } from '../router/types.js'; import { ChainMap } from '../types.js'; @@ -23,6 +25,7 @@ import { gasOverhead } from './config.js'; import { HypERC20Deployer } from './deploy.js'; import { ContractVerificationStatus, + DerivedWarpRouteDeployConfig, HypTokenRouterConfig, HypTokenRouterVirtualConfig, WarpRouteDeployConfig, @@ -121,69 +124,104 @@ export async function expandWarpDeployConfig(params: { }), ); - return objMap(warpDeployConfig, (chain, config) => { - const [remoteRouters, destinationGas] = - getDefaultRemoteRouterAndDestinationGasConfig( - multiProvider, - chain, - deployedRoutersAddresses, - warpDeployConfig, - ); - - const chainConfig: WarpRouteDeployConfigMailboxRequired[string] & - Partial = { - // Default Expansion - ...derivedTokenMetadata, - remoteRouters, - destinationGas, - hook: zeroAddress, - interchainSecurityModule: zeroAddress, - proxyAdmin: isDeployedAsProxyByChain[chain] - ? { owner: config.owner } - : undefined, - isNft: false, - // User-specified config takes precedence - ...config, - }; + return promiseObjAll( + objMap(warpDeployConfig, async (chain, config) => { + const [remoteRouters, destinationGas] = + getDefaultRemoteRouterAndDestinationGasConfig( + multiProvider, + chain, + deployedRoutersAddresses, + warpDeployConfig, + ); + + const chainConfig: WarpRouteDeployConfigMailboxRequired[string] & + Partial = { + // Default Expansion + ...derivedTokenMetadata, + remoteRouters, + destinationGas, + hook: zeroAddress, + interchainSecurityModule: zeroAddress, + proxyAdmin: isDeployedAsProxyByChain[chain] + ? { owner: config.owner } + : undefined, + isNft: false, + // User-specified config takes precedence + ...config, + }; - // Expand EVM warpDeployConfig virtual to the control states - if ( - expandedOnChainWarpConfig?.[chain]?.contractVerificationStatus && - multiProvider.getProtocol(chain) === ProtocolType.Ethereum - ) { - // For most cases, we set to Verified - chainConfig.contractVerificationStatus = objMap( - expandedOnChainWarpConfig[chain].contractVerificationStatus ?? {}, - (_, status) => { - // Skipped for local e2e testing - if (status === ContractVerificationStatus.Skipped) - return ContractVerificationStatus.Skipped; - - return ContractVerificationStatus.Verified; - }, + // Properly set the remote routers addresses to their 32 bytes representation + // as that is how they are set on chain + const formattedRemoteRouters = objMap( + chainConfig.remoteRouters ?? {}, + (_domainId, { address }) => ({ + address: addressToBytes32(address), + }), ); - } - // Properly set the remote routers addresses to their 32 bytes representation - // as that is how they are set on chain - const formattedRemoteRouters = objMap( - chainConfig.remoteRouters ?? {}, - (_domainId, { address }) => ({ - address: addressToBytes32(address), - }), - ); + chainConfig.remoteRouters = formattedRemoteRouters; + + const isEVMChain = + multiProvider.getProtocol(chain) === ProtocolType.Ethereum; + + // Expand EVM warpDeployConfig virtual to the control states + if ( + expandedOnChainWarpConfig?.[chain]?.contractVerificationStatus && + multiProvider.getProtocol(chain) === ProtocolType.Ethereum + ) { + // For most cases, we set to Verified + chainConfig.contractVerificationStatus = objMap( + expandedOnChainWarpConfig[chain].contractVerificationStatus ?? {}, + (_, status) => { + // Skipped for local e2e testing + if (status === ContractVerificationStatus.Skipped) + return ContractVerificationStatus.Skipped; + + return ContractVerificationStatus.Verified; + }, + ); + } - chainConfig.remoteRouters = formattedRemoteRouters; + // Expand the hook config only if we have an explicit config in the deploy config + // and the current chain is an EVM one. + // if we have an address we leave it like that to avoid deriving + if ( + isEVMChain && + chainConfig.hook && + typeof chainConfig.hook !== 'string' + ) { + const reader = new EvmHookReader(multiProvider, chain); + + chainConfig.hook = await reader.deriveHookConfig(chainConfig.hook); + } - return chainConfig; - }); + // Expand the ism config only if we have an explicit config in the deploy config + // if we have an address we leave it like that to avoid deriving + if ( + isEVMChain && + chainConfig.interchainSecurityModule && + typeof chainConfig.interchainSecurityModule !== 'string' + ) { + const reader = new EvmIsmReader(multiProvider, chain); + + chainConfig.interchainSecurityModule = await reader.deriveIsmConfig( + chainConfig.interchainSecurityModule, + ); + } + + return chainConfig; + }), + ); } export async function expandVirtualWarpDeployConfig(params: { multiProvider: MultiProvider; - onChainWarpConfig: WarpRouteDeployConfigMailboxRequired; + onChainWarpConfig: DerivedWarpRouteDeployConfig; deployedRoutersAddresses: ChainMap
; -}): Promise { +}): Promise< + DerivedWarpRouteDeployConfig & + Record> +> { const { multiProvider, onChainWarpConfig, deployedRoutersAddresses } = params; return promiseObjAll( objMap(onChainWarpConfig, async (chain, config) => { @@ -195,6 +233,7 @@ export async function expandVirtualWarpDeployConfig(params: { return { ...warpVirtualConfig, ...config, + hook: config.hook ?? zeroAddress, }; }), ); @@ -237,6 +276,13 @@ const sortArraysInConfigToCheck = (a: any, b: any): number => { return 0; }; +const FIELDS_TO_IGNORE = new Set([ + // gas is removed because the destinationGas is the result of + // expanding the config based on the gas value for each chain + // see `expandWarpDeployConfig` function + 'gas', +]); + /** * transforms the provided {@link HypTokenRouterConfig}, removing the address, totalSupply and ownerOverrides * field where they are not required for the config comparison @@ -244,8 +290,15 @@ const sortArraysInConfigToCheck = (a: any, b: any): number => { export function transformConfigToCheck( obj: HypTokenRouterConfig, ): HypTokenRouterConfig { + const filteredObj = Object.fromEntries( + Object.entries(obj).filter( + ([key, _value]) => + !FIELDS_TO_IGNORE.has(key as keyof HypTokenRouterConfig), + ), + ); + return sortArraysInObject( - transformObj(obj, transformWarpDeployConfigToCheck), + transformObj(filteredObj, transformWarpDeployConfigToCheck), sortArraysInConfigToCheck, ); } diff --git a/typescript/sdk/src/token/types.ts b/typescript/sdk/src/token/types.ts index b0e21ca1daa..cff609ad02b 100644 --- a/typescript/sdk/src/token/types.ts +++ b/typescript/sdk/src/token/types.ts @@ -160,6 +160,8 @@ export type DerivedTokenRouterConfig = z.infer & > & DerivedMailboxClientFields; +export type DerivedWarpRouteDeployConfig = ChainMap; + export function derivedHookAddress(config: DerivedTokenRouterConfig) { return typeof config.hook === 'string' ? config.hook : config.hook.address; } From 575013ec6927c1058b5506ec3ff8af466f23ea53 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 13 May 2025 10:59:42 +0100 Subject: [PATCH 169/223] feat: basic evm gas price estimator (#6204) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/Cargo.lock | 1 + .../src/rpc_clients/provider.rs | 64 ++++- rust/main/submitter/Cargo.toml | 1 + .../chains/ethereum/adapter.rs | 3 +- .../ethereum/adapter/gas_limit_estimator.rs | 2 +- .../ethereum/adapter/gas_price_estimator.rs | 259 ++++++++++++++++++ 6 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 36d4b26a958..57d11584ece 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -10623,6 +10623,7 @@ dependencies = [ "chrono", "derive-new", "ethers", + "ethers-core", "eyre", "futures-util", "hyperlane-base", diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index da8e717810f..9987cf28841 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -11,7 +11,7 @@ use ethers::{prelude::Middleware, types::TransactionReceipt}; use ethers_contract::builders::ContractCall; use ethers_core::abi::Function; use ethers_core::types::transaction::eip2718::TypedTransaction; -use ethers_core::types::BlockId; +use ethers_core::types::{BlockId, FeeHistory, U256 as EthersU256}; use ethers_core::{abi::Address, types::BlockNumber}; use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, H512, U256}; use tokio::time::sleep; @@ -26,6 +26,24 @@ use crate::{ get_finalized_block_number, BuildableWithProvider, ConnectionConf, EthereumReorgPeriod, }; +// From +// gas_limit: QUANTITY, 32 bytes - The maximum amount of gas that can be used. +// max_fee_per_gas: QUANTITY, 32 bytes - The maximum fee per unit of gas that the sender is willing to pay. +// max_priority_fee_per_gas: QUANTITY, 32 bytes - The maximum priority fee per unit of gas to incentivize miners. +// gas_per_pubdata_limit: QUANTITY, 32 bytes - The gas limit per unit of public data. +/// Response from the zkSync estimate fee endpoint. +#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)] +pub struct ZksyncEstimateFeeResponse { + /// Gas limit + pub gas_limit: EthersU256, + /// Max fee + pub max_fee_per_gas: EthersU256, + /// Max priority fee + pub max_priority_fee_per_gas: EthersU256, + /// Gas per pubdata limit + pub gas_per_pubdata_limit: EthersU256, +} + /// Connection to an ethereum provider. Useful for querying information about /// the blockchain. #[derive(Debug, Clone, new)] @@ -100,6 +118,23 @@ pub trait EvmProviderForSubmitter: Send + Sync { /// Get the next nonce to use for a given address (using the finalized block) async fn get_next_nonce_on_finalized_block(&self, address: &Address) -> ChainResult; + + /// Get the fee history + async fn fee_history( + &self, + block_count: U256, + last_block: BlockNumber, + reward_percentiles: &[f64], + ) -> ChainResult; + + /// Estimate the fee for a zkSync transaction + async fn zk_estimate_fee( + &self, + tx: &TypedTransaction, + ) -> ChainResult; + + /// Get default sender + fn default_sender(&self) -> Option
; } #[async_trait] @@ -172,6 +207,33 @@ where .map_err(ChainCommunicationError::from_other) .map(Into::into) } + + async fn fee_history( + &self, + block_count: U256, + last_block: BlockNumber, + reward_percentiles: &[f64], + ) -> ChainResult { + self.provider + .fee_history(block_count, last_block, reward_percentiles) + .await + .map_err(ChainCommunicationError::from_other) + } + + async fn zk_estimate_fee( + &self, + tx: &TypedTransaction, + ) -> ChainResult { + self.provider + .provider() + .request("zks_estimateFee", [tx.clone()]) + .await + .map_err(ChainCommunicationError::from_other) + } + + fn default_sender(&self) -> Option
{ + self.provider.default_sender() + } } #[async_trait] diff --git a/rust/main/submitter/Cargo.toml b/rust/main/submitter/Cargo.toml index bd9eae377d4..4ef3c85857c 100644 --- a/rust/main/submitter/Cargo.toml +++ b/rust/main/submitter/Cargo.toml @@ -17,6 +17,7 @@ async-trait.workspace = true chrono.workspace = true derive-new.workspace = true ethers.workspace = true +ethers-core.workspace = true eyre.workspace = true futures-util.workspace = true prometheus.workspace = true diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs index 89d7835f668..b460e326492 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -28,6 +28,7 @@ use super::transaction::Precursor; use super::EthereumTxPrecursor; mod gas_limit_estimator; +mod gas_price_estimator; mod tx_status_checker; pub struct EthereumTxAdapter { @@ -109,7 +110,7 @@ impl AdaptsChain for EthereumTxAdapter { async fn estimate_tx(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { let precursor = tx.precursor_mut(); - gas_limit_estimator::estimate_tx( + gas_limit_estimator::estimate_gas_limit( &self.provider, precursor, &self.connection_conf.transaction_overrides, diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs index fe1110421b1..d1d55af4897 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_limit_estimator.rs @@ -16,7 +16,7 @@ pub const GAS_LIMIT_BUFFER: u32 = 75_000; pub const DEFAULT_GAS_LIMIT_MULTIPLIER_NUMERATOR: u32 = 11; pub const DEFAULT_GAS_LIMIT_MULTIPLIER_DENOMINATOR: u32 = 10; -pub async fn estimate_tx( +pub async fn estimate_gas_limit( provider: &Box, tx_precursor: &mut EthereumTxPrecursor, transaction_overrides: &TransactionOverrides, diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs new file mode 100644 index 00000000000..75e846e8556 --- /dev/null +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs @@ -0,0 +1,259 @@ +// the evm provider-building logic returns a box. `EvmProviderForSubmitter` is only implemented for the underlying type rather than the boxed type. +// implementing the trait for the boxed type would require a lot of boilerplate code. +#![allow(clippy::borrowed_box)] + +use std::{str::FromStr, sync::Arc}; + +use ethers::{ + abi::Detokenize, + contract::builders::ContractCall, + providers::{Middleware, ProviderError}, + types::{ + transaction::eip2718::TypedTransaction, Block, Eip1559TransactionRequest, H160, + H256 as TxHash, + }, +}; +use futures_util::try_join; +use hyperlane_core::{ChainCommunicationError, ChainResult, HyperlaneDomain, U256}; +use hyperlane_ethereum::{ + EvmProviderForSubmitter, TransactionOverrides, ZksyncEstimateFeeResponse, +}; +use tracing::{debug, warn}; + +use ethers_core::{ + types::{BlockNumber, U256 as EthersU256}, + utils::{ + eip1559_default_estimator, EIP1559_FEE_ESTIMATION_PAST_BLOCKS, + EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE, + }, +}; + +use crate::{chain_tx_adapter::EthereumTxPrecursor, SubmitterError}; + +type FeeEstimator = fn(EthersU256, Vec>) -> (EthersU256, EthersU256); + +const EVM_RELAYER_ADDRESS: &str = "0x74cae0ecc47b02ed9b9d32e000fd70b9417970c5"; + +pub type Eip1559Fee = ( + EthersU256, // base fee + EthersU256, // max fee + EthersU256, // max priority fee +); + +#[allow(unused)] +pub async fn estimate_gas_price( + provider: &Box, + tx_precursor: &mut EthereumTxPrecursor, + transaction_overrides: &TransactionOverrides, + domain: &HyperlaneDomain, +) -> Result<(), SubmitterError> { + let tx = tx_precursor.tx.clone(); + + if let Some(gas_price) = transaction_overrides.gas_price { + // If the gas price is set, we treat as a non-EIP-1559 chain. + tx_precursor.tx.set_gas_price(gas_price); + return Ok(()); + } + + let eip1559_fee_result = estimate_eip1559_fees(provider, None, domain, &tx).await; + let ((base_fee, max_fee, max_priority_fee), _) = match eip1559_fee_result { + Ok(result) => result, + Err(err) => { + warn!(?err, "Failed to estimate EIP-1559 fees"); + // Assume it's not an EIP 1559 chain + apply_legacy_overrides(tx_precursor, transaction_overrides); + return Ok(()); + } + }; + + // If the base fee is zero, just treat the chain as a non-EIP-1559 chain. + // This is useful for BSC, where the base fee is zero, there's a minimum gas price + // generally enforced by nodes of 3 gwei, but EIP 1559 estimation suggests a priority + // fee lower than 3 gwei because of privileged transactions being included by block + // producers that have a lower priority fee. + if base_fee.is_zero() { + apply_legacy_overrides(tx_precursor, transaction_overrides); + return Ok(()); + } + + // Apply overrides for EIP 1559 tx params if they exist. + let (max_fee, max_priority_fee) = + apply_1559_overrides(max_fee, max_priority_fee, transaction_overrides); + + // Is EIP 1559 chain + let mut request = Eip1559TransactionRequest::new(); + if let Some(from) = tx.from() { + request = request.from(*from); + } + if let Some(to) = tx.to() { + request = request.to(to.clone()); + } + if let Some(data) = tx.data() { + request = request.data(data.clone()); + } + if let Some(value) = tx.value() { + request = request.value(*value); + } + request = request.max_fee_per_gas(max_fee); + request = request.max_priority_fee_per_gas(max_priority_fee); + + let eip_1559_tx = TypedTransaction::Eip1559(request); + tx_precursor.tx = eip_1559_tx.clone(); + Ok(()) +} + +fn apply_legacy_overrides( + tx_precursor: &mut EthereumTxPrecursor, + transaction_overrides: &TransactionOverrides, +) { + let gas_price = tx_precursor.tx.gas_price(); + // if no gas price was set in the tx, leave the tx as is and return early + let Some(mut gas_price) = gas_price else { + return; + }; + + let min_price_override = transaction_overrides + .min_gas_price + .map(Into::into) + .unwrap_or(0.into()); + gas_price = gas_price.max(min_price_override); + gas_price = apply_gas_price_cap(gas_price, transaction_overrides); + tx_precursor.tx.set_gas_price(gas_price); +} + +fn apply_1559_overrides( + max_fee: EthersU256, + max_priority_fee: EthersU256, + transaction_overrides: &TransactionOverrides, +) -> (EthersU256, EthersU256) { + let mut max_fee = transaction_overrides + .max_fee_per_gas + .map(Into::into) + .unwrap_or(max_fee); + if let Some(min_fee) = transaction_overrides.min_fee_per_gas { + max_fee = max_fee.max(min_fee.into()); + } + + let mut max_priority_fee = transaction_overrides + .max_priority_fee_per_gas + .map(Into::into) + .unwrap_or(max_priority_fee); + + if let Some(min_priority_fee) = transaction_overrides.min_priority_fee_per_gas { + max_priority_fee = max_priority_fee.max(min_priority_fee.into()); + } + max_fee = apply_gas_price_cap(max_fee, transaction_overrides); + (max_fee, max_priority_fee) +} + +fn apply_gas_price_cap( + gas_price: EthersU256, + transaction_overrides: &TransactionOverrides, +) -> EthersU256 { + if let Some(gas_price_cap) = transaction_overrides.gas_price_cap { + if gas_price > gas_price_cap.into() { + warn!( + ?gas_price, + ?gas_price_cap, + "Gas price for transaction is higher than the gas price cap. Capping it to the gas price cap." + ); + return gas_price_cap.into(); + } + } + gas_price +} + +/// Use this to estimate EIP 1559 fees with some chain-specific logic. +pub(crate) async fn estimate_eip1559_fees( + provider: &Box, + estimator: Option, + domain: &HyperlaneDomain, + tx: &TypedTransaction, +) -> ChainResult<(Eip1559Fee, Block)> { + if domain.is_zksync_stack() { + estimate_eip1559_fees_zksync(provider, tx).await + } else { + estimate_eip1559_fees_default(provider, estimator).await + } +} + +async fn estimate_eip1559_fees_zksync( + provider: &Box, + tx: &TypedTransaction, +) -> ChainResult<(Eip1559Fee, Block)> { + let latest_block = latest_block(provider).await?; + + let base_fee_per_gas = latest_block + .base_fee_per_gas + .ok_or_else(|| ProviderError::CustomError("EIP-1559 not activated".into()))?; + + let response = zksync_estimate_fee(provider, tx).await?; + let max_fee_per_gas = response.max_fee_per_gas; + let max_priority_fee_per_gas = response.max_priority_fee_per_gas; + + Ok(( + (base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas), + latest_block, + )) +} + +async fn zksync_estimate_fee( + provider: &Box, + tx: &TypedTransaction, +) -> ChainResult { + let mut tx = tx.clone(); + tx.set_from( + // use the sender in the provider if one is set, otherwise default to the EVM relayer address + provider + .default_sender() + .unwrap_or(H160::from_str(EVM_RELAYER_ADDRESS).unwrap()), + ); + + let result = provider.zk_estimate_fee(&tx).await?; + Ok(result) +} + +/// Logic for a vanilla EVM chain to get EIP-1559 fees. +/// Pretty much a copy of the logic in ethers-rs (https://github.com/hyperlane-xyz/ethers-rs/blob/c9ced035628da59376c369be035facda1648577a/ethers-providers/src/provider.rs#L478) +/// but returns the base fee as well as the max fee and max priority fee. +/// Gets a heuristic recommendation of max fee per gas and max priority fee per gas for +/// EIP-1559 compatible transactions. +async fn estimate_eip1559_fees_default( + provider: &Box, + estimator: Option, +) -> ChainResult<((EthersU256, EthersU256, EthersU256), Block)> { + let latest_block = latest_block(provider); + + let fee_history = provider.fee_history( + EIP1559_FEE_ESTIMATION_PAST_BLOCKS.into(), + BlockNumber::Latest, + &[EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE], + ); + + let (latest_block, fee_history) = try_join!(latest_block, fee_history)?; + + let base_fee_per_gas = latest_block + .base_fee_per_gas + .ok_or_else(|| ProviderError::CustomError("EIP-1559 not activated".into()))?; + + // use the provided fee estimator function, or fallback to the default implementation. + let (max_fee_per_gas, max_priority_fee_per_gas) = if let Some(es) = estimator { + es(base_fee_per_gas, fee_history.reward) + } else { + eip1559_default_estimator(base_fee_per_gas, fee_history.reward) + }; + + Ok(( + (base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas), + latest_block, + )) +} + +async fn latest_block(provider: &Box) -> ChainResult> { + let latest_block = provider + .get_block(BlockNumber::Latest) + .await + .map_err(ChainCommunicationError::from_other)? + .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?; + Ok(latest_block) +} From b36080265b62eea390bd7f8e8cea58779cc285d4 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Tue, 13 May 2025 10:26:42 -0400 Subject: [PATCH 170/223] feat(utils,sdk,cli): metadata checks trigger false positives in cli checker [eng-1554] (#6168) ### Description Removes symbol and name token metadata checking in the CLI checker and syncs config getters with the current deployed config on chain to avoid failures due to the registry config being incorrect Companion PR in the registry https://github.com/hyperlane-xyz/hyperlane-registry/pull/841 ### Drive-by changes - Implements the `isCosmosIbcDenomAddress` in the utils package ### Related issues - Fixes #[ENG-1554](https://linear.app/hyperlane-xyz/issue/ENG-1554/metadata-checks-might-cause-cli-checker-to-fail) ### Backward compatibility - YES ### Testing - Manual - unit - e2e --- .changeset/tricky-news-retire.md | 6 +++ .registryrc | 2 +- ...ptimismPolygonZeroNetworkUSDCWarpConfig.ts | 1 + ...ePolygonScrollZeroNetworkUSDTWarpConfig.ts | 9 +++++ ...rumEthereumSolanaTreasureSMOLWarpConfig.ts | 3 ++ .../getArbitrumNeutronTiaWarpConfig.ts | 1 + .../getBaseSolanaTRUMPWarpConfig.ts | 2 +- .../getBaseZeroNetworkCBBTCWarpConfig.ts | 1 + .../getEthereumVictionETHWarpConfig.ts | 1 + .../getEthereumVictionUSDCWarpConfig.ts | 1 + .../getEthereumVictionUSDTWarpConfig.ts | 1 + .../getInevmInjectiveINJWarpConfig.ts | 1 + .../getMantapacificNeutronTiaWarpConfig.ts | 3 ++ typescript/sdk/src/token/configUtils.ts | 37 +++++++++++++++++-- typescript/utils/src/addresses.ts | 4 ++ typescript/utils/src/index.ts | 1 + 16 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 .changeset/tricky-news-retire.md diff --git a/.changeset/tricky-news-retire.md b/.changeset/tricky-news-retire.md new file mode 100644 index 00000000000..bbf6aeb2802 --- /dev/null +++ b/.changeset/tricky-news-retire.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/utils': minor +'@hyperlane-xyz/sdk': minor +--- + +Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas diff --git a/.registryrc b/.registryrc index 22e6d1bcf31..23d88f2f007 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -5b610083b651afe4beb39848f87329cbe84ba348 +50c45bfc47885f636d3db0b7baebc861f6596f8e diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts index 9491c9a2d8e..f98037e7208 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.ts @@ -94,6 +94,7 @@ export const getArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDCWarpConfig type: TokenType.collateral, token: tokens.zeronetwork.USDC, interchainSecurityModule: ISM_CONFIG, + gas: 300000, }; const ethereum: HypTokenRouterConfig = { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts index d85288e24a8..f8efff120ee 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig.ts @@ -120,6 +120,15 @@ export const getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig }, type: TokenType.synthetic, interchainSecurityModule: ISM_CONFIG, + gas: 300000, + destinationGas: { + '1': '0', + '137': '0', + '5000': '0', + '34443': '0', + '42161': '68000', + '534352': '0', + }, }; return { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts index 0574aa279d0..9bdc9d0951e 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumSolanaTreasureSMOLWarpConfig.ts @@ -37,6 +37,7 @@ export async function getArbitrumEthereumSolanaTreasureSMOLWarpConfig( symbol, decimals: 18, owner: evmOwner, + gas: 70000, }, ethereum: { ...routerConfig.ethereum, @@ -45,6 +46,7 @@ export async function getArbitrumEthereumSolanaTreasureSMOLWarpConfig( symbol, decimals: 18, owner: evmOwner, + gas: 70000, }, // Not intended to be fully connected with Solana, but is connected with treasure and ethereum arbitrum: { @@ -60,6 +62,7 @@ export async function getArbitrumEthereumSolanaTreasureSMOLWarpConfig( 1: { address: '0x53cce6d10e43d1b3d11872ad22ec2acd8d2537b8' }, 61166: { address: '0xb73e4f558F7d4436d77a18f56e4EE9d01764c641' }, }, + gas: 70000, }, }; return tokenConfig; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.ts index fbef85634a5..e1dc0c5ab17 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.ts @@ -21,6 +21,7 @@ export const getArbitrumNeutronTiaWarpConfig = async ( token: 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7', foreignDeployment: neutronRouter, + gas: 600000, }; const arbitrum: HypTokenRouterConfig = { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseSolanaTRUMPWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseSolanaTRUMPWarpConfig.ts index ab318a42e93..1eee7a241be 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseSolanaTRUMPWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseSolanaTRUMPWarpConfig.ts @@ -61,8 +61,8 @@ export const getTRUMPWarpConfig = async ( symbol, token: '6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN', owner: DEPLOYER, - gas: SEALEVEL_WARP_ROUTE_HANDLER_GAS_AMOUNT, foreignDeployment: '21tAY4poz2VXvghqdSQpn9j7gYravQmGpuQi8pHPx9DS', + gas: 64000, }, base: { ...routerConfig.base, diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseZeroNetworkCBBTCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseZeroNetworkCBBTCWarpConfig.ts index c61c25a9b51..801db6a5846 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseZeroNetworkCBBTCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBaseZeroNetworkCBBTCWarpConfig.ts @@ -40,6 +40,7 @@ export const getBaseZeroNetworkCBBTCWarpConfig = async ( }, type: TokenType.synthetic, interchainSecurityModule: ISM_CONFIG, + gas: 300000, }; return { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.ts index bcc6f84db93..55153b258b7 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.ts @@ -30,6 +30,7 @@ export const getEthereumVictionETHWarpConfig = async ( type: TokenType.native, gas: 65_000, interchainSecurityModule: ethers.constants.AddressZero, + hook: '0xb87ac8ea4533ae017604e44470f7c1e550ac6f10', }; return { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.ts index 1651e484085..23676804e7e 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.ts @@ -34,6 +34,7 @@ export const getEthereumVictionUSDCWarpConfig = async ( token: tokens.ethereum.USDC, gas: 65_000, interchainSecurityModule: ethers.constants.AddressZero, + hook: '0xb87ac8ea4533ae017604e44470f7c1e550ac6f10', }; return { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.ts index 4ef5f492edd..30f8c9cdb9f 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.ts @@ -34,6 +34,7 @@ export const getEthereumVictionUSDTWarpConfig = async ( token: tokens.ethereum.USDT, gas: 65_000, interchainSecurityModule: ethers.constants.AddressZero, + hook: '0xb87ac8ea4533ae017604e44470f7c1e550ac6f10', }; return { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.ts index 6938e4c7392..653bce16c82 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.ts @@ -18,6 +18,7 @@ export const getInevmInjectiveINJWarpConfig = async ( ...abacusWorksEnvOwnerConfig.injective, type: TokenType.native, foreignDeployment: injectiveRouter, + gas: 68000, }; const inevm: HypTokenRouterConfig = { diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.ts index 29803b0a5e0..949c102c94d 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.ts @@ -17,6 +17,9 @@ export const getMantapacificNeutronTiaWarpConfig = async ( // @ts-ignore - foreignDeployment configs don't conform to the HypTokenRouterConfig const neutron: HypTokenRouterConfig = { foreignDeployment: neutronRouter, + owner: abacusWorksEnvOwnerConfig.neutron.owner, + type: TokenType.native, + gas: 0, }; const mantapacific: HypTokenRouterConfig = { diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index 19f000b311b..1d876f90ff0 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -5,7 +5,10 @@ import { ProtocolType, TransformObjectTransformer, addressToBytes32, + intersection, isAddressEvm, + isCosmosIbcDenomAddress, + objFilter, objMap, promiseObjAll, sortArraysInObject, @@ -78,10 +81,16 @@ export function getRouterAddressesFromWarpCoreConfig( warpCoreConfig: WarpCoreConfig, ): ChainMap
{ return Object.fromEntries( - warpCoreConfig.tokens.map((token) => [ - token.chainName, - token.addressOrDenom, - ]), + warpCoreConfig.tokens + // Removing IBC denom addresses because they are on the same + // chain as the actual warp token but they are only used + // used to pay the IGP hook + .filter( + (token) => + token.addressOrDenom && + !isCosmosIbcDenomAddress(token.addressOrDenom), + ) + .map((token) => [token.chainName, token.addressOrDenom]), ) as ChainMap
; } @@ -161,6 +170,21 @@ export async function expandWarpDeployConfig(params: { chainConfig.remoteRouters = formattedRemoteRouters; + const remoteGasDomainsToKeep = intersection( + new Set(Object.keys(chainConfig.destinationGas ?? {})), + new Set(Object.keys(formattedRemoteRouters)), + ); + + // If the deploy config specified a custom config for remote routers + // we should not have all the gas settings set + const formattedDestinationGas = objFilter( + chainConfig.destinationGas ?? {}, + (domainId, _gasSetting): _gasSetting is string => + remoteGasDomainsToKeep.has(domainId), + ); + + chainConfig.destinationGas = formattedDestinationGas; + const isEVMChain = multiProvider.getProtocol(chain) === ProtocolType.Ethereum; @@ -281,6 +305,11 @@ const FIELDS_TO_IGNORE = new Set([ // expanding the config based on the gas value for each chain // see `expandWarpDeployConfig` function 'gas', + // Removing symbol and token metadata as they are not critical for + // checking, even if they are set "incorrectly" they do not affect how + // the warp route works + 'symbol', + 'name', ]); /** diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index d6a4f92c8a4..2fc6620629d 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -48,6 +48,10 @@ export function isAddressCosmos(address: Address) { ); } +export function isCosmosIbcDenomAddress(address: Address): boolean { + return IBC_DENOM_REGEX.test(address); +} + export function getAddressProtocolType(address: Address) { if (!address) return undefined; if (isAddressEvm(address)) { diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index 71527a6a601..1182f91a82c 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -20,6 +20,7 @@ export { getAddressProtocolType, isAddress, isAddressCosmos, + isCosmosIbcDenomAddress, isAddressEvm, isAddressSealevel, isValidAddress, From aec8961728b4de3545579c637573cd90bfa8ceb1 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Tue, 13 May 2025 10:58:28 -0400 Subject: [PATCH 171/223] feat(cli): improve cli checker output verbosity [ENG-1612] (#6183) ### Description This PR implements the `keepOnlyDiffObjects` function to format checker's violations to only output fields that have mismatches to avoid cluttering the terminal output for big configs ```shell ethereum: hook: hooks: - protocolFee: EXPECTED: "158365200000000" ACTUAL: "129871800000000" type: protocolFee type: aggregationHook type: xERC20Lockbox swell: hook: hooks: - protocolFee: EXPECTED: "158365200000000" ACTUAL: "129871800000000" type: protocolFee type: aggregationHook type: xERC20 zircuit: hook: hooks: - protocolFee: EXPECTED: "158365200000000" ACTUAL: "129871800000000" type: protocolFee type: aggregationHook type: xERC20 ``` ### Drive-by changes ### Related issues - Fixes [ENG-1612](https://linear.app/hyperlane-xyz/issue/ENG-1612/reduce-verbosity-in-cli-checker-violations-output) ### Backward compatibility -YES ### Testing - Manual - unit --- .changeset/floppy-beans-invent.md | 5 + typescript/cli/src/check/warp.ts | 12 ++- typescript/utils/src/index.ts | 1 + typescript/utils/src/objects.test.ts | 148 +++++++++++++++++++++++++++ typescript/utils/src/objects.ts | 37 +++++++ 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 .changeset/floppy-beans-invent.md diff --git a/.changeset/floppy-beans-invent.md b/.changeset/floppy-beans-invent.md new file mode 100644 index 00000000000..25e11723f66 --- /dev/null +++ b/.changeset/floppy-beans-invent.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Updates the `warp check` command output to only show fields that have diffs with the expected config diff --git a/typescript/cli/src/check/warp.ts b/typescript/cli/src/check/warp.ts index 92d13284b30..cccc7d6ad89 100644 --- a/typescript/cli/src/check/warp.ts +++ b/typescript/cli/src/check/warp.ts @@ -8,7 +8,11 @@ import { derivedIsmAddress, transformConfigToCheck, } from '@hyperlane-xyz/sdk'; -import { ObjectDiff, diffObjMerge } from '@hyperlane-xyz/utils'; +import { + ObjectDiff, + diffObjMerge, + keepOnlyDiffObjects, +} from '@hyperlane-xyz/utils'; import { log, logGreen } from '../logger.js'; import { formatYamlViolationsOutput } from '../utils/output.js'; @@ -56,7 +60,11 @@ export async function runWarpRouteCheck({ ); if (isInvalid) { - log(formatYamlViolationsOutput(yamlStringify(violations, null, 2))); + log( + formatYamlViolationsOutput( + yamlStringify(keepOnlyDiffObjects(violations), null, 2), + ), + ); process.exit(1); } diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index 1182f91a82c..d9591aa4ab3 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -114,6 +114,7 @@ export { deepEquals, deepFind, diffObjMerge, + keepOnlyDiffObjects, invertKeysAndValues, isObjEmpty, isObject, diff --git a/typescript/utils/src/objects.test.ts b/typescript/utils/src/objects.test.ts index 3be1a4bb5f9..7665afa6968 100644 --- a/typescript/utils/src/objects.test.ts +++ b/typescript/utils/src/objects.test.ts @@ -10,6 +10,7 @@ import { invertKeysAndValues, isObjEmpty, isObject, + keepOnlyDiffObjects, mustGet, objFilter, objKeys, @@ -513,4 +514,151 @@ describe('Object utilities', () => { expect(sortArraysInObject(input)).to.deep.equal(expected); }); }); + + describe(keepOnlyDiffObjects.name, () => { + const testCases: { input: any; expected: any }[] = [ + { + input: { + a: { + foo: { expected: 1, actual: 2 }, + bar: { something: true }, + nested: { + baz: { expected: 'x', actual: 'y' }, + qux: { nope: 0 }, + }, + }, + arr: [ + { alpha: { expected: 10, actual: 20 } }, + { beta: { wrong: true } }, + ], + plain: 123, + }, + expected: { + a: { + foo: { expected: 1, actual: 2 }, + nested: { + baz: { expected: 'x', actual: 'y' }, + }, + }, + arr: [{ alpha: { expected: 10, actual: 20 } }], + }, + }, + { + input: { + ethereum: { + mailbox: '0xc005dc82818d67af737725bd4bf75435d065d239', + owner: '0xd1e6626310fd54eceb5b9a51da2ec329d6d4b68a', + hook: { + type: 'aggregationHook', + hooks: [ + { + type: 'protocolFee', + protocolFee: { + expected: '158365200000000', + actual: '129871800000000', + }, + beneficiary: '0x8410927c286a38883bc23721e640f31d3e3e79f8', + }, + ], + }, + interchainSecurityModule: { + type: 'staticAggregationIsm', + modules: [ + { + owner: '0xd1e6626310fd54eceb5b9a51da2ec329d6d4b68a', + type: 'defaultFallbackRoutingIsm', + domains: {}, + }, + { + owner: '0xd1e6626310fd54eceb5b9a51da2ec329d6d4b68a', + type: 'domainRoutingIsm', + domains: { + berachain: { + type: 'staticAggregationIsm', + modules: [ + { + type: 'merkleRootMultisigIsm', + validators: [ + '0xa7341aa60faad0ce728aa9aeb67bb880f55e4392', + '0xae09cb3febc4cad59ef5a56c1df741df4eb1f4b6', + ], + threshold: 1, + }, + { + type: 'messageIdMultisigIsm', + validators: [ + '0xa7341aa60faad0ce728aa9aeb67bb880f55e4392', + '0xae09cb3febc4cad59ef5a56c1df741df4eb1f4b6', + ], + threshold: 1, + }, + ], + threshold: 1, + }, + }, + }, + ], + threshold: 2, + }, + decimals: { + expected: 18, + actual: 10, + }, + isNft: false, + type: 'xERC20Lockbox', + token: '0xbc5511354c4a9a50de928f56db01dd327c4e56d5', + remoteRouters: { + '80094': { + address: { + expected: + '0x00000000000000000000000025a851bf599cb8aef00ac1d1a9fb575ebf9d94b0', + actual: + '0x00000000000000000000000025a851bf599cb8aef00ac1d1a9fb575ebf9d94b1', + }, + }, + }, + }, + }, + expected: { + ethereum: { + type: 'xERC20Lockbox', + hook: { + type: 'aggregationHook', + hooks: [ + { + type: 'protocolFee', + protocolFee: { + expected: '158365200000000', + actual: '129871800000000', + }, + }, + ], + }, + decimals: { + expected: 18, + actual: 10, + }, + remoteRouters: { + '80094': { + address: { + expected: + '0x00000000000000000000000025a851bf599cb8aef00ac1d1a9fb575ebf9d94b0', + actual: + '0x00000000000000000000000025a851bf599cb8aef00ac1d1a9fb575ebf9d94b1', + }, + }, + }, + }, + }, + }, + ]; + + for (const { expected, input } of testCases) { + it(`should keep only the fields that have diff objects`, () => { + const act = keepOnlyDiffObjects(input); + + expect(act).to.eql(expected); + }); + } + }); }); diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index cdb51ca262f..018a166a386 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -330,6 +330,43 @@ export function diffObjMerge( }; } +// Recursively visit all the fields in an object and keep +// only those that describe mismatches +export function keepOnlyDiffObjects(obj: any): any { + const result: ObjectDiff = {}; + + if (Array.isArray(obj)) { + return obj + .map((item) => (isObject(item) ? keepOnlyDiffObjects(item) : {})) + .filter((item) => !isObjEmpty(item)); + } else if (isObject(obj)) { + const casted = obj as ObjectDiffOutput; + + if (!isNullish(casted.expected) && !isNullish(casted.actual)) { + return obj; + } else { + const filtered = Object.fromEntries( + Object.entries(obj) + .map(([key, value]): [string, any] => [ + key, + keepOnlyDiffObjects(value), + ]) + .filter(([_key, value]) => !isObjEmpty(value)), + ); + + // if this object has a type field we include to easily + // identify the type of the hook or ism + if (!isObjEmpty(filtered) && obj.type) { + filtered.type = obj.type; + } + + return filtered; + } + } + + return result; +} + export function mustGet(obj: Record, key: string): T { const value = obj[key]; if (!value) { From 430d1b410dec877a0e88305d3cc9002e0068d20b Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Tue, 13 May 2025 17:16:22 +0200 Subject: [PATCH 172/223] chore: add cosmos native signer strategy to cli (#6124) ### Description This PR implements and adds the cosmos native signer strategy to the cli context so users can also sign and deploy to cosmos native chains. Following PRs will use this to support the goal of having full cosmos native support in the hyperlane CLI. Note that this is the second PR of https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6050 which is being split up in order to make it easier to review ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual testing with local cosmos chain --- typescript/cli/package.json | 2 + typescript/cli/src/config/strategy.ts | 26 ++- typescript/cli/src/context/context.ts | 27 ++- .../signer/BaseMultiProtocolSigner.ts | 7 +- .../signer/MultiProtocolSignerFactory.ts | 68 ++++++- .../signer/MultiProtocolSignerManager.ts | 168 ++++++++++++------ typescript/cli/src/context/types.ts | 5 + yarn.lock | 2 + 8 files changed, 233 insertions(+), 72 deletions(-) diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 24e13a6e588..b91078a44c7 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -5,9 +5,11 @@ "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", + "@cosmjs/stargate": "^0.32.4", "@eslint/js": "^9.15.0", "@ethersproject/abi": "*", "@ethersproject/providers": "*", + "@hyperlane-xyz/cosmos-sdk": "12.5.0", "@hyperlane-xyz/registry": "15.0.0", "@hyperlane-xyz/sdk": "12.5.0", "@hyperlane-xyz/utils": "12.5.0", diff --git a/typescript/cli/src/config/strategy.ts b/typescript/cli/src/config/strategy.ts index cb2abaf437a..64110fbe40d 100644 --- a/typescript/cli/src/config/strategy.ts +++ b/typescript/cli/src/config/strategy.ts @@ -10,15 +10,17 @@ import { import { ProtocolType, assert, + errorToString, isAddress, isPrivateKeyEvm, } from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; -import { errorRed, log, logBlue, logGreen } from '../logger.js'; +import { errorRed, log, logBlue, logGreen, logRed } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { indentYamlOrJson, + isFile, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js'; @@ -36,6 +38,28 @@ export async function readChainSubmissionStrategyConfig( return parseResult; } +/** + * Safely reads chain submission strategy config, returns empty object if any errors occur + */ +export async function safeReadChainSubmissionStrategyConfig( + filePath: string, +): Promise { + try { + const trimmedFilePath = filePath.trim(); + if (!isFile(trimmedFilePath)) { + logBlue(`File ${trimmedFilePath} does not exist, returning empty config`); + return {}; + } + return await readChainSubmissionStrategyConfig(trimmedFilePath); + } catch (error) { + logRed( + `Failed to read strategy config, defaulting to empty config:`, + errorToString(error), + ); + return {}; + } +} + export async function createStrategyConfig({ context, outPath, diff --git a/typescript/cli/src/context/context.ts b/typescript/cli/src/context/context.ts index 941bd76d37a..e46e2fdcc4a 100644 --- a/typescript/cli/src/context/context.ts +++ b/typescript/cli/src/context/context.ts @@ -7,12 +7,14 @@ import { ChainMap, ChainMetadata, ChainName, + MultiProtocolProvider, MultiProvider, } from '@hyperlane-xyz/sdk'; import { isNullish, rootLogger } from '@hyperlane-xyz/utils'; +import { DEFAULT_STRATEGY_CONFIG_PATH } from '../commands/options.js'; import { isSignCommand } from '../commands/signCommands.js'; -import { readChainSubmissionStrategyConfig } from '../config/strategy.js'; +import { safeReadChainSubmissionStrategyConfig } from '../config/strategy.js'; import { forkNetworkToMultiProvider, verifyAnvil } from '../deploy/dry-run.js'; import { logBlue } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; @@ -54,13 +56,15 @@ export async function contextMiddleware(argv: Record) { } export async function signerMiddleware(argv: Record) { - const { key, requiresKey, multiProvider, strategyPath } = argv.context; + const { key, requiresKey, multiProvider, strategyPath, chainMetadata } = + argv.context; + const multiProtocolProvider = new MultiProtocolProvider(chainMetadata); if (!requiresKey) return argv; - const strategyConfig = strategyPath - ? await readChainSubmissionStrategyConfig(strategyPath) - : {}; + const strategyConfig = await safeReadChainSubmissionStrategyConfig( + strategyPath ?? DEFAULT_STRATEGY_CONFIG_PATH, + ); /** * Intercepts Hyperlane command to determine chains. @@ -79,6 +83,7 @@ export async function signerMiddleware(argv: Record) { strategyConfig, chains, multiProvider, + multiProtocolProvider, { key }, ); @@ -86,7 +91,8 @@ export async function signerMiddleware(argv: Record) { * @notice Attaches signers to MultiProvider and assigns it to argv.multiProvider */ argv.multiProvider = await multiProtocolSigner.getMultiProvider(); - argv.multiProtocolSigner = multiProtocolSigner; + argv.multiProtocolProvider = multiProtocolProvider; + argv.context.multiProtocolSigner = multiProtocolSigner; return argv; } @@ -120,12 +126,14 @@ export async function getContext({ } const multiProvider = await getMultiProvider(registry); + const multiProtocolProvider = await getMultiProtocolProvider(registry); return { registry, requiresKey, chainMetadata: multiProvider.metadata, multiProvider, + multiProtocolProvider, key, skipConfirmation: !!skipConfirmation, signerAddress, @@ -168,6 +176,7 @@ export async function getDryRunContext( await verifyAnvil(); let multiProvider = await getMultiProvider(registry); + const multiProtocolProvider = await getMultiProtocolProvider(registry); multiProvider = await forkNetworkToMultiProvider(multiProvider, chain); const { impersonatedKey, impersonatedSigner } = await getImpersonatedSigner({ fromAddress, @@ -182,6 +191,7 @@ export async function getDryRunContext( key: impersonatedKey, signer: impersonatedSigner, multiProvider: multiProvider, + multiProtocolProvider: multiProtocolProvider, skipConfirmation: !!skipConfirmation, isDryRun: true, dryRunChain: chain, @@ -200,6 +210,11 @@ async function getMultiProvider(registry: IRegistry, signer?: ethers.Signer) { return multiProvider; } +async function getMultiProtocolProvider(registry: IRegistry) { + const chainMetadata = await registry.getMetadata(); + return new MultiProtocolProvider(chainMetadata); +} + /** * Requests and saves Block Explorer API keys for the specified chains, prompting the user if necessary. * diff --git a/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts b/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts index b91242b42df..e6257e5ee7b 100644 --- a/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts +++ b/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts @@ -1,8 +1,11 @@ import { Signer } from 'ethers'; +import { SigningHyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; import { ChainName, ChainSubmissionStrategy } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; +export type TypedSigner = Signer | SigningHyperlaneModuleClient; + export interface SignerConfig { privateKey: string; address?: Address; // For chains like StarkNet that require address @@ -11,12 +14,12 @@ export interface SignerConfig { export interface IMultiProtocolSigner { getSignerConfig(chain: ChainName): Promise | SignerConfig; - getSigner(config: SignerConfig): Signer; + getSigner(config: SignerConfig): Promise; } export abstract class BaseMultiProtocolSigner implements IMultiProtocolSigner { constructor(protected config: ChainSubmissionStrategy) {} abstract getSignerConfig(chain: ChainName): Promise; - abstract getSigner(config: SignerConfig): Signer; + abstract getSigner(config: SignerConfig): Promise; } diff --git a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts b/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts index d6f83572fb6..45c5cfc8fcd 100644 --- a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts +++ b/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts @@ -1,7 +1,13 @@ +import { + DirectSecp256k1HdWallet, + DirectSecp256k1Wallet, +} from '@cosmjs/proto-signing'; +import { GasPrice } from '@cosmjs/stargate'; import { password } from '@inquirer/prompts'; -import { Signer, Wallet } from 'ethers'; +import { Signer, Wallet, ethers } from 'ethers'; import { Wallet as ZKSyncWallet } from 'zksync-ethers'; +import { SigningHyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; import { ChainName, ChainSubmissionStrategy, @@ -9,7 +15,7 @@ import { MultiProvider, TxSubmitterType, } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; +import { ProtocolType, assert, ensure0x } from '@hyperlane-xyz/utils'; import { BaseMultiProtocolSigner, @@ -30,6 +36,8 @@ export class MultiProtocolSignerFactory { if (technicalStack === ChainTechnicalStack.ZkSync) return new ZKSyncSignerStrategy(strategyConfig); return new EthereumSignerStrategy(strategyConfig); + case ProtocolType.CosmosNative: + return new CosmosNativeSignerStrategy(strategyConfig); default: throw new Error(`Unsupported protocol: ${protocol}`); } @@ -52,7 +60,7 @@ class EthereumSignerStrategy extends BaseMultiProtocolSigner { return { privateKey }; } - getSigner(config: SignerConfig): Signer { + async getSigner(config: SignerConfig): Promise { return new Wallet(config.privateKey); } } @@ -73,7 +81,59 @@ class ZKSyncSignerStrategy extends BaseMultiProtocolSigner { return { privateKey }; } - getSigner(config: SignerConfig): Signer { + async getSigner(config: SignerConfig): Promise { return new ZKSyncWallet(config.privateKey); } } + +class CosmosNativeSignerStrategy extends BaseMultiProtocolSigner { + async getSignerConfig(chain: ChainName): Promise { + const submitter = this.config[chain]?.submitter as { + privateKey?: string; + }; + + const privateKey = + submitter?.privateKey ?? + (await password({ + message: `Please enter the private key for chain ${chain}`, + })); + + return { + privateKey, + }; + } + + async getSigner({ + privateKey, + extraParams, + }: SignerConfig): Promise { + assert( + extraParams?.provider && extraParams?.prefix && extraParams?.gasPrice, + 'Missing Cosmos Signer arguments', + ); + + let wallet; + + if (ethers.utils.isHexString(ensure0x(privateKey))) { + wallet = await DirectSecp256k1Wallet.fromKey( + Buffer.from(privateKey, 'hex'), + extraParams.prefix, + ); + } else { + wallet = await DirectSecp256k1HdWallet.fromMnemonic(privateKey, { + prefix: extraParams.prefix, + }); + } + + const cometClient = extraParams?.provider.getCometClient(); + + // parse gas price so it has the correct format + const gasPrice = GasPrice.fromString( + `${extraParams.gasPrice.amount}${extraParams.gasPrice.denom}`, + ); + + return SigningHyperlaneModuleClient.createWithSigner(cometClient, wallet, { + gasPrice, + }); + } +} diff --git a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts b/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts index 394e0b1391b..097333e56a7 100644 --- a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts +++ b/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts @@ -1,16 +1,22 @@ import { Signer } from 'ethers'; import { Logger } from 'pino'; +import { SigningHyperlaneModuleClient } from '@hyperlane-xyz/cosmos-sdk'; import { ChainName, ChainSubmissionStrategy, + MultiProtocolProvider, MultiProvider, } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert, rootLogger } from '@hyperlane-xyz/utils'; import { ENV } from '../../../utils/env.js'; -import { IMultiProtocolSigner } from './BaseMultiProtocolSigner.js'; +import { + IMultiProtocolSigner, + SignerConfig, + TypedSigner, +} from './BaseMultiProtocolSigner.js'; import { MultiProtocolSignerFactory } from './MultiProtocolSignerFactory.js'; export interface MultiProtocolSignerOptions { @@ -24,13 +30,14 @@ export interface MultiProtocolSignerOptions { */ export class MultiProtocolSignerManager { protected readonly signerStrategies: Map; - protected readonly signers: Map; + protected readonly signers: Map; public readonly logger: Logger; constructor( protected readonly submissionStrategy: ChainSubmissionStrategy, protected readonly chains: ChainName[], protected readonly multiProvider: MultiProvider, + protected readonly multiProtocolProvider: MultiProtocolProvider, protected readonly options: MultiProtocolSignerOptions = {}, ) { this.logger = @@ -43,17 +50,19 @@ export class MultiProtocolSignerManager { this.initializeStrategies(); } + protected get compatibleChains(): ChainName[] { + return this.chains.filter( + (chain) => + this.multiProvider.getProtocol(chain) === ProtocolType.Ethereum || + this.multiProvider.getProtocol(chain) === ProtocolType.CosmosNative, + ); + } + /** * @notice Sets up chain-specific signer strategies */ protected initializeStrategies(): void { - for (const chain of this.chains) { - if (this.multiProvider.getProtocol(chain) !== ProtocolType.Ethereum) { - this.logger.debug( - `Skipping signer strategy initialization for non-EVM chain ${chain}`, - ); - continue; - } + for (const chain of this.compatibleChains) { const strategy = MultiProtocolSignerFactory.getSignerStrategy( chain, this.submissionStrategy, @@ -67,15 +76,9 @@ export class MultiProtocolSignerManager { * @dev Configures signers for EVM chains in MultiProvider */ async getMultiProvider(): Promise { - for (const chain of this.chains) { - if (this.multiProvider.getProtocol(chain) !== ProtocolType.Ethereum) { - this.logger.debug( - `Skipping signer initialization for non-EVM chain ${chain}`, - ); - continue; - } + for (const chain of this.compatibleChains) { const signer = await this.initSigner(chain); - this.multiProvider.setSigner(chain, signer); + this.multiProvider.setSigner(chain, signer as Signer); } return this.multiProvider; @@ -84,82 +87,129 @@ export class MultiProtocolSignerManager { /** * @notice Creates signer for specific chain */ - async initSigner(chain: ChainName): Promise { - const { privateKey } = await this.resolveConfig(chain); + async initSigner(chain: ChainName): Promise { + const config = await this.resolveConfig(chain); + const signerStrategy = this.getSignerStrategyOrFail(chain); + const signer = await signerStrategy.getSigner(config); - const signerStrategy = this.signerStrategies.get(chain); - assert(signerStrategy, `No signer strategy found for chain ${chain}`); - - return signerStrategy.getSigner({ privateKey }); + this.signers.set(chain, signer); + return signer; } /** * @notice Creates signers for all chains */ async initAllSigners(): Promise { - const signerConfigs = await this.resolveAllConfigs(); - - for (const { chain, privateKey } of signerConfigs) { + for (const chain of this.compatibleChains) { const signerStrategy = this.signerStrategies.get(chain); if (signerStrategy) { - this.signers.set(chain, signerStrategy.getSigner({ privateKey })); + await this.initSigner(chain); } } return this.signers; } - /** - * @notice Resolves all chain configurations - */ - private async resolveAllConfigs(): Promise< - Array<{ chain: ChainName; privateKey: string }> - > { - return Promise.all(this.chains.map((chain) => this.resolveConfig(chain))); - } - /** * @notice Resolves single chain configuration */ private async resolveConfig( chain: ChainName, - ): Promise<{ chain: ChainName; privateKey: string }> { - const signerStrategy = this.signerStrategies.get(chain); - assert(signerStrategy, `No signer strategy found for chain ${chain}`); + ): Promise<{ chain: ChainName } & SignerConfig> { + const { protocol } = this.multiProvider.getChainMetadata(chain); - let privateKey: string; - - if (this.options.key) { - this.logger.debug( - `Using private key passed via CLI --key flag for chain ${chain}`, - ); - privateKey = this.options.key; - } else if (ENV.HYP_KEY) { - this.logger.debug(`Using private key from .env for chain ${chain}`); - privateKey = ENV.HYP_KEY; - } else { - privateKey = await this.extractPrivateKey(chain, signerStrategy); + // For Cosmos, we must use strategy config + if (protocol === ProtocolType.CosmosNative) { + return this.resolveCosmosNativeConfig(chain, this.options.key); } - return { chain, privateKey }; + // For other protocols, try CLI/ENV keys first, then fallback to strategy + const config = await this.extractPrivateKey(chain); + return { chain, ...config }; } /** * @notice Gets private key from strategy */ - private async extractPrivateKey( - chain: ChainName, - signerStrategy: IMultiProtocolSigner, - ): Promise { + private async extractPrivateKey(chain: ChainName): Promise { + if (this.options.key) { + this.logger.info( + `Using private key passed via CLI --key flag for chain ${chain}`, + ); + return { privateKey: this.options.key }; + } + + if (ENV.HYP_KEY) { + this.logger.info(`Using private key from .env for chain ${chain}`); + return { privateKey: ENV.HYP_KEY }; + } + + const signerStrategy = this.getSignerStrategyOrFail(chain); const strategyConfig = await signerStrategy.getSignerConfig(chain); assert( strategyConfig.privateKey, `No private key found for chain ${chain}`, ); - - this.logger.debug( + this.logger.info( `Extracting private key from strategy config/user prompt for chain ${chain}`, ); - return strategyConfig.privateKey; + + return { privateKey: strategyConfig.privateKey }; + } + + private async resolveCosmosNativeConfig( + chain: ChainName, + key?: string, + ): Promise<{ chain: ChainName } & SignerConfig> { + const signerStrategy = this.getSignerStrategyOrFail(chain); + + if (!key) { + const strategyConfig = await signerStrategy.getSignerConfig(chain); + key = strategyConfig.privateKey; + } + + const provider = + await this.multiProtocolProvider.getCosmJsNativeProvider(chain); + const { bech32Prefix, gasPrice } = + this.multiProvider.getChainMetadata(chain); + + assert(key, `No private key found for chain ${chain}`); + assert(provider, 'No Cosmos Native Provider found'); + + this.logger.info(`Using strategy config for Cosmos Native chain ${chain}`); + + return { + chain, + privateKey: key, + extraParams: { provider, prefix: bech32Prefix, gasPrice }, + }; + } + + private getSignerStrategyOrFail(chain: ChainName): IMultiProtocolSigner { + const strategy = this.signerStrategies.get(chain); + assert(strategy, `No signer strategy found for chain ${chain}`); + return strategy; + } + + protected getSpecificSigner(chain: ChainName): T { + return this.signers.get(chain) as T; + } + + getEVMSigner(chain: ChainName): Signer { + const protocolType = this.multiProvider.getChainMetadata(chain).protocol; + assert( + protocolType === ProtocolType.Ethereum, + `Chain ${chain} is not an Ethereum chain`, + ); + return this.getSpecificSigner(chain); + } + + getCosmosNativeSigner(chain: ChainName): SigningHyperlaneModuleClient { + const protocolType = this.multiProvider.getProtocol(chain); + assert( + protocolType === ProtocolType.CosmosNative, + `Chain ${chain} is not a Cosmos Native chain`, + ); + return this.getSpecificSigner(chain); } } diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index 68858724957..14f792bf3f4 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -5,11 +5,14 @@ import type { IRegistry } from '@hyperlane-xyz/registry'; import type { ChainMap, ChainMetadata, + MultiProtocolProvider, MultiProvider, WarpCoreConfig, WarpRouteDeployConfigMailboxRequired, } from '@hyperlane-xyz/sdk'; +import { MultiProtocolSignerManager } from './strategies/signer/MultiProtocolSignerManager.js'; + export interface ContextSettings { registryUris: string[]; key?: string; @@ -25,6 +28,7 @@ export interface CommandContext { registry: IRegistry; chainMetadata: ChainMap; multiProvider: MultiProvider; + multiProtocolProvider: MultiProtocolProvider; skipConfirmation: boolean; key?: string; // just for evm chains backward compatibility @@ -35,6 +39,7 @@ export interface CommandContext { export interface WriteCommandContext extends CommandContext { key: string; signer: ethers.Signer; + multiProtocolSigner?: MultiProtocolSignerManager; isDryRun?: boolean; dryRunChain?: string; } diff --git a/yarn.lock b/yarn.lock index 741f1df3003..02fabac6ffe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7641,9 +7641,11 @@ __metadata: dependencies: "@aws-sdk/client-kms": "npm:^3.577.0" "@aws-sdk/client-s3": "npm:^3.577.0" + "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" + "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" "@hyperlane-xyz/registry": "npm:15.0.0" "@hyperlane-xyz/sdk": "npm:12.5.0" "@hyperlane-xyz/utils": "npm:12.5.0" From 9a87ab6bc45587943a5bf26b153501624600628d Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Tue, 13 May 2025 17:52:46 +0100 Subject: [PATCH 173/223] fix: Retry on getting Relayer balance in E2E since Relayer may not be started yet (#6201) ### Description Retry on getting Relayer balance in E2E since Relayer may not be started yet ### Backward compatibility Yes ### Testing Ethereum E2E locally --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/hyperlane-base/src/settings/loader/mod.rs | 7 +++++++ rust/main/utils/run-locally/src/main.rs | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/rust/main/hyperlane-base/src/settings/loader/mod.rs b/rust/main/hyperlane-base/src/settings/loader/mod.rs index c66b4b813bc..e7b55ebd2f5 100644 --- a/rust/main/hyperlane-base/src/settings/loader/mod.rs +++ b/rust/main/hyperlane-base/src/settings/loader/mod.rs @@ -22,6 +22,9 @@ where T: DeserializeOwned + Debug, R: FromRawConf, { + let now = chrono::Utc::now(); + println!("Loading settings: {:?}", now); + let root_path = ConfigPath::default(); let mut base_config_sources = vec![]; @@ -128,5 +131,9 @@ where if res.is_err() { eprintln!("Loaded config for debugging: {formatted_config}"); } + + let now = chrono::Utc::now(); + println!("Loaded settings: {:?}", now); + res } diff --git a/rust/main/utils/run-locally/src/main.rs b/rust/main/utils/run-locally/src/main.rs index c8956c28a4b..068e2ea43ea 100644 --- a/rust/main/utils/run-locally/src/main.rs +++ b/rust/main/utils/run-locally/src/main.rs @@ -371,7 +371,16 @@ fn main() -> ExitCode { log!("Success: Post startup invariants are met"); } - let starting_relayer_balance: f64 = agent_balance_sum(9092).unwrap(); + let starting_relayer_balance = loop { + let result = agent_balance_sum(9092); + log!("Relayer balance result: {:?}", result); + if let Ok(starting_relayer_balance) = result { + break starting_relayer_balance; + } else { + log!("Relayer balance not yet available"); + sleep(Duration::from_secs(5)); + } + }; // wait for CI invariants to pass let mut test_passed = wait_for_condition( From e9d58d682f984bfa9a37492087d74cdd5fb24932 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 13 May 2025 16:16:14 -0400 Subject: [PATCH 174/223] feat: rework server setup for agents (#6164) ### Description I wanted to upgrade and rework some of the axum server code - upgrade axum to 0.8.4 - remove `.get_route()` and just return the whole `Router` struct already configured - update tests to not require spinning up the router with an address, we can interact with the router directly with `.oneshot()` ### Backward compatibility Yes ### Testing Existing unittests --- rust/main/Cargo.lock | 91 +++++- rust/main/Cargo.toml | 3 +- rust/main/agents/relayer/Cargo.toml | 2 + rust/main/agents/relayer/src/msg/op_queue.rs | 2 +- .../agents/relayer/src/msg/op_submitter.rs | 2 +- rust/main/agents/relayer/src/relayer.rs | 6 +- .../src/server/environment_variable.rs | 80 +++--- .../relayer/src/server/list_messages.rs | 131 +++++---- .../relayer/src/server/message_retry.rs | 269 +++++++----------- rust/main/agents/relayer/src/server/mod.rs | 34 +-- .../main/agents/relayer/src/test_utils/mod.rs | 2 + .../agents/relayer/src/test_utils/request.rs | 30 ++ rust/main/agents/validator/Cargo.toml | 2 + rust/main/agents/validator/src/main.rs | 3 + .../agents/validator/src/server/eigen_node.rs | 157 ++++++---- rust/main/agents/validator/src/server/mod.rs | 12 +- rust/main/agents/validator/src/submit.rs | 1 + .../agents/validator/src/test_utils/mod.rs | 1 + .../validator/src/test_utils/request.rs | 16 ++ rust/main/agents/validator/src/validator.rs | 5 +- .../hyperlane-base/src/contract_sync/mod.rs | 2 +- .../hyperlane-base/src/server/base_server.rs | 34 ++- .../hyperlane-base/src/settings/chains.rs | 2 +- rust/main/utils/run-locally/src/server.rs | 2 +- 24 files changed, 487 insertions(+), 402 deletions(-) create mode 100644 rust/main/agents/relayer/src/test_utils/request.rs create mode 100644 rust/main/agents/validator/src/test_utils/mod.rs create mode 100644 rust/main/agents/validator/src/test_utils/request.rs diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 57d11584ece..2b83f8ae6f3 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -1011,7 +1011,6 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core 0.3.4", - "axum-macros", "bitflags 1.3.2", "bytes", "futures-util", @@ -1019,18 +1018,14 @@ dependencies = [ "http-body 0.4.6", "hyper 0.14.30", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", "pin-project-lite", "rustversion", "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", "sync_wrapper 0.1.2", - "tokio", "tower 0.4.13", "tower-layer", "tower-service", @@ -1050,7 +1045,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -1063,6 +1058,41 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +dependencies = [ + "axum-core 0.5.2", + "axum-macros", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-core" version = "0.3.4" @@ -1100,13 +1130,32 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-macros" -version = "0.3.8" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ - "heck 0.4.1", "proc-macro2 1.0.93", "quote 1.0.37", "syn 2.0.98", @@ -5184,7 +5233,7 @@ dependencies = [ "async-trait", "aws-config", "aws-sdk-s3", - "axum 0.6.20", + "axum 0.8.4", "backtrace", "backtrace-oneline", "bs58 0.5.1", @@ -6310,6 +6359,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.9.1" @@ -8128,7 +8183,7 @@ name = "relayer" version = "0.1.0" dependencies = [ "async-trait", - "axum 0.6.20", + "axum 0.8.4", "chrono", "config", "console-subscriber", @@ -8143,6 +8198,7 @@ dependencies = [ "eyre", "futures", "futures-util", + "http-body-util", "hyperlane-base", "hyperlane-core", "hyperlane-ethereum", @@ -8168,6 +8224,7 @@ dependencies = [ "tokio", "tokio-metrics", "tokio-test", + "tower 0.4.13", "tracing", "tracing-futures", "tracing-subscriber", @@ -11085,9 +11142,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -11389,8 +11446,10 @@ dependencies = [ "futures-util", "pin-project-lite", "sync_wrapper 1.0.2", + "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -11808,7 +11867,7 @@ version = "0.1.0" dependencies = [ "async-trait", "aws-config", - "axum 0.6.20", + "axum 0.8.4", "chrono", "config", "console-subscriber", @@ -11818,6 +11877,7 @@ dependencies = [ "eyre", "futures", "futures-util", + "http-body-util", "hyperlane-base", "hyperlane-core", "hyperlane-cosmos", @@ -11834,6 +11894,7 @@ dependencies = [ "thiserror 1.0.63", "tokio", "tokio-test", + "tower 0.4.13", "tracing", "tracing-futures", ] diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 123acd9e58f..612f053953a 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -45,7 +45,7 @@ aws-sdk-s3 = "=1.65.0" aws-sdk-sso = "=1.50.0" aws-sdk-ssooidc = "=1.50.0" aws-sdk-sts = "=1.50.0" -axum = "0.6.1" +axum = "0.8.4" backtrace = "0.3" cc = "1.2.2" base64 = "0.21.2" @@ -89,6 +89,7 @@ elliptic-curve = "0.13.8" getrandom = { version = "0.2", features = ["js"] } hex = "0.4.3" http = "1.2.0" +http-body-util = "0.1" hyper = "0.14" hyper-tls = "0.5.0" hyperlane-cosmwasm-interface = "=0.0.6-rc6" diff --git a/rust/main/agents/relayer/Cargo.toml b/rust/main/agents/relayer/Cargo.toml index b37ff4c9bec..3f67da1f202 100644 --- a/rust/main/agents/relayer/Cargo.toml +++ b/rust/main/agents/relayer/Cargo.toml @@ -60,9 +60,11 @@ submitter = { path = "../../submitter" } [dev-dependencies] axum = { workspace = true, features = ["macros"] } +http-body-util.workspace = true once_cell.workspace = true mockall.workspace = true tokio-test.workspace = true +tower.workspace = true tracing-test.workspace = true tracing-subscriber.workspace = true hyperlane-test = { path = "../../hyperlane-test" } diff --git a/rust/main/agents/relayer/src/msg/op_queue.rs b/rust/main/agents/relayer/src/msg/op_queue.rs index 464fd203056..f947554ede2 100644 --- a/rust/main/agents/relayer/src/msg/op_queue.rs +++ b/rust/main/agents/relayer/src/msg/op_queue.rs @@ -6,7 +6,7 @@ use prometheus::{IntGauge, IntGaugeVec}; use tokio::sync::{broadcast::Receiver, Mutex}; use tracing::{debug, instrument}; -use crate::server::{MessageRetryQueueResponse, MessageRetryRequest}; +use crate::server::message_retry::{MessageRetryQueueResponse, MessageRetryRequest}; pub type OperationPriorityQueue = Arc>>>; diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index fa2c4b5788e..243e180efb0 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -26,7 +26,7 @@ use submitter::{ }; use crate::msg::pending_message::CONFIRM_DELAY; -use crate::server::MessageRetryRequest; +use crate::server::message_retry::MessageRetryRequest; use super::op_batch::OperationBatch; use super::op_queue::OpQueue; diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index a26227568d6..f2dc223f41c 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -542,10 +542,10 @@ impl BaseAgent for Relayer { // run server start_entity_init = Instant::now(); - let custom_routes = relayer_server::Server::new(self.destination_chains.len()) + let relayer_router = relayer_server::Server::new(self.destination_chains.len()) .with_op_retry(sender.clone()) .with_message_queue(prep_queues) - .routes(); + .router(); let server = self .core .settings @@ -553,7 +553,7 @@ impl BaseAgent for Relayer { .expect("Failed to create server"); let server_task = tokio::spawn( async move { - server.run_with_custom_routes(custom_routes); + server.run_with_custom_router(relayer_router); } .instrument(info_span!("Relayer server")), ); diff --git a/rust/main/agents/relayer/src/server/environment_variable.rs b/rust/main/agents/relayer/src/server/environment_variable.rs index ded358ef5a9..d172d44d4cc 100644 --- a/rust/main/agents/relayer/src/server/environment_variable.rs +++ b/rust/main/agents/relayer/src/server/environment_variable.rs @@ -63,24 +63,25 @@ async fn set_environment_variable( impl EnvironmentVariableApi { pub fn router(&self) -> Router { - Router::new() - .route("/", routing::get(get_environment_variable)) - .route("/", routing::post(set_environment_variable)) - .with_state(self.clone()) - } - - pub fn get_route(&self) -> (&'static str, Router) { - (ENVIRONMENT_VARIABLE, self.router()) + Router::new().nest( + ENVIRONMENT_VARIABLE, + Router::new() + .route("/", routing::get(get_environment_variable)) + .route("/", routing::post(set_environment_variable)) + .with_state(self.clone()), + ) } } #[cfg(test)] mod tests { use std::env::VarError::NotPresent; - use std::net::SocketAddr; - use axum::http::StatusCode; + use axum::http::{header::CONTENT_TYPE, Method, Request, StatusCode}; use serde_json::{json, Value}; + use tower::ServiceExt; + + use crate::test_utils::request::parse_body_to_json; use super::*; @@ -89,83 +90,68 @@ mod tests { #[derive(Debug)] struct TestServerSetup { - pub socket_address: SocketAddr, + pub app: Router, } fn setup_test_server() -> TestServerSetup { let api = EnvironmentVariableApi::new(); - let (path, router) = api.get_route(); - - let app = Router::new().nest(path, router); + let app = api.router(); - let server = - axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); - - TestServerSetup { - socket_address: addr, - } + TestServerSetup { app } } #[tracing_test::traced_test] #[tokio::test] async fn test_environment_variable() { - let TestServerSetup { - socket_address: addr, - .. - } = setup_test_server(); + let TestServerSetup { app } = setup_test_server(); let set = set(); - let response = request(addr, &set, true).await; + let response = request(&app, &set, Method::POST).await; assert_eq!(NAME, response.name); assert_eq!(Some(VALUE.to_string()), response.value); assert_eq!("set", response.message); assert_eq!(VALUE, env::var(NAME).unwrap()); let get = get_or_remove(); - let response = request(addr, &get, false).await; + let response = request(&app, &get, Method::GET).await; assert_eq!(NAME, response.name); assert_eq!(Some(VALUE.to_string()), response.value); assert_eq!("got", response.message); assert_eq!(VALUE, env::var(NAME).unwrap()); let remove = get_or_remove(); - let response = request(addr, &remove, true).await; + let response = request(&app, &remove, Method::POST).await; assert_eq!(NAME, response.name); assert_eq!(None, response.value); assert_eq!("unset", response.message); assert_eq!(Err(NotPresent), env::var(NAME)); let get = get_or_remove(); - let response = request(addr, &get, false).await; + let response = request(&app, &get, Method::GET).await; assert_eq!(NAME, response.name); assert_eq!(None, response.value); assert_eq!("got", response.message); assert_eq!(Err(NotPresent), env::var(NAME)); } - async fn request(addr: SocketAddr, body: &Value, post: bool) -> EnvironmentVariableResponse { - let client = reqwest::Client::new(); - - let builder = if post { - client.post(format!("http://{}{}", addr, ENVIRONMENT_VARIABLE)) - } else { - client.get(format!("http://{}{}", addr, ENVIRONMENT_VARIABLE)) - }; - - let request = builder.json(&body).build().unwrap(); - let response = tokio::spawn(client.execute(request)) + async fn request(app: &Router, body: &Value, method: Method) -> EnvironmentVariableResponse { + let api_url = ENVIRONMENT_VARIABLE; + let request = Request::builder() + .uri(api_url) + .method(method) + .header(CONTENT_TYPE, "application/json") + .body(serde_json::to_string(body).expect("Failed to serialize body")) + .expect("Failed to build request"); + + let response = app + .clone() + .oneshot(request) .await - .unwrap() - .unwrap(); + .expect("Failed to send request"); assert_eq!(response.status(), StatusCode::OK); - let response = response - .json::() - .await - .unwrap(); + let response: EnvironmentVariableResponse = parse_body_to_json(response.into_body()).await; response } diff --git a/rust/main/agents/relayer/src/server/list_messages.rs b/rust/main/agents/relayer/src/server/list_messages.rs index 662bd1769cf..d3b5f304bb0 100644 --- a/rust/main/agents/relayer/src/server/list_messages.rs +++ b/rust/main/agents/relayer/src/server/list_messages.rs @@ -11,28 +11,39 @@ use crate::msg::op_queue::OperationPriorityQueue; const LIST_OPERATIONS_API_BASE: &str = "/list_operations"; +#[derive(Clone, Debug, new)] +pub struct ServerState { + pub op_queues: HashMap, +} +impl ServerState { + pub fn router(self) -> Router { + Router::new().nest( + LIST_OPERATIONS_API_BASE, + Router::new() + .route("/", routing::get(handler)) + .with_state(self), + ) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Deserialize)] -pub struct ListOperationsRequest { +pub struct QueryParams { destination_domain: u32, } -impl PartialEq for &ListOperationsRequest { +impl PartialEq for &QueryParams { fn eq(&self, other: &QueueOperation) -> bool { self.destination_domain == other.destination_domain().id() } } -#[derive(new, Clone)] -pub struct ListOperationsApi { - op_queues: HashMap, -} - -async fn list_operations( - State(queues): State>, - Query(request): Query, +async fn handler( + State(state): State, + Query(query_params): Query, ) -> String { - let domain = request.destination_domain; - let Some(op_queue) = queues.get(&domain) else { + let domain = query_params.destination_domain; + + let Some(op_queue) = state.op_queues.get(&domain) else { return format!("No queue found for domain {}", domain); }; format_queue(op_queue.clone()).await @@ -83,29 +94,24 @@ pub async fn format_queue(queue: OperationPriorityQueue) -> String { } } -impl ListOperationsApi { - pub fn router(&self) -> Router { - Router::new() - .route("/", routing::get(list_operations)) - .with_state(self.op_queues.clone()) - } - - pub fn get_route(&self) -> (&'static str, Router) { - (LIST_OPERATIONS_API_BASE, self.router()) - } -} - #[cfg(test)] mod tests { - use axum::http::StatusCode; - use std::{cmp::Reverse, net::SocketAddr, sync::Arc}; + use axum::{ + body::Body, + http::{Method, Request, StatusCode}, + }; + use std::{cmp::Reverse, sync::Arc}; use tokio::sync::{self, Mutex}; + use tower::ServiceExt; use hyperlane_core::KnownHyperlaneDomain; - use crate::msg::op_queue::{ - test::{dummy_metrics_and_label, MockPendingOperation}, - OpQueue, + use crate::{ + msg::op_queue::{ + test::{dummy_metrics_and_label, MockPendingOperation}, + OpQueue, + }, + test_utils::request::parse_body_to_string, }; use super::*; @@ -118,7 +124,13 @@ mod tests { const RECIPIENT_ADDRESS_1: &str = "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08"; - fn setup_test_server() -> (SocketAddr, OperationPriorityQueue) { + #[derive(Debug)] + struct TestServerSetup { + pub app: Router, + pub op_queue: OperationPriorityQueue, + } + + fn setup_test_server() -> TestServerSetup { let (metrics, queue_metrics_label) = dummy_metrics_and_label(); let broadcaster = sync::broadcast::Sender::new(100); let op_queue = OpQueue::new( @@ -129,18 +141,13 @@ mod tests { let mut op_queues_map = HashMap::new(); op_queues_map.insert(DUMMY_DOMAIN as u32, op_queue.queue.clone()); - let list_operations_api = ListOperationsApi::new(op_queues_map); - let (path, router) = list_operations_api.get_route(); - - let app = Router::new().nest(path, router); - - // Running the app in the background using a test server - let server = - axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); + let message_retry_api = ServerState::new(op_queues_map); + let app = message_retry_api.router(); - (addr, op_queue.queue.clone()) + TestServerSetup { + op_queue: op_queue.queue.clone(), + app, + } } fn generate_dummy_operation_1(retry_count: u32) -> QueueOperation { @@ -165,7 +172,7 @@ mod tests { #[tokio::test] async fn test_message_id_retry() { - let (addr, op_queue) = setup_test_server(); + let TestServerSetup { app, op_queue } = setup_test_server(); let retry_count_1 = 1; let retry_count_2 = 2; let dummy_operation_1 = generate_dummy_operation_1(retry_count_1); @@ -212,24 +219,27 @@ mod tests { op_queue.lock().await.push(Reverse(dummy_operation_1)); op_queue.lock().await.push(Reverse(dummy_operation_2)); - // Send a GET request to the server - let response = reqwest::get(format!( - "http://{}{}?destination_domain={}", - addr, LIST_OPERATIONS_API_BASE, DUMMY_DOMAIN as u32 - )) - .await - .unwrap(); + let api_url = format!( + "{LIST_OPERATIONS_API_BASE}?destination_domain={}", + DUMMY_DOMAIN as u32 + ); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app.oneshot(request).await.expect("Failed to send request"); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let response_text = response.text().await.unwrap(); + let response_text = parse_body_to_string(response.into_body()).await; assert_eq!(response_text, expected_response); } #[tokio::test] async fn test_sorted_by_retry_count() { - let (addr, op_queue) = setup_test_server(); + let TestServerSetup { app, op_queue } = setup_test_server(); let retry_count_1 = 4; let retry_count_2 = 1; let dummy_operation_1 = generate_dummy_operation_1(retry_count_1); @@ -276,18 +286,21 @@ mod tests { op_queue.lock().await.push(Reverse(dummy_operation_1)); op_queue.lock().await.push(Reverse(dummy_operation_2)); - // Send a GET request to the server - let response = reqwest::get(format!( - "http://{}{}?destination_domain={}", - addr, LIST_OPERATIONS_API_BASE, DUMMY_DOMAIN as u32 - )) - .await - .unwrap(); + let api_url = format!( + "{LIST_OPERATIONS_API_BASE}?destination_domain={}", + DUMMY_DOMAIN as u32 + ); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app.oneshot(request).await.expect("Failed to send request"); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let response_text = response.text().await.unwrap(); + let response_text = parse_body_to_string(response.into_body()).await; assert_eq!(response_text, expected_response); } } diff --git a/rust/main/agents/relayer/src/server/message_retry.rs b/rust/main/agents/relayer/src/server/message_retry.rs index a2fa56bc32a..d1331691d67 100644 --- a/rust/main/agents/relayer/src/server/message_retry.rs +++ b/rust/main/agents/relayer/src/server/message_retry.rs @@ -7,10 +7,17 @@ use tokio::sync::{broadcast::Sender, mpsc}; const MESSAGE_RETRY_API_BASE: &str = "/message_retry"; #[derive(Clone, Debug, new)] -pub struct MessageRetryApi { +pub struct ServerState { retry_request_transmitter: Sender, destination_chains: usize, } +impl ServerState { + pub fn router(self) -> Router { + Router::new() + .route(MESSAGE_RETRY_API_BASE, routing::post(handler)) + .with_state(self) + } +} #[derive(Clone, Debug)] pub struct MessageRetryRequest { @@ -37,14 +44,14 @@ pub struct MessageRetryResponse { pub matched: u64, } -async fn retry_message( - State(state): State, - Json(retry_req_payload): Json, +async fn handler( + State(state): State, + Json(payload): Json, ) -> Result, String> { let uuid = uuid::Uuid::new_v4(); let uuid_string = uuid.to_string(); - tracing::debug!(?retry_req_payload); + tracing::debug!(?payload); tracing::debug!(uuid = uuid_string, "Sending message retry request"); // Create a channel that can hold each chain's SerialSubmitter @@ -56,7 +63,7 @@ async fn retry_message( .retry_request_transmitter .send(MessageRetryRequest { uuid: uuid_string.clone(), - pattern: retry_req_payload, + pattern: payload, transmitter, }) .map_err(|err| { @@ -86,56 +93,39 @@ async fn retry_message( Ok(Json(resp)) } -impl MessageRetryApi { - pub fn router(&self) -> Router { - Router::new() - .route("/", routing::post(retry_message)) - .with_state(self.clone()) - } - - pub fn get_route(&self) -> (&'static str, Router) { - (MESSAGE_RETRY_API_BASE, self.router()) - } -} - #[cfg(test)] mod tests { - use crate::{msg::op_queue::test::MockPendingOperation, server::ENDPOINT_MESSAGES_QUEUE_SIZE}; - - use super::*; - use axum::http::StatusCode; + use axum::{ + body::Body, + http::{header::CONTENT_TYPE, Method, Request, Response, StatusCode}, + }; use hyperlane_core::{HyperlaneMessage, QueueOperation}; - use serde::de::DeserializeOwned; use serde_json::json; - use std::net::SocketAddr; use tokio::sync::broadcast::{Receiver, Sender}; + use tower::ServiceExt; + + use crate::{ + msg::op_queue::test::MockPendingOperation, server::ENDPOINT_MESSAGES_QUEUE_SIZE, + test_utils::request::parse_body_to_json, + }; + + use super::*; #[derive(Debug)] struct TestServerSetup { - pub socket_address: SocketAddr, + pub app: Router, pub retry_req_rx: Receiver, } fn setup_test_server() -> TestServerSetup { let broadcast_tx = Sender::new(ENDPOINT_MESSAGES_QUEUE_SIZE); - let message_retry_api = MessageRetryApi::new(broadcast_tx.clone(), 10); - let (path, retry_router) = message_retry_api.get_route(); - - let app = Router::new().nest(path, retry_router); - - // Running the app in the background using a test server - let server = - axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); - + let message_retry_api = ServerState::new(broadcast_tx.clone(), 10); let retry_req_rx = broadcast_tx.subscribe(); - TestServerSetup { - socket_address: addr, - retry_req_rx, - } + let app = message_retry_api.router(); + + TestServerSetup { app, retry_req_rx } } async fn send_retry_responses_future( @@ -153,34 +143,26 @@ mod tests { } } - async fn parse_response_to_json(response: reqwest::Response) -> T { - let resp_body = response - .text() - .await - .expect("Failed to parse response body"); - let resp_json: T = - serde_json::from_str(&resp_body).expect("Failed to deserialize response body"); - resp_json + async fn send_retry_request(app: Router, body: &serde_json::Value) -> Response { + let api_url = MESSAGE_RETRY_API_BASE; + let request = Request::builder() + .uri(api_url) + .method(Method::POST) + .header(CONTENT_TYPE, "application/json") + .body(serde_json::to_string(body).expect("Failed to serialize body")) + .expect("Failed to build request"); + let response = app.oneshot(request).await.expect("Failed to send request"); + response } #[tracing_test::traced_test] #[tokio::test] async fn test_message_id_retry() { - let TestServerSetup { - socket_address: addr, - retry_req_rx, - .. - } = setup_test_server(); + let TestServerSetup { app, retry_req_rx } = setup_test_server(); - let client = reqwest::Client::new(); // Create a random message with a random message ID let message = HyperlaneMessage::default(); let pending_operation = MockPendingOperation::with_message_data(message.clone()); - let matching_list_body = json!([ - { - "messageid": message.id() - } - ]); // spawn a task to respond to message retry request let respond_task = send_retry_responses_future( @@ -188,44 +170,35 @@ mod tests { vec![Box::new(pending_operation.clone()) as QueueOperation], vec![(1, 1)], ); + tokio::task::spawn(async { respond_task.await }); - // Send a POST request to the server - let response = client - .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) - .json(&matching_list_body) // Set the request body - .send(); + let body = json!([ + { + "messageid": message.id() + } + ]); - let (_t1, response_res) = tokio::join!(respond_task, response); + // Send a POST request to the server + let response = send_retry_request(app, &body).await; - let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + let resp_json: MessageRetryResponse = parse_body_to_json(response.into_body()).await; assert_eq!(resp_json.evaluated, 1); assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_destination_domain_retry() { - let TestServerSetup { - socket_address: addr, - retry_req_rx, - .. - } = setup_test_server(); + let TestServerSetup { app, retry_req_rx } = setup_test_server(); - let client = reqwest::Client::new(); let message = HyperlaneMessage { // Use a random destination domain destination: 42, ..Default::default() }; let pending_operation = MockPendingOperation::with_message_data(message.clone()); - let matching_list_body = json!([ - { - "destinationdomain": message.destination - } - ]); // spawn a task to respond to message retry request let respond_task = send_retry_responses_future( @@ -233,44 +206,35 @@ mod tests { vec![Box::new(pending_operation.clone()) as QueueOperation], vec![(1, 1)], ); + tokio::task::spawn(async { respond_task.await }); - // Send a POST request to the server - let response = client - .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) - .json(&matching_list_body) // Set the request body - .send(); + let body = json!([ + { + "destinationdomain": message.destination + } + ]); - let (_t1, response_res) = tokio::join!(respond_task, response); + // Send a POST request to the server + let response = send_retry_request(app, &body).await; - let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + let resp_json: MessageRetryResponse = parse_body_to_json(response.into_body()).await; assert_eq!(resp_json.evaluated, 1); assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_origin_domain_retry() { - let TestServerSetup { - socket_address: addr, - retry_req_rx, - .. - } = setup_test_server(); + let TestServerSetup { app, retry_req_rx } = setup_test_server(); - let client = reqwest::Client::new(); let message = HyperlaneMessage { // Use a random origin domain origin: 42, ..Default::default() }; let pending_operation = MockPendingOperation::with_message_data(message.clone()); - let matching_list_body = json!([ - { - "origindomain": message.origin - } - ]); // spawn a task to respond to message retry request let respond_task = send_retry_responses_future( @@ -278,41 +242,31 @@ mod tests { vec![Box::new(pending_operation.clone()) as QueueOperation], vec![(1, 1)], ); + tokio::task::spawn(async { respond_task.await }); - // Send a POST request to the server - let response = client - .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) - .json(&matching_list_body) // Set the request body - .send(); - - let (_t1, response_res) = tokio::join!(respond_task, response); + let body = json!([ + { + "origindomain": message.origin + } + ]); - let response = response_res.unwrap(); + // Send a POST request to the server + let response = send_retry_request(app, &body).await; // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + let resp_json: MessageRetryResponse = parse_body_to_json(response.into_body()).await; assert_eq!(resp_json.evaluated, 1); assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_sender_address_retry() { - let TestServerSetup { - socket_address: addr, - retry_req_rx, - .. - } = setup_test_server(); + let TestServerSetup { app, retry_req_rx } = setup_test_server(); - let client = reqwest::Client::new(); let message = HyperlaneMessage::default(); let pending_operation = MockPendingOperation::with_message_data(message.clone()); - let matching_list_body = json!([ - { - "senderaddress": message.sender - } - ]); // spawn a task to respond to message retry request let respond_task = send_retry_responses_future( @@ -320,40 +274,31 @@ mod tests { vec![Box::new(pending_operation.clone()) as QueueOperation], vec![(1, 1)], ); + tokio::task::spawn(async { respond_task.await }); - // Send a POST request to the server - let response = client - .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) - .json(&matching_list_body) // Set the request body - .send(); + let body = json!([ + { + "senderaddress": message.sender + } + ]); - let (_t1, response_res) = tokio::join!(respond_task, response); + // Send a POST request to the server + let response = send_retry_request(app, &body).await; - let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + let resp_json: MessageRetryResponse = parse_body_to_json(response.into_body()).await; assert_eq!(resp_json.evaluated, 1); assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_recipient_address_retry() { - let TestServerSetup { - socket_address: addr, - retry_req_rx, - .. - } = setup_test_server(); + let TestServerSetup { app, retry_req_rx } = setup_test_server(); - let client = reqwest::Client::new(); let message = HyperlaneMessage::default(); let pending_operation = MockPendingOperation::with_message_data(message.clone()); - let matching_list_body = json!([ - { - "recipientaddress": message.recipient - } - ]); // spawn a task to respond to message retry request let respond_task = send_retry_responses_future( @@ -361,40 +306,45 @@ mod tests { vec![Box::new(pending_operation.clone()) as QueueOperation], vec![(1, 1)], ); + tokio::task::spawn(async { respond_task.await }); - // Send a POST request to the server - let response = client - .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) - .json(&matching_list_body) // Set the request body - .send(); + let body = json!([ + { + "recipientaddress": message.recipient + } + ]); - let (_t1, response_res) = tokio::join!(respond_task, response); + // Send a POST request to the server + let response = send_retry_request(app, &body).await; - let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + let resp_json: MessageRetryResponse = parse_body_to_json(response.into_body()).await; assert_eq!(resp_json.evaluated, 1); assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_multiple_retry() { - let TestServerSetup { - socket_address: addr, - retry_req_rx, - .. - } = setup_test_server(); + let TestServerSetup { app, retry_req_rx } = setup_test_server(); - let client = reqwest::Client::new(); let message = HyperlaneMessage { // Use a random origin domain origin: 42, ..Default::default() }; let pending_operation = MockPendingOperation::with_message_data(message.clone()); - let matching_list_body = json!([ + + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + tokio::task::spawn(async { respond_task.await }); + + let body = json!([ { "origindomain": message.origin }, @@ -406,26 +356,13 @@ mod tests { } ]); - // spawn a task to respond to message retry request - let respond_task = send_retry_responses_future( - retry_req_rx, - vec![Box::new(pending_operation.clone()) as QueueOperation], - vec![(1, 1)], - ); - // Send a POST request to the server - let response = client - .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) - .json(&matching_list_body) // Set the request body - .send(); - - let (_t1, response_res) = tokio::join!(respond_task, response); + let response = send_retry_request(app, &body).await; - let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + let resp_json: MessageRetryResponse = parse_body_to_json(response.into_body()).await; assert_eq!(resp_json.evaluated, 1); assert_eq!(resp_json.matched, 1); } diff --git a/rust/main/agents/relayer/src/server/mod.rs b/rust/main/agents/relayer/src/server/mod.rs index c01af7310c5..8278f79734d 100644 --- a/rust/main/agents/relayer/src/server/mod.rs +++ b/rust/main/agents/relayer/src/server/mod.rs @@ -8,26 +8,26 @@ use crate::msg::op_queue::OperationPriorityQueue; pub const ENDPOINT_MESSAGES_QUEUE_SIZE: usize = 100; -pub use list_messages::*; -pub use message_retry::*; - use crate::server::environment_variable::EnvironmentVariableApi; -mod environment_variable; -mod list_messages; -mod message_retry; +pub mod environment_variable; +pub mod list_messages; +pub mod message_retry; #[derive(new)] pub struct Server { destination_chains: usize, #[new(default)] - retry_transmitter: Option>, + retry_transmitter: Option>, #[new(default)] op_queues: Option>, } impl Server { - pub fn with_op_retry(mut self, transmitter: Sender) -> Self { + pub fn with_op_retry( + mut self, + transmitter: Sender, + ) -> Self { self.retry_transmitter = Some(transmitter); self } @@ -37,24 +37,24 @@ impl Server { self } - /// Returns a vector of agent-specific endpoint routes to be served. - /// Can be extended with additional routes and feature flags to enable/disable individually. - pub fn routes(self) -> Vec<(&'static str, Router)> { - let mut routes = vec![]; + // return a custom router that can be used in combination with other routers + pub fn router(self) -> Router { + let mut router = Router::new(); + if let Some(tx) = self.retry_transmitter { - routes.push(MessageRetryApi::new(tx, self.destination_chains).get_route()); + router = + router.merge(message_retry::ServerState::new(tx, self.destination_chains).router()) } if let Some(op_queues) = self.op_queues { - routes.push(ListOperationsApi::new(op_queues).get_route()); + router = router.merge(list_messages::ServerState::new(op_queues).router()); } let expose_environment_variable_endpoint = env::var("HYPERLANE_RELAYER_ENVIRONMENT_VARIABLE_ENDPOINT_ENABLED") .map_or(false, |v| v == "true"); if expose_environment_variable_endpoint { - routes.push(EnvironmentVariableApi::new().get_route()); + router = router.merge(EnvironmentVariableApi::new().router()); } - - routes + router } } diff --git a/rust/main/agents/relayer/src/test_utils/mod.rs b/rust/main/agents/relayer/src/test_utils/mod.rs index 44f8d8eb82b..47730f509b2 100644 --- a/rust/main/agents/relayer/src/test_utils/mod.rs +++ b/rust/main/agents/relayer/src/test_utils/mod.rs @@ -3,3 +3,5 @@ pub mod mock_aggregation_ism; pub mod mock_base_builder; pub mod mock_ism; pub mod mock_routing_ism; + +pub mod request; diff --git a/rust/main/agents/relayer/src/test_utils/request.rs b/rust/main/agents/relayer/src/test_utils/request.rs new file mode 100644 index 00000000000..2e9d838f701 --- /dev/null +++ b/rust/main/agents/relayer/src/test_utils/request.rs @@ -0,0 +1,30 @@ +use axum::body::Body; +use http_body_util::BodyExt; +use serde::de::DeserializeOwned; + +pub async fn parse_body_to_json(body: Body) -> T { + let resp_body: Vec = body + .collect() + .await + .expect("Failed to collect body data") + .to_bytes() + .into_iter() + .collect(); + let resp_json: T = + serde_json::from_slice(&resp_body).expect("Failed to deserialize response body"); + resp_json +} + +pub async fn parse_body_to_string(body: Body) -> String { + let resp_body: Vec = body + .collect() + .await + .expect("Failed to collect body data") + .to_bytes() + .into_iter() + .collect(); + + let resp_body_string = + String::from_utf8(resp_body).expect("Failed to parse body string to UTF-8"); + resp_body_string +} diff --git a/rust/main/agents/validator/Cargo.toml b/rust/main/agents/validator/Cargo.toml index 7c9ac96dea6..33ecdbc7dce 100644 --- a/rust/main/agents/validator/Cargo.toml +++ b/rust/main/agents/validator/Cargo.toml @@ -42,8 +42,10 @@ hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } rusoto_core = '*' [dev-dependencies] +http-body-util.workspace = true mockall.workspace = true tokio-test.workspace = true +tower.workspace = true reqwest.workspace = true hyperlane-test = { path = "../../hyperlane-test" } k256.workspace = true diff --git a/rust/main/agents/validator/src/main.rs b/rust/main/agents/validator/src/main.rs index 7f9923d679b..d0bdac06d77 100644 --- a/rust/main/agents/validator/src/main.rs +++ b/rust/main/agents/validator/src/main.rs @@ -14,6 +14,9 @@ mod settings; mod submit; mod validator; +#[cfg(test)] +mod test_utils; + #[tokio::main(flavor = "multi_thread")] async fn main() -> Result<()> { // Logging is not initialised at this point, so, using `println!` diff --git a/rust/main/agents/validator/src/server/eigen_node.rs b/rust/main/agents/validator/src/server/eigen_node.rs index 7a927d72775..b0a765567f2 100644 --- a/rust/main/agents/validator/src/server/eigen_node.rs +++ b/rust/main/agents/validator/src/server/eigen_node.rs @@ -11,8 +11,8 @@ //! eg. response 200 - healthy, 206 - partially healthy, 503 - unhealthy //! - /node/services - List of Services //! eg. response [{"id":"hyperlane-validator-indexer","name":"indexer","description":"indexes the messages from the origin chain mailbox","status":"up"},{"id":"hyperlane-validator-submitter","name":"submitter","description":"signs messages indexed from the indexer","status":"up"}] -//! - /node/services/:service_id/health - Service Health -//! eg. response 200 - healthy, 503 - unhealthy +//! - /node/services/{service_id}/health - Service Health +//! eg. response 200 - healthy, 503 - unhealthy use axum::{ http::StatusCode, @@ -57,10 +57,6 @@ pub struct EigenNodeApi { } impl EigenNodeApi { - pub fn get_route(&self) -> (&'static str, Router) { - (EIGEN_NODE_API_BASE, self.router()) - } - pub fn router(&self) -> Router { let core_metrics_clone = self.core_metrics.clone(); let origin_chain = self.origin_chain.clone(); @@ -70,16 +66,19 @@ impl EigenNodeApi { let health_route = get(move || { Self::node_health_handler(origin_chain.clone(), core_metrics_clone.clone()) }); - let services_route = Router::new() - .route("/", get(Self::node_services_handler)) - .route("/:service_id/health", get(Self::service_health_handler)); - let node_route = Router::new() + let router = Router::new() .route("/health", health_route) - .nest("/services", services_route) + .route("/services", get(Self::node_services_handler)) + .route( + "/services/{service_id}/health", + get(Self::service_health_handler), + ) .route("/", get(Self::node_info_handler)); - Router::new().nest("/node", node_route) + let node_router = Router::new().nest("/node", router); + + Router::new().nest(EIGEN_NODE_API_BASE, node_router) } pub async fn node_info_handler() -> impl IntoResponse { @@ -144,16 +143,26 @@ impl EigenNodeApi { #[cfg(test)] mod tests { - use std::net::SocketAddr; + use crate::test_utils::request::parse_body_to_json; use super::*; - use axum::http::StatusCode; + use axum::{ + body::Body, + http::{Method, Request, StatusCode}, + }; use prometheus::Registry; + use tower::ServiceExt; const PARTIALLY_HEALTHY_OBSERVED_CHECKPOINT: i64 = 34; const HEALTHY_OBSERVED_CHECKPOINT: i64 = 42; - async fn setup_test_server() -> (reqwest::Client, SocketAddr, Arc) { + #[derive(Debug)] + struct TestServerSetup { + pub app: Router, + pub core_metrics: Arc, + } + + fn setup_test_server() -> TestServerSetup { let core_metrics = Arc::new(CoreMetrics::new("dummy_validator", 37582, Registry::new()).unwrap()); // Initialize the Prometheus registry @@ -168,29 +177,23 @@ mod tests { ); let app = node_api.router(); - // Running the app in the background using a test server - let server = - axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); - - // Create a client - let client = reqwest::Client::new(); - - (client, addr, core_metrics) + TestServerSetup { app, core_metrics } } #[tokio::test] async fn test_eigen_node_api() { - let (client, addr, _) = setup_test_server().await; - let res = client - .get(format!("http://{}/node", addr)) - .send() - .await - .expect("Failed to send request"); + let TestServerSetup { app, .. } = setup_test_server(); + + let api_url = format!("{EIGEN_NODE_API_BASE}/node"); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app.oneshot(request).await.expect("Failed to send request"); // Check that the response status is OK - assert_eq!(res.status(), StatusCode::OK); + assert_eq!(response.status(), StatusCode::OK); // what to expect when you're expecting let expected = NodeInfo { @@ -200,52 +203,80 @@ mod tests { }; // check the response body if needed - let json: NodeInfo = res.json().await.expect("Failed to parse json"); + let json: NodeInfo = parse_body_to_json(response.into_body()).await; assert_eq!(json, expected); } #[tokio::test] async fn test_eigen_node_health_api() { - let (client, addr, core_metrics) = setup_test_server().await; - let res = client - .get(format!("http://{}/node/health", addr)) - .send() + let TestServerSetup { app, core_metrics } = setup_test_server(); + + let api_url = format!("{EIGEN_NODE_API_BASE}/node/health"); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app + .clone() + .oneshot(request) .await .expect("Failed to send request"); - assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE); + assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE); core_metrics .latest_checkpoint() .with_label_values(&["validator_processed", "ethereum"]) .set(PARTIALLY_HEALTHY_OBSERVED_CHECKPOINT); - let res = client - .get(format!("http://{}/node/health", addr)) - .send() + + let api_url = format!("{EIGEN_NODE_API_BASE}/node/health"); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app + .clone() + .oneshot(request) .await .expect("Failed to send request"); - assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT); + assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); core_metrics .latest_checkpoint() .with_label_values(&["validator_processed", "ethereum"]) .set(HEALTHY_OBSERVED_CHECKPOINT); - let res = client - .get(format!("http://{}/node/health", addr)) - .send() + + let api_url = format!("{EIGEN_NODE_API_BASE}/node/health"); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app + .clone() + .oneshot(request) .await .expect("Failed to send request"); - assert_eq!(res.status(), StatusCode::OK); + assert_eq!(response.status(), StatusCode::OK); } #[tokio::test] async fn test_eigen_node_services_handler() { - let (client, addr, _) = setup_test_server().await; - let res = client - .get(format!("http://{}/node/services", addr)) - .send() + let TestServerSetup { app, .. } = setup_test_server(); + + let api_url = format!("{EIGEN_NODE_API_BASE}/node/services"); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app + .clone() + .oneshot(request) .await .expect("Failed to send request"); - assert_eq!(res.status(), StatusCode::OK); + assert_eq!(response.status(), StatusCode::OK); let expected_services = vec![ Service { @@ -261,23 +292,27 @@ mod tests { status: ServiceStatus::Up, }, ]; - let services: Vec = res.json().await.expect("Failed to parse json"); + let services: Vec = parse_body_to_json(response.into_body()).await; assert_eq!(services, expected_services); } #[tokio::test] async fn test_service_health_handler() { - let (client, addr, _) = setup_test_server().await; - let res = client - .get(format!( - "http://{}/node/services/hyperlane-validator-indexer/health", - addr - )) - .send() + let TestServerSetup { app, .. } = setup_test_server(); + + let api_url = + format!("{EIGEN_NODE_API_BASE}/node/services/hyperlane-validator-indexer/health"); + let request = Request::builder() + .uri(api_url) + .method(Method::GET) + .body(Body::empty()) + .expect("Failed to build request"); + let response = app + .clone() + .oneshot(request) .await .expect("Failed to send request"); - // Check that the response status is OK - assert_eq!(res.status(), StatusCode::OK); + assert_eq!(response.status(), StatusCode::OK); } } diff --git a/rust/main/agents/validator/src/server/mod.rs b/rust/main/agents/validator/src/server/mod.rs index c6b45c93de0..0f21bc29794 100644 --- a/rust/main/agents/validator/src/server/mod.rs +++ b/rust/main/agents/validator/src/server/mod.rs @@ -1,19 +1,17 @@ pub mod eigen_node; -use std::{sync::Arc, vec}; +pub use eigen_node::EigenNodeApi; + +use std::sync::Arc; use axum::Router; -pub use eigen_node::EigenNodeApi; use hyperlane_base::CoreMetrics; use hyperlane_core::HyperlaneDomain; /// Returns a vector of validator-specific endpoint routes to be served. /// Can be extended with additional routes and feature flags to enable/disable individually. -pub fn routes( - origin_chain: HyperlaneDomain, - metrics: Arc, -) -> Vec<(&'static str, Router)> { +pub fn router(origin_chain: HyperlaneDomain, metrics: Arc) -> Router { let eigen_node_api = EigenNodeApi::new(origin_chain, metrics); - vec![eigen_node_api.get_route()] + eigen_node_api.router() } diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs index 3d3c1319fa4..6f7de570b76 100644 --- a/rust/main/agents/validator/src/submit.rs +++ b/rust/main/agents/validator/src/submit.rs @@ -461,6 +461,7 @@ impl ValidatorSubmitterMetrics { #[cfg(test)] mod test { use super::*; + use async_trait::async_trait; use eyre::Result; use hyperlane_base::db::{ diff --git a/rust/main/agents/validator/src/test_utils/mod.rs b/rust/main/agents/validator/src/test_utils/mod.rs new file mode 100644 index 00000000000..be9378d97ea --- /dev/null +++ b/rust/main/agents/validator/src/test_utils/mod.rs @@ -0,0 +1 @@ +pub mod request; diff --git a/rust/main/agents/validator/src/test_utils/request.rs b/rust/main/agents/validator/src/test_utils/request.rs new file mode 100644 index 00000000000..3870fa37048 --- /dev/null +++ b/rust/main/agents/validator/src/test_utils/request.rs @@ -0,0 +1,16 @@ +use axum::body::Body; +use http_body_util::BodyExt; +use serde::de::DeserializeOwned; + +pub async fn parse_body_to_json(body: Body) -> T { + let resp_body: Vec = body + .collect() + .await + .expect("Failed to collect body data") + .to_bytes() + .into_iter() + .collect(); + let resp_json: T = + serde_json::from_slice(&resp_body).expect("Failed to deserialize response body"); + resp_json +} diff --git a/rust/main/agents/validator/src/validator.rs b/rust/main/agents/validator/src/validator.rs index 8c23f8b494e..83477084d59 100644 --- a/rust/main/agents/validator/src/validator.rs +++ b/rust/main/agents/validator/src/validator.rs @@ -192,8 +192,7 @@ impl BaseAgent for Validator { let mut tasks = vec![]; // run server - let custom_routes = - validator_server::routes(self.origin_chain.clone(), self.core.metrics.clone()); + let router = validator_server::router(self.origin_chain.clone(), self.core.metrics.clone()); let server = self .core .settings @@ -201,7 +200,7 @@ impl BaseAgent for Validator { .expect("Failed to create server"); let server_task = tokio::spawn( async move { - server.run_with_custom_routes(custom_routes); + server.run_with_custom_router(router); } .instrument(info_span!("Validator server")), ); diff --git a/rust/main/hyperlane-base/src/contract_sync/mod.rs b/rust/main/hyperlane-base/src/contract_sync/mod.rs index 652e0fca316..e1b39889ca5 100644 --- a/rust/main/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/main/hyperlane-base/src/contract_sync/mod.rs @@ -3,7 +3,7 @@ use std::{ time::UNIX_EPOCH, }; -use axum::async_trait; +use async_trait::async_trait; use broadcast::BroadcastMpscSender; use cursors::*; use derive_new::new; diff --git a/rust/main/hyperlane-base/src/server/base_server.rs b/rust/main/hyperlane-base/src/server/base_server.rs index e023594e9d8..b84d93d3be4 100644 --- a/rust/main/hyperlane-base/src/server/base_server.rs +++ b/rust/main/hyperlane-base/src/server/base_server.rs @@ -1,9 +1,10 @@ -use crate::CoreMetrics; use axum::{http::StatusCode, response::IntoResponse, routing::get, Router}; use derive_new::new; -use std::{net::SocketAddr, sync::Arc}; +use std::sync::Arc; use tokio::task::JoinHandle; +use crate::CoreMetrics; + /// A server that serves agent-specific routes #[derive(new, Debug)] pub struct Server { @@ -14,7 +15,7 @@ pub struct Server { impl Server { /// Run an HTTP server pub fn run(self: Arc) -> JoinHandle<()> { - self.run_with_custom_routes(vec![]) + self.run_with_custom_router(Router::new()) } /// Run an HTTP server serving agent-specific different routes @@ -23,30 +24,27 @@ impl Server { /// - metrics - serving OpenMetrics format reports on `/metrics` /// (this is compatible with Prometheus, which ought to be configured to scrape this endpoint) /// - custom_routes - additional routes to be served by the server as per the specific agent - pub fn run_with_custom_routes( - self: Arc, - custom_routes: Vec<(&str, Router)>, - ) -> JoinHandle<()> { + pub fn run_with_custom_router(self: Arc, router: Router) -> JoinHandle<()> { let port = self.listen_port; tracing::info!(port, "starting server on 0.0.0.0"); let core_metrics_clone = self.core_metrics.clone(); - let mut app = Router::new().route( - "/metrics", - get(move || Self::gather_metrics(core_metrics_clone)), - ); - - for (route, router) in custom_routes { - app = app.nest(route, router); - } + let app = Router::new() + .route( + "/metrics", + get(move || Self::gather_metrics(core_metrics_clone)), + ) + .merge(router); tokio::task::Builder::new() .name("agent::server") .spawn(async move { - let addr = SocketAddr::from(([0, 0, 0, 0], port)); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let url = format!("0.0.0.0:{}", port); + let listener = tokio::net::TcpListener::bind(url) + .await + .expect("Failed to bind to TCP port"); + axum::serve(listener, app) .await .expect("Failed to start server"); }) diff --git a/rust/main/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs index a2d0d1bbbcc..7989dfe2072 100644 --- a/rust/main/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -use axum::async_trait; +use async_trait::async_trait; use ethers::prelude::Selector; use eyre::{eyre, Context, Report, Result}; use serde_json::Value; diff --git a/rust/main/utils/run-locally/src/server.rs b/rust/main/utils/run-locally/src/server.rs index 9617cd89d58..1fe82d4cde4 100644 --- a/rust/main/utils/run-locally/src/server.rs +++ b/rust/main/utils/run-locally/src/server.rs @@ -3,7 +3,7 @@ use std::{io, time::Duration}; use maplit::hashmap; use reqwest::Url; -use relayer::server::MessageRetryResponse; +use relayer::server::message_retry::MessageRetryResponse; use crate::{fetch_metric, RELAYER_METRICS_PORT}; From 07faaddc4fcf3fbbdfc310b191f91c4216cb2520 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Tue, 13 May 2025 18:17:04 -0400 Subject: [PATCH 175/223] feat: Remove createWarpRouteConfigId() from metrics.ts and monitor-warp-route-balances.ts (#6215) ### Description Given that these scripts have the warpRouteId already at some point (used to fetch the warpCore), we pass it through each function instead of using createWarpRouteConfigId() with warpCore ### Backward compatibility Yes - should not change any existing functionality --- .../scripts/warp-routes/monitor/metrics.ts | 15 +++------- .../monitor/monitor-warp-route-balances.ts | 29 ++++++++----------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/typescript/infra/scripts/warp-routes/monitor/metrics.ts b/typescript/infra/scripts/warp-routes/monitor/metrics.ts index cb156bf25c5..38645957de2 100644 --- a/typescript/infra/scripts/warp-routes/monitor/metrics.ts +++ b/typescript/infra/scripts/warp-routes/monitor/metrics.ts @@ -1,6 +1,5 @@ import { Gauge, Registry } from 'prom-client'; -import { createWarpRouteConfigId } from '@hyperlane-xyz/registry'; import { ChainName, Token, TokenStandard, WarpCore } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; @@ -95,7 +94,7 @@ export function updateTokenBalanceMetrics( warpCore: WarpCore, token: Token, balanceInfo: WarpRouteBalance, - collateralTokenSymbol: string, + warpRouteId: string, ) { const allChains = warpCore.getTokenChains().sort(); const relatedChains = allChains.filter( @@ -116,10 +115,7 @@ export function updateTokenBalanceMetrics( token_standard: // as we are reporting the total supply for clarity we report the standard as xERC20 token.standard !== TokenStandard.EvmHypXERC20 ? token.standard : 'xERC20', - warp_route_id: createWarpRouteConfigId( - collateralTokenSymbol, - warpCore.getTokenChains(), - ), + warp_route_id: warpRouteId, // TODO: consider deprecating this label given that we have the value at risk metric related_chain_names: relatedChains.join(','), }; @@ -174,7 +170,7 @@ export function updateManagedLockboxBalanceMetrics( tokenAddress: string, lockBoxAddress: string, balanceInfo: WarpRouteBalance, - collateralTokenSymbol: string, + warpRouteId: string, ) { const metrics: WarpRouteMetrics = { chain_name: chainName, @@ -182,10 +178,7 @@ export function updateManagedLockboxBalanceMetrics( token_name: tokenName, wallet_address: lockBoxAddress, token_standard: 'EvmManagedLockbox', // TODO: we should eventually a new TokenStandard for this - warp_route_id: createWarpRouteConfigId( - collateralTokenSymbol, - warpCore.getTokenChains(), - ), + warp_route_id: warpRouteId, related_chain_names: warpCore .getTokenChains() .filter((_chainName) => _chainName !== chainName) diff --git a/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts b/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts index 7747a73f9a8..0078cb53eb7 100644 --- a/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts @@ -1,7 +1,6 @@ import { Contract, PopulatedTransaction } from 'ethers'; import { IXERC20VS__factory } from '@hyperlane-xyz/core'; -import { createWarpRouteConfigId } from '@hyperlane-xyz/registry'; import { ChainMap, ChainMetadata, @@ -90,6 +89,7 @@ async function main() { warpCore, warpDeployConfig, chainMetadata, + warpRouteId, ); } @@ -99,12 +99,12 @@ async function pollAndUpdateWarpRouteMetrics( warpCore: WarpCore, warpDeployConfig: WarpRouteDeployConfig | null, chainMetadata: ChainMap, + warpRouteId: string, ) { const tokenPriceGetter = new CoinGeckoTokenPriceGetter({ chainMetadata, apiKey: await getCoinGeckoApiKey(), }); - const collateralTokenSymbol = getWarpRouteCollateralTokenSymbol(warpCore); while (true) { await tryFn(async () => { @@ -115,7 +115,7 @@ async function pollAndUpdateWarpRouteMetrics( warpDeployConfig, token, tokenPriceGetter, - collateralTokenSymbol, + warpRouteId, ), ), ); @@ -130,7 +130,7 @@ async function updateTokenMetrics( warpDeployConfig: WarpRouteDeployConfig | null, token: Token, tokenPriceGetter: CoinGeckoTokenPriceGetter, - collateralTokenSymbol: string, + warpRouteId: string, ) { const promises = [ tryFn(async () => { @@ -142,12 +142,7 @@ async function updateTokenMetrics( if (!balanceInfo) { return; } - updateTokenBalanceMetrics( - warpCore, - token, - balanceInfo, - collateralTokenSymbol, - ); + updateTokenBalanceMetrics(warpCore, token, balanceInfo, warpRouteId); }, 'Getting bridged balance and value'), ]; @@ -158,7 +153,11 @@ async function updateTokenMetrics( if (token.protocol === ProtocolType.Sealevel && !token.isNative()) { promises.push( tryFn(async () => { - const balance = await getSealevelAtaPayerBalance(warpCore, token); + const balance = await getSealevelAtaPayerBalance( + warpCore, + token, + warpRouteId, + ); updateNativeWalletBalanceMetrics(balance); }, 'Getting ATA payer balance'), ); @@ -238,7 +237,7 @@ async function updateTokenMetrics( tokenAddress, lockbox.lockbox, balance, - collateralTokenSymbol, + warpRouteId, ); } }, `Updating extra lockbox balance for contract at "${lockbox.lockbox}" on chain ${token.chainName}`), @@ -329,6 +328,7 @@ function formatBigInt(warpToken: Token, num: bigint): number { async function getSealevelAtaPayerBalance( warpCore: WarpCore, token: Token, + warpRouteId: string, ): Promise { if (token.protocol !== ProtocolType.Sealevel || token.isNative()) { throw new Error( @@ -347,11 +347,6 @@ async function getSealevelAtaPayerBalance( warpCore.multiProvider, ataPayer, ); - - const warpRouteId = createWarpRouteConfigId( - token.symbol, - warpCore.getTokenChains(), - ); return { chain: token.chainName, walletAddress: ataPayer.toString(), From e2a47274c5050a594a2f26054d73a959c7a59921 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 14 May 2025 10:30:59 +0100 Subject: [PATCH 176/223] feat: may 12 mainnets (#6209) ### Description feat: may 12 mainnets - kyve - miracle - ontology ### Drive-by changes - fix bouncebit index.chunk after moving to private RPC ### Related issues https://github.com/hyperlane-xyz/hyperlane-registry/pull/848 ### Backward compatibility ### Testing --- .changeset/spotty-ways-lick.md | 5 + .registryrc | 2 +- rust/main/config/mainnet_config.json | 165 ++++++++++++++++- .../config/environments/mainnet3/agent.ts | 18 +- .../mainnet3/aw-validators/hyperlane.json | 9 + .../mainnet3/balances/dailyRelayerBurn.json | 2 + .../desiredRelayerBalanceOverrides.json | 5 +- .../balances/desiredRelayerBalances.json | 4 +- .../balances/highUrgencyRelayerBalance.json | 3 + .../lowUrgencyEngKeyFunderBalance.json | 3 + .../balances/lowUrgencyKeyFunderBalance.json | 3 + .../mainnet3/core/verification.json | 140 ++++++++++++++ .../config/environments/mainnet3/funding.ts | 4 +- .../environments/mainnet3/gasPrices.json | 8 + .../mainnet3/ism/verification.json | 172 ++++++++++++++++++ .../middleware/accounts/verification.json | 42 +++++ .../config/environments/mainnet3/owners.ts | 3 + .../mainnet3/supportedChainNames.ts | 2 + .../environments/mainnet3/tokenPrices.json | 2 + .../environments/mainnet3/validators.ts | 21 +++ .../scripts/validators/announce-validators.ts | 3 +- typescript/infra/src/config/chain.ts | 2 - typescript/sdk/src/consts/multisigIsm.ts | 20 ++ 23 files changed, 620 insertions(+), 18 deletions(-) create mode 100644 .changeset/spotty-ways-lick.md diff --git a/.changeset/spotty-ways-lick.md b/.changeset/spotty-ways-lick.md new file mode 100644 index 00000000000..c3fbf956669 --- /dev/null +++ b/.changeset/spotty-ways-lick.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Deploy to new chains: ontology, miraclechain, kyve. diff --git a/.registryrc b/.registryrc index 23d88f2f007..ce08f975f64 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -50c45bfc47885f636d3db0b7baebc861f6596f8e +4d879c24b4bed89a770570dd7d54d3423edab496 diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index d34dd7485ad..3edd7009417 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -8262,7 +8262,7 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0xB2b0A80b2fa3fC9aB1564A4FaF013d4D6084B325", "index": { - "chunk": 999, + "chunk": 100, "from": 7598788 } }, @@ -9383,7 +9383,35 @@ "http": "https://dappnode4.ont.io:10339" } ], - "technicalStack": "other" + "technicalStack": "other", + "aggregationHook": "0xA220a0096E362c2D6BCb10744F5BCADB1B1d023f", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x3E12271EbD523d0886D0D51A4FF8D8e046CF2E1D", + "interchainAccountIsm": "0x8BdD5bf519714515083801448A99F84882A8F61E", + "interchainAccountRouter": "0x718f11e349374481Be8c8B7589eC4B4316ddDCc2", + "interchainGasPaymaster": "0x11EF91d17c5ad3330DbCa709a8841743d3Af6819", + "interchainSecurityModule": "0xAC25979425F296A9B20b37128FC5d997CEC9Fc5a", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0xb5668713E9BA8bC96f97D691663E70b54CE90b0A", + "pausableHook": "0xC34aD1fB13a9eE1A0C0AB75aD9B9ceeA0690Cc74", + "pausableIsm": "0xD59dA396F162Ed93a41252Cebb8d5DD4F093238C", + "protocolFee": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xAC25979425F296A9B20b37128FC5d997CEC9Fc5a", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0xc5068BB6803ADbe5600DE5189fe27A4dAcE31170", + "testRecipient": "0xdBa3b98DC83fec149c8C8F6617700b9e45937a2b", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x4e128A1b613A9C9Ecf650FeE461c353612559fcf", + "index": { + "from": 18791289 + } }, "peaq": { "blockExplorers": [ @@ -9490,6 +9518,139 @@ "mailbox": "GzwNZJ2EXUWQCEoV6zoBGLxkdsnjkLvm6zHReMEC2JSA", "merkleTreeHook": "GzwNZJ2EXUWQCEoV6zoBGLxkdsnjkLvm6zHReMEC2JSA", "validatorAnnounce": "Gh4LUk91bj4QGYWsBY4QyDGhs68n8Maf8oeTRZoQZAEf" + }, + "kyve": { + "bech32Prefix": "kyve", + "blockExplorers": [ + { + "apiUrl": "https://explorer.kyve.network/kyve", + "family": "other", + "name": "Ping.Pub", + "url": "https://explorer.kyve.network/kyve" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": 1 + }, + "canonicalAsset": "ukyve", + "chainId": "kyve-1", + "contractAddressBytes": 32, + "deployer": { + "name": "KYVE", + "url": "https://kyve.network" + }, + "displayName": "KYVE", + "domainId": 1264145989, + "gasCurrencyCoinGeckoId": "kyve-network", + "gasPrice": { + "denom": "ukyve", + "amount": "3" + }, + "grpcUrls": [ + { + "http": "https://grpc-raw.kyve.network" + } + ], + "index": { + "chunk": 10, + "from": 11592376 + }, + "name": "kyve", + "nativeToken": { + "decimals": 6, + "denom": "ukyve", + "name": "KYVE", + "symbol": "KYVE" + }, + "protocol": "cosmosnative", + "restUrls": [ + { + "http": "https://api.kyve.network" + } + ], + "rpcUrls": [ + { + "http": "https://rpc.kyve.network" + } + ], + "signer": { + "key": "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26", + "prefix": "kyve", + "type": "cosmosKey" + }, + "slip44": 118, + "technicalStack": "other", + "interchainGasPaymaster": "0x726f757465725f706f73745f6469737061746368000000040000000000000002", + "interchainSecurityModule": "0x726f757465725f69736d00000000000000000000000000010000000000000002", + "mailbox": "0x68797065726c616e650000000000000000000000000000000000000000000000", + "merkleTreeHook": "0x726f757465725f706f73745f6469737061746368000000030000000000000000", + "validatorAnnounce": "0x68797065726c616e650000000000000000000000000000000000000000000000" + }, + "miraclechain": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.miracleplay.io/api", + "family": "blockscout", + "name": "Miracle Chain explorer", + "url": "https://explorer.miracleplay.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 0 + }, + "chainId": 92278, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Miraclechain", + "domainId": 92278, + "gasCurrencyCoinGeckoId": "miracle-play", + "index": { + "from": 84964 + }, + "name": "miraclechain", + "nativeToken": { + "decimals": 18, + "name": "MiraclePlay", + "symbol": "MPT" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.miracleplay.io" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0xA256e1A0Bd9fFa340DFe9a0F3C68FE57720463Ed", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x0dc95Af5156fb0cC34a8c9BD646B748B9989A956", + "interchainAccountIsm": "0x23cc88CF424d48fDB05b4f0A8Ff6099aa4D56D8e", + "interchainAccountRouter": "0x40Ca4155c0334F7e0F6d7F80536B59EF8831c9fb", + "interchainGasPaymaster": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "interchainSecurityModule": "0x44AE7f69Ca8c6EE978015f8b2C9008ee265bD12a", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x64F077915B41479901a23Aa798B30701589C5063", + "pausableHook": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "pausableIsm": "0x0DbB60c348DF645c295Fd0ce26F87bB850710185", + "protocolFee": "0x946E9f4540E032a9fAc038AE58187eFcad9DE952", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x44AE7f69Ca8c6EE978015f8b2C9008ee265bD12a", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x794Fe7970EE45945b0ad2667f99A5bBc9ddfB5d7", + "testRecipient": "0xc8826EA18D9884A1A335b2Cd7d5f44B159084301", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x5b1A451c85e5bcCd79A56eb638aBd9998fA215f9" } }, "defaultRpcConsensusType": "fallback" diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index b20fb98c6f1..56a8934b2bd 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -131,6 +131,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< ink: true, kaia: true, kroma: true, + kyve: true, linea: true, lisk: true, lukso: true, @@ -144,6 +145,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< metis: true, milkyway: true, mint: true, + miraclechain: true, mode: true, molten: true, moonbeam: true, @@ -152,7 +154,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< neutron: true, nibiru: true, oortmainnet: true, - ontology: false, + ontology: true, opbnb: true, optimism: true, orderly: true, @@ -278,6 +280,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< ink: true, kaia: true, kroma: true, + kyve: true, linea: true, lisk: true, lukso: true, @@ -291,6 +294,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< metis: true, milkyway: true, mint: true, + miraclechain: true, mode: true, molten: true, moonbeam: true, @@ -299,7 +303,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< neutron: true, nibiru: true, oortmainnet: true, - ontology: false, + ontology: true, opbnb: true, optimism: true, orderly: true, @@ -425,6 +429,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< injective: true, kaia: true, kroma: true, + kyve: true, linea: true, lisk: true, lukso: true, @@ -438,6 +443,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< metis: true, milkyway: true, mint: true, + miraclechain: true, mode: true, molten: true, moonbeam: true, @@ -446,7 +452,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< neutron: true, nibiru: true, oortmainnet: true, - ontology: false, + ontology: true, opbnb: true, optimism: true, orderly: true, @@ -863,7 +869,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: '6df66f8-20250508-104808', + tag: 'abdf139-20250514-081104', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -874,7 +880,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd3f8da9-20250509-160937', + tag: 'abdf139-20250514-081104', }, resources: scraperResources, }, @@ -889,7 +895,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd3f8da9-20250509-160937', + tag: 'abdf139-20250514-081104', }, blacklist, // We're temporarily (ab)using the RC relayer as a way to increase diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index 76831191d7e..a947c9e2a5e 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -226,6 +226,9 @@ "kroma": { "validators": ["0x71b83c21342787d758199e4b8634d3a15f02dc6e"] }, + "kyve": { + "validators": ["0x8576ddc0cd96325f85528e53f333357afb8bf044"] + }, "linea": { "validators": ["0xf2d5409a59e0f5ae7635aff73685624904a77d94"] }, @@ -269,6 +272,9 @@ "mint": { "validators": ["0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9"] }, + "miraclechain": { + "validators": ["0x8fc655174e99194399822ce2d3a0f71d9fc2de7b"] + }, "mode": { "validators": ["0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7"] }, @@ -301,6 +307,9 @@ "oortmainnet": { "validators": ["0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7"] }, + "ontology": { + "validators": ["0x2578b0a330c492e1a1682684e27e6a93649befd5"] + }, "opbnb": { "validators": ["0x1bdf52749ef2411ab9c28742dea92f209e96c9c4"] }, diff --git a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json index 77b45aae3a7..75b8d64ac74 100644 --- a/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json +++ b/typescript/infra/config/environments/mainnet3/balances/dailyRelayerBurn.json @@ -64,6 +64,7 @@ "ink": 0.0233, "kaia": 31.1, "kroma": 0.00195, + "kyve": 282, "linea": 0.0765, "lisk": 0.0104, "lukso": 3.66, @@ -77,6 +78,7 @@ "metis": 0.229, "milkyway": 3.13, "mint": 0.00272, + "miraclechain": 133, "mode": 0.0229, "molten": 24.4, "moonbeam": 46.5, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json index 63cc4a51204..ee64eccf9f9 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalanceOverrides.json @@ -3,10 +3,11 @@ "arthera": 0.5, "coti": 0.1, "deepbrainchain": 100, - "infinityvmmainnet": 0, "fluence": 2000, + "infinityvmmainnet": 0, + "miraclechain": 0.05, "nibiru": 10, - "ontology": 0, + "ontology": 10, "osmosis": 0, "plume": 0.05, "reactive": 0.1, diff --git a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json index a6d6a4677c5..b00e46738d0 100644 --- a/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json +++ b/typescript/infra/config/environments/mainnet3/balances/desiredRelayerBalances.json @@ -64,6 +64,7 @@ "ink": 0.186, "kaia": 250, "kroma": 0.0156, + "kyve": 2260, "linea": 0.3, "lisk": 0.0832, "lukso": 29.3, @@ -77,6 +78,7 @@ "metis": 1.83, "milkyway": 25, "mint": 0.0218, + "miraclechain": 0.05, "mode": 0.2, "molten": 195, "moonbeam": 372, @@ -84,7 +86,7 @@ "nero": 25, "neutron": 204, "nibiru": 10, - "ontology": 0, + "ontology": 10, "oortmainnet": 589, "opbnb": 0.0422, "optimism": 0.306, diff --git a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json index c7ec29c7e3c..bfee9313ac3 100644 --- a/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/highUrgencyRelayerBalance.json @@ -63,6 +63,7 @@ "ink": 0.0466, "kaia": 62.2, "kroma": 0.0039, + "kyve": 564, "linea": 0.075, "lisk": 0.0208, "lukso": 7.32, @@ -76,6 +77,7 @@ "metis": 0.458, "milkyway": 6.26, "mint": 0.00544, + "miraclechain": 0.0125, "mode": 0.0458, "molten": 48.8, "moonbeam": 93, @@ -83,6 +85,7 @@ "nero": 6.26, "neutron": 51, "nibiru": 2.5, + "ontology": 2.5, "oortmainnet": 147, "opbnb": 0.0106, "optimism": 0.0766, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json index 0d2ca0aefac..6c6a922dd2a 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyEngKeyFunderBalance.json @@ -63,6 +63,7 @@ "ink": 0.14, "kaia": 187, "kroma": 0.0117, + "kyve": 1690, "linea": 0.225, "lisk": 0.0624, "lukso": 22, @@ -76,6 +77,7 @@ "metis": 1.37, "milkyway": 18.8, "mint": 0.0163, + "miraclechain": 0.0375, "mode": 0.137, "molten": 146, "moonbeam": 279, @@ -83,6 +85,7 @@ "nero": 18.8, "neutron": 153, "nibiru": 7.5, + "ontology": 7.5, "oortmainnet": 442, "opbnb": 0.0317, "optimism": 0.23, diff --git a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json index 85fa481a86b..6b1128759fa 100644 --- a/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json +++ b/typescript/infra/config/environments/mainnet3/balances/lowUrgencyKeyFunderBalance.json @@ -63,6 +63,7 @@ "ink": 0.28, "kaia": 500, "kroma": 0.0234, + "kyve": 3380, "linea": 0.45, "lisk": 0.2, "lukso": 43.9, @@ -76,6 +77,7 @@ "metis": 2.75, "milkyway": 37.6, "mint": 0.0326, + "miraclechain": 0.075, "mode": 0.4, "molten": 293, "moonbeam": 700, @@ -83,6 +85,7 @@ "nero": 37.6, "neutron": 306, "nibiru": 15, + "ontology": 15, "oortmainnet": 883, "opbnb": 0.0634, "optimism": 0.46, diff --git a/typescript/infra/config/environments/mainnet3/core/verification.json b/typescript/infra/config/environments/mainnet3/core/verification.json index 500f249753e..e9497d404f3 100644 --- a/typescript/infra/config/environments/mainnet3/core/verification.json +++ b/typescript/infra/config/environments/mainnet3/core/verification.json @@ -8922,5 +8922,145 @@ "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", "isProxy": false } + ], + "miraclechain": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000016876", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "MerkleTreeHook", + "address": "0x64F077915B41479901a23Aa798B30701589C5063", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x0dc95Af5156fb0cC34a8c9BD646B748B9989A956", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000064f077915b41479901a23aa798b30701589c5063", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xF6CC9B10c607afB777380bF71F272E4D7037C3A9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x794Fe7970EE45945b0ad2667f99A5bBc9ddfB5d7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xF9aE87E9ACE51aa16AED25Ca38F17D258aECb73f", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD233433AeC23F8382DAd87D808F60557Ea35399f", + "constructorArguments": "000000000000000000000000f9ae87e9ace51aa16aed25ca38f17d258aecb73f0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xF9aE87E9ACE51aa16AED25Ca38F17D258aECb73f" + }, + { + "name": "ProtocolFee", + "address": "0x946E9f4540E032a9fAc038AE58187eFcad9DE952", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x5b1A451c85e5bcCd79A56eb638aBd9998fA215f9", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "ontology": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000003a", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "MerkleTreeHook", + "address": "0xb5668713E9BA8bC96f97D691663E70b54CE90b0A", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x3E12271EbD523d0886D0D51A4FF8D8e046CF2E1D", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000b5668713e9ba8bc96f97d691663e70b54ce90b0a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xC34aD1fB13a9eE1A0C0AB75aD9B9ceeA0690Cc74", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xc5068BB6803ADbe5600DE5189fe27A4dAcE31170", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x8E273260EAd8B72A085B19346A676d355740e875", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x11EF91d17c5ad3330DbCa709a8841743d3Af6819", + "constructorArguments": "0000000000000000000000008e273260ead8b72a085b19346a676d355740e8750000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x8E273260EAd8B72A085B19346A676d355740e875" + }, + { + "name": "ProtocolFee", + "address": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x4e128A1b613A9C9Ecf650FeE461c353612559fcf", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index a7ca3734d71..44d1d06c7a0 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -19,7 +19,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'cd6230c-20250502-094639', + tag: '918f4d4-20250513-080241', }, // We're currently using the same deployer/key funder key as mainnet2. // To minimize nonce clobbering we offset the key funder cron @@ -33,7 +33,7 @@ export const keyFunderConfig: KeyFunderConfig< [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], }, - chainsToSkip: ['ontology'], + chainsToSkip: [], // desired balance config, must be set for each chain desiredBalancePerChain: desiredRelayerBalancePerChain, // if not set, keyfunder defaults to 0 diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index 6d2d805240e..fbf8f600633 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -259,6 +259,10 @@ "amount": "0.001000252", "decimals": 9 }, + "kyve": { + "amount": "3.0", + "decimals": 1 + }, "linea": { "amount": "0.332044669", "decimals": 9 @@ -311,6 +315,10 @@ "amount": "0.001000252", "decimals": 9 }, + "miraclechain": { + "amount": "0.01", + "decimals": 9 + }, "mode": { "amount": "0.001000252", "decimals": 9 diff --git a/typescript/infra/config/environments/mainnet3/ism/verification.json b/typescript/infra/config/environments/mainnet3/ism/verification.json index 7c1aeb360b1..1946559b3fa 100644 --- a/typescript/infra/config/environments/mainnet3/ism/verification.json +++ b/typescript/infra/config/environments/mainnet3/ism/verification.json @@ -11186,5 +11186,177 @@ "constructorArguments": "", "isProxy": true } + ], + "miraclechain": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "ontology": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } ] } diff --git a/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json b/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json index 982c4a3320a..504727106f1 100644 --- a/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json +++ b/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json @@ -2709,5 +2709,47 @@ "isProxy": true, "expectedimplementation": "0x5CBD4c5f9CD55747285652f815Cc7b9A2Ef6c586" } + ], + "miraclechain": [ + { + "name": "InterchainAccountIsm", + "address": "0x23cc88CF424d48fDB05b4f0A8Ff6099aa4D56D8e", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xA697222b77cDe62A8C47E627d5A1c49A87018236", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x40Ca4155c0334F7e0F6d7F80536B59EF8831c9fb", + "constructorArguments": "000000000000000000000000a697222b77cde62a8c47e627d5a1c49a870182360000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023cc88cf424d48fdb05b4f0a8ff6099aa4d56d8e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xA697222b77cDe62A8C47E627d5A1c49A87018236" + } + ], + "ontology": [ + { + "name": "InterchainAccountIsm", + "address": "0x8BdD5bf519714515083801448A99F84882A8F61E", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xAAeb91195C025C9D35F8FF70e13049D8cD25703D", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x718f11e349374481Be8c8B7589eC4B4316ddDCc2", + "constructorArguments": "000000000000000000000000aaeb91195c025c9d35f8ff70e13049d8cd25703d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bdd5bf519714515083801448a99f84882a8f61e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xAAeb91195C025C9D35F8FF70e13049D8cD25703D" + } ] } diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index da1e4b73413..f9eda01e46d 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -87,6 +87,9 @@ export const chainOwners: ChainMap = { milkyway: { owner: 'TODO: configure milkyway owner', }, + kyve: { + owner: 'TODO: configure kyve owner', + }, soon: { // Squads vault owner: 'E3QPSn2Upk2EiidSsUqSQpRCc7BhzWZCKpVncemz3p62', diff --git a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts index f382f890173..b22619bab8e 100644 --- a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts +++ b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts @@ -68,6 +68,7 @@ export const mainnet3SupportedChainNames = [ 'injective', 'kaia', 'kroma', + 'kyve', 'linea', 'lisk', 'lukso', @@ -81,6 +82,7 @@ export const mainnet3SupportedChainNames = [ 'metis', 'milkyway', 'mint', + 'miraclechain', 'mode', 'molten', 'moonbeam', diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index ffebf8bea9e..0a8609f182f 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -64,6 +64,7 @@ "injective": "9.14", "kaia": "0.110084", "kroma": "1796.24", + "kyve": "0.0110901", "linea": "1796.24", "lisk": "1796.24", "lukso": "0.889957", @@ -77,6 +78,7 @@ "metis": "14.33", "milkyway": "0.127219", "mint": "1796.24", + "miraclechain": "0.02341571", "mode": "1796.24", "molten": "0.146111", "moonbeam": "0.074762", diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index 6b373a7a28e..820d2956b4a 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -1776,5 +1776,26 @@ export const validatorChainConfig = ( 'svmbnb', ), }, + + miraclechain: { + interval: 5, + reorgPeriod: getReorgPeriod('miraclechain'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x8fc655174e99194399822ce2d3a0f71d9fc2de7b'], + }, + 'miraclechain', + ), + }, + kyve: { + interval: 5, + reorgPeriod: getReorgPeriod('kyve'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x8576ddc0cd96325f85528e53f333357afb8bf044'], + }, + 'kyve', + ), + }, }; }; diff --git a/typescript/infra/scripts/validators/announce-validators.ts b/typescript/infra/scripts/validators/announce-validators.ts index 5ed496ad1be..5d63fa29e9e 100644 --- a/typescript/infra/scripts/validators/announce-validators.ts +++ b/typescript/infra/scripts/validators/announce-validators.ts @@ -73,8 +73,7 @@ async function main() { Object.entries(agentConfig.validators.chains) .filter(([validatorChain, _]) => { // Ensure we skip lumia, as we don't have the addresses in registry. - // temporarily skip ontology as we do not have funds, will undo when we deploy - if (validatorChain === 'lumia' || validatorChain === 'ontology') { + if (validatorChain === 'lumia') { return false; } diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index 85df79c4f05..a55e05edf32 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -24,8 +24,6 @@ import { DeployEnvironment } from './environment.js'; // Used by scripts like check-owner-ica.ts to exclude chains that are temporarily // unsupported (e.g. zksync, zeronetwork) or have known issues (e.g. lumia). export const chainsToSkip: ChainName[] = [ - 'ontology', - // TODO: remove once zksync PR is merged into main // mainnets 'zksync', diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index a46b0e51444..5e5c4cca4c5 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -1256,6 +1256,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + kyve: { + threshold: 1, + validators: [ + { + address: '0x8576ddc0cd96325f85528e53f333357afb8bf044', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + kyvetestnet: { threshold: 1, validators: [ @@ -1462,6 +1472,16 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + miraclechain: { + threshold: 1, + validators: [ + { + address: '0x8fc655174e99194399822ce2d3a0f71d9fc2de7b', + alias: AW_VALIDATOR_ALIAS, + }, + ], + }, + milkyway: { threshold: 1, validators: [ From 0fffbfb145294349b2008ca754ee1a416cb63089 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 14 May 2025 13:47:01 +0100 Subject: [PATCH 177/223] feat: update svm gas oracles (#6218) ### Description feat: update svm gas oracles ### Drive-by changes interconnect svmbnb<>soon<>solana<>bsc ### Related issues ### Backward compatibility ### Testing --- .../mainnet3/gas-oracle-configs.json | 128 ++++++++++-------- .../sealevel-helpers/print-gas-oracles.ts | 5 +- 2 files changed, 76 insertions(+), 57 deletions(-) diff --git a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json index 1a462112e2e..89f8ea9c120 100644 --- a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json +++ b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json @@ -2,23 +2,23 @@ "solanamainnet": { "rivalz": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 }, "everclear": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 }, "infinityvmmainnet": { "oracleConfig": { - "tokenExchangeRate": "91246426181641219", + "tokenExchangeRate": "104137739516800888", "gasPrice": "0", "tokenDecimals": 18 }, @@ -26,12 +26,20 @@ }, "sophon": { "oracleConfig": { - "tokenExchangeRate": "91246426181641219", - "gasPrice": "2261877890384", + "tokenExchangeRate": "104137739516800888", + "gasPrice": "2159824777033", "tokenDecimals": 18 }, "overhead": 333774 }, + "bsc": { + "oracleConfig": { + "tokenExchangeRate": "62110871980005554012", + "gasPrice": "2576829126", + "tokenDecimals": 18 + }, + "overhead": 166887 + }, "svmbnb": { "oracleConfig": { "tokenExchangeRate": "6211087198000555", @@ -40,146 +48,146 @@ }, "overhead": 600000 }, + "soon": { + "oracleConfig": { + "tokenExchangeRate": "18705637322965842", + "gasPrice": "2855", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, "artela": { "oracleConfig": { - "tokenExchangeRate": "1026217531480017", - "gasPrice": "54661512337856", + "tokenExchangeRate": "152446195501249", + "gasPrice": "419949153702354", "tokenDecimals": 18 }, "overhead": 166887 }, "base": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "855619625", "tokenDecimals": 18 }, "overhead": 166887 }, "arbitrum": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "585851107", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "855619625", "tokenDecimals": 18 }, "overhead": 166887 }, "avalanche": { "oracleConfig": { - "tokenExchangeRate": "2101405194963197274", - "gasPrice": "26693853424", + "tokenExchangeRate": "2049430713690641488", + "gasPrice": "31237772694", "tokenDecimals": 18 }, "overhead": 166887 }, "flowmainnet": { "oracleConfig": { - "tokenExchangeRate": "44627167102621814", - "gasPrice": "1256960858155", + "tokenExchangeRate": "38209490419327964", + "gasPrice": "1675490829198", "tokenDecimals": 18 }, "overhead": 166887 }, "form": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 }, "worldchain": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 }, "optimism": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "855619625", "tokenDecimals": 18 }, "overhead": 166887 }, "trumpchain": { "oracleConfig": { - "tokenExchangeRate": "1470892390048056451", - "gasPrice": "38136441958", + "tokenExchangeRate": "1114273812829769508", + "gasPrice": "57454146413", "tokenDecimals": 18 }, "overhead": 166887 }, "ethereum": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "1000000000", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "1867802855", "tokenDecimals": 18 }, "overhead": 166887 }, "eclipsemainnet": { "oracleConfig": { - "tokenExchangeRate": "23937222458787030", - "gasPrice": "1955", + "tokenExchangeRate": "18705637322965842", + "gasPrice": "2855", "tokenDecimals": 9 }, "overhead": 600000 }, "superseed": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 }, "ink": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 }, - "soon": { - "oracleConfig": { - "tokenExchangeRate": "23937222458787030", - "gasPrice": "1955", - "tokenDecimals": 9 - }, - "overhead": 600000 - }, "sonicsvm": { "oracleConfig": { "tokenExchangeRate": "1500000000000000", - "gasPrice": "31196", + "gasPrice": "35603", "tokenDecimals": 9 }, "overhead": 600000 }, "treasure": { "oracleConfig": { - "tokenExchangeRate": "21069803516028955", - "gasPrice": "1504591348228", + "tokenExchangeRate": "16820119411274646", + "gasPrice": "2151008858941", "tokenDecimals": 18 }, "overhead": 333774 }, "hyperevm": { "oracleConfig": { - "tokenExchangeRate": "2108704909057728572", - "gasPrice": "26601447181", + "tokenExchangeRate": "2073382393779505692", + "gasPrice": "30876914446", "tokenDecimals": 18 }, "overhead": 166887 }, "mint": { "oracleConfig": { - "tokenExchangeRate": "239372224587870308412", - "gasPrice": "234340482", + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "342247900", "tokenDecimals": 18 }, "overhead": 166887 @@ -189,23 +197,23 @@ "ethereum": { "oracleConfig": { "tokenExchangeRate": "15000000000000000000", - "gasPrice": "1000000000", + "gasPrice": "1867802855", "tokenDecimals": 18 }, "overhead": 166460 }, "solanamainnet": { "oracleConfig": { - "tokenExchangeRate": "93995867894608", - "gasPrice": "74869", + "tokenExchangeRate": "120284594486260", + "gasPrice": "85447", "tokenDecimals": 9 }, "overhead": 600000 }, "stride": { "oracleConfig": { - "tokenExchangeRate": "115107915040", - "gasPrice": "10190", + "tokenExchangeRate": "219389112813", + "gasPrice": "7808", "tokenDecimals": 6 }, "overhead": 600000 @@ -222,18 +230,26 @@ }, "solanamainnet": { "oracleConfig": { - "tokenExchangeRate": "93995867894608", - "gasPrice": "74869", + "tokenExchangeRate": "120284594486260", + "gasPrice": "85447", "tokenDecimals": 9 }, "overhead": 600000 + }, + "bsc": { + "oracleConfig": { + "tokenExchangeRate": "4980654032868658976", + "gasPrice": "2664685207", + "tokenDecimals": 18 + }, + "overhead": 159736 } }, "sonicsvm": { "solanamainnet": { "oracleConfig": { "tokenExchangeRate": "1500000000000000", - "gasPrice": "74869", + "gasPrice": "85447", "tokenDecimals": 9 }, "overhead": 600000 diff --git a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts index c95d355abe8..8c8342c16ae 100644 --- a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts +++ b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts @@ -103,10 +103,13 @@ function getChainConnections( ['solanamainnet', 'everclear'], ['solanamainnet', 'infinityvmmainnet'], ['solanamainnet', 'sophon'], - // for svmBNB + // for svmBNB routes solana<>bsc<>svmbnb<>soon + ['solanamainnet', 'bsc'], ['svmbnb', 'solanamainnet'], ['svmbnb', 'bsc'], ['svmbnb', 'soon'], + ['soon', 'solanamainnet'], + ['soon', 'bsc'], // All warp routes ...Object.values(WarpRouteIds).map(getWarpChains), ]; From abf32cdafe9db9d18ce93ff1a2776e2fd5974026 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 14 May 2025 10:50:19 -0400 Subject: [PATCH 178/223] feat: reduce ccip read dos vector (#6207) ### Description - add a 30s timeout on CCIP read requests - add a regex to prevent localhost, or local IP address requests ### Related issues - https://linear.app/hyperlane-xyz/issue/ENG-1614/relayer-ccip-read-dos-vector-potential-security-concerns ### Backward compatibility Yes ### Testing Add unit test to ensure regex is correct --- .../relayer/src/msg/metadata/ccip_read/mod.rs | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs b/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs index db0c90dc810..29f161d735b 100644 --- a/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs +++ b/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs @@ -1,12 +1,15 @@ #![allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + +use std::time::Duration; + use async_trait::async_trait; use cache_types::SerializedOffchainLookup; use derive_more::Deref; use derive_new::new; use ethers::{abi::AbiDecode, core::utils::hex::decode as hex_decode}; use hyperlane_base::cache::FunctionCallCache; -use regex::Regex; -use reqwest::Client; +use regex::{Regex, RegexSet, RegexSetBuilder}; +use reqwest::{header::CONTENT_TYPE, Client}; use serde::{Deserialize, Serialize}; use serde_json::json; use tracing::{info, instrument, warn}; @@ -24,6 +27,8 @@ use super::{ mod cache_types; +pub const DEFAULT_TIMEOUT: u64 = 30; + #[derive(Serialize, Deserialize)] struct OffchainResponse { data: String, @@ -127,7 +132,14 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { let info = self.call_get_offchain_verify_info(ism, message).await?; + let ccip_url_regex = create_ccip_url_regex(); + for url in info.urls.iter() { + if ccip_url_regex.is_match(url) { + tracing::warn!(?ism_address, url, "Suspicious CCIP read url"); + continue; + } + // Need to explicitly convert the sender H160 the hex because the `ToString` implementation // for `H160` truncates the output. (e.g. `0xc66a…7b6f` instead of returning // the full address) @@ -143,7 +155,8 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { }); Client::new() .post(interpolated_url) - .header("Content-Type", "application/json") + .header(CONTENT_TYPE, "application/json") + .timeout(Duration::from_secs(DEFAULT_TIMEOUT)) .json(&body) .send() .await @@ -173,3 +186,99 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { Err(MetadataBuildError::CouldNotFetch) } } + +fn create_ccip_url_regex() -> RegexSet { + RegexSetBuilder::new([ + r#"^(https?:\/\/)localhost"#, + r#"^(https?:\/\/)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"#, + r#"localhost"#, + r#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"#, + ]) + .case_insensitive(true) + .build() + .unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ccip_regex_filter() { + let set = create_ccip_url_regex(); + + let urls = [ + "localhost", + "localhost:80", + "localhost:443", + "0.0.0.0", + "0.0.0.0:80", + "0.0.0.0:443", + "127.0.0.1", + "127.0.0.1:80", + "127.0.0.1:443", + "http://localhost", + "http://localhost:80", + "http://localhost:443", + "http://0.0.0.0", + "http://0.0.0.0:80", + "http://0.0.0.0:443", + "http://127.0.0.1", + "http://127.0.0.1:80", + "http://127.0.0.1:443", + "https://localhost", + "https://localhost:80", + "https://localhost:443", + "https://0.0.0.0", + "https://0.0.0.0:80", + "https://0.0.0.0:443", + "https://127.0.0.1", + "https://127.0.0.1:80", + "https://127.0.0.1:443", + "https://hyperlane.xyz", + "https://docs.hyperlane.xyz/", + "http://docs.hyperlane.xyz/", + "http://docs.hyperlane.xyz:443", + "http://localhost.com", + "hyperlane.xyz", + "docs.hyperlane.xyz/", + "docs.hyperlane.xyz/", + ]; + + let filtered: Vec<_> = urls.into_iter().filter(|s| set.is_match(s)).collect(); + + let expected = [ + "localhost", + "localhost:80", + "localhost:443", + "0.0.0.0", + "0.0.0.0:80", + "0.0.0.0:443", + "127.0.0.1", + "127.0.0.1:80", + "127.0.0.1:443", + "http://localhost", + "http://localhost:80", + "http://localhost:443", + "http://0.0.0.0", + "http://0.0.0.0:80", + "http://0.0.0.0:443", + "http://127.0.0.1", + "http://127.0.0.1:80", + "http://127.0.0.1:443", + "https://localhost", + "https://localhost:80", + "https://localhost:443", + "https://0.0.0.0", + "https://0.0.0.0:80", + "https://0.0.0.0:443", + "https://127.0.0.1", + "https://127.0.0.1:80", + "https://127.0.0.1:443", + ]; + + for (actual, expected) in filtered.into_iter().zip(expected.into_iter()) { + assert_eq!(actual, expected); + } + } +} From d7c73431944b6d32dec9128e1f319882d345335d Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 14 May 2025 17:01:26 +0100 Subject: [PATCH 179/223] chore: resume relayer with reorg failsafe; trial new submitter on linea (#6219) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 56a8934b2bd..f6f965936ad 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -855,7 +855,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd3f8da9-20250509-160937', + tag: '0fffbfb-20250514-130038', }, blacklist, gasPaymentEnforcement: gasPaymentEnforcement, @@ -895,7 +895,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'abdf139-20250514-081104', + tag: '0ad4122-20250514-154514', }, blacklist, // We're temporarily (ab)using the RC relayer as a way to increase @@ -933,7 +933,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd3f8da9-20250509-160937', + tag: '0fffbfb-20250514-130038', }, blacklist, gasPaymentEnforcement, From 6a70b8dd028bf30d75812be5994568b5fa63358e Mon Sep 17 00:00:00 2001 From: Jason Guo <33064781+Xaroz@users.noreply.github.com> Date: Wed, 14 May 2025 12:19:55 -0400 Subject: [PATCH 180/223] chore: move executeDeploy logic from CLI to SDK (#6222) ### Description - Move logic from `executeDeploy` that was in the `CLI` to the `SDK`, now this function is named `executeWarpDeploy` - Instead of using the CLI context, the function will receive the parameters ### Drive-by changes No ### Related issues fix #6138 ### Backward compatibility Yes ### Testing Manual local test --- .changeset/orange-women-show.md | 6 + typescript/cli/src/deploy/warp.ts | 191 +------------------------- typescript/sdk/src/deploy/warp.ts | 215 ++++++++++++++++++++++++++++++ typescript/sdk/src/index.ts | 1 + 4 files changed, 228 insertions(+), 185 deletions(-) create mode 100644 .changeset/orange-women-show.md create mode 100644 typescript/sdk/src/deploy/warp.ts diff --git a/.changeset/orange-women-show.md b/.changeset/orange-women-show.md new file mode 100644 index 00000000000..21243e13531 --- /dev/null +++ b/.changeset/orange-women-show.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +Move executeDeploy logic from CLI to SDK diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 4878a1244c0..bdbe53cce31 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -2,7 +2,6 @@ import { confirm } from '@inquirer/prompts'; import { groupBy } from 'lodash-es'; import { stringify as yamlStringify } from 'yaml'; -import { ProxyAdmin__factory } from '@hyperlane-xyz/core'; import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; import { AddWarpRouteOptions, ChainAddresses } from '@hyperlane-xyz/registry'; import { @@ -15,18 +14,11 @@ import { ChainSubmissionStrategySchema, ContractVerifier, EvmERC20WarpModule, - EvmHookModule, - EvmIsmModule, ExplorerLicenseType, - HookConfig, HypERC20Deployer, HypERC20Factories, - HypERC721Deployer, HypERC721Factories, - HypTokenRouterConfig, HyperlaneContractsMap, - HyperlaneProxyFactoryDeployer, - IsmConfig, IsmType, MultiProvider, MultisigIsmConfig, @@ -45,6 +37,7 @@ import { WarpRouteDeployConfigSchema, attachContractsMap, connectContractsMap, + executeWarpDeploy, expandWarpDeployConfig, extractIsmAndHookFactoryAddresses, getRouterAddressesFromWarpCoreConfig, @@ -56,7 +49,6 @@ import { splitWarpCoreAndExtendedConfigs, } from '@hyperlane-xyz/sdk'; import { - Address, ProtocolType, assert, objMap, @@ -159,41 +151,22 @@ async function executeDeploy( const { warpDeployConfig, - context: { multiProvider, isDryRun, dryRunChain }, + context: { multiProvider, isDryRun, dryRunChain, registry }, } = params; - const deployer = warpDeployConfig.isNft - ? new HypERC721Deployer(multiProvider) - : new HypERC20Deployer(multiProvider); // TODO: replace with EvmERC20WarpModule - const config: WarpRouteDeployConfigMailboxRequired = isDryRun && dryRunChain ? { [dryRunChain]: warpDeployConfig[dryRunChain] } : warpDeployConfig; + const registryAddresses = await registry.getAddresses(); - const contractVerifier = new ContractVerifier( + const deployedContracts = await executeWarpDeploy( multiProvider, - apiKeys, - coreBuildArtifact, - ExplorerLicenseType.MIT, - ); - - const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer( - multiProvider, - contractVerifier, - ); - - // For each chain in WarpRouteConfig, deploy each Ism Factory, if it's not in the registry - // Then return a modified config with the ism and/or hook address as a string - const modifiedConfig = await resolveWarpIsmAndHook( config, - params.context, - ismFactoryDeployer, - contractVerifier, + registryAddresses, + apiKeys, ); - const deployedContracts = await deployer.deploy(modifiedConfig); - logGreen('✅ Warp contract deployments complete'); return deployedContracts; } @@ -210,158 +183,6 @@ async function writeDeploymentArtifacts( log(indentYamlOrJson(yamlStringify(warpCoreConfig, null, 2), 4)); } -async function resolveWarpIsmAndHook( - warpConfig: WarpRouteDeployConfigMailboxRequired, - context: WriteCommandContext, - ismFactoryDeployer: HyperlaneProxyFactoryDeployer, - contractVerifier?: ContractVerifier, -): Promise { - return promiseObjAll( - objMap(warpConfig, async (chain, config) => { - const registryAddresses = await context.registry.getAddresses(); - const ccipContractCache = new CCIPContractCache(registryAddresses); - const chainAddresses = registryAddresses[chain]; - - if (!chainAddresses) { - throw `Registry factory addresses not found for ${chain}.`; - } - - config.interchainSecurityModule = await createWarpIsm({ - ccipContractCache, - chain, - chainAddresses, - context, - contractVerifier, - ismFactoryDeployer, - warpConfig: config, - }); // TODO write test - - config.hook = await createWarpHook({ - ccipContractCache, - chain, - chainAddresses, - context, - contractVerifier, - ismFactoryDeployer, - warpConfig: config, - }); - return config; - }), - ); -} - -/** - * Deploys the Warp ISM for a given config - * - * @returns The deployed ism address - */ -async function createWarpIsm({ - ccipContractCache, - chain, - chainAddresses, - context, - contractVerifier, - warpConfig, -}: { - ccipContractCache: CCIPContractCache; - chain: string; - chainAddresses: Record; - context: WriteCommandContext; - contractVerifier?: ContractVerifier; - warpConfig: HypTokenRouterConfig; - ismFactoryDeployer: HyperlaneProxyFactoryDeployer; -}): Promise { - const { interchainSecurityModule } = warpConfig; - if ( - !interchainSecurityModule || - typeof interchainSecurityModule === 'string' - ) { - logGray( - `Config Ism is ${ - !interchainSecurityModule ? 'empty' : interchainSecurityModule - }, skipping deployment.`, - ); - return interchainSecurityModule; - } - - logBlue(`Loading registry factory addresses for ${chain}...`); - - logGray( - `Creating ${interchainSecurityModule.type} ISM for token on ${chain} chain...`, - ); - - logGreen( - `Finished creating ${interchainSecurityModule.type} ISM for token on ${chain} chain.`, - ); - - const evmIsmModule = await EvmIsmModule.create({ - chain, - mailbox: chainAddresses.mailbox, - multiProvider: context.multiProvider, - proxyFactoryFactories: extractIsmAndHookFactoryAddresses(chainAddresses), - config: interchainSecurityModule, - ccipContractCache, - contractVerifier, - }); - const { deployedIsm } = evmIsmModule.serialize(); - return deployedIsm; -} - -async function createWarpHook({ - ccipContractCache, - chain, - chainAddresses, - context, - contractVerifier, - warpConfig, -}: { - ccipContractCache: CCIPContractCache; - chain: string; - chainAddresses: Record; - context: WriteCommandContext; - contractVerifier?: ContractVerifier; - warpConfig: HypTokenRouterConfig; - ismFactoryDeployer: HyperlaneProxyFactoryDeployer; -}): Promise { - const { hook } = warpConfig; - - if (!hook || typeof hook === 'string') { - logGray(`Config Hook is ${!hook ? 'empty' : hook}, skipping deployment.`); - return hook; - } - - logBlue(`Loading registry factory addresses for ${chain}...`); - - logGray(`Creating ${hook.type} Hook for token on ${chain} chain...`); - - // If config.proxyadmin.address exists, then use that. otherwise deploy a new proxyAdmin - const proxyAdminAddress: Address = - warpConfig.proxyAdmin?.address ?? - ( - await context.multiProvider.handleDeploy( - chain, - new ProxyAdmin__factory(), - [], - ) - ).address; - - const evmHookModule = await EvmHookModule.create({ - chain, - multiProvider: context.multiProvider, - coreAddresses: { - mailbox: chainAddresses.mailbox, - proxyAdmin: proxyAdminAddress, - }, - config: hook, - ccipContractCache, - contractVerifier, - proxyFactoryFactories: extractIsmAndHookFactoryAddresses(chainAddresses), - }); - logGreen(`Finished creating ${hook.type} Hook for token on ${chain} chain.`); - const { deployedHook } = evmHookModule.serialize(); - return deployedHook; -} - async function getWarpCoreConfig( params: DeployParams, contracts: HyperlaneContractsMap, diff --git a/typescript/sdk/src/deploy/warp.ts b/typescript/sdk/src/deploy/warp.ts new file mode 100644 index 00000000000..badc4f3a741 --- /dev/null +++ b/typescript/sdk/src/deploy/warp.ts @@ -0,0 +1,215 @@ +import { ProxyAdmin__factory } from '@hyperlane-xyz/core'; +import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; +import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { + Address, + objMap, + promiseObjAll, + rootLogger, +} from '@hyperlane-xyz/utils'; + +import { CCIPContractCache } from '../ccip/utils.js'; +import { HyperlaneContractsMap } from '../contracts/types.js'; +import { EvmHookModule } from '../hook/EvmHookModule.js'; +import { HookConfig } from '../hook/types.js'; +import { EvmIsmModule } from '../ism/EvmIsmModule.js'; +import { IsmConfig } from '../ism/types.js'; +import { MultiProvider } from '../providers/MultiProvider.js'; +import { HypERC20Factories, HypERC721Factories } from '../token/contracts.js'; +import { HypERC20Deployer, HypERC721Deployer } from '../token/deploy.js'; +import { + HypTokenRouterConfig, + WarpRouteDeployConfigMailboxRequired, +} from '../token/types.js'; +import { ChainMap } from '../types.js'; +import { extractIsmAndHookFactoryAddresses } from '../utils/ism.js'; + +import { HyperlaneProxyFactoryDeployer } from './HyperlaneProxyFactoryDeployer.js'; +import { ContractVerifier } from './verify/ContractVerifier.js'; +import { ExplorerLicenseType } from './verify/types.js'; + +export async function executeWarpDeploy( + multiProvider: MultiProvider, + warpDeployConfig: WarpRouteDeployConfigMailboxRequired, + registryAddresses: ChainMap, + apiKeys: ChainMap, +): Promise> { + const deployer = warpDeployConfig.isNft + ? new HypERC721Deployer(multiProvider) + : new HypERC20Deployer(multiProvider); // TODO: replace with EvmERC20WarpModule + + const contractVerifier = new ContractVerifier( + multiProvider, + apiKeys, + coreBuildArtifact, + ExplorerLicenseType.MIT, + ); + + const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer( + multiProvider, + contractVerifier, + ); + + // For each chain in WarpRouteConfig, deploy each Ism Factory, if it's not in the registry + // Then return a modified config with the ism and/or hook address as a string + const modifiedConfig = await resolveWarpIsmAndHook( + warpDeployConfig, + multiProvider, + registryAddresses, + ismFactoryDeployer, + contractVerifier, + ); + + const deployedContracts = await deployer.deploy(modifiedConfig); + + return deployedContracts; +} + +async function resolveWarpIsmAndHook( + warpConfig: WarpRouteDeployConfigMailboxRequired, + multiProvider: MultiProvider, + registryAddresses: ChainMap, + ismFactoryDeployer: HyperlaneProxyFactoryDeployer, + contractVerifier?: ContractVerifier, +): Promise { + return promiseObjAll( + objMap(warpConfig, async (chain, config) => { + const ccipContractCache = new CCIPContractCache(registryAddresses); + const chainAddresses = registryAddresses[chain]; + + if (!chainAddresses) { + throw `Registry factory addresses not found for ${chain}.`; + } + + config.interchainSecurityModule = await createWarpIsm({ + ccipContractCache, + chain, + chainAddresses, + multiProvider, + contractVerifier, + ismFactoryDeployer, + warpConfig: config, + }); // TODO write test + + config.hook = await createWarpHook({ + ccipContractCache, + chain, + chainAddresses, + multiProvider, + contractVerifier, + ismFactoryDeployer, + warpConfig: config, + }); + return config; + }), + ); +} + +/** + * Deploys the Warp ISM for a given config + * + * @returns The deployed ism address + */ +async function createWarpIsm({ + ccipContractCache, + chain, + chainAddresses, + multiProvider, + contractVerifier, + warpConfig, +}: { + ccipContractCache: CCIPContractCache; + chain: string; + chainAddresses: Record; + multiProvider: MultiProvider; + contractVerifier?: ContractVerifier; + warpConfig: HypTokenRouterConfig; + ismFactoryDeployer: HyperlaneProxyFactoryDeployer; +}): Promise { + const { interchainSecurityModule } = warpConfig; + if ( + !interchainSecurityModule || + typeof interchainSecurityModule === 'string' + ) { + rootLogger.info( + `Config Ism is ${ + !interchainSecurityModule ? 'empty' : interchainSecurityModule + }, skipping deployment.`, + ); + return interchainSecurityModule; + } + + rootLogger.info(`Loading registry factory addresses for ${chain}...`); + rootLogger.info( + `Creating ${interchainSecurityModule.type} ISM for token on ${chain} chain...`, + ); + rootLogger.info( + `Finished creating ${interchainSecurityModule.type} ISM for token on ${chain} chain.`, + ); + + const evmIsmModule = await EvmIsmModule.create({ + chain, + mailbox: chainAddresses.mailbox, + multiProvider, + proxyFactoryFactories: extractIsmAndHookFactoryAddresses(chainAddresses), + config: interchainSecurityModule, + ccipContractCache, + contractVerifier, + }); + const { deployedIsm } = evmIsmModule.serialize(); + return deployedIsm; +} + +async function createWarpHook({ + ccipContractCache, + chain, + chainAddresses, + multiProvider, + contractVerifier, + warpConfig, +}: { + ccipContractCache: CCIPContractCache; + chain: string; + chainAddresses: Record; + multiProvider: MultiProvider; + contractVerifier?: ContractVerifier; + warpConfig: HypTokenRouterConfig; + ismFactoryDeployer: HyperlaneProxyFactoryDeployer; +}): Promise { + const { hook } = warpConfig; + + if (!hook || typeof hook === 'string') { + rootLogger.info( + `Config Hook is ${!hook ? 'empty' : hook}, skipping deployment.`, + ); + return hook; + } + + rootLogger.info(`Loading registry factory addresses for ${chain}...`); + rootLogger.info(`Creating ${hook.type} Hook for token on ${chain} chain...`); + + // If config.proxyadmin.address exists, then use that. otherwise deploy a new proxyAdmin + const proxyAdminAddress: Address = + warpConfig.proxyAdmin?.address ?? + (await multiProvider.handleDeploy(chain, new ProxyAdmin__factory(), [])) + .address; + + const evmHookModule = await EvmHookModule.create({ + chain, + multiProvider, + coreAddresses: { + mailbox: chainAddresses.mailbox, + proxyAdmin: proxyAdminAddress, + }, + config: hook, + ccipContractCache, + contractVerifier, + proxyFactoryFactories: extractIsmAndHookFactoryAddresses(chainAddresses), + }); + rootLogger.info( + `Finished creating ${hook.type} Hook for token on ${chain} chain.`, + ); + + const { deployedHook } = evmHookModule.serialize(); + return deployedHook; +} diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index e116956ff86..d9a029b2347 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -120,6 +120,7 @@ export { VerificationInput, } from './deploy/verify/types.js'; export * as verificationUtils from './deploy/verify/utils.js'; +export { executeWarpDeploy } from './deploy/warp.js'; export { SealevelOverheadIgpAdapter, SealevelIgpAdapter, From e25af4b9ac10e5d03ce455cfc3fa2c3fb85ae8c2 Mon Sep 17 00:00:00 2001 From: mshojaei-txfusion <138107084+mshojaei-txfusion@users.noreply.github.com> Date: Wed, 14 May 2025 20:24:08 +0330 Subject: [PATCH 181/223] fix: warp route id lookup source (#6223) ### Description Fixed warp route ID lookup to use deployment configs instead of warp routes when prompting users for selection on warp deployment. ### Drive-by changes None ### Related issues N/A ### Backward compatibility Yes - This change only affects the user selection prompt behavior and doesn't impact infrastructure compatibility. ### Testing Manual testing of the warp route ID selection workflow --- .changeset/giant-animals-boil.md | 5 +++++ typescript/cli/src/config/warp.ts | 1 + typescript/cli/src/utils/warp.ts | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .changeset/giant-animals-boil.md diff --git a/.changeset/giant-animals-boil.md b/.changeset/giant-animals-boil.md new file mode 100644 index 00000000000..bc4af885166 --- /dev/null +++ b/.changeset/giant-animals-boil.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Fixed warp route ID lookup to use deployment configs instead of warp routes when prompting users for selection on warp deployment diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 750ef93458a..13fb3bdfa5e 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -388,6 +388,7 @@ export async function getWarpRouteDeployConfig({ warpRouteId: providedWarpRouteId, context, symbol, + promptByDeploymentConfigs: true, }); warpDeployConfig = await readWarpRouteDeployConfig({ diff --git a/typescript/cli/src/utils/warp.ts b/typescript/cli/src/utils/warp.ts index f5023e38110..796728ff3ea 100644 --- a/typescript/cli/src/utils/warp.ts +++ b/typescript/cli/src/utils/warp.ts @@ -57,16 +57,20 @@ export async function useProvidedWarpRouteIdOrPrompt({ context, warpRouteId, symbol, + promptByDeploymentConfigs, }: { context: CommandContext; warpRouteId?: string; symbol?: string; + promptByDeploymentConfigs?: boolean; }): Promise { if (warpRouteId) return warpRouteId; assert(!context.skipConfirmation, 'Warp route ID is required'); const { ids: routeIds } = filterWarpRoutesIds( - (await context.registry.listRegistryContent()).deployments.warpRoutes, + (await context.registry.listRegistryContent()).deployments[ + promptByDeploymentConfigs ? 'warpDeployConfig' : 'warpRoutes' + ], symbol ? { symbol } : undefined, ); From 6cadc4b87d29277c1b28dc474ab9c5c3eba5ca38 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 17:48:03 +0000 Subject: [PATCH 182/223] Version Packages (#6116) 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 publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). 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@12.6.0 ### Minor Changes - 2ae0f72: Add contract verification to CLI Warp Checker - 672d6d1: adds logic to expand an ism or hook config if it is partially defined in the input file for the warp checker - aec8961: Updates the `warp check` command output to only show fields that have diffs with the expected config - e25af4b: Fixed warp route ID lookup to use deployment configs instead of warp routes when prompting users for selection on warp deployment - ce0b173: Refactored warp route configuration functions to use object parameters instead of positional parameters for improved clarity and flexibility. - 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. - 6a70b8d: Move executeDeploy logic from CLI to SDK - 248d2e1: Enables the CLI to warp check routes that include non EVM routes - de7c6ae: Added registry based warp configs support - e381a8d: Update Registry version to 15.0.0 ### Patch Changes - 166f849: Remove outputting isNft in warp init config - f6ed6ad: Fixed proxy admin ownership transfer logic when the config is not specified in the input file ## @hyperlane-xyz/cosmos-sdk@12.6.0 ### Minor Changes - 76f0eba: Add Cosmos Native ISM Reader & Module ### Patch Changes - @hyperlane-xyz/cosmos-types@12.6.0 ## @hyperlane-xyz/helloworld@12.6.0 ### Minor Changes - 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. - e381a8d: Update Registry version to 15.0.0 ### Patch Changes - Updated dependencies [76f0eba] - Updated dependencies [2ae0f72] - Updated dependencies [672d6d1] - Updated dependencies [1f370e6] - Updated dependencies [7d56f2c] - Updated dependencies [6a70b8d] - Updated dependencies [d182d7d] - Updated dependencies [248d2e1] - Updated dependencies [e2a4727] - Updated dependencies [b360802] - Updated dependencies [f6ed6ad] - Updated dependencies [31ee1c6] - Updated dependencies [a36d5c1] - @hyperlane-xyz/sdk@12.6.0 - @hyperlane-xyz/core@7.1.5 ## @hyperlane-xyz/sdk@12.6.0 ### Minor Changes - 76f0eba: Add Cosmos Native ISM Reader & Module - 2ae0f72: Add contract verification to CLI Warp Checker - 672d6d1: adds logic to expand an ism or hook config if it is partially defined in the input file for the warp checker - 1f370e6: Add HookModule.resolveHookAddresses() to resolve all HookConfig addresses - 6a70b8d: Move executeDeploy logic from CLI to SDK - d182d7d: Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function - 248d2e1: Enables the CLI to warp check routes that include non EVM routes - e2a4727: Deploy to new chains: ontology, miraclechain, kyve. - b360802: Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas - 31ee1c6: Adds fiatCollateral token on chain config derivation logic as it was incorrectly inferred as collateral - a36d5c1: add cosmos native hook module & reader ### Patch Changes - 7d56f2c: Pass remote chain to adjustForPrecisionLoss for better error logging - f6ed6ad: Fixed proxy admin ownership transfer logic when the config is not specified in the input file - Updated dependencies [76f0eba] - Updated dependencies [d182d7d] - Updated dependencies [b360802] - @hyperlane-xyz/cosmos-sdk@12.6.0 - @hyperlane-xyz/utils@12.6.0 - @hyperlane-xyz/core@7.1.5 ## @hyperlane-xyz/utils@12.6.0 ### Minor Changes - d182d7d: Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function - b360802: Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas ## @hyperlane-xyz/widgets@12.6.0 ### Minor Changes - 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. - e381a8d: Update Registry version to 15.0.0 ### Patch Changes - Updated dependencies [76f0eba] - Updated dependencies [2ae0f72] - Updated dependencies [672d6d1] - Updated dependencies [1f370e6] - Updated dependencies [7d56f2c] - Updated dependencies [6a70b8d] - Updated dependencies [d182d7d] - Updated dependencies [248d2e1] - Updated dependencies [e2a4727] - Updated dependencies [b360802] - Updated dependencies [f6ed6ad] - Updated dependencies [31ee1c6] - Updated dependencies [a36d5c1] - @hyperlane-xyz/cosmos-sdk@12.6.0 - @hyperlane-xyz/sdk@12.6.0 - @hyperlane-xyz/utils@12.6.0 ## @hyperlane-xyz/core@7.1.5 ### Patch Changes - Updated dependencies [d182d7d] - Updated dependencies [b360802] - @hyperlane-xyz/utils@12.6.0 ## @hyperlane-xyz/cosmos-types@12.6.0 ## @hyperlane-xyz/infra@12.6.0 ### Minor Changes - 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. ### Patch Changes - Updated dependencies [76f0eba] - Updated dependencies [2ae0f72] - Updated dependencies [672d6d1] - Updated dependencies [1770318] - Updated dependencies [1f370e6] - Updated dependencies [7d56f2c] - Updated dependencies [6a70b8d] - Updated dependencies [d182d7d] - Updated dependencies [248d2e1] - Updated dependencies [e2a4727] - Updated dependencies [b360802] - Updated dependencies [e381a8d] - Updated dependencies [f6ed6ad] - Updated dependencies [31ee1c6] - Updated dependencies [a36d5c1] - @hyperlane-xyz/sdk@12.6.0 - @hyperlane-xyz/helloworld@12.6.0 - @hyperlane-xyz/utils@12.6.0 ## @hyperlane-xyz/ccip-server@12.6.0 ## @hyperlane-xyz/github-proxy@12.6.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/chilly-lizards-watch.md | 6 ---- .changeset/curly-bobcats-doubt.md | 5 --- .changeset/dark-socks-carry.md | 6 ---- .changeset/dry-rings-allow.md | 6 ---- .changeset/floppy-beans-invent.md | 5 --- .changeset/giant-animals-boil.md | 5 --- .changeset/little-houses-crash.md | 5 --- .changeset/loose-numbers-tease.md | 8 ----- .changeset/modern-owls-beam.md | 5 --- .changeset/nasty-bugs-cross.md | 5 --- .changeset/orange-women-show.md | 6 ---- .changeset/slimy-ears-strive.md | 6 ---- .changeset/soft-foxes-sit.md | 6 ---- .changeset/spotty-ways-lick.md | 5 --- .changeset/thirty-chicken-smash.md | 5 --- .changeset/tricky-news-retire.md | 6 ---- .changeset/upset-humans-spend.md | 7 ---- .changeset/wise-files-listen.md | 6 ---- .changeset/wise-hairs-hope.md | 5 --- .changeset/yummy-toes-pick.md | 5 --- solidity/CHANGELOG.md | 8 +++++ solidity/contracts/PackageVersioned.sol | 2 +- solidity/package.json | 4 +-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 20 +++++++++++ typescript/cli/package.json | 8 ++--- typescript/cli/src/version.ts | 2 +- typescript/cosmos-sdk/CHANGELOG.md | 10 ++++++ typescript/cosmos-sdk/package.json | 4 +-- typescript/cosmos-types/CHANGELOG.md | 2 ++ typescript/cosmos-types/package.json | 2 +- typescript/github-proxy/CHANGELOG.md | 2 ++ typescript/github-proxy/package.json | 2 +- typescript/helloworld/CHANGELOG.md | 25 ++++++++++++++ typescript/helloworld/package.json | 6 ++-- typescript/infra/CHANGELOG.md | 27 +++++++++++++++ typescript/infra/package.json | 8 ++--- typescript/sdk/CHANGELOG.md | 27 +++++++++++++++ typescript/sdk/package.json | 8 ++--- typescript/utils/CHANGELOG.md | 7 ++++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 26 +++++++++++++++ typescript/widgets/package.json | 8 ++--- yarn.lock | 44 ++++++++++++------------- 45 files changed, 207 insertions(+), 164 deletions(-) delete mode 100644 .changeset/chilly-lizards-watch.md delete mode 100644 .changeset/curly-bobcats-doubt.md delete mode 100644 .changeset/dark-socks-carry.md delete mode 100644 .changeset/dry-rings-allow.md delete mode 100644 .changeset/floppy-beans-invent.md delete mode 100644 .changeset/giant-animals-boil.md delete mode 100644 .changeset/little-houses-crash.md delete mode 100644 .changeset/loose-numbers-tease.md delete mode 100644 .changeset/modern-owls-beam.md delete mode 100644 .changeset/nasty-bugs-cross.md delete mode 100644 .changeset/orange-women-show.md delete mode 100644 .changeset/slimy-ears-strive.md delete mode 100644 .changeset/soft-foxes-sit.md delete mode 100644 .changeset/spotty-ways-lick.md delete mode 100644 .changeset/thirty-chicken-smash.md delete mode 100644 .changeset/tricky-news-retire.md delete mode 100644 .changeset/upset-humans-spend.md delete mode 100644 .changeset/wise-files-listen.md delete mode 100644 .changeset/wise-hairs-hope.md delete mode 100644 .changeset/yummy-toes-pick.md diff --git a/.changeset/chilly-lizards-watch.md b/.changeset/chilly-lizards-watch.md deleted file mode 100644 index b3feec545a9..00000000000 --- a/.changeset/chilly-lizards-watch.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cosmos-sdk': minor -'@hyperlane-xyz/sdk': minor ---- - -Add Cosmos Native ISM Reader & Module diff --git a/.changeset/curly-bobcats-doubt.md b/.changeset/curly-bobcats-doubt.md deleted file mode 100644 index 45e93eb1dca..00000000000 --- a/.changeset/curly-bobcats-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@hyperlane-xyz/cli": patch ---- - -Remove outputting isNft in warp init config diff --git a/.changeset/dark-socks-carry.md b/.changeset/dark-socks-carry.md deleted file mode 100644 index a53c71c450d..00000000000 --- a/.changeset/dark-socks-carry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -Add contract verification to CLI Warp Checker diff --git a/.changeset/dry-rings-allow.md b/.changeset/dry-rings-allow.md deleted file mode 100644 index ab516ae9d30..00000000000 --- a/.changeset/dry-rings-allow.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -adds logic to expand an ism or hook config if it is partially defined in the input file for the warp checker diff --git a/.changeset/floppy-beans-invent.md b/.changeset/floppy-beans-invent.md deleted file mode 100644 index 25e11723f66..00000000000 --- a/.changeset/floppy-beans-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Updates the `warp check` command output to only show fields that have diffs with the expected config diff --git a/.changeset/giant-animals-boil.md b/.changeset/giant-animals-boil.md deleted file mode 100644 index bc4af885166..00000000000 --- a/.changeset/giant-animals-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Fixed warp route ID lookup to use deployment configs instead of warp routes when prompting users for selection on warp deployment diff --git a/.changeset/little-houses-crash.md b/.changeset/little-houses-crash.md deleted file mode 100644 index 2f2a858863e..00000000000 --- a/.changeset/little-houses-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Refactored warp route configuration functions to use object parameters instead of positional parameters for improved clarity and flexibility. diff --git a/.changeset/loose-numbers-tease.md b/.changeset/loose-numbers-tease.md deleted file mode 100644 index 76e8b46367e..00000000000 --- a/.changeset/loose-numbers-tease.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@hyperlane-xyz/helloworld': minor -'@hyperlane-xyz/widgets': minor -'@hyperlane-xyz/infra': minor -'@hyperlane-xyz/cli': minor ---- - -Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. diff --git a/.changeset/modern-owls-beam.md b/.changeset/modern-owls-beam.md deleted file mode 100644 index 85cd9e274af..00000000000 --- a/.changeset/modern-owls-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Add HookModule.resolveHookAddresses() to resolve all HookConfig addresses diff --git a/.changeset/nasty-bugs-cross.md b/.changeset/nasty-bugs-cross.md deleted file mode 100644 index da87565008b..00000000000 --- a/.changeset/nasty-bugs-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': patch ---- - -Pass remote chain to adjustForPrecisionLoss for better error logging diff --git a/.changeset/orange-women-show.md b/.changeset/orange-women-show.md deleted file mode 100644 index 21243e13531..00000000000 --- a/.changeset/orange-women-show.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -Move executeDeploy logic from CLI to SDK diff --git a/.changeset/slimy-ears-strive.md b/.changeset/slimy-ears-strive.md deleted file mode 100644 index 61ec1965789..00000000000 --- a/.changeset/slimy-ears-strive.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/utils': minor -'@hyperlane-xyz/sdk': minor ---- - -Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function diff --git a/.changeset/soft-foxes-sit.md b/.changeset/soft-foxes-sit.md deleted file mode 100644 index 94dcf8e8c10..00000000000 --- a/.changeset/soft-foxes-sit.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -Enables the CLI to warp check routes that include non EVM routes diff --git a/.changeset/spotty-ways-lick.md b/.changeset/spotty-ways-lick.md deleted file mode 100644 index c3fbf956669..00000000000 --- a/.changeset/spotty-ways-lick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Deploy to new chains: ontology, miraclechain, kyve. diff --git a/.changeset/thirty-chicken-smash.md b/.changeset/thirty-chicken-smash.md deleted file mode 100644 index e43082852f0..00000000000 --- a/.changeset/thirty-chicken-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Added registry based warp configs support diff --git a/.changeset/tricky-news-retire.md b/.changeset/tricky-news-retire.md deleted file mode 100644 index bbf6aeb2802..00000000000 --- a/.changeset/tricky-news-retire.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/utils': minor -'@hyperlane-xyz/sdk': minor ---- - -Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas diff --git a/.changeset/upset-humans-spend.md b/.changeset/upset-humans-spend.md deleted file mode 100644 index dd85b208b63..00000000000 --- a/.changeset/upset-humans-spend.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/helloworld': minor -'@hyperlane-xyz/widgets': minor -'@hyperlane-xyz/cli': minor ---- - -Update Registry version to 15.0.0 diff --git a/.changeset/wise-files-listen.md b/.changeset/wise-files-listen.md deleted file mode 100644 index e4cb0596c5d..00000000000 --- a/.changeset/wise-files-listen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': patch -'@hyperlane-xyz/sdk': patch ---- - -Fixed proxy admin ownership transfer logic when the config is not specified in the input file diff --git a/.changeset/wise-hairs-hope.md b/.changeset/wise-hairs-hope.md deleted file mode 100644 index 6d41b57f697..00000000000 --- a/.changeset/wise-hairs-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Adds fiatCollateral token on chain config derivation logic as it was incorrectly inferred as collateral diff --git a/.changeset/yummy-toes-pick.md b/.changeset/yummy-toes-pick.md deleted file mode 100644 index 467813a0ff0..00000000000 --- a/.changeset/yummy-toes-pick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -add cosmos native hook module & reader diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index 697c4ea8d81..08f0de83f5b 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/core +## 7.1.5 + +### Patch Changes + +- Updated dependencies [d182d7d] +- Updated dependencies [b360802] + - @hyperlane-xyz/utils@12.6.0 + ## 7.1.4 ### Patch Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index 75451fea043..fab433e1594 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.1.4"; + string public constant PACKAGE_VERSION = "7.1.5"; } diff --git a/solidity/package.json b/solidity/package.json index 41326ed33c4..7400be33fce 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.1.4", + "version": "7.1.5", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.5.0", + "@hyperlane-xyz/utils": "12.6.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 1724bca2a39..061e999d1e6 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 12.6.0 + ## 12.5.0 ## 12.4.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index eb69ad7c914..ab5661e8ee0 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.5.0", + "version": "12.6.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 8e543f869d8..d5759fc53f7 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,25 @@ # @hyperlane-xyz/cli +## 12.6.0 + +### Minor Changes + +- 2ae0f72: Add contract verification to CLI Warp Checker +- 672d6d1: adds logic to expand an ism or hook config if it is partially defined in the input file for the warp checker +- aec8961: Updates the `warp check` command output to only show fields that have diffs with the expected config +- e25af4b: Fixed warp route ID lookup to use deployment configs instead of warp routes when prompting users for selection on warp deployment +- ce0b173: Refactored warp route configuration functions to use object parameters instead of positional parameters for improved clarity and flexibility. +- 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. +- 6a70b8d: Move executeDeploy logic from CLI to SDK +- 248d2e1: Enables the CLI to warp check routes that include non EVM routes +- de7c6ae: Added registry based warp configs support +- e381a8d: Update Registry version to 15.0.0 + +### Patch Changes + +- 166f849: Remove outputting isNft in warp init config +- f6ed6ad: Fixed proxy admin ownership transfer logic when the config is not specified in the input file + ## 12.5.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index b91078a44c7..7f0d71d5cee 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.5.0", + "version": "12.6.0", "description": "A command-line utility for common Hyperlane operations", "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", @@ -9,10 +9,10 @@ "@eslint/js": "^9.15.0", "@ethersproject/abi": "*", "@ethersproject/providers": "*", - "@hyperlane-xyz/cosmos-sdk": "12.5.0", + "@hyperlane-xyz/cosmos-sdk": "12.6.0", "@hyperlane-xyz/registry": "15.0.0", - "@hyperlane-xyz/sdk": "12.5.0", - "@hyperlane-xyz/utils": "12.5.0", + "@hyperlane-xyz/sdk": "12.6.0", + "@hyperlane-xyz/utils": "12.6.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 12eb1b0e454..d3fb2e95e65 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.5.0'; +export const VERSION = '12.6.0'; diff --git a/typescript/cosmos-sdk/CHANGELOG.md b/typescript/cosmos-sdk/CHANGELOG.md index 77447efc3e1..f7d7a30ab09 100644 --- a/typescript/cosmos-sdk/CHANGELOG.md +++ b/typescript/cosmos-sdk/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/cosmos-sdk +## 12.6.0 + +### Minor Changes + +- 76f0eba: Add Cosmos Native ISM Reader & Module + +### Patch Changes + +- @hyperlane-xyz/cosmos-types@12.6.0 + ## 12.5.0 ### Patch Changes diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 6c384a3f7cc..07f1af432a0 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-sdk", - "version": "12.5.0", + "version": "12.6.0", "description": "Hyperlane TypeScript SDK for the Cosmos Hyperlane SDK module", "type": "module", "exports": { @@ -46,6 +46,6 @@ }, "dependencies": { "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/cosmos-types": "12.5.0" + "@hyperlane-xyz/cosmos-types": "12.6.0" } } diff --git a/typescript/cosmos-types/CHANGELOG.md b/typescript/cosmos-types/CHANGELOG.md index f5fd40abdd0..df6e0515081 100644 --- a/typescript/cosmos-types/CHANGELOG.md +++ b/typescript/cosmos-types/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cosmos-types +## 12.6.0 + ## 12.5.0 ## 12.4.0 diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index 1cf57bbaa96..7db3c37d3f3 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-types", - "version": "12.5.0", + "version": "12.6.0", "description": "Hyperlane TypeScript SDK types for the Cosmos Hyperlane SDK module", "type": "module", "exports": { diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index e31647aeeaa..24528718db5 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 12.6.0 + ## 12.5.0 ## 12.4.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index 4f7a6e9ffde..0c09010baad 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.5.0", + "version": "12.6.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index c7f20d8bcb2..6d79dd7b4fc 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,30 @@ # @hyperlane-xyz/helloworld +## 12.6.0 + +### Minor Changes + +- 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. +- e381a8d: Update Registry version to 15.0.0 + +### Patch Changes + +- Updated dependencies [76f0eba] +- Updated dependencies [2ae0f72] +- Updated dependencies [672d6d1] +- Updated dependencies [1f370e6] +- Updated dependencies [7d56f2c] +- Updated dependencies [6a70b8d] +- Updated dependencies [d182d7d] +- Updated dependencies [248d2e1] +- Updated dependencies [e2a4727] +- Updated dependencies [b360802] +- Updated dependencies [f6ed6ad] +- Updated dependencies [31ee1c6] +- Updated dependencies [a36d5c1] + - @hyperlane-xyz/sdk@12.6.0 + - @hyperlane-xyz/core@7.1.5 + ## 12.5.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 57cb8a7ff00..b7f199ba0d8 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": "12.5.0", + "version": "12.6.0", "dependencies": { - "@hyperlane-xyz/core": "7.1.4", + "@hyperlane-xyz/core": "7.1.5", "@hyperlane-xyz/registry": "15.0.0", - "@hyperlane-xyz/sdk": "12.5.0", + "@hyperlane-xyz/sdk": "12.6.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index fc6308a2e69..bd0ebcd720b 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,32 @@ # @hyperlane-xyz/infra +## 12.6.0 + +### Minor Changes + +- 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. + +### Patch Changes + +- Updated dependencies [76f0eba] +- Updated dependencies [2ae0f72] +- Updated dependencies [672d6d1] +- Updated dependencies [1770318] +- Updated dependencies [1f370e6] +- Updated dependencies [7d56f2c] +- Updated dependencies [6a70b8d] +- Updated dependencies [d182d7d] +- Updated dependencies [248d2e1] +- Updated dependencies [e2a4727] +- Updated dependencies [b360802] +- Updated dependencies [e381a8d] +- Updated dependencies [f6ed6ad] +- Updated dependencies [31ee1c6] +- Updated dependencies [a36d5c1] + - @hyperlane-xyz/sdk@12.6.0 + - @hyperlane-xyz/helloworld@12.6.0 + - @hyperlane-xyz/utils@12.6.0 + ## 12.5.0 ### Patch Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 2e3098c96ef..932ce8d4cd7 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": "12.5.0", + "version": "12.6.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.5.0", + "@hyperlane-xyz/helloworld": "12.6.0", "@hyperlane-xyz/registry": "15.0.0", - "@hyperlane-xyz/sdk": "12.5.0", - "@hyperlane-xyz/utils": "12.5.0", + "@hyperlane-xyz/sdk": "12.6.0", + "@hyperlane-xyz/utils": "12.6.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index de41c56362c..3197e3d4da9 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,32 @@ # @hyperlane-xyz/sdk +## 12.6.0 + +### Minor Changes + +- 76f0eba: Add Cosmos Native ISM Reader & Module +- 2ae0f72: Add contract verification to CLI Warp Checker +- 672d6d1: adds logic to expand an ism or hook config if it is partially defined in the input file for the warp checker +- 1f370e6: Add HookModule.resolveHookAddresses() to resolve all HookConfig addresses +- 6a70b8d: Move executeDeploy logic from CLI to SDK +- d182d7d: Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function +- 248d2e1: Enables the CLI to warp check routes that include non EVM routes +- e2a4727: Deploy to new chains: ontology, miraclechain, kyve. +- b360802: Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas +- 31ee1c6: Adds fiatCollateral token on chain config derivation logic as it was incorrectly inferred as collateral +- a36d5c1: add cosmos native hook module & reader + +### Patch Changes + +- 7d56f2c: Pass remote chain to adjustForPrecisionLoss for better error logging +- f6ed6ad: Fixed proxy admin ownership transfer logic when the config is not specified in the input file +- Updated dependencies [76f0eba] +- Updated dependencies [d182d7d] +- Updated dependencies [b360802] + - @hyperlane-xyz/cosmos-sdk@12.6.0 + - @hyperlane-xyz/utils@12.6.0 + - @hyperlane-xyz/core@7.1.5 + ## 12.5.0 ### Patch Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 7eeaf970c2a..5fc5bcbbae2 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,16 +1,16 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.5.0", + "version": "12.6.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.122", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.1.4", - "@hyperlane-xyz/cosmos-sdk": "12.5.0", - "@hyperlane-xyz/utils": "12.5.0", + "@hyperlane-xyz/core": "7.1.5", + "@hyperlane-xyz/cosmos-sdk": "12.6.0", + "@hyperlane-xyz/utils": "12.6.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 23584624c4e..5be8a8bde0c 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperlane-xyz/utils +## 12.6.0 + +### Minor Changes + +- d182d7d: Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function +- b360802: Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas + ## 12.5.0 ## 12.4.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 812cd21fa86..2e9e6e40e9f 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": "12.5.0", + "version": "12.6.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index 93b80544879..e6c17f07a0b 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,31 @@ # @hyperlane-xyz/widgets +## 12.6.0 + +### Minor Changes + +- 1770318: Upgraded @hyperlane-xyz/registry to v14.0.0 and updated warp route config API usage. +- e381a8d: Update Registry version to 15.0.0 + +### Patch Changes + +- Updated dependencies [76f0eba] +- Updated dependencies [2ae0f72] +- Updated dependencies [672d6d1] +- Updated dependencies [1f370e6] +- Updated dependencies [7d56f2c] +- Updated dependencies [6a70b8d] +- Updated dependencies [d182d7d] +- Updated dependencies [248d2e1] +- Updated dependencies [e2a4727] +- Updated dependencies [b360802] +- Updated dependencies [f6ed6ad] +- Updated dependencies [31ee1c6] +- Updated dependencies [a36d5c1] + - @hyperlane-xyz/cosmos-sdk@12.6.0 + - @hyperlane-xyz/sdk@12.6.0 + - @hyperlane-xyz/utils@12.6.0 + ## 12.5.0 ### Patch Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 6c1bec042ba..583d2c38ec4 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.5.0", + "version": "12.6.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -10,9 +10,9 @@ "@cosmjs/stargate": "^0.32.4", "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/cosmos-sdk": "12.5.0", - "@hyperlane-xyz/sdk": "12.5.0", - "@hyperlane-xyz/utils": "12.5.0", + "@hyperlane-xyz/cosmos-sdk": "12.6.0", + "@hyperlane-xyz/sdk": "12.6.0", + "@hyperlane-xyz/utils": "12.6.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index 02fabac6ffe..89ec5538b00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7645,10 +7645,10 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" - "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" + "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.5.0" - "@hyperlane-xyz/utils": "npm:12.5.0" + "@hyperlane-xyz/sdk": "npm:12.6.0" + "@hyperlane-xyz/utils": "npm:12.6.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7688,14 +7688,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.1.4, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.5, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.5.0" + "@hyperlane-xyz/utils": "npm:12.6.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7735,13 +7735,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-sdk@npm:12.5.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": +"@hyperlane-xyz/cosmos-sdk@npm:12.6.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk" dependencies: "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/cosmos-types": "npm:12.5.0" + "@hyperlane-xyz/cosmos-types": "npm:12.6.0" "@types/mocha": "npm:^10.0.1" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" @@ -7757,7 +7757,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-types@npm:12.5.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": +"@hyperlane-xyz/cosmos-types@npm:12.6.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" dependencies: @@ -7791,14 +7791,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.5.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:12.6.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.4" + "@hyperlane-xyz/core": "npm:7.1.5" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.5.0" + "@hyperlane-xyz/sdk": "npm:12.6.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7847,10 +7847,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.5.0" + "@hyperlane-xyz/helloworld": "npm:12.6.0" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.5.0" - "@hyperlane-xyz/utils": "npm:12.5.0" + "@hyperlane-xyz/sdk": "npm:12.6.0" + "@hyperlane-xyz/utils": "npm:12.6.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7922,7 +7922,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.5.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:12.6.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7932,9 +7932,9 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.4" - "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" - "@hyperlane-xyz/utils": "npm:12.5.0" + "@hyperlane-xyz/core": "npm:7.1.5" + "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" + "@hyperlane-xyz/utils": "npm:12.6.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -7979,7 +7979,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.5.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:12.6.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8021,10 +8021,10 @@ __metadata: "@emotion/styled": "npm:^11.13.0" "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" - "@hyperlane-xyz/cosmos-sdk": "npm:12.5.0" + "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.5.0" - "@hyperlane-xyz/utils": "npm:12.5.0" + "@hyperlane-xyz/sdk": "npm:12.6.0" + "@hyperlane-xyz/utils": "npm:12.6.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From 178443c62826140743118777f0c5c3da38048c52 Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Wed, 14 May 2025 19:54:11 -0400 Subject: [PATCH 183/223] feat: CCIP-read ISM relayer authentication (#6144) ### Description This PR adds authentication from the relayer key on both rust and typescript relayer to allow servers to refuse a response. ### Backward compatibility Yes ### Testing Unit tests with manual assertion of the same signature across rust/typescript --------- Co-authored-by: Trevor Porter --- rust/main/Cargo.lock | 1 + rust/main/agents/relayer/Cargo.toml | 1 + .../relayer/src/msg/metadata/base_builder.rs | 7 + .../relayer/src/msg/metadata/ccip_read/mod.rs | 124 +++++++++++++++++- rust/main/agents/relayer/src/msg/op_batch.rs | 1 + rust/main/agents/relayer/src/relayer.rs | 15 +++ .../relayer/src/test_utils/dummy_data.rs | 1 + .../src/test_utils/mock_base_builder.rs | 4 + rust/main/agents/validator/src/settings.rs | 10 +- .../src/libs/account/tests.rs | 8 +- ...stCccipReadIsm.sol => TestCcipReadIsm.sol} | 15 ++- solidity/test/test/TestCcipReadIsm.t.sol | 49 +++++++ .../src/ism/metadata/ccipread.hardhat-test.ts | 58 +++++++- typescript/sdk/src/ism/metadata/ccipread.ts | 15 ++- 14 files changed, 286 insertions(+), 23 deletions(-) rename solidity/contracts/isms/ccip-read/{TestCccipReadIsm.sol => TestCcipReadIsm.sol} (74%) create mode 100644 solidity/test/test/TestCcipReadIsm.t.sol diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 2b83f8ae6f3..94c3cfb0e01 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -8217,6 +8217,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "sha3 0.10.8", "strum 0.26.3", "submitter", "tempfile", diff --git a/rust/main/agents/relayer/Cargo.toml b/rust/main/agents/relayer/Cargo.toml index 3f67da1f202..339e725f480 100644 --- a/rust/main/agents/relayer/Cargo.toml +++ b/rust/main/agents/relayer/Cargo.toml @@ -34,6 +34,7 @@ regex.workspace = true reqwest = { workspace = true, features = ["json"] } serde.workspace = true serde_json.workspace = true +sha3.workspace = true strum.workspace = true thiserror.workspace = true tokio = { workspace = true, features = [ diff --git a/rust/main/agents/relayer/src/msg/metadata/base_builder.rs b/rust/main/agents/relayer/src/msg/metadata/base_builder.rs index 4973e53728f..10a371cc6d1 100644 --- a/rust/main/agents/relayer/src/msg/metadata/base_builder.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base_builder.rs @@ -6,6 +6,7 @@ use std::{collections::HashMap, fmt::Debug, str::FromStr, sync::Arc}; use derive_new::new; use eyre::Context; use futures::{stream, StreamExt}; +use hyperlane_ethereum::Signers; use tokio::sync::RwLock; use tracing::{debug, info, warn}; @@ -42,6 +43,7 @@ pub struct BaseMetadataBuilder { db: HyperlaneRocksDB, app_context_classifier: IsmAwareAppContextClassifier, ism_cache_policy_classifier: IsmCachePolicyClassifier, + signer: Option, } impl Debug for BaseMetadataBuilder { @@ -61,6 +63,7 @@ pub trait BuildsBaseMetadata: Send + Sync + Debug { fn app_context_classifier(&self) -> &IsmAwareAppContextClassifier; fn ism_cache_policy_classifier(&self) -> &IsmCachePolicyClassifier; fn cache(&self) -> &OptionalCache>; + fn get_signer(&self) -> Option<&Signers>; async fn get_proof(&self, leaf_index: u32, checkpoint: Checkpoint) -> eyre::Result; async fn highest_known_leaf_index(&self) -> Option; @@ -269,6 +272,10 @@ impl BuildsBaseMetadata for BaseMetadataBuilder { app_context.map(|ctx| (self.metrics.clone(), ctx)), )) } + + fn get_signer(&self) -> Option<&Signers> { + self.signer.as_ref() + } } impl BaseMetadataBuilder { diff --git a/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs b/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs index 29f161d735b..d9c59e5550a 100644 --- a/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs +++ b/rust/main/agents/relayer/src/msg/metadata/ccip_read/mod.rs @@ -12,12 +12,14 @@ use regex::{Regex, RegexSet, RegexSetBuilder}; use reqwest::{header::CONTENT_TYPE, Client}; use serde::{Deserialize, Serialize}; use serde_json::json; +use sha3::{digest::Update, Digest, Keccak256}; use tracing::{info, instrument, warn}; use hyperlane_core::{ - utils::bytes_to_hex, CcipReadIsm, HyperlaneMessage, RawHyperlaneMessage, H256, + utils::bytes_to_hex, CcipReadIsm, HyperlaneMessage, HyperlaneSignerExt, RawHyperlaneMessage, + Signable, H160, H256, }; -use hyperlane_ethereum::OffchainLookup; +use hyperlane_ethereum::{OffchainLookup, Signers}; use super::{ base::{MessageMetadataBuildParams, MetadataBuildError}, @@ -39,7 +41,53 @@ pub struct CcipReadIsmMetadataBuilder { base: MessageMetadataBuilder, } +/// An authenticated offchain lookup payload +#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)] +pub struct HyperlaneAuthenticatedOffchainLookup { + url_template: Vec, + sender: H160, + call_data: Vec, +} + +impl Signable for HyperlaneAuthenticatedOffchainLookup { + fn signing_hash(&self) -> H256 { + H256::from_slice( + Keccak256::new() + .chain(b"HYPERLANE_OFFCHAINLOOKUP") + .chain(self.sender) + .chain(self.call_data.as_slice()) + .chain(self.url_template.as_slice()) + .finalize() + .as_slice(), + ) + } +} + impl CcipReadIsmMetadataBuilder { + /// Generate a relayer authentication signature (EIP-191) over call_data and sender and the url template + async fn generate_signature_hex( + signer: &Signers, + info: &OffchainLookup, + url: &str, + ) -> Result { + // Derive the hash over call_data and sender + let signable = HyperlaneAuthenticatedOffchainLookup { + url_template: url.to_owned().into(), + call_data: info.call_data.clone().to_vec(), + sender: info.sender.into(), + }; + // EIP-191 compliant signature over the signing hash of the HyperlaneOffchainLookupAttestation. + let signed = signer + .sign(signable) + .await + .map_err(|e| MetadataBuildError::FailedToBuild(e.to_string()))?; + + let sig_bytes: [u8; 65] = signed.signature.into(); + let sig_hex = bytes_to_hex(&sig_bytes); + + Ok(sig_hex) + } + /// Returns info on how to query for offchain information /// This method will attempt to get the value from cache first. If it is a cache miss, /// it will request it from the ISM contract. The result will be cached for future use. @@ -57,6 +105,7 @@ impl CcipReadIsmMetadataBuilder { let call_params = (ism.address(), message.id()); let info_from_cache = self + .base .base_builder() .cache() .get_cached_call_result::(ism_domain, fn_key, &call_params) @@ -97,7 +146,8 @@ impl CcipReadIsmMetadataBuilder { } }; - self.base_builder() + self.base + .base_builder() .cache() .cache_call_result( ism_domain, @@ -125,6 +175,7 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { _params: MessageMetadataBuildParams, ) -> Result { let ism = self + .base .base_builder() .build_ccip_read_ism(ism_address) .await @@ -140,6 +191,13 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { continue; } + // Compute relayer authentication signature via EIP-191 + let maybe_signature_hex = if let Some(signer) = self.base.base_builder().get_signer() { + Some(Self::generate_signature_hex(signer, &info, url).await?) + } else { + None + }; + // Need to explicitly convert the sender H160 the hex because the `ToString` implementation // for `H160` truncates the output. (e.g. `0xc66a…7b6f` instead of returning // the full address) @@ -149,10 +207,13 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { .replace("{sender}", sender_as_bytes) .replace("{data}", data_as_bytes); let res = if !url.contains("{data}") { - let body = json!({ + let mut body = json!({ "sender": sender_as_bytes, "data": data_as_bytes }); + if let Some(signature_hex) = &maybe_signature_hex { + body["signature"] = json!(signature_hex); + } Client::new() .post(interpolated_url) .header(CONTENT_TYPE, "application/json") @@ -200,9 +261,62 @@ fn create_ccip_url_regex() -> RegexSet { } #[cfg(test)] -mod tests { +mod test { + use std::{str::FromStr, vec}; + + use ethers::types::H160; + use hyperlane_core::SignedType; + use super::*; + #[tokio::test] + async fn test_generate_signature_hex() { + // default hardhat key + let signer = Signers::Local( + ethers::signers::Wallet::from_str( + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + ) + .unwrap(), + ); + let url = "http://example.com/namespace".to_string(); + let info = OffchainLookup { + // from TestCcipReadIsm.sol + call_data: "callDataToReturn".as_bytes().to_vec().into(), + // from ccipread.hardhat-test.ts + sender: H160::from_str("4ee6ecad1c2dae9f525404de8555724e3c35d07b").unwrap(), + urls: vec![url.clone()], + callback_function: [0, 0, 0, 0], + extra_data: vec![].into(), + }; + + let signature_hex = + CcipReadIsmMetadataBuilder::generate_signature_hex(&signer, &info, &url) + .await + .unwrap(); + + // 65 bytes = 130 hex chars + 2 for 0x + assert_eq!(signature_hex.len(), 132); + // Get the control from the hardhat test + assert_eq!( + signature_hex, + "0x62e58f20c0b7ec4f071835eaf7aa2716707375740774188ecc60e7d91b565f7363deeba366b2609aee6b870ac6504a6cf482f00ecc0e9cbe34422bdcf88a4bd11b" + ); + + // Test the signature is valid + let signable = HyperlaneAuthenticatedOffchainLookup { + url_template: url.into(), + sender: info.sender.into(), + call_data: info.call_data.clone().to_vec(), + }; + let signed = SignedType { + value: signable, + signature: ethers::types::Signature::from_str(&signature_hex) + .unwrap() + .into(), + }; + assert!(signer.verify(&signed).is_ok()); + } + #[test] fn test_ccip_regex_filter() { let set = create_ccip_url_regex(); diff --git a/rust/main/agents/relayer/src/msg/op_batch.rs b/rust/main/agents/relayer/src/msg/op_batch.rs index 4d2b5abeed1..e50541d6963 100644 --- a/rust/main/agents/relayer/src/msg/op_batch.rs +++ b/rust/main/agents/relayer/src/msg/op_batch.rs @@ -386,6 +386,7 @@ mod tests { base_db.clone(), IsmAwareAppContextClassifier::new(default_ism_getter.clone(), vec![]), IsmCachePolicyClassifier::new(default_ism_getter, Default::default()), + None, ); let message_context = Arc::new(MessageContext { destination_mailbox: arb_mailbox, diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index f2dc223f41c..ac07f0e4fa5 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -345,6 +345,20 @@ impl BaseAgent for Relayer { for (origin, validator_announce) in validator_announces.iter() { let db = dbs.get(origin).unwrap().clone(); let default_ism_getter = DefaultIsmCache::new(dest_mailbox.clone()); + // Extract optional Ethereum signer for CCIP-read authentication + let eth_signer: Option = if let Some(builder) = + &destination_chain_setup.signer + { + match builder.build().await { + Ok(s) => Some(s), + Err(err) => { + warn!(error = ?err, "Failed to build Ethereum signer for CCIP-read ISM"); + None + } + } + } else { + None + }; let metadata_builder = BaseMetadataBuilder::new( origin.clone(), destination_chain_setup.clone(), @@ -362,6 +376,7 @@ impl BaseAgent for Relayer { default_ism_getter.clone(), settings.ism_cache_configs.clone(), ), + eth_signer, ); msg_ctxs.insert( diff --git a/rust/main/agents/relayer/src/test_utils/dummy_data.rs b/rust/main/agents/relayer/src/test_utils/dummy_data.rs index d8962f44fcf..a5ac08bfd04 100644 --- a/rust/main/agents/relayer/src/test_utils/dummy_data.rs +++ b/rust/main/agents/relayer/src/test_utils/dummy_data.rs @@ -76,6 +76,7 @@ pub fn dummy_metadata_builder( db.clone(), IsmAwareAppContextClassifier::new(default_ism_getter.clone(), vec![]), IsmCachePolicyClassifier::new(default_ism_getter, Default::default()), + None, ) } diff --git a/rust/main/agents/relayer/src/test_utils/mock_base_builder.rs b/rust/main/agents/relayer/src/test_utils/mock_base_builder.rs index 401bde37de3..f54262111a9 100644 --- a/rust/main/agents/relayer/src/test_utils/mock_base_builder.rs +++ b/rust/main/agents/relayer/src/test_utils/mock_base_builder.rs @@ -12,6 +12,7 @@ use hyperlane_core::{ accumulator::merkle::Proof, AggregationIsm, CcipReadIsm, Checkpoint, HyperlaneDomain, HyperlaneMessage, InterchainSecurityModule, MultisigIsm, RoutingIsm, H256, }; +use hyperlane_ethereum::Signers; use crate::msg::metadata::{ BuildsBaseMetadata, IsmAwareAppContextClassifier, IsmCachePolicyClassifier, @@ -105,6 +106,9 @@ impl MockBaseMetadataBuilder { #[async_trait::async_trait] impl BuildsBaseMetadata for MockBaseMetadataBuilder { + fn get_signer(&self) -> Option<&Signers> { + None // Mock implementation returning None + } fn origin_domain(&self) -> &HyperlaneDomain { self.responses .origin_domain diff --git a/rust/main/agents/validator/src/settings.rs b/rust/main/agents/validator/src/settings.rs index ddf23dd7560..2460c26579f 100644 --- a/rust/main/agents/validator/src/settings.rs +++ b/rust/main/agents/validator/src/settings.rs @@ -336,7 +336,7 @@ mod test { #[test] fn test_get_rpc_urls_explicit() { - let expected = vec![ + let expected = [ RpcConfig { url: "http://my-rpc-url.com".to_string(), public: true, @@ -393,9 +393,9 @@ mod test { assert_eq!(parsed.len(), 2); assert_eq!(parsed[0].url, "http://my-rpc-url.com"); - assert_eq!(parsed[0].public, false); + assert!(!parsed[0].public); assert_eq!(parsed[1].url, "http://my-rpc-url-2.com"); - assert_eq!(parsed[1].public, false); + assert!(!parsed[1].public); } #[test] @@ -421,8 +421,8 @@ mod test { assert_eq!(parsed.len(), 2); assert_eq!(parsed[0].url, "http://my-rpc-url-3.com"); - assert_eq!(parsed[0].public, false); + assert!(!parsed[0].public); assert_eq!(parsed[1].url, "http://my-rpc-url-4.com"); - assert_eq!(parsed[1].public, false); + assert!(!parsed[1].public); } } diff --git a/rust/main/chains/hyperlane-cosmos-native/src/libs/account/tests.rs b/rust/main/chains/hyperlane-cosmos-native/src/libs/account/tests.rs index 9daecc360de..c1e6c06e9d5 100644 --- a/rust/main/chains/hyperlane-cosmos-native/src/libs/account/tests.rs +++ b/rust/main/chains/hyperlane-cosmos-native/src/libs/account/tests.rs @@ -59,14 +59,14 @@ fn test_ethereum_style() { fn compressed_public_key() -> PublicKey { let hex = hex::decode(COMPRESSED_PUBLIC_KEY).unwrap(); let tendermint = tendermint::PublicKey::from_raw_secp256k1(&hex).unwrap(); - let pub_key = PublicKey::from(tendermint); - pub_key + + PublicKey::from(tendermint) } fn decompressed_public_key() -> PublicKey { let hex = hex::decode(COMPRESSED_PUBLIC_KEY).unwrap(); let decompressed = decompress_public_key(&hex).unwrap(); let tendermint = tendermint::PublicKey::from_raw_secp256k1(&decompressed).unwrap(); - let pub_key = PublicKey::from(tendermint); - pub_key + + PublicKey::from(tendermint) } diff --git a/solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol b/solidity/contracts/isms/ccip-read/TestCcipReadIsm.sol similarity index 74% rename from solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol rename to solidity/contracts/isms/ccip-read/TestCcipReadIsm.sol index 0d70177ec3d..3e041a3592a 100644 --- a/solidity/contracts/isms/ccip-read/TestCccipReadIsm.sol +++ b/solidity/contracts/isms/ccip-read/TestCcipReadIsm.sol @@ -21,22 +21,27 @@ import "./AbstractCcipReadIsm.sol"; */ contract TestCcipReadIsm is AbstractCcipReadIsm { string[] public urls; + bytes public calldataToReturn = bytes("callDataToReturn"); constructor(string[] memory _urls) { urls = _urls; } - function getOffchainVerifyInfo( - bytes calldata _message - ) external view override { + function getOffchainVerifyInfo(bytes calldata) external view override { // Revert with OffchainLookup to instruct off-chain resolution - revert OffchainLookup(address(this), urls, _message, bytes4(0), ""); + revert OffchainLookup( + address(this), + urls, + calldataToReturn, + bytes4(0), + "" + ); } function verify( bytes calldata metadata, bytes calldata - ) external view override returns (bool) { + ) external pure override returns (bool) { bool ok = abi.decode(metadata, (bool)); require(ok, "TestCcipReadIsm: invalid metadata"); return true; diff --git a/solidity/test/test/TestCcipReadIsm.t.sol b/solidity/test/test/TestCcipReadIsm.t.sol new file mode 100644 index 00000000000..ef11ef117ab --- /dev/null +++ b/solidity/test/test/TestCcipReadIsm.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import "forge-std/Test.sol"; +import "../../contracts/isms/ccip-read/TestCcipReadIsm.sol"; + +contract TestCcipReadIsmTest is Test { + TestCcipReadIsm internal ism; + string[] internal urls; + + function setUp() public { + // Initialize with a single URL (actual value not used in Test contract) + urls = new string[](1); + urls[0] = "http://example.com/{data}"; + ism = new TestCcipReadIsm(urls); + } + + function testGetOffchainVerifyInfoRevertsWithOffchainLookup() public { + bytes memory message = hex"1234"; + vm.expectRevert( + abi.encodeWithSelector( + OffchainLookup.selector, + address(ism), + urls, + ism.calldataToReturn(), + bytes4(0), + "" + ) + ); + ism.getOffchainVerifyInfo(message); + } + + function testVerifyReturnsTrueOnValidMetadata() public { + // Encode a boolean 'true' as metadata + bytes memory metadata = abi.encode(true); + bool result = ism.verify(metadata, ""); + assertTrue( + result, + "Expected verify() to return true for valid metadata" + ); + } + + function testVerifyRevertsOnInvalidMetadata() public { + // Encode a boolean 'false' as metadata + bytes memory invalid = abi.encode(false); + vm.expectRevert("TestCcipReadIsm: invalid metadata"); + ism.verify(invalid, ""); + } +} diff --git a/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts b/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts index 93c32f290d5..673fc7fbd7f 100644 --- a/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts +++ b/typescript/sdk/src/ism/metadata/ccipread.hardhat-test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { ethers } from 'ethers'; import hre from 'hardhat'; import sinon from 'sinon'; @@ -25,8 +26,9 @@ describe('CCIP-Read ISM Integration', () => { let metadataBuilder: BaseMetadataBuilder; let ismFactory: HyperlaneIsmFactory; let fetchStub: sinon.SinonStub; + const CCIP_READ_SERVER_URL = 'http://example.com/namespace'; - before(async () => { + beforeEach(async () => { // Set up a local test multi-provider and Hyperlane core const signers = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ @@ -51,7 +53,7 @@ describe('CCIP-Read ISM Integration', () => { domain, new TestCcipReadIsm__factory(), // Pass in desired offchain URLs for the ISM constructor: - [['http://example.com/{data}']], + [[CCIP_READ_SERVER_URL]], ); // Configure the TestRecipient to use the CCIP-Read ISM @@ -97,7 +99,57 @@ describe('CCIP-Read ISM Integration', () => { await expect(mailbox.process(metadata, message.message)).to.not.be.reverted; }); - after(() => { + it('sends signature field in request when calling fetch', async () => { + const { dispatchTx, message } = await core.sendMessage( + 'test1', + 'test2', + testRecipient.address, + '0x1234', + ); + + // Derive the on-chain ISM config for CCIP-Read + const derivedIsm = (await new EvmIsmReader( + multiProvider, + 'test2', + ).deriveIsmConfig(ccipReadIsm.address)) as WithAddress; + + // Build the metadata using the CCIP-Read builder + const context: MetadataContext> = { + ism: derivedIsm, + message, + dispatchTx, + hook: {} as any, + }; + await metadataBuilder.build(context); + + // Verify that fetch was called exactly once + expect(fetchStub.calledOnce).to.be.true; + const [url, options] = fetchStub.getCall(0).args; + const payload = JSON.parse(options.body as string); + expect(url).to.equal(CCIP_READ_SERVER_URL.replace('{data}', payload.data)); + + // Should include sender, data, and signature + expect(payload).to.include.keys('sender', 'data', 'signature'); + expect(payload.sender).to.equal(ccipReadIsm.address); + + // Verify that signature is valid over (data, sender) + const messageHash = ethers.utils.solidityKeccak256( + ['string', 'address', 'bytes', 'string'], + [ + 'HYPERLANE_OFFCHAINLOOKUP', + payload.sender, + payload.data, + CCIP_READ_SERVER_URL, + ], + ); + const recovered = ethers.utils.verifyMessage( + ethers.utils.arrayify(messageHash), + payload.signature, + ); + expect(recovered).to.equal((await hre.ethers.getSigners())[0].address); + }); + + afterEach(() => { fetchStub.restore(); }); }); diff --git a/typescript/sdk/src/ism/metadata/ccipread.ts b/typescript/sdk/src/ism/metadata/ccipread.ts index cd61a21bb0c..4de9bf02b39 100644 --- a/typescript/sdk/src/ism/metadata/ccipread.ts +++ b/typescript/sdk/src/ism/metadata/ccipread.ts @@ -44,6 +44,10 @@ export class CcipReadMetadataBuilder implements MetadataBuilder { ]; const callDataHex = utils.hexlify(callData); + const signer = this.core.multiProvider.getSigner( + message.parsed.destination, + ); + for (const urlTemplate of urls) { const url = urlTemplate .replace('{sender}', sender) @@ -54,10 +58,19 @@ export class CcipReadMetadataBuilder implements MetadataBuilder { const res = await fetch(url); responseJson = await res.json(); } else { + // Compute and sign authentication signature + const messageHash = utils.solidityKeccak256( + ['string', 'address', 'bytes', 'string'], + ['HYPERLANE_OFFCHAINLOOKUP', sender, callDataHex, urlTemplate], + ); + + const signature = await signer.signMessage( + utils.arrayify(messageHash), + ); const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ sender, data: callDataHex }), + body: JSON.stringify({ sender, data: callDataHex, signature }), }); responseJson = await res.json(); } From 2a0c84813b56f47ef74e761f5680ca40aeaa5d61 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Thu, 15 May 2025 13:44:26 +0100 Subject: [PATCH 184/223] feat: Support VSXERC20 token standard in warp monitor (#6225) ### Description - When we extended the oUSDT route, we changed the token standards (`EvmHypXERC20Lockbox` => `EvmHypVSXERC20Lockbox`, `EvmHypXERC20` => `EvmHypVSXERC20`). - This was a change that is an accurate representation of the tokens. - This PR introduces support for these token standards in the monitoring service ### Backward compatibility Yes ### Testing Manual --- .../monitor/monitor-warp-route-balances.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts b/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts index 0078cb53eb7..afa472e4b2e 100644 --- a/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts @@ -272,12 +272,16 @@ async function getTokenBridgedBalance( // Only record value for collateralized and xERC20 lockbox tokens. if ( token.isCollateralized() || - token.standard === TokenStandard.EvmHypXERC20Lockbox + token.standard === TokenStandard.EvmHypXERC20Lockbox || + token.standard === TokenStandard.EvmHypVSXERC20Lockbox ) { tokenPrice = await tryGetTokenPrice(token, tokenPriceGetter); } - if (token.standard === TokenStandard.EvmHypXERC20Lockbox) { + if ( + token.standard === TokenStandard.EvmHypXERC20Lockbox || + token.standard === TokenStandard.EvmHypVSXERC20Lockbox + ) { tokenAddress = (await (adapter as EvmHypXERC20LockboxAdapter).getXERC20()) .address; } @@ -363,7 +367,10 @@ async function getXERC20Info( throw new Error(`Unsupported XERC20 protocol type ${token.protocol}`); } - if (token.standard === TokenStandard.EvmHypXERC20) { + if ( + token.standard === TokenStandard.EvmHypXERC20 || + token.standard === TokenStandard.EvmHypVSXERC20 + ) { const adapter = token.getAdapter( warpCore.multiProvider, ) as EvmHypXERC20Adapter; @@ -371,7 +378,10 @@ async function getXERC20Info( limits: await getXERC20Limit(token, adapter), xERC20Address: (await adapter.getXERC20()).address, }; - } else if (token.standard === TokenStandard.EvmHypXERC20Lockbox) { + } else if ( + token.standard === TokenStandard.EvmHypXERC20Lockbox || + token.standard === TokenStandard.EvmHypVSXERC20Lockbox + ) { const adapter = token.getAdapter( warpCore.multiProvider, ) as EvmHypXERC20LockboxAdapter; @@ -567,7 +577,8 @@ function getWarpRouteCollateralTokenSymbol(warpCore: WarpCore): string { const collateralTokens = warpCore.tokens.filter( (token) => token.isCollateralized() || - token.standard === TokenStandard.EvmHypXERC20Lockbox, + token.standard === TokenStandard.EvmHypXERC20Lockbox || + token.standard === TokenStandard.EvmHypVSXERC20Lockbox, ); if (collateralTokens.length === 0) { From cc61fdbad6050bdc17a96df07533f4e82f7b92c3 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Thu, 15 May 2025 15:20:25 +0100 Subject: [PATCH 185/223] fix: changelog typos (#6234) ### Description fix: changelog typos ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/sdk/CHANGELOG.md | 2 +- typescript/utils/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 3197e3d4da9..b4d385a5754 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -12,7 +12,7 @@ - d182d7d: Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function - 248d2e1: Enables the CLI to warp check routes that include non EVM routes - e2a4727: Deploy to new chains: ontology, miraclechain, kyve. -- b360802: Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas +- b360802: Add the isCosmosIbcDenomAddress function and improve the config expansion logic to correctly format the destination gas - 31ee1c6: Adds fiatCollateral token on chain config derivation logic as it was incorrectly inferred as collateral - a36d5c1: add cosmos native hook module & reader diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 5be8a8bde0c..b111af6886f 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -5,7 +5,7 @@ ### Minor Changes - d182d7d: Adds the sortArraysInObject function to properly sort arrays in an object recursively given an optional sort function -- b360802: Add the isCosmosIbcDenomAddress function and improve the config expasion logic to correctly format the destination gas +- b360802: Add the isCosmosIbcDenomAddress function and improve the config expansion logic to correctly format the destination gas ## 12.5.0 From 5b833e87d958e4c7b4ff3dd25866297ee442bdb2 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Thu, 15 May 2025 11:02:13 -0400 Subject: [PATCH 186/223] chore: update svm igp config (#6236) ### Description Updates the solana igp config to add abstract ### Drive-by changes no ### Related issues ### Backward compatibility ### Testing --- .../mainnet3/gas-oracle-configs.json | 40 +++++++++++++++++++ .../sealevel-helpers/print-gas-oracles.ts | 3 ++ 2 files changed, 43 insertions(+) diff --git a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json index 89f8ea9c120..41edc7e89c9 100644 --- a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json +++ b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json @@ -56,6 +56,14 @@ }, "overhead": 600000 }, + "abstract": { + "oracleConfig": { + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "193418836", + "tokenDecimals": 18 + }, + "overhead": 333774 + }, "artela": { "oracleConfig": { "tokenExchangeRate": "152446195501249", @@ -194,6 +202,22 @@ } }, "eclipsemainnet": { + "sonicsvm": { + "oracleConfig": { + "tokenExchangeRate": "120284594486260", + "gasPrice": "35603", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, + "soon": { + "oracleConfig": { + "tokenExchangeRate": "1500000000000000", + "gasPrice": "2855", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, "ethereum": { "oracleConfig": { "tokenExchangeRate": "15000000000000000000", @@ -243,9 +267,25 @@ "tokenDecimals": 18 }, "overhead": 159736 + }, + "eclipsemainnet": { + "oracleConfig": { + "tokenExchangeRate": "1500000000000000", + "gasPrice": "2855", + "tokenDecimals": 9 + }, + "overhead": 600000 } }, "sonicsvm": { + "eclipsemainnet": { + "oracleConfig": { + "tokenExchangeRate": "18705637322965842", + "gasPrice": "2855", + "tokenDecimals": 9 + }, + "overhead": 600000 + }, "solanamainnet": { "oracleConfig": { "tokenExchangeRate": "1500000000000000", diff --git a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts index 8c8342c16ae..1a6c46ba7a9 100644 --- a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts +++ b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts @@ -108,8 +108,11 @@ function getChainConnections( ['svmbnb', 'solanamainnet'], ['svmbnb', 'bsc'], ['svmbnb', 'soon'], + ['sonicsvm', 'eclipsemainnet'], ['soon', 'solanamainnet'], ['soon', 'bsc'], + ['soon', 'eclipsemainnet'], + ['abstract', 'solanamainnet'], // All warp routes ...Object.values(WarpRouteIds).map(getWarpChains), ]; From f8696c78981bbc9d6df14bdbad59778e997e78e6 Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Thu, 15 May 2025 17:40:02 +0200 Subject: [PATCH 187/223] feat: Add Starknet contract ABI fetching and contract artifact generation [STARKNET-01] (#5830) ### Description 1st Starknet PR This PR adds the `@hyperlane-xyz/starknet-core` package for fetching Starknet contract ABI and generating contract artifacts. The package provides TypeScript tooling for interacting with Cairo contracts ABI on Starknet, including auto-generated compiled contract ABIs from Astraly Labs Hyperlane repo. ### Drive-by changes None ### Related issues Resolves ENG-1270 ### Backward compatibility This is a new package that doesn't modify existing functionality ### Testing Manual testing of contract fetching and artifact generation --- .changeset/popular-trains-repair.md | 5 + Dockerfile | 1 + package.json | 3 +- starknet/.gitignore | 3 + starknet/README.md | 96 +++++++ starknet/eslint.config.mjs | 15 + starknet/package.json | 47 ++++ starknet/scripts/StarknetArtifactGenerator.ts | 266 ++++++++++++++++++ starknet/scripts/Templates.ts | 42 +++ starknet/scripts/fetch-contracts-release.sh | 154 ++++++++++ starknet/scripts/generate-artifacts.ts | 25 ++ starknet/scripts/prettier.ts | 7 + starknet/src/artifacts/index.ts | 7 + starknet/src/const.ts | 10 + starknet/src/contract-retriever.ts | 53 ++++ starknet/src/errors.ts | 9 + starknet/src/index.ts | 2 + starknet/src/types.ts | 36 +++ starknet/tsconfig.json | 8 + typescript/sdk/package.json | 2 +- yarn.lock | 29 +- 21 files changed, 813 insertions(+), 7 deletions(-) create mode 100644 .changeset/popular-trains-repair.md create mode 100644 starknet/.gitignore create mode 100644 starknet/README.md create mode 100644 starknet/eslint.config.mjs create mode 100644 starknet/package.json create mode 100644 starknet/scripts/StarknetArtifactGenerator.ts create mode 100644 starknet/scripts/Templates.ts create mode 100755 starknet/scripts/fetch-contracts-release.sh create mode 100644 starknet/scripts/generate-artifacts.ts create mode 100644 starknet/scripts/prettier.ts create mode 100644 starknet/src/artifacts/index.ts create mode 100644 starknet/src/const.ts create mode 100644 starknet/src/contract-retriever.ts create mode 100644 starknet/src/errors.ts create mode 100644 starknet/src/index.ts create mode 100644 starknet/src/types.ts create mode 100644 starknet/tsconfig.json diff --git a/.changeset/popular-trains-repair.md b/.changeset/popular-trains-repair.md new file mode 100644 index 00000000000..d0cab3f6198 --- /dev/null +++ b/.changeset/popular-trains-repair.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/starknet-core': major +--- + +feat: Add Starknet contract ABI fetching and contract artifact generation diff --git a/Dockerfile b/Dockerfile index a12642cefc3..1b4fac27cb6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ COPY typescript/github-proxy/package.json ./typescript/github-proxy/ COPY typescript/cosmos-types/package.json ./typescript/cosmos-types/ COPY typescript/cosmos-sdk/package.json ./typescript/cosmos-sdk/ COPY solidity/package.json ./solidity/ +COPY starknet/package.json ./starknet/ RUN yarn install && yarn cache clean diff --git a/package.json b/package.json index 5938b5b8bcb..f8ddfc9ae65 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ }, "workspaces": [ "solidity", - "typescript/*" + "typescript/*", + "starknet" ], "resolutions": { "async": "^2.6.4", diff --git a/starknet/.gitignore b/starknet/.gitignore new file mode 100644 index 00000000000..830b8da0b81 --- /dev/null +++ b/starknet/.gitignore @@ -0,0 +1,3 @@ +.env +dist +release diff --git a/starknet/README.md b/starknet/README.md new file mode 100644 index 00000000000..c2be3e15f87 --- /dev/null +++ b/starknet/README.md @@ -0,0 +1,96 @@ +# Hyperlane Starknet Core + +The Hyperlane Starknet Core package provides TypeScript tooling for interacting with Hyperlane's Cairo contracts on Starknet. It includes TypeScript artifacts autogenerated from Cairo contracts developed in collaboration with Astraly Labs. + +## Features + +- Pre-compiled Cairo contract ABI artifacts +- TypeScript bindings for Starknet contracts + +## Installation + +```bash +# Install with NPM +npm install @hyperlane-xyz/starknet-core + +# Or with Yarn +yarn add @hyperlane-xyz/starknet-core +``` + +## Requirements + +- Node.js 18 or newer +- For development: `curl`, `jq`, and `unzip` utilities + +## Usage + +```typescript +import { + ContractType, + getCompiledContract, +} from '@hyperlane-xyz/starknet-core'; + +// Get the Hyperlane mailbox contract +const mailboxContract = getCompiledContract('mailbox'); + +// Get token contracts +const tokenContract = getCompiledContract('erc20', ContractType.TOKEN); + +// Get mock contracts +const mockContract = getCompiledContract('mock_validator', ContractType.MOCK); +``` + +## Contract Categories + +Contracts are organized into three categories: + +- `contracts`: Core Hyperlane protocol contracts +- `tokens`: Token implementation contracts +- `mocks`: Test and mock contracts + +## Development + +### Setup + +1. Clone the repository +2. Install dependencies: `yarn install` + +### Build Process + +The build process combines multiple steps in a specific order: + +```bash +yarn build +``` + +This command runs: + +1. TypeScript compilation (`tsc`) +2. Fetching contract artifacts from GitHub (`fetch-contracts`) +3. Generating TypeScript ABI artifacts from Cairo contracts (`generate-artifacts`) + +All build output is placed in the `dist` directory. + +### Individual Build Steps + +You can also run the individual build steps separately: + +#### Fetching Contract Artifacts + +```bash +yarn fetch-contracts +``` + +This downloads the contract artifacts from the [Hyperlane Starknet repository](https://github.com/hyperlane-xyz/hyperlane_starknet). + +#### Generating TypeScript Artifacts + +```bash +yarn generate-artifacts +``` + +This creates JavaScript and TypeScript declaration files in the `dist/artifacts` directory. + +## License + +Apache 2.0 diff --git a/starknet/eslint.config.mjs b/starknet/eslint.config.mjs new file mode 100644 index 00000000000..dc3a8cf60fa --- /dev/null +++ b/starknet/eslint.config.mjs @@ -0,0 +1,15 @@ +import MonorepoDefaults from '../eslint.config.mjs'; + +export default [ + ...MonorepoDefaults, + { + files: ['**/*.ts'], + rules: { + 'no-console': 'off', + 'no-restricted-imports': 'off', + }, + }, + { + ignores: ['scripts/**/*'], + }, +]; diff --git a/starknet/package.json b/starknet/package.json new file mode 100644 index 00000000000..9a979e8ec01 --- /dev/null +++ b/starknet/package.json @@ -0,0 +1,47 @@ +{ + "name": "@hyperlane-xyz/starknet-core", + "description": "Core cairo contracts for Hyperlane", + "version": "1.0.0", + "type": "module", + "homepage": "https://www.hyperlane.xyz", + "license": "Apache-2.0", + "scripts": { + "fetch-contracts": "./scripts/fetch-contracts-release.sh", + "generate-artifacts": "tsx ./scripts/generate-artifacts.ts", + "build": "tsc && yarn fetch-contracts && yarn generate-artifacts", + "clean": "rm -rf ./dist ./release", + "lint": "eslint -c ./eslint.config.mjs .", + "prettier": "prettier --write ./src ./package.json" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "keywords": [ + "Hyperlane", + "Cairo", + "Starknet" + ], + "dependencies": { + "starknet": "^6.24.1" + }, + "devDependencies": { + "@eslint/js": "^9.15.0", + "@typescript-eslint/eslint-plugin": "^8.1.6", + "@typescript-eslint/parser": "^8.1.6", + "eslint": "^9.15.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-import": "^2.31.0", + "globby": "^14.1.0", + "prettier": "^3.5.3", + "tsx": "^4.19.1", + "typescript": "5.3.3" + } +} diff --git a/starknet/scripts/StarknetArtifactGenerator.ts b/starknet/scripts/StarknetArtifactGenerator.ts new file mode 100644 index 00000000000..85d04ae017b --- /dev/null +++ b/starknet/scripts/StarknetArtifactGenerator.ts @@ -0,0 +1,266 @@ +import { promises as fs } from 'fs'; +import { globby } from 'globby'; +import { basename, join } from 'path'; +import { CompiledContract } from 'starknet'; + +import { CONTRACT_SUFFIXES } from '../src/const.js'; +import { ContractClass, ContractType } from '../src/types.js'; + +import { Templates } from './Templates.js'; +import { prettierOutputTransformer } from './prettier.js'; + +type ProcessedFileInfo = { type: ContractType; sierra: boolean; casm: boolean }; +type ProcessedFilesMap = Map; +export type ReadonlyProcessedFilesMap = ReadonlyMap; + +export class StarknetArtifactGenerator { + private compiledContractsDir: string; + private rootOutputDir: string; + + constructor(compiledContractsDir: string, rootOutputDir: string) { + this.compiledContractsDir = compiledContractsDir; + this.rootOutputDir = rootOutputDir; + } + + getContractTypeFromPath(path: string): ContractType { + const fileName = basename(path); + if (fileName.startsWith('token_')) { + return ContractType.TOKEN; + } else if (fileName.startsWith('mocks_')) { + return ContractType.MOCK; + } + return ContractType.CONTRACT; + } + + getContractClassFromPath(filePath: string): ContractClass { + if (filePath.includes('compiled_contract_class')) { + return ContractClass.CASM; + } else if (filePath.includes('contract_class')) { + return ContractClass.SIERRA; + } else { + throw new Error(`Cannot determine contract class from path: ${filePath}`); + } + } + + /** + * @notice Retrieves paths of all relevant artifact files + */ + async getArtifactPaths() { + const sierraPattern = `${this.compiledContractsDir}/**/*${CONTRACT_SUFFIXES.SIERRA_JSON}`; + const [sierraFiles] = await Promise.all([globby(sierraPattern)]); + return { sierraFiles }; + } + + /** + * @notice Creates the output directory if it doesn't exist + */ + async createOutputDirectory() { + await fs.mkdir(this.rootOutputDir, { recursive: true }); + } + + /** + * @notice Reads and parses a JSON artifact file + */ + async readArtifactFile(filePath: string) { + const content = await fs.readFile(filePath, 'utf-8'); + return JSON.parse(content); + } + + /** + * @notice Generates JavaScript content for a contract artifact + */ + generateJavaScriptContent( + name: string, + artifact: any, + contractClass: ContractClass, + ) { + // For Sierra contracts, extract the ABI if the file contains contract_class in its name + if (contractClass === ContractClass.SIERRA) { + const abiOnly: CompiledContract = { + sierra_program: [], + contract_class_version: artifact.contract_class_version, + entry_points_by_type: artifact.entry_points_by_type, + abi: + typeof artifact.abi === 'string' + ? JSON.parse(artifact.abi) + : artifact.abi, + }; + + return Templates.jsArtifact(name, abiOnly); + } + // For other contract types, return the full artifact + return Templates.jsArtifact(name, artifact); + } + + /** + * @notice Generates TypeScript declaration content for a contract artifact + */ + generateDeclarationContent(name: string, isSierra: boolean) { + const type = isSierra ? 'CompiledContract' : 'CairoAssembly'; + return Templates.dtsArtifact(name, type); + } + + /** + * @notice Generates index file contents with categorized contracts + */ + generateIndexContents(processedFilesMap: ReadonlyProcessedFilesMap): { + jsContent: string; + dtsContent: string; + } { + const imports: string[] = []; + const contractExports: string[] = []; + const tokenExports: string[] = []; + const mockExports: string[] = []; + + processedFilesMap.forEach((value, name) => { + // Extracts the contract name by removing the prefix (contracts_, token_, or mocks_) + // Example: "token_HypErc20" becomes "HypErc20" + const baseName = name.replace( + new RegExp(`^(${Object.values(ContractType).join('|')})_?`), + '', + ); + + let sierraVarName: string | undefined; + let casmVarName: string | undefined; + + if (value.sierra) { + sierraVarName = `${value.type}_${baseName}_sierra`; + imports.push( + `import { ${name} as ${sierraVarName} } from './${name}.${ContractClass.SIERRA}.js';`, + ); + } + + if (value.casm) { + casmVarName = `${value.type}_${baseName}_casm`; + imports.push( + `import { ${name} as ${casmVarName} } from './${name}.${ContractClass.CASM}.js';`, + ); + } + + // exports with type guard filter + const exports = [ + sierraVarName ? `contract_class: ${sierraVarName}` : null, + casmVarName ? `compiled_contract_class: ${casmVarName}` : null, + ].filter((e): e is string => e !== null); + + const exportString = `${baseName}: { ${exports.join(', ')} },`; + + switch (value.type) { + case ContractType.TOKEN: + tokenExports.push(exportString); + break; + case ContractType.MOCK: + mockExports.push(exportString); + break; + default: // ContractType.CONTRACT + contractExports.push(exportString); + break; + } + }); + + return { + jsContent: Templates.jsIndex( + imports.join('\n'), + contractExports, + tokenExports, + mockExports, + ), + dtsContent: Templates.dtsIndex(), + }; + } + + /** + * @notice Processes a single artifact file + */ + async processArtifact(filePath: string) { + const contractType = this.getContractTypeFromPath(filePath); + const contractClass = this.getContractClassFromPath(filePath); + + const baseFileName = basename(filePath); + const name = baseFileName + .replace('.json', '') + .replace(`.${ContractClass.SIERRA}`, '') + .replace(`.${ContractClass.CASM}`, ''); + + const artifact = await this.readArtifactFile(filePath); + + // Generate and write files + const jsContent = this.generateJavaScriptContent( + name, + artifact, + contractClass, + ); + const dtsContent = this.generateDeclarationContent( + name, + contractClass === ContractClass.SIERRA, + ); + + const outputFileName = `${name}.${contractClass}`; + await fs.writeFile( + join(this.rootOutputDir, outputFileName + '.js'), + jsContent, // No prettier output needed + ); + await fs.writeFile( + join(this.rootOutputDir, outputFileName + '.d.ts'), + await prettierOutputTransformer(dtsContent), + ); + + return { name, contractType, contractClass }; + } + + private _aggregateProcessingResults( + processingResults: Array<{ + name: string; + contractType: ContractType; + contractClass: ContractClass; + }>, + ): ProcessedFilesMap { + const processedFilesMap = new Map(); + for (const result of processingResults) { + const fileInfo = processedFilesMap.get(result.name) || { + type: result.contractType, + sierra: false, + casm: false, + }; + + if (result.contractClass === ContractClass.SIERRA) { + fileInfo.sierra = true; + } else { + fileInfo.casm = true; + } + processedFilesMap.set(result.name, fileInfo); + } + return processedFilesMap; + } + + async generate(): Promise { + try { + await this.createOutputDirectory(); + + const { sierraFiles } = await this.getArtifactPaths(); + + const processingResults = await Promise.all( + sierraFiles.map((file) => this.processArtifact(file)), + ); + + const processedFilesMap = + this._aggregateProcessingResults(processingResults); + + const { jsContent, dtsContent } = + this.generateIndexContents(processedFilesMap); + + await fs.writeFile( + join(this.rootOutputDir, 'index.js'), + await prettierOutputTransformer(jsContent), + ); + await fs.writeFile( + join(this.rootOutputDir, 'index.d.ts'), + await prettierOutputTransformer(dtsContent), + ); + + return processedFilesMap; + } catch (error) { + throw error; + } + } +} diff --git a/starknet/scripts/Templates.ts b/starknet/scripts/Templates.ts new file mode 100644 index 00000000000..ee301e9c1cd --- /dev/null +++ b/starknet/scripts/Templates.ts @@ -0,0 +1,42 @@ +export class Templates { + static jsArtifact(name: string, artifact: any) { + return `export const ${name} = ${JSON.stringify(artifact)};`; + } + + static dtsArtifact(name: string, type: string) { + return ` + import type { CompiledContract, CairoAssembly } from 'starknet'; + export declare const ${name}: ${type}; + `; + } + + static jsIndex( + imports: string, + contractExports: string[], + tokenExports: string[], + mockExports: string[], + ) { + return ` +${imports} + +export const starknetContracts = { + contracts: { +${contractExports.join('\n')} + }, + token: { +${tokenExports.join('\n')} + }, + mocks: { +${mockExports.join('\n')} + }, +}; +`; + } + + static dtsIndex() { + return `import type { CairoAssembly, CompiledContract } from 'starknet'; + +import type { StarknetContracts } from '../types'; +export declare const starknetContracts: StarknetContracts;`; + } +} diff --git a/starknet/scripts/fetch-contracts-release.sh b/starknet/scripts/fetch-contracts-release.sh new file mode 100755 index 00000000000..d79af81ad3e --- /dev/null +++ b/starknet/scripts/fetch-contracts-release.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash + +# Strict mode configuration +set -euo pipefail +IFS=$'\n\t' + +# Constants +readonly REPO="hyperlane-xyz/hyperlane-starknet" +readonly GITHUB_RELEASES_API="https://api.github.com/repos/${REPO}/releases" +readonly TARGET_DIR="./release" +readonly VERSION="v0.3.2" + +# Color definitions +declare -r COLOR_GREEN='\033[0;32m' +declare -r COLOR_RED='\033[0;31m' +declare -r COLOR_RESET='\033[0m' + +log_error() { + echo -e "${COLOR_RED}Error: $1${COLOR_RESET}" >&2 +} + +log_success() { + echo -e "${COLOR_GREEN}$1${COLOR_RESET}" +} + +check_dependencies() { + local -r required_tools=("curl" "jq" "unzip") + + for tool in "${required_tools[@]}"; do + if ! command -v "$tool" &> /dev/null; then + log_error "$tool is not installed" + exit 1 + fi + done +} + +check_if_contracts_exist() { + if [[ -d "$TARGET_DIR" ]] && [[ "$(ls -A "$TARGET_DIR" 2>/dev/null)" ]]; then + log_success "Contracts already present in $TARGET_DIR, skipping fetch" + return 0 + fi + return 1 +} + +verify_version_exists() { + local version=$1 + if ! curl --output /dev/null --silent --head --fail "${GITHUB_RELEASES_API}/tags/${version}"; then + log_error "Version ${version} does not exist" + exit 1 + fi +} + +get_release_info() { + local version=$1 + local release_info + + release_info=$(curl -sf "${GITHUB_RELEASES_API}/tags/${version}") || { + log_error "Failed to fetch release information for version ${version}" + exit 1 + } + echo "$release_info" +} + +download_and_extract() { + local version=$1 + local download_url=$2 + local base_url="${download_url%/*}" + local filename="${download_url##*/}" + + if ! mkdir -p "$TARGET_DIR"; then + log_error "Failed to create target directory" + exit 1 + fi + + log_success "Downloading version ${version} from ${download_url}" + + if ! curl -L "$download_url" -o "${TARGET_DIR}/release.zip"; then + log_error "Download failed" + exit 1 + fi + + if ! verify_checksum "${TARGET_DIR}/release.zip" "$base_url" "$filename"; then + rm -f "${TARGET_DIR}/release.zip" + exit 1 + fi + + if ! unzip -o "${TARGET_DIR}/release.zip" -d "${TARGET_DIR}"; then + log_error "Extraction failed" + exit 1 + fi +} + +verify_checksum() { + local file_path="$1" + local base_url="$2" + local filename="$3" + local checksum_filename + checksum_filename="${filename%.zip}.CHECKSUM" + + local downloaded_checksum + downloaded_checksum="$(sha256sum "$file_path" | cut -d' ' -f1)" + log_success "File checksum: ${downloaded_checksum}" + + local expected_checksum + if ! expected_checksum="$(curl -sL "${base_url}/${checksum_filename}")"; then + log_error "Failed to fetch checksum file" + return 1 + fi + + if [[ "${downloaded_checksum}" != "$(echo "${expected_checksum}" | awk '{print $1}')" ]]; then + log_error "Checksum verification failed" + return 1 + fi + + return 0 +} + +cleanup() { + rm -f "$TARGET_DIR/release.zip" + rm -f "$TARGET_DIR"/*.md5 + rm -f "$TARGET_DIR"/*.sha256 +} + +main() { + trap cleanup EXIT + + check_dependencies + + # Skip if contracts already exist + if check_if_contracts_exist; then + exit 0 + fi + + log_success "Using version ${VERSION} from package.json" + verify_version_exists "$VERSION" + + local release_info + release_info=$(get_release_info "$VERSION") + + local download_url + download_url=$(echo "$release_info" | jq -r '.assets[] | select(.name | startswith("hyperlane-starknet") and endswith(".zip")) | .browser_download_url') + + if [[ -z "$download_url" ]]; then + log_error "Could not find ZIP download URL for release" + exit 1 + fi + + # Process download and file checksum verification and extraction + download_and_extract "$VERSION" "$download_url" + + log_success "Successfully downloaded and extracted version ${VERSION}" +} + +main diff --git a/starknet/scripts/generate-artifacts.ts b/starknet/scripts/generate-artifacts.ts new file mode 100644 index 00000000000..d7955f36afd --- /dev/null +++ b/starknet/scripts/generate-artifacts.ts @@ -0,0 +1,25 @@ +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +import { StarknetArtifactGenerator } from './StarknetArtifactGenerator.js'; + +const cwd = process.cwd(); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const DEFAULT_ROOT_OUTPUT_DIR = join(__dirname, '../dist/artifacts/'); +const DEFAULT_COMPILED_CONTRACTS_DIR = join(cwd, 'release'); + +(async () => { + try { + const generator = new StarknetArtifactGenerator( + DEFAULT_COMPILED_CONTRACTS_DIR, + DEFAULT_ROOT_OUTPUT_DIR, + ); + const processedFiles = await generator.generate(); + console.log(`Successfully generated ${processedFiles.size} artifacts`); + } catch (error) { + console.error('Artifact generation failed:', error); + process.exit(1); + } +})(); diff --git a/starknet/scripts/prettier.ts b/starknet/scripts/prettier.ts new file mode 100644 index 00000000000..11fc18a5688 --- /dev/null +++ b/starknet/scripts/prettier.ts @@ -0,0 +1,7 @@ +import prettier from 'prettier'; + +export async function prettierOutputTransformer( + output: string, +): Promise { + return prettier.format(output, { parser: 'typescript' }); +} diff --git a/starknet/src/artifacts/index.ts b/starknet/src/artifacts/index.ts new file mode 100644 index 00000000000..66ceae09a7a --- /dev/null +++ b/starknet/src/artifacts/index.ts @@ -0,0 +1,7 @@ +// Default empty artifact array when `yarn generate-artifacts` hasn't been run +// This file will be populated with contract artifacts in `dist/artifacts` directory after running the `generate-artifacts` command +export const starknetContracts = { + contracts: {}, + token: {}, + mocks: {}, +} as const; diff --git a/starknet/src/const.ts b/starknet/src/const.ts new file mode 100644 index 00000000000..1d7a37694ee --- /dev/null +++ b/starknet/src/const.ts @@ -0,0 +1,10 @@ +export const CONTRACT_SUFFIXES = { + SIERRA_JSON: '.contract_class.json', // Sierra is the high-level representation + ASSEMBLY_JSON: '.compiled_contract_class.json', // Cairo assembly (CASM) is the low-level bytecode +} as const; + +export const ERR_CODES = { + INVALID_CONTRACT_TYPE: 'INVALID_CONTRACT_TYPE', + CONTRACT_NOT_FOUND: 'CONTRACT_NOT_FOUND', + SIERRA_NOT_FOUND: 'SIERRA_NOT_FOUND', +} as const; diff --git a/starknet/src/contract-retriever.ts b/starknet/src/contract-retriever.ts new file mode 100644 index 00000000000..aaabdebcc17 --- /dev/null +++ b/starknet/src/contract-retriever.ts @@ -0,0 +1,53 @@ +import { CompiledContract } from 'starknet'; + +import { starknetContracts } from './artifacts/index.js'; +import { ERR_CODES } from './const.js'; +import { ContractError } from './errors.js'; +import { ContractType, StarknetContractGroup } from './types.js'; + +/** + * @notice Retrieves a compiled contract + * @param name The name of the contract to retrieve + * @param contractType The type of contract to retrieve + * @returns {CompiledContract} The contract data + * @throws {ContractError} If the contract is not found + */ +export function getCompiledContract( + name: string, + contractType: ContractType = ContractType.CONTRACT, +): CompiledContract { + const group = getContractGroup(contractType); + const contract = group[name]; + + if (!contract) { + throw new ContractError(ERR_CODES.CONTRACT_NOT_FOUND, { + name, + type: contractType, + }); + } + + if (!contract.contract_class) { + throw new ContractError(ERR_CODES.SIERRA_NOT_FOUND, { + name, + type: contractType, + }); + } + + return contract.contract_class; +} + +/** + * @notice Helper function to get the correct contract group + * @param type The type of contract to retrieve + * @returns {StarknetContractGroup} The contract group + * @throws {ContractError} If the contract group is non-existent + */ +function getContractGroup(type: ContractType): StarknetContractGroup { + const group = starknetContracts[type]; + if (!group) { + throw new ContractError(ERR_CODES.INVALID_CONTRACT_TYPE, { + type, + }); + } + return group; +} diff --git a/starknet/src/errors.ts b/starknet/src/errors.ts new file mode 100644 index 00000000000..df7e846fc4c --- /dev/null +++ b/starknet/src/errors.ts @@ -0,0 +1,9 @@ +export class ContractError extends Error { + constructor( + public readonly code: string, + public readonly details?: unknown, + ) { + super(`[${code}] ${details ? JSON.stringify(details) : ''}`); + this.name = 'ContractError'; + } +} diff --git a/starknet/src/index.ts b/starknet/src/index.ts new file mode 100644 index 00000000000..b0003896182 --- /dev/null +++ b/starknet/src/index.ts @@ -0,0 +1,2 @@ +export * from './contract-retriever.js'; +export * from './types.js'; diff --git a/starknet/src/types.ts b/starknet/src/types.ts new file mode 100644 index 00000000000..8d39b5e6371 --- /dev/null +++ b/starknet/src/types.ts @@ -0,0 +1,36 @@ +import type { CairoAssembly, CompiledContract } from 'starknet'; + +/** + * Represents a group of Starknet contracts + * both Sierra (contract_class) and CASM (compiled_contract_class) formats. + */ +export interface StarknetContractGroup { + [name: string]: { + contract_class: CompiledContract; + compiled_contract_class: CairoAssembly; + }; +} + +/** + * Defines the overall structure for organizing Starknet contracts + * into logical categories (contracts, token, mocks). + */ +export interface StarknetContracts { + contracts: StarknetContractGroup; + token: StarknetContractGroup; + mocks: StarknetContractGroup; +} + +/** + * @notice Contract file type enum + */ +export enum ContractType { + CONTRACT = 'contracts', + TOKEN = 'token', + MOCK = 'mocks', +} + +export enum ContractClass { + SIERRA = 'contract_class', + CASM = 'compiled_contract_class', +} diff --git a/starknet/tsconfig.json b/starknet/tsconfig.json new file mode 100644 index 00000000000..0ba1b01ae9e --- /dev/null +++ b/starknet/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"] +} diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 5fc5bcbbae2..ed139458d3a 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -21,7 +21,7 @@ "cross-fetch": "^3.1.5", "ethers": "^5.7.2", "pino": "^8.19.0", - "starknet": "^6.23.1", + "starknet": "^6.24.1", "viem": "^2.21.45", "zksync-ethers": "^5.10.0", "zod": "^3.21.2" diff --git a/yarn.lock b/yarn.lock index 89ec5538b00..3b0e5b2c3a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7965,7 +7965,7 @@ __metadata: pino: "npm:^8.19.0" prettier: "npm:^3.5.3" sinon: "npm:^13.0.2" - starknet: "npm:^6.23.1" + starknet: "npm:^6.24.1" ts-node: "npm:^10.8.0" tsx: "npm:^4.19.1" typescript: "npm:5.3.3" @@ -7979,6 +7979,25 @@ __metadata: languageName: unknown linkType: soft +"@hyperlane-xyz/starknet-core@workspace:starknet": + version: 0.0.0-use.local + resolution: "@hyperlane-xyz/starknet-core@workspace:starknet" + dependencies: + "@eslint/js": "npm:^9.15.0" + "@typescript-eslint/eslint-plugin": "npm:^8.1.6" + "@typescript-eslint/parser": "npm:^8.1.6" + eslint: "npm:^9.15.0" + eslint-config-prettier: "npm:^9.1.0" + eslint-import-resolver-typescript: "npm:^3.6.3" + eslint-plugin-import: "npm:^2.31.0" + globby: "npm:^14.1.0" + prettier: "npm:^3.5.3" + starknet: "npm:^6.24.1" + tsx: "npm:^4.19.1" + typescript: "npm:5.3.3" + languageName: unknown + linkType: soft + "@hyperlane-xyz/utils@npm:12.6.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" @@ -35347,9 +35366,9 @@ __metadata: languageName: node linkType: hard -"starknet@npm:^6.23.1": - version: 6.23.1 - resolution: "starknet@npm:6.23.1" +"starknet@npm:^6.24.1": + version: 6.24.1 + resolution: "starknet@npm:6.24.1" dependencies: "@noble/curves": "npm:1.7.0" "@noble/hashes": "npm:1.6.0" @@ -35362,7 +35381,7 @@ __metadata: pako: "npm:^2.0.4" starknet-types-07: "npm:@starknet-io/types-js@^0.7.10" ts-mixer: "npm:^6.0.3" - checksum: 10/ad02fddad1f24bc9b107ca8ef894264176297a59b65b6db6f1d28c9af3900035373b01f002443295bcd2c70968665f206790e4eadd4a601a3639c5da6bb719a3 + checksum: 10/7feb01cfe9310a6669a7ca2926c525084f8ec137918db87e6087a0e0ca806a3bbaedb7a25da44cf1fddc3d87cf4e05a39e0180166af87a83c156a8f49bf67197 languageName: node linkType: hard From 1482467faf063b67d2dcbaba31d08ed06f8bf785 Mon Sep 17 00:00:00 2001 From: Andrey Taranov <86911+antigremlin@users.noreply.github.com> Date: Thu, 15 May 2025 17:35:36 +0100 Subject: [PATCH 188/223] feat: add tUSD/eclipsemainnet-ethereum warp route (#6220) ### Description This PR adds the Turbo USD (tUSD) warp route from Ethereum to Eclipse. Client: Nucleus/Eclipse. ### Related issues - [Nucleus Deployment](https://www.notion.so/hyperlanexyz/Nucleus-1dd6d35200d680feb39cc050ed2e1059?pvs=4) ### Backward compatibility Yes ### Testing Testing only possible via UI as our CLI tools don't support EVM-SVM roundtrip transfers. The UI testing is not yet possible because the tUSD token on Ethereum is not fully configured. --- .../program-ids.json | 10 ++++++++++ .../token-config.json | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/token-config.json diff --git a/rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/program-ids.json new file mode 100644 index 00000000000..09139d330f3 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/program-ids.json @@ -0,0 +1,10 @@ +{ + "eclipsemainnet": { + "hex": "0x3c141cbabe775eb1cb82d357bed3724e39e4fd2de256aee2a69de336d360d97a", + "base58": "53XFTNcTRTzrNyD3QodXbRBZ249G8v3iSHh8FzYeHZoX" + }, + "ethereum": { + "hex": "0x00000000000000000000000069b1a59e98d94e1b9647c08cb295fc52597d20f0", + "base58": "1111111111112UQUH3pY5HJ7m8iy1sgSB5Gs9dvX" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/token-config.json new file mode 100644 index 00000000000..e0d83cd240b --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/tUSD-eclipsemainnet-ethereum/token-config.json @@ -0,0 +1,17 @@ +{ + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "remoteDecimals": 6, + "name": "Turbo USD", + "symbol": "tUSD", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/71b97e47ffe47348b2ea9cb1bde344ca78daea50/deployments/warp_routes/tUSD/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "ethereum": { + "type": "collateral", + "decimals": 6, + "token": "0x722a851B6798D65b80526562Fc3a36E19b1F883b", + "foreignDeployment": "0x69B1A59e98D94E1B9647C08Cb295Fc52597D20f0" + } +} From 0de63e097c04c8b8ec1eac14e05bc116963ad689 Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Fri, 16 May 2025 12:34:45 +0200 Subject: [PATCH 189/223] feat: add Starknet address and transaction utilities [STARKNET-02] (#5851) ### Description 2nd Starknet PR This PR introduces utilities for starknet addresses. [Diff with previous PR - feat: Add Starknet contract ABI fetching and contract artifact generation #5830](https://github.com/txfusion/hyperlane-monorepo/pull/27/files) ### Drive-by changes - Exports for starknet address from typescript/utils ### Related issues None ### Backward compatibility Yes ### Testing None --- .changeset/metal-coats-remain.md | 5 +++ typescript/utils/package.json | 1 + typescript/utils/src/addresses.test.ts | 14 ++++++ typescript/utils/src/addresses.ts | 60 +++++++++++++++++++++++++- typescript/utils/src/index.ts | 7 +++ yarn.lock | 1 + 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .changeset/metal-coats-remain.md diff --git a/.changeset/metal-coats-remain.md b/.changeset/metal-coats-remain.md new file mode 100644 index 00000000000..bda7ba34165 --- /dev/null +++ b/.changeset/metal-coats-remain.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/utils': minor +--- + +Add Starknet address and tx utils diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 2e9e6e40e9f..fbf537b863f 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -9,6 +9,7 @@ "ethers": "^5.7.2", "lodash-es": "^4.17.21", "pino": "^8.19.0", + "starknet": "^6.24.1", "yaml": "2.4.5" }, "devDependencies": { diff --git a/typescript/utils/src/addresses.test.ts b/typescript/utils/src/addresses.test.ts index 2313ce384cf..89b706b59d7 100644 --- a/typescript/utils/src/addresses.test.ts +++ b/typescript/utils/src/addresses.test.ts @@ -15,6 +15,10 @@ const COS_NON_ZERO_ADDR = 'neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq'; const SOL_ZERO_ADDR = '111111'; const SOL_NON_ZERO_ADDR = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'; +const STARKNET_ZERO_ADDR = + '0x0000000000000000000000000000000000000000000000000000000000000000'; +const STARKNET_NON_ZERO_ADDR = + '0x0000000000000000000000000000000000000000000000000000000000000001'; // TODO increase address utility test coverage describe('Address utilities', () => { @@ -24,22 +28,26 @@ describe('Address utilities', () => { expect(isZeroishAddress(ETH_ZERO_ADDR)).to.be.true; expect(isZeroishAddress(COS_ZERO_ADDR)).to.be.true; expect(isZeroishAddress(SOL_ZERO_ADDR)).to.be.true; + expect(isZeroishAddress(STARKNET_ZERO_ADDR)).to.be.true; }); it('Identifies non-0-ish addresses', () => { expect(isZeroishAddress(ETH_NON_ZERO_ADDR)).to.be.false; expect(isZeroishAddress(COS_NON_ZERO_ADDR)).to.be.false; expect(isZeroishAddress(SOL_NON_ZERO_ADDR)).to.be.false; + expect(isZeroishAddress(STARKNET_NON_ZERO_ADDR)).to.be.false; }); }); describe('addressToBytes', () => { it('Converts addresses to bytes', () => { expect(addressToBytes(ETH_NON_ZERO_ADDR).length).to.equal(32); + expect(addressToBytes(STARKNET_NON_ZERO_ADDR).length).to.equal(32); }); it('Rejects zeroish addresses', () => { expect(() => addressToBytes(ETH_ZERO_ADDR)).to.throw(Error); expect(() => addressToBytes(COS_ZERO_ADDR)).to.throw(Error); expect(() => addressToBytes(SOL_ZERO_ADDR)).to.throw(Error); + expect(() => addressToBytes(STARKNET_ZERO_ADDR)).to.throw(Error); }); }); @@ -62,6 +70,12 @@ describe('Address utilities', () => { ProtocolType.Ethereum, ), ).to.equal(ETH_NON_ZERO_ADDR); + expect( + bytesToProtocolAddress( + addressToBytes(STARKNET_NON_ZERO_ADDR), + ProtocolType.Starknet, + ), + ).to.equal(STARKNET_NON_ZERO_ADDR); }); it('Rejects zeroish addresses', () => { expect(() => diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index 2fc6620629d..0af833cb153 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -1,6 +1,12 @@ import { fromBech32, normalizeBech32, toBech32 } from '@cosmjs/encoding'; import { PublicKey } from '@solana/web3.js'; import { Wallet, utils as ethersUtils } from 'ethers'; +import { + addAddressPadding, + encode, + num, + validateAndParseAddress, +} from 'starknet'; import { isNullish } from './typeof.js'; import { Address, HexString, ProtocolType } from './types.js'; @@ -8,6 +14,7 @@ import { assert } from './validation.js'; const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/; const SEALEVEL_ADDRESS_REGEX = /^[a-zA-Z0-9]{36,44}$/; +const STARKNET_ADDRESS_REGEX = /^(0x)?[0-9a-fA-F]{64}$/; const HEX_BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/; @@ -24,10 +31,12 @@ const COSMOS_FACTORY_TOKEN_REGEX = new RegExp( const EVM_TX_HASH_REGEX = /^0x([A-Fa-f0-9]{64})$/; const SEALEVEL_TX_HASH_REGEX = /^[a-zA-Z1-9]{88}$/; const COSMOS_TX_HASH_REGEX = /^(0x)?[A-Fa-f0-9]{64}$/; +const STARKNET_TX_HASH_REGEX = /^(0x)?[0-9a-fA-F]{64}$/; const EVM_ZEROISH_ADDRESS_REGEX = /^(0x)?0*$/; const SEALEVEL_ZEROISH_ADDRESS_REGEX = /^1+$/; const COSMOS_ZEROISH_ADDRESS_REGEX = /^[a-z]{1,10}?1[0]+$/; +const STARKNET_ZEROISH_ADDRESS_REGEX = /^(0x)?0*$/; export const ZERO_ADDRESS_HEX_32 = '0x0000000000000000000000000000000000000000000000000000000000000000'; @@ -52,6 +61,10 @@ export function isCosmosIbcDenomAddress(address: Address): boolean { return IBC_DENOM_REGEX.test(address); } +export function isAddressStarknet(address: Address) { + return STARKNET_ADDRESS_REGEX.test(address); +} + export function getAddressProtocolType(address: Address) { if (!address) return undefined; if (isAddressEvm(address)) { @@ -60,6 +73,8 @@ export function getAddressProtocolType(address: Address) { return ProtocolType.Cosmos; } else if (isAddressSealevel(address)) { return ProtocolType.Sealevel; + } else if (isAddressStarknet(address)) { + return ProtocolType.Starknet; } else { return undefined; } @@ -116,6 +131,15 @@ export function isValidAddressCosmos(address: Address) { } } +export function isValidAddressStarknet(address: Address) { + try { + const isValid = address && validateAndParseAddress(address); + return !!isValid; + } catch { + return false; + } +} + export function isValidAddress(address: Address, protocol?: ProtocolType) { return routeAddressUtil( { @@ -123,6 +147,7 @@ export function isValidAddress(address: Address, protocol?: ProtocolType) { [ProtocolType.Sealevel]: isValidAddressSealevel, [ProtocolType.Cosmos]: isValidAddressCosmos, [ProtocolType.CosmosNative]: isValidAddressCosmos, + [ProtocolType.Starknet]: isValidAddressStarknet, }, address, false, @@ -157,6 +182,14 @@ export function normalizeAddressCosmos(address: Address) { } } +export function normalizeAddressStarknet(address: Address) { + if (isZeroishAddress(address)) return address; + try { + return validateAndParseAddress(address); + } catch { + return address; + } +} export function normalizeAddress(address: Address, protocol?: ProtocolType) { return routeAddressUtil( { @@ -183,6 +216,10 @@ export function eqAddressCosmos(a1: Address, a2: Address) { return normalizeAddressCosmos(a1) === normalizeAddressCosmos(a2); } +export function eqAddressStarknet(a1: Address, a2: Address) { + return normalizeAddressStarknet(a1) === normalizeAddressStarknet(a2); +} + export function eqAddress(a1: Address, a2: Address) { const p1 = getAddressProtocolType(a1); const p2 = getAddressProtocolType(a2); @@ -193,6 +230,7 @@ export function eqAddress(a1: Address, a2: Address) { [ProtocolType.Sealevel]: (_a1) => eqAddressSol(_a1, a2), [ProtocolType.Cosmos]: (_a1) => eqAddressCosmos(_a1, a2), [ProtocolType.CosmosNative]: (_a1) => eqAddressCosmos(_a1, a2), + [ProtocolType.Starknet]: (_a1) => eqAddressStarknet(_a1, a2), }, a1, false, @@ -212,6 +250,10 @@ export function isValidTransactionHashCosmos(input: string) { return COSMOS_TX_HASH_REGEX.test(input); } +export function isValidTransactionHashStarknet(input: string) { + return STARKNET_TX_HASH_REGEX.test(input); +} + export function isValidTransactionHash(input: string, protocol: ProtocolType) { if (protocol === ProtocolType.Ethereum) { return isValidTransactionHashEvm(input); @@ -221,6 +263,8 @@ export function isValidTransactionHash(input: string, protocol: ProtocolType) { return isValidTransactionHashCosmos(input); } else if (protocol === ProtocolType.CosmosNative) { return isValidTransactionHashCosmos(input); + } else if (protocol === ProtocolType.Starknet) { + return isValidTransactionHashStarknet(input); } else { return false; } @@ -230,7 +274,8 @@ export function isZeroishAddress(address: Address) { return ( EVM_ZEROISH_ADDRESS_REGEX.test(address) || SEALEVEL_ZEROISH_ADDRESS_REGEX.test(address) || - COSMOS_ZEROISH_ADDRESS_REGEX.test(address) + COSMOS_ZEROISH_ADDRESS_REGEX.test(address) || + STARKNET_ZEROISH_ADDRESS_REGEX.test(address) ); } @@ -275,6 +320,11 @@ export function addressToBytesCosmos(address: Address): Uint8Array { return fromBech32(address).data; } +export function addressToBytesStarknet(address: Address): Uint8Array { + const normalizedAddress = validateAndParseAddress(address); + return num.hexToBytes(normalizedAddress); +} + export function addressToBytes( address: Address, protocol?: ProtocolType, @@ -285,6 +335,7 @@ export function addressToBytes( [ProtocolType.Sealevel]: addressToBytesSol, [ProtocolType.Cosmos]: addressToBytesCosmos, [ProtocolType.CosmosNative]: addressToBytesCosmos, + [ProtocolType.Starknet]: addressToBytesStarknet, }, address, new Uint8Array(), @@ -353,6 +404,11 @@ export function bytesToAddressCosmos( return toBech32(prefix, bytes); } +export function bytesToAddressStarknet(bytes: Uint8Array): Address { + const hexString = encode.buf2hex(bytes); + return addAddressPadding(hexString); +} + export function bytesToProtocolAddress( bytes: Uint8Array, toProtocol: ProtocolType, @@ -370,6 +426,8 @@ export function bytesToProtocolAddress( return bytesToAddressCosmos(bytes, prefix!); } else if (toProtocol === ProtocolType.CosmosNative) { return bytesToAddressCosmos(bytes, prefix!); + } else if (toProtocol === ProtocolType.Starknet) { + return bytesToAddressStarknet(bytes); } else { throw new Error(`Unsupported protocol for address ${toProtocol}`); } diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index d9591aa4ab3..412f83fea06 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -5,10 +5,12 @@ export { addressToBytesCosmos, addressToBytesEvm, addressToBytesSol, + addressToBytesStarknet, bytes32ToAddress, bytesToAddressCosmos, bytesToAddressEvm, bytesToAddressSol, + bytesToAddressStarknet, bytesToProtocolAddress, capitalizeAddress, convertToProtocolAddress, @@ -17,26 +19,31 @@ export { eqAddressCosmos, eqAddressEvm, eqAddressSol, + eqAddressStarknet, getAddressProtocolType, isAddress, isAddressCosmos, isCosmosIbcDenomAddress, isAddressEvm, isAddressSealevel, + isAddressStarknet, isValidAddress, isValidAddressCosmos, isValidAddressEvm, isValidAddressSealevel, + isValidAddressStarknet, isPrivateKeyEvm, isValidTransactionHash, isValidTransactionHashCosmos, isValidTransactionHashEvm, isValidTransactionHashSealevel, + isValidTransactionHashStarknet, isZeroishAddress, normalizeAddress, normalizeAddressCosmos, normalizeAddressEvm, normalizeAddressSealevel, + normalizeAddressStarknet, padBytesToLength, shortenAddress, strip0x, diff --git a/yarn.lock b/yarn.lock index 3b0e5b2c3a5..9bc85b233ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8023,6 +8023,7 @@ __metadata: pino: "npm:^8.19.0" prettier: "npm:^3.5.3" sinon: "npm:^13.0.2" + starknet: "npm:^6.24.1" typescript: "npm:5.3.3" yaml: "npm:2.4.5" languageName: unknown From b98468d529672b1006dc41ba173be1ec936101b3 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 16 May 2025 12:28:11 +0100 Subject: [PATCH 190/223] fix: monorepo docker builds (#6245) ### Description fix: monorepo docker builds ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1b4fac27cb6..edbe7038b0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:20-alpine WORKDIR /hyperlane-monorepo -RUN apk add --update --no-cache git g++ make py3-pip jq +RUN apk add --update --no-cache git g++ make py3-pip jq bash curl RUN yarn set version 4.5.1 @@ -22,7 +22,7 @@ COPY typescript/github-proxy/package.json ./typescript/github-proxy/ COPY typescript/cosmos-types/package.json ./typescript/cosmos-types/ COPY typescript/cosmos-sdk/package.json ./typescript/cosmos-sdk/ COPY solidity/package.json ./solidity/ -COPY starknet/package.json ./starknet/ +COPY starknet/package.json ./starknet/ RUN yarn install && yarn cache clean @@ -30,6 +30,7 @@ RUN yarn install && yarn cache clean COPY tsconfig.json ./ COPY typescript ./typescript COPY solidity ./solidity +COPY starknet ./starknet RUN yarn build From 69b4640a75c7b6772348a9dcccfc0c87b6699133 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 16 May 2025 14:51:33 +0100 Subject: [PATCH 191/223] fix: disable suavetoliman + sonicsvmtestnet in infra (#6248) ### Description fix: disable suavetoliman + sonicsvmtestnet in infra ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .../infra/config/environments/testnet4/agent.ts | 16 ++++++++-------- .../testnet4/aw-validators/hyperlane.json | 6 ------ .../config/environments/testnet4/funding.ts | 4 ++-- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 600926f7215..ad13ea9a095 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -86,8 +86,8 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< soneiumtestnet: true, somniatestnet: true, sonicblaze: true, - sonicsvmtestnet: true, - suavetoliman: true, + sonicsvmtestnet: false, + suavetoliman: false, subtensortestnet: true, superpositiontestnet: true, unichaintestnet: true, @@ -134,8 +134,8 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< soneiumtestnet: true, somniatestnet: true, sonicblaze: true, - sonicsvmtestnet: true, - suavetoliman: true, + sonicsvmtestnet: false, + suavetoliman: false, subtensortestnet: true, superpositiontestnet: true, unichaintestnet: true, @@ -183,7 +183,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< soneiumtestnet: true, sonicblaze: true, sonicsvmtestnet: false, - suavetoliman: true, + suavetoliman: false, subtensortestnet: true, superpositiontestnet: false, unichaintestnet: true, @@ -389,7 +389,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '1363c76-20250507-120738', + tag: 'b98468d-20250516-114242', }, blacklist: [...releaseCandidateHelloworldMatchingList, ...relayBlacklist], gasPaymentEnforcement, @@ -413,7 +413,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '1363c76-20250507-120738', + tag: 'b98468d-20250516-114242', }, resources: scraperResources, }, @@ -428,7 +428,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd9e0b4b-20250425-145730', + tag: 'b98468d-20250516-114242', }, blacklist: relayBlacklist, gasPaymentEnforcement, diff --git a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json index 516f5e78c31..4d8a5b38e2e 100644 --- a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json @@ -136,12 +136,6 @@ "sonicblaze": { "validators": ["0xe5b98110d0688691ea280edea9a4faa1e3617ba1"] }, - "sonicsvmtestnet": { - "validators": ["0x83d4ef35f170ec822a0eaadb22a0c40003d8de23"] - }, - "suavetoliman": { - "validators": ["0xf58f6e30aabba34e8dd7f79b3168507192e2cc9b"] - }, "subtensortestnet": { "validators": ["0xbe2cd57e9fd46b12107cfec7a2db61aa23edbe33"] }, diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 17317a86706..3aba1b94f72 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -10,7 +10,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '2f5ddd8-20250506-163536', + tag: 'b98468d-20250516-114244', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron @@ -24,7 +24,7 @@ export const keyFunderConfig: KeyFunderConfig< [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], }, - chainsToSkip: ['hyperliquidevmtestnet'], + chainsToSkip: ['hyperliquidevmtestnet', 'suavetoliman'], // desired balance config desiredBalancePerChain: { abstracttestnet: '0.1', From bc58283e62385e759d8fc56ab8cd6dc791061cd1 Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Fri, 16 May 2025 15:53:53 +0200 Subject: [PATCH 192/223] feat: Starknet SDK logic integration [STARKNET-03] (#5838) ### Description 3nd Starknet PR [Diff with previous PR - feat: Starknet address utils #5851](https://github.com/txfusion/hyperlane-monorepo/pull/28/files) This PR introduces Starknet logic to enable token warping on the UI. ### Drive-by changes - Utilities for working with Hyperlane messaging on Starknet - Starknet testChain obj ### Related issues None ### Backward compatibility Yes ### Testing Manual testing of token warping using warp ui template --- .changeset/hip-papayas-kiss.md | 5 + typescript/sdk/package.json | 1 + typescript/sdk/src/app/MultiProtocolApp.ts | 9 + typescript/sdk/src/consts/testChains.ts | 19 ++ typescript/sdk/src/core/MultiProtocolCore.ts | 2 + .../src/core/adapters/StarknetCoreAdapter.ts | 127 ++++++++ typescript/sdk/src/index.ts | 14 + .../src/providers/MultiProtocolProvider.ts | 10 + typescript/sdk/src/providers/MultiProvider.ts | 2 +- typescript/sdk/src/providers/ProviderType.ts | 7 +- typescript/sdk/src/providers/rpcHealthTest.ts | 13 + .../src/providers/transactionFeeEstimators.ts | 21 ++ typescript/sdk/src/token/Token.ts | 17 + .../token/adapters/StarknetTokenAdapter.ts | 293 ++++++++++++++++++ typescript/sdk/src/utils/starknet.ts | 129 ++++++++ typescript/sdk/src/warp/WarpCore.ts | 5 + .../sdk/src/warp/test-warp-core-config.yaml | 8 + yarn.lock | 3 +- 18 files changed, 679 insertions(+), 6 deletions(-) create mode 100644 .changeset/hip-papayas-kiss.md create mode 100644 typescript/sdk/src/core/adapters/StarknetCoreAdapter.ts create mode 100644 typescript/sdk/src/token/adapters/StarknetTokenAdapter.ts create mode 100644 typescript/sdk/src/utils/starknet.ts diff --git a/.changeset/hip-papayas-kiss.md b/.changeset/hip-papayas-kiss.md new file mode 100644 index 00000000000..8ed6b86f2d6 --- /dev/null +++ b/.changeset/hip-papayas-kiss.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +feat: Starknet SDK logic integration diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index ed139458d3a..9ba90d65260 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -10,6 +10,7 @@ "@cosmjs/stargate": "^0.32.4", "@hyperlane-xyz/core": "7.1.5", "@hyperlane-xyz/cosmos-sdk": "12.6.0", + "@hyperlane-xyz/starknet-core": "1.0.0", "@hyperlane-xyz/utils": "12.6.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", diff --git a/typescript/sdk/src/app/MultiProtocolApp.ts b/typescript/sdk/src/app/MultiProtocolApp.ts index 8f2baa322e2..d5426971308 100644 --- a/typescript/sdk/src/app/MultiProtocolApp.ts +++ b/typescript/sdk/src/app/MultiProtocolApp.ts @@ -17,6 +17,7 @@ import { CosmJsWasmProvider, EthersV5Provider, SolanaWeb3Provider, + StarknetJsProvider, TypedProvider, } from '../providers/ProviderType.js'; import { ChainMap, ChainName } from '../types.js'; @@ -104,6 +105,14 @@ export class BaseSealevelAdapter extends BaseAppAdapter { } } +export class BaseStarknetAdapter extends BaseAppAdapter { + public readonly protocol: ProtocolType = ProtocolType.Starknet; + + public getProvider(): StarknetJsProvider['provider'] { + return this.multiProvider.getStarknetProvider(this.chainName); + } +} + /** * A version of HyperlaneApp that can support different * provider types across different protocol types. diff --git a/typescript/sdk/src/consts/testChains.ts b/typescript/sdk/src/consts/testChains.ts index a983e145fd2..b63e48ce490 100644 --- a/typescript/sdk/src/consts/testChains.ts +++ b/typescript/sdk/src/consts/testChains.ts @@ -143,6 +143,24 @@ export const testSealevelChain: ChainMetadata = { rpcUrls: [{ http: 'http://127.0.0.1:8899' }], }; +export const testStarknetChain: ChainMetadata = { + chainId: '0x534e5f5345504f4c4941', + domainId: 5854809, + name: 'starknetdevnet', + nativeToken: { + decimals: 18, + denom: '0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7', + name: 'Ether', + symbol: 'ETH', + }, + protocol: ProtocolType.Starknet, + rpcUrls: [ + { + http: 'http://127.0.0.1:5050', + }, + ], +}; + export const multiProtocolTestChainMetadata: ChainMap = { ...testChainMetadata, testcosmos: testCosmosChain, @@ -150,6 +168,7 @@ export const multiProtocolTestChainMetadata: ChainMap = { testxerc20: testXERC20, testvsxerc20: testVSXERC20, testxerc20lockbox: testXERC20Lockbox, + starknetdevnet: testStarknetChain, }; export const multiProtocolTestChains: Array = Object.keys( diff --git a/typescript/sdk/src/core/MultiProtocolCore.ts b/typescript/sdk/src/core/MultiProtocolCore.ts index f230d4c7b68..c5e5d31893e 100644 --- a/typescript/sdk/src/core/MultiProtocolCore.ts +++ b/typescript/sdk/src/core/MultiProtocolCore.ts @@ -9,6 +9,7 @@ import { CosmNativeCoreAdapter } from './adapters/CosmNativeCoreAdapter.js'; import { CosmWasmCoreAdapter } from './adapters/CosmWasmCoreAdapter.js'; import { EvmCoreAdapter } from './adapters/EvmCoreAdapter.js'; import { SealevelCoreAdapter } from './adapters/SealevelCoreAdapter.js'; +import { StarknetCoreAdapter } from './adapters/StarknetCoreAdapter.js'; import { ICoreAdapter } from './adapters/types.js'; import { CoreAddresses } from './contracts.js'; @@ -41,6 +42,7 @@ export class MultiProtocolCore extends MultiProtocolApp< if (protocol === ProtocolType.Sealevel) return SealevelCoreAdapter; if (protocol === ProtocolType.Cosmos) return CosmWasmCoreAdapter; if (protocol === ProtocolType.CosmosNative) return CosmNativeCoreAdapter; + if (protocol === ProtocolType.Starknet) return StarknetCoreAdapter; throw new Error(`No adapter for protocol ${protocol}`); } diff --git a/typescript/sdk/src/core/adapters/StarknetCoreAdapter.ts b/typescript/sdk/src/core/adapters/StarknetCoreAdapter.ts new file mode 100644 index 00000000000..f0dc96fbdf2 --- /dev/null +++ b/typescript/sdk/src/core/adapters/StarknetCoreAdapter.ts @@ -0,0 +1,127 @@ +import { + CallData, + InvokeTransactionReceiptResponse, + ParsedEvents, + events as eventsUtils, +} from 'starknet'; + +import { getCompiledContract } from '@hyperlane-xyz/starknet-core'; +import { Address, HexString, pollAsync } from '@hyperlane-xyz/utils'; + +import { BaseStarknetAdapter } from '../../app/MultiProtocolApp.js'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; +import { + ProviderType, + StarknetJsTransactionReceipt, +} from '../../providers/ProviderType.js'; +import { ChainName } from '../../types.js'; +import { + getStarknetMailboxContract, + parseStarknetDispatchEvents, +} from '../../utils/starknet.js'; + +import { ICoreAdapter } from './types.js'; + +export class StarknetCoreAdapter + extends BaseStarknetAdapter + implements ICoreAdapter +{ + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { mailbox: Address }, + ) { + super(chainName, multiProvider, addresses); + } + + extractMessageIds( + sourceTx: StarknetJsTransactionReceipt, + ): Array<{ messageId: string; destination: ChainName }> { + if (sourceTx.type !== ProviderType.Starknet) { + throw new Error( + `Unsupported provider type for StarknetCoreAdapter ${sourceTx.type}`, + ); + } + + let parsedEvents: ParsedEvents = []; + sourceTx.receipt.match({ + success: (txR) => { + const emittedEvents = + (txR as InvokeTransactionReceiptResponse).events?.map((event) => { + return { + block_hash: (txR as any).block_hash, + block_number: (txR as any).block_number, + transaction_hash: (txR as any).transaction_hash, + ...event, + }; + }) || []; + + if (emittedEvents.length === 0) return; + const mailboxAbi = getCompiledContract('mailbox').abi; + parsedEvents = eventsUtils.parseEvents( + emittedEvents, + eventsUtils.getAbiEvents(mailboxAbi), + CallData.getAbiStruct(mailboxAbi), + CallData.getAbiEnum(mailboxAbi), + ); + }, + _: () => { + throw Error('This transaction was not successful.'); + }, + }); + + if (!parsedEvents || parsedEvents.length === 0) return []; + + const messages = parseStarknetDispatchEvents( + parsedEvents, + (domain) => this.multiProvider.tryGetChainName(domain) ?? undefined, + ); + + return messages.map(({ id, parsed }) => ({ + messageId: id, + destination: this.multiProvider.getChainName(parsed.destination), + })); + } + + async waitForMessageProcessed( + messageId: HexString, + destination: ChainName, + delayMs = 5000, + maxAttempts = 60, + ): Promise { + const destAdapter = new StarknetCoreAdapter( + destination, + this.multiProvider, + { mailbox: this.addresses.mailbox }, + ); + + const mailboxContract = getStarknetMailboxContract( + destAdapter.addresses.mailbox, + destAdapter.getProvider(), + ); + + await pollAsync( + async () => { + const isDelivered = await mailboxContract.call('delivered', [ + messageId, + ]); + + if (!isDelivered) { + throw new Error( + `Message ${messageId} not yet delivered on ${destination}`, + ); + } + + this.logger.debug( + `Message ${messageId} confirmed delivered on ${destination}`, + ); + + return isDelivered; + }, + delayMs, + maxAttempts, + ); + + return true; + } +} diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index d9a029b2347..517aa926a63 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -60,6 +60,7 @@ export { export { CosmWasmCoreAdapter } from './core/adapters/CosmWasmCoreAdapter.js'; export { EvmCoreAdapter } from './core/adapters/EvmCoreAdapter.js'; export { SealevelCoreAdapter } from './core/adapters/SealevelCoreAdapter.js'; +export { StarknetCoreAdapter } from './core/adapters/StarknetCoreAdapter.js'; export { ICoreAdapter } from './core/adapters/types.js'; export { CoreAddresses, @@ -366,6 +367,10 @@ export { SolanaWeb3Provider, SolanaWeb3Transaction, SolanaWeb3TransactionReceipt, + StarknetJsContract, + StarknetJsProvider, + StarknetJsTransaction, + StarknetJsTransactionReceipt, TypedContract, TypedProvider, TypedTransaction, @@ -712,3 +717,12 @@ export { CCIPContractCache, } from './ccip/utils.js'; export { HyperlaneCCIPDeployer } from './ccip/HyperlaneCCIPDeployer.js'; + +export { + StarknetContractName, + getStarknetContract, + getStarknetHypERC20Contract, + getStarknetHypERC20CollateralContract, + getStarknetMailboxContract, + getStarknetEtherContract, +} from './utils/starknet.js'; diff --git a/typescript/sdk/src/providers/MultiProtocolProvider.ts b/typescript/sdk/src/providers/MultiProtocolProvider.ts index ffcecd9fe59..1e30cdf4714 100644 --- a/typescript/sdk/src/providers/MultiProtocolProvider.ts +++ b/typescript/sdk/src/providers/MultiProtocolProvider.ts @@ -25,6 +25,7 @@ import { ProviderMap, ProviderType, SolanaWeb3Provider, + StarknetJsProvider, TypedProvider, TypedTransaction, ViemProvider, @@ -215,6 +216,15 @@ export class MultiProtocolProvider< ); } + getStarknetProvider( + chainNameOrId: ChainNameOrId, + ): StarknetJsProvider['provider'] { + return this.getSpecificProvider( + chainNameOrId, + ProviderType.Starknet, + ); + } + setProvider( chainNameOrId: ChainNameOrId, provider: TypedProvider, diff --git a/typescript/sdk/src/providers/MultiProvider.ts b/typescript/sdk/src/providers/MultiProvider.ts index 7b5f923061f..9f9f8b0fe26 100644 --- a/typescript/sdk/src/providers/MultiProvider.ts +++ b/typescript/sdk/src/providers/MultiProvider.ts @@ -316,7 +316,7 @@ export class MultiProvider extends ChainMetadataManager { // setup contract factory const overrides = this.getTransactionOverrides(chainNameOrId); const signer = this.getSigner(chainNameOrId); - const contractFactory = await factory.connect(signer); + const contractFactory = factory.connect(signer); // estimate gas const deployTx = contractFactory.getDeployTransaction(...params); diff --git a/typescript/sdk/src/providers/ProviderType.ts b/typescript/sdk/src/providers/ProviderType.ts index 590d8a2e68f..80012225254 100644 --- a/typescript/sdk/src/providers/ProviderType.ts +++ b/typescript/sdk/src/providers/ProviderType.ts @@ -19,8 +19,7 @@ import { Contract as StarknetContract, Invocation as StarknetInvocation, Provider as StarknetProvider, - ReceiptTx as StarknetReceiptTx, - TransactionReceipt as StarknetTxReceipt, + GetTransactionReceiptResponse as StarknetTxReceipt, } from 'starknet'; import type { GetContractReturnType, @@ -356,9 +355,9 @@ export interface CosmJsNativeTransactionReceipt } export interface StarknetJsTransactionReceipt - extends TypedTransactionReceiptBase { + extends TypedTransactionReceiptBase { type: ProviderType.Starknet; - receipt: StarknetTxReceipt | StarknetReceiptTx; + receipt: StarknetTxReceipt; } export interface ZKSyncTransactionReceipt diff --git a/typescript/sdk/src/providers/rpcHealthTest.ts b/typescript/sdk/src/providers/rpcHealthTest.ts index 57b8e4579dc..459f918d9c3 100644 --- a/typescript/sdk/src/providers/rpcHealthTest.ts +++ b/typescript/sdk/src/providers/rpcHealthTest.ts @@ -10,6 +10,7 @@ import { EthersV5Provider, ProviderType, SolanaWeb3Provider, + StarknetJsProvider, } from './ProviderType.js'; import { protocolToDefaultProviderBuilder } from './providerBuilders.js'; @@ -30,6 +31,8 @@ export async function isRpcHealthy( provider.type === ProviderType.CosmJsNative ) return isCosmJsProviderHealthy(provider.provider, metadata); + else if (provider.type === ProviderType.Starknet) + return isStarknetJsProviderHealthy(provider.provider, metadata); else throw new Error( `Unsupported provider type ${provider.type}, new health check required`, @@ -88,3 +91,13 @@ export async function isCosmJsProviderHealthy( rootLogger.debug(`Block number is okay for ${metadata.name}`); return true; } + +export async function isStarknetJsProviderHealthy( + provider: StarknetJsProvider['provider'], + metadata: ChainMetadata, +): Promise { + const blockNumber = await provider.getBlockNumber(); + if (!blockNumber || blockNumber < 0) return false; + rootLogger.debug(`Block number is okay for ${metadata.name}`); + return true; +} diff --git a/typescript/sdk/src/providers/transactionFeeEstimators.ts b/typescript/sdk/src/providers/transactionFeeEstimators.ts index 56fa4885e74..2a96683142d 100644 --- a/typescript/sdk/src/providers/transactionFeeEstimators.ts +++ b/typescript/sdk/src/providers/transactionFeeEstimators.ts @@ -22,6 +22,8 @@ import { ProviderType, SolanaWeb3Provider, SolanaWeb3Transaction, + StarknetJsProvider, + StarknetJsTransaction, TypedProvider, TypedTransaction, ViemProvider, @@ -326,9 +328,28 @@ export function estimateTransactionFee({ sender, senderPubKey, }); + } else if ( + transaction.type === ProviderType.Starknet && + provider.type === ProviderType.Starknet + ) { + return estimateTransactionFeeStarknet({ transaction, provider, sender }); } else { throw new Error( `Unsupported transaction type ${transaction.type} or provider type ${provider.type} for gas estimation`, ); } } + +// Starknet does not support gas estimation without starknet account +// TODO: Figure out a way to inject starknet account +export async function estimateTransactionFeeStarknet({ + transaction: _transaction, + provider: _provider, + sender: _sender, +}: { + transaction: StarknetJsTransaction; + provider: StarknetJsProvider; + sender: Address; +}): Promise { + return { gasUnits: 0, gasPrice: 0, fee: 0 }; +} diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts index a09be1b85aa..43edec5e820 100644 --- a/typescript/sdk/src/token/Token.ts +++ b/typescript/sdk/src/token/Token.ts @@ -65,6 +65,11 @@ import { SealevelNativeTokenAdapter, SealevelTokenAdapter, } from './adapters/SealevelTokenAdapter.js'; +import { + StarknetHypCollateralAdapter, + StarknetHypNativeAdapter, + StarknetHypSyntheticAdapter, +} from './adapters/StarknetTokenAdapter.js'; import { PROTOCOL_TO_DEFAULT_NATIVE_TOKEN } from './nativeTokenMetadata.js'; // Declaring the interface in addition to class allows @@ -296,6 +301,18 @@ export class Token implements IToken { return new CosmNativeHypSyntheticAdapter(chainName, multiProvider, { token: addressOrDenom, }); + } else if (standard === TokenStandard.StarknetHypNative) { + return new StarknetHypNativeAdapter(chainName, multiProvider, { + warpRouter: addressOrDenom, + }); + } else if (standard === TokenStandard.StarknetHypSynthetic) { + return new StarknetHypSyntheticAdapter(chainName, multiProvider, { + warpRouter: addressOrDenom, + }); + } else if (standard === TokenStandard.StarknetHypCollateral) { + return new StarknetHypCollateralAdapter(chainName, multiProvider, { + warpRouter: addressOrDenom, + }); } else { throw new Error(`No hyp adapter found for token standard: ${standard}`); } diff --git a/typescript/sdk/src/token/adapters/StarknetTokenAdapter.ts b/typescript/sdk/src/token/adapters/StarknetTokenAdapter.ts new file mode 100644 index 00000000000..177a96351a6 --- /dev/null +++ b/typescript/sdk/src/token/adapters/StarknetTokenAdapter.ts @@ -0,0 +1,293 @@ +import { BigNumber } from 'ethers'; +import { CairoOption, CairoOptionVariant, Call, Contract, num } from 'starknet'; + +import { + Address, + Domain, + Numberish, + ProtocolType, + assert, +} from '@hyperlane-xyz/utils'; + +import { BaseStarknetAdapter } from '../../app/MultiProtocolApp.js'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; +import { ChainName } from '../../types.js'; +import { + getStarknetEtherContract, + getStarknetHypERC20CollateralContract, + getStarknetHypERC20Contract, +} from '../../utils/starknet.js'; +import { PROTOCOL_TO_DEFAULT_NATIVE_TOKEN } from '../nativeTokenMetadata.js'; +import { TokenMetadata } from '../types.js'; + +import { + IHypTokenAdapter, + InterchainGasQuote, + TransferParams, + TransferRemoteParams, +} from './ITokenAdapter.js'; + +export class StarknetHypSyntheticAdapter + extends BaseStarknetAdapter + implements IHypTokenAdapter +{ + public readonly contract: Contract; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { warpRouter: Address }, + ) { + super(chainName, multiProvider, addresses); + this.contract = getStarknetHypERC20Contract( + addresses.warpRouter, + multiProvider.getStarknetProvider(chainName), + ); + } + + async getBalance(address: Address): Promise { + return this.contract.balanceOf(address); + } + + async getMetadata(_isNft?: boolean): Promise { + const [decimals, symbol, name] = await Promise.all([ + this.contract.decimals(), + this.contract.symbol(), + this.contract.name(), + ]); + return { decimals, symbol, name }; + } + + async isApproveRequired( + owner: Address, + spender: Address, + weiAmountOrId: Numberish, + ): Promise { + const allowance = await this.contract.allowance(owner, spender); + return BigNumber.from(allowance.toString()).lt( + BigNumber.from(weiAmountOrId), + ); + } + + async isRevokeApprovalRequired( + _owner: Address, + _spender: Address, + ): Promise { + return false; + } + + async populateApproveTx({ + weiAmountOrId, + recipient, + }: TransferParams): Promise { + return this.contract.populateTransaction.approve(recipient, weiAmountOrId); + } + + async populateTransferTx({ + weiAmountOrId, + recipient, + }: TransferParams): Promise { + return this.contract.populateTransaction.transfer(recipient, weiAmountOrId); + } + + async getTotalSupply(): Promise { + return undefined; + } + + async quoteTransferRemoteGas( + _destination: Domain, + ): Promise { + return { amount: 0n }; + } + + async populateTransferRemoteTx({ + weiAmountOrId, + destination, + recipient, + interchainGas, + }: TransferRemoteParams): Promise { + const nonOption = new CairoOption(CairoOptionVariant.None); + const transferTx = this.contract.populateTransaction.transfer_remote( + destination, + recipient, + BigInt(weiAmountOrId.toString()), + 0n, + nonOption, + nonOption, + ); + + return { + ...transferTx, + value: interchainGas?.amount + ? BigNumber.from(interchainGas.amount) + : BigNumber.from(0), + }; + } + + async getMinimumTransferAmount(_recipient: Address): Promise { + return 0n; + } + + async getDomains(): Promise { + return this.contract.domains(); + } + + async getRouterAddress(domain: Domain): Promise { + const routerAddresses = await this.contract.routers(domain); + return Buffer.from(routerAddresses); + } + + async getAllRouters(): Promise> { + const domains = await this.getDomains(); + const routers: Buffer[] = await Promise.all( + domains.map((d) => this.getRouterAddress(d)), + ); + return domains.map((d, i) => ({ domain: d, address: routers[i] })); + } + + async getBridgedSupply(): Promise { + return undefined; + } +} + +export class StarknetHypCollateralAdapter extends StarknetHypSyntheticAdapter { + public readonly collateralContract: Contract; + protected wrappedTokenAddress?: Address; + + constructor( + chainName: ChainName, + multiProvider: MultiProtocolProvider, + addresses: { warpRouter: Address }, + ) { + super(chainName, multiProvider, addresses); + this.collateralContract = getStarknetHypERC20CollateralContract( + addresses.warpRouter, + multiProvider.getStarknetProvider(chainName), + ); + } + + protected async getWrappedTokenAddress(): Promise
{ + if (!this.wrappedTokenAddress) { + this.wrappedTokenAddress = num.toHex64( + await this.collateralContract.get_wrapped_token(), + ); + } + return this.wrappedTokenAddress!; + } + + protected async getWrappedTokenAdapter(): Promise { + return new StarknetHypSyntheticAdapter(this.chainName, this.multiProvider, { + warpRouter: await this.getWrappedTokenAddress(), + }); + } + + async getBalance(address: Address): Promise { + const adapter = await this.getWrappedTokenAdapter(); + return adapter.getBalance(address); + } + + override getBridgedSupply(): Promise { + return this.getBalance(this.addresses.warpRouter); + } + + override async getMetadata(isNft?: boolean): Promise { + const adapter = await this.getWrappedTokenAdapter(); + return adapter.getMetadata(isNft); + } + + override async isApproveRequired( + owner: Address, + spender: Address, + weiAmountOrId: Numberish, + ): Promise { + const adapter = await this.getWrappedTokenAdapter(); + return adapter.isApproveRequired(owner, spender, weiAmountOrId); + } + + override async populateApproveTx(params: TransferParams): Promise { + const adapter = await this.getWrappedTokenAdapter(); + return adapter.populateApproveTx(params); + } + + override async populateTransferTx(params: TransferParams): Promise { + const adapter = await this.getWrappedTokenAdapter(); + return adapter.populateTransferTx(params); + } +} + +export class StarknetHypNativeAdapter extends StarknetHypSyntheticAdapter { + public readonly collateralContract: Contract; + public readonly nativeContract: Contract; + + constructor( + chainName: ChainName, + multiProvider: MultiProtocolProvider, + addresses: { warpRouter: Address }, + ) { + super(chainName, multiProvider, addresses); + this.collateralContract = getStarknetHypERC20CollateralContract( + addresses.warpRouter, + multiProvider.getStarknetProvider(chainName), + ); + const nativeAddress = + multiProvider.getChainMetadata(chainName)?.nativeToken?.denom; + const tokenAddress = + nativeAddress ?? + PROTOCOL_TO_DEFAULT_NATIVE_TOKEN[ProtocolType.Starknet]!.denom; + assert(tokenAddress, `Native address not found for chain ${chainName}`); + this.nativeContract = getStarknetEtherContract( + tokenAddress, + multiProvider.getStarknetProvider(chainName), + ); + } + + async getBalance(address: Address): Promise { + return this.nativeContract.balanceOf(address); + } + + async isApproveRequired( + owner: Address, + spender: Address, + weiAmountOrId: Numberish, + ): Promise { + const allowance = await this.nativeContract.allowance(owner, spender); + return BigNumber.from(allowance.toString()).lt( + BigNumber.from(weiAmountOrId), + ); + } + + async populateApproveTx({ + weiAmountOrId, + recipient, + }: TransferParams): Promise { + return this.nativeContract.populateTransaction.approve( + recipient, + weiAmountOrId, + ); + } + + async populateTransferRemoteTx({ + weiAmountOrId, + destination, + recipient, + interchainGas, + }: TransferRemoteParams): Promise { + const nonOption = new CairoOption(CairoOptionVariant.None); + const transferTx = + this.collateralContract.populateTransaction.transfer_remote( + destination, + recipient, + BigInt(weiAmountOrId.toString()), + BigInt(weiAmountOrId.toString()), + nonOption, + nonOption, + ); + + return { + ...transferTx, + value: interchainGas?.amount + ? BigNumber.from(interchainGas.amount) + : BigNumber.from(0), + }; + } +} diff --git a/typescript/sdk/src/utils/starknet.ts b/typescript/sdk/src/utils/starknet.ts new file mode 100644 index 00000000000..637e615accb --- /dev/null +++ b/typescript/sdk/src/utils/starknet.ts @@ -0,0 +1,129 @@ +import { utils } from 'ethers'; +import { + AccountInterface, + Contract, + ParsedEvent, + ParsedEvents, + ParsedStruct, + ProviderInterface, +} from 'starknet'; + +import { + ContractType, + getCompiledContract, +} from '@hyperlane-xyz/starknet-core'; + +import { DispatchedMessage } from '../core/types.js'; + +export enum StarknetContractName { + MAILBOX = 'mailbox', + HYP_ERC20 = 'HypErc20', + HYP_ERC20_COLLATERAL = 'HypErc20Collateral', + HYP_NATIVE = 'HypNative', + ETHER = 'Ether', + MERKLE_TREE_HOOK = 'merkle_tree_hook', + NOOP_ISM = 'noop_ism', + HOOK = 'hook', + PROTOCOL_FEE = 'protocol_fee', + VALIDATOR_ANNOUNCE = 'validator_announce', + MESSAGE_RECIPIENT = 'message_recipient', + DOMAIN_ROUTING_HOOK = 'domain_routing_hook', + FALLBACK_DOMAIN_ROUTING_HOOK = 'fallback_domain_routing_hook', + STATIC_AGGREGATION_HOOK = 'static_aggregation_hook', +} + +/** + * Creates a Starknet contract instance with the given parameters + */ +export function getStarknetContract( + contractName: string, + address: string, + providerOrAccount?: ProviderInterface | AccountInterface, + contractType: ContractType = ContractType.CONTRACT, +): Contract { + const { abi } = getCompiledContract(contractName, contractType); + return new Contract(abi, address, providerOrAccount); +} + +export function getStarknetMailboxContract( + address: string, + providerOrAccount?: ProviderInterface | AccountInterface, +): Contract { + return getStarknetContract( + StarknetContractName.MAILBOX, + address, + providerOrAccount, + ); +} + +export function getStarknetHypERC20Contract( + address: string, + providerOrAccount?: ProviderInterface | AccountInterface, +): Contract { + return getStarknetContract( + StarknetContractName.HYP_ERC20, + address, + providerOrAccount, + ContractType.TOKEN, + ); +} + +export function getStarknetHypERC20CollateralContract( + address: string, + providerOrAccount?: ProviderInterface | AccountInterface, +): Contract { + return getStarknetContract( + StarknetContractName.HYP_ERC20_COLLATERAL, + address, + providerOrAccount, + ContractType.TOKEN, + ); +} + +export function getStarknetEtherContract( + address: string, + providerOrAccount?: ProviderInterface | AccountInterface, +): Contract { + return getStarknetContract( + StarknetContractName.ETHER, + address, + providerOrAccount, + ContractType.TOKEN, + ); +} + +const DISPATCH_EVENT = 'contracts::mailbox::mailbox::Dispatch'; +const DISPATCH_ID_EVENT = 'contracts::mailbox::mailbox::DispatchId'; + +export function parseStarknetDispatchEvents( + parsedEvents: ParsedEvents, + chainNameResolver: (domain: number) => string | undefined, +): DispatchedMessage[] { + return parsedEvents + .filter((event: ParsedEvent) => DISPATCH_EVENT in event) + .map((dispatchEvent: ParsedEvent) => { + const message = dispatchEvent[DISPATCH_EVENT].message as ParsedStruct; + const originChain = chainNameResolver(Number(message.origin)); + const destinationChain = chainNameResolver(Number(message.destination)); + + return { + parsed: { + ...message, + originChain, + destinationChain, + }, + id: parseStarknetDispatchIdEvents(parsedEvents)[0], + message: message.raw, + } as DispatchedMessage; + }); +} + +export function parseStarknetDispatchIdEvents( + parsedEvents: ParsedEvents, +): string[] { + return parsedEvents + .filter((event: ParsedEvent) => DISPATCH_ID_EVENT in event) + .map((dispatchEvent: ParsedEvent) => + utils.hexlify(dispatchEvent[DISPATCH_ID_EVENT].id as bigint), + ); +} diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index 6617e6d89a2..208236cd358 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -227,6 +227,11 @@ export class WarpCore { interchainFee, }); + // Starknet does not support gas estimation without starknet account + if (originToken.protocol === ProtocolType.Starknet) { + return { gasUnits: 0n, gasPrice: 0n, fee: 0n }; + } + // Typically the transfers require a single transaction if (txs.length === 1) { try { diff --git a/typescript/sdk/src/warp/test-warp-core-config.yaml b/typescript/sdk/src/warp/test-warp-core-config.yaml index 3b1944f5e6d..4c551ebbd79 100644 --- a/typescript/sdk/src/warp/test-warp-core-config.yaml +++ b/typescript/sdk/src/warp/test-warp-core-config.yaml @@ -124,6 +124,14 @@ tokens: symbol: atom name: atom addressOrDenom: atom + - chainName: starknetdevnet + standard: StarknetHypSynthetic + decimals: 18 + symbol: ETH + name: Ether on starknet + addressOrDenom: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' + connections: + - { token: ethereum|test1|0x1234567890123456789012345678901234567890 } options: interchainFeeConstants: diff --git a/yarn.lock b/yarn.lock index 9bc85b233ce..5b1406d5b24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7934,6 +7934,7 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@hyperlane-xyz/core": "npm:7.1.5" "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" + "@hyperlane-xyz/starknet-core": "npm:1.0.0" "@hyperlane-xyz/utils": "npm:12.6.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -7979,7 +7980,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/starknet-core@workspace:starknet": +"@hyperlane-xyz/starknet-core@npm:1.0.0, @hyperlane-xyz/starknet-core@workspace:starknet": version: 0.0.0-use.local resolution: "@hyperlane-xyz/starknet-core@workspace:starknet" dependencies: From 72b90f84edafec9d31c5a3deeda2a182f2329939 Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Fri, 16 May 2025 16:53:04 +0200 Subject: [PATCH 193/223] chore: add cosmos core module & reader (#6241) ### Description This PR implements and adds the Cosmos Native Core Reader and Module. Note that this is the fourth PR of https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6050 which is being split up in order to make it easier to review ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual Tests --- .changeset/better-carrots-tell.md | 5 + .../sdk/src/core/CosmosNativeCoreModule.ts | 469 ++++++++++++++++++ .../sdk/src/core/CosmosNativeCoreReader.ts | 51 ++ typescript/sdk/src/index.ts | 2 + 4 files changed, 527 insertions(+) create mode 100644 .changeset/better-carrots-tell.md create mode 100644 typescript/sdk/src/core/CosmosNativeCoreModule.ts create mode 100644 typescript/sdk/src/core/CosmosNativeCoreReader.ts diff --git a/.changeset/better-carrots-tell.md b/.changeset/better-carrots-tell.md new file mode 100644 index 00000000000..d0167a92cc3 --- /dev/null +++ b/.changeset/better-carrots-tell.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +add cosmos native core module & reader diff --git a/typescript/sdk/src/core/CosmosNativeCoreModule.ts b/typescript/sdk/src/core/CosmosNativeCoreModule.ts new file mode 100644 index 00000000000..9d724fc7ec2 --- /dev/null +++ b/typescript/sdk/src/core/CosmosNativeCoreModule.ts @@ -0,0 +1,469 @@ +import { + COSMOS_MODULE_MESSAGE_REGISTRY as R, + SigningHyperlaneModuleClient, +} from '@hyperlane-xyz/cosmos-sdk'; +import { DeployedCoreAddresses, HookConfig } from '@hyperlane-xyz/sdk'; +import { + Address, + ChainId, + Domain, + ProtocolType, + eqAddress, + rootLogger, +} from '@hyperlane-xyz/utils'; + +import { CosmosNativeHookModule } from '../hook/CosmosNativeHookModule.js'; +import { DerivedHookConfig, HookType } from '../hook/types.js'; +import { CosmosNativeIsmModule } from '../ism/CosmosNativeIsmModule.js'; +import { DerivedIsmConfig, IsmConfig, IsmType } from '../ism/types.js'; +import { MultiProvider } from '../providers/MultiProvider.js'; +import { AnnotatedCosmJsNativeTransaction } from '../providers/ProviderType.js'; +import { ChainName, ChainNameOrId } from '../types.js'; + +import { + HyperlaneModule, + HyperlaneModuleParams, +} from './AbstractHyperlaneModule.js'; +import { CosmosNativeCoreReader } from './CosmosNativeCoreReader.js'; +import { CoreConfig, CoreConfigSchema, DerivedCoreConfig } from './types.js'; + +export class CosmosNativeCoreModule extends HyperlaneModule< + ProtocolType.CosmosNative, + CoreConfig, + Record +> { + protected logger = rootLogger.child({ module: 'CosmosNativeCoreModule' }); + protected coreReader: CosmosNativeCoreReader; + + public readonly chainName: ChainName; + public readonly chainId: ChainId; + public readonly domainId: Domain; + + constructor( + protected readonly multiProvider: MultiProvider, + protected readonly signer: SigningHyperlaneModuleClient, + args: HyperlaneModuleParams>, + ) { + super(args); + + this.chainName = multiProvider.getChainName(args.chain); + this.chainId = multiProvider.getChainId(args.chain); + this.domainId = multiProvider.getDomainId(args.chain); + + this.coreReader = new CosmosNativeCoreReader(this.multiProvider, signer); + } + + /** + * Reads the core configuration from the mailbox address + * @returns The core config. + */ + public async read(): Promise { + return this.coreReader.deriveCoreConfig(this.args.addresses.mailbox); + } + + /** + * Deploys the Core contracts. + * @returns The created CosmosNativeCoreModule instance. + */ + public static async create(params: { + chain: ChainNameOrId; + config: CoreConfig; + multiProvider: MultiProvider; + signer: SigningHyperlaneModuleClient; + }): Promise { + const { chain, config, multiProvider, signer } = params; + const addresses = await CosmosNativeCoreModule.deploy({ + config, + multiProvider, + chain, + signer, + }); + + // Create CoreModule and deploy the Core contracts + const module = new CosmosNativeCoreModule(multiProvider, signer, { + addresses, + chain, + config, + }); + + return module; + } + + /** + * Deploys the core Hyperlane contracts. + * @returns The deployed core contract addresses. + */ + static async deploy(params: { + config: CoreConfig; + multiProvider: MultiProvider; + chain: ChainNameOrId; + signer: SigningHyperlaneModuleClient; + }): Promise { + const { config, multiProvider, chain, signer } = params; + + const chainName = multiProvider.getChainName(chain); + const domainId = multiProvider.getDomainId(chain); + + // 1. Deploy default ISM + const ismModule = await CosmosNativeIsmModule.create({ + chain: chainName, + config: config.defaultIsm, + addresses: { + deployedIsm: '', + mailbox: '', + }, + multiProvider, + signer, + }); + + const { deployedIsm: defaultIsm } = ismModule.serialize(); + + // 2. Deploy Mailbox with initial configuration + const { response: mailbox } = await signer.createMailbox({ + local_domain: domainId, + default_ism: defaultIsm, + default_hook: '', + required_hook: '', + }); + + // 3. Deploy default hook + const defaultHookModule = await CosmosNativeHookModule.create({ + chain: chainName, + config: config.defaultHook, + addresses: { + deployedHook: '', + mailbox: mailbox.id, + }, + multiProvider, + signer, + }); + + const { deployedHook: defaultHook } = defaultHookModule.serialize(); + + // 4. Deploy required hook + const requiredHookModule = await CosmosNativeHookModule.create({ + chain: chainName, + config: config.requiredHook, + addresses: { + deployedHook: '', + mailbox: mailbox.id, + }, + multiProvider, + signer, + }); + + const { deployedHook: requiredHook } = requiredHookModule.serialize(); + + // 5. Update the configuration with the newly created hooks + await signer.setMailbox({ + mailbox_id: mailbox.id, + default_ism: defaultIsm, + default_hook: defaultHook, + required_hook: requiredHook, + new_owner: config.owner || '', + }); + + const addresses: DeployedCoreAddresses = { + mailbox: mailbox.id, + staticMerkleRootMultisigIsmFactory: '', + proxyAdmin: '', + staticMerkleRootWeightedMultisigIsmFactory: '', + staticAggregationHookFactory: '', + staticAggregationIsmFactory: '', + staticMessageIdMultisigIsmFactory: '', + staticMessageIdWeightedMultisigIsmFactory: '', + validatorAnnounce: '', + testRecipient: '', + interchainAccountIsm: '', + interchainAccountRouter: '', + domainRoutingIsmFactory: '', + }; + + if (typeof config.defaultIsm !== 'string') { + switch (config.defaultIsm.type) { + case IsmType.MERKLE_ROOT_MULTISIG: { + addresses.staticMerkleRootMultisigIsmFactory = defaultIsm; + break; + } + case IsmType.MESSAGE_ID_MULTISIG: { + addresses.staticMessageIdMultisigIsmFactory = defaultIsm; + break; + } + case IsmType.ROUTING: { + addresses.domainRoutingIsmFactory = defaultIsm; + break; + } + } + } + + if (typeof config.defaultHook !== 'string') { + switch (config.defaultHook.type) { + case HookType.INTERCHAIN_GAS_PAYMASTER: { + addresses.interchainGasPaymaster = defaultHook; + break; + } + case HookType.MERKLE_TREE: { + addresses.merkleTreeHook = defaultHook; + break; + } + } + } + + if (typeof config.requiredHook !== 'string') { + switch (config.requiredHook.type) { + case HookType.INTERCHAIN_GAS_PAYMASTER: { + addresses.interchainGasPaymaster = requiredHook; + break; + } + case HookType.MERKLE_TREE: { + addresses.merkleTreeHook = requiredHook; + break; + } + } + } + + return addresses; + } + + /** + * Updates the core contracts with the provided configuration. + * + * @param expectedConfig - The configuration for the core contracts to be updated. + * @returns An array of Cosmos transactions that were executed to update the contract. + */ + public async update( + expectedConfig: CoreConfig, + ): Promise { + CoreConfigSchema.parse(expectedConfig); + const actualConfig = await this.read(); + + const transactions: AnnotatedCosmJsNativeTransaction[] = []; + transactions.push( + ...(await this.createDefaultIsmUpdateTxs(actualConfig, expectedConfig)), + ...(await this.createDefaultHookUpdateTxs(actualConfig, expectedConfig)), + ...(await this.createRequiredHookUpdateTxs(actualConfig, expectedConfig)), + ...this.createMailboxOwnerUpdateTxs(actualConfig, expectedConfig), + ); + + return transactions; + } + + private createMailboxOwnerUpdateTxs( + actualConfig: CoreConfig, + expectedConfig: CoreConfig, + ): AnnotatedCosmJsNativeTransaction[] { + if (eqAddress(actualConfig.owner, expectedConfig.owner)) { + return []; + } + + return [ + { + annotation: `Transferring ownership of Mailbox from ${actualConfig.owner} to ${expectedConfig.owner}`, + typeUrl: R.MsgSetMailbox.proto.type, + value: R.MsgSetMailbox.proto.converter.create({ + owner: actualConfig.owner, + mailbox_id: this.args.addresses.mailbox, + new_owner: expectedConfig.owner, + }), + }, + ]; + } + + /** + * Create a transaction to update an existing ISM config, or deploy a new ISM and return a tx to setDefaultIsm + * + * @param actualConfig - The on-chain router configuration, including the ISM configuration, and address. + * @param expectedConfig - The expected token router configuration, including the ISM configuration. + * @returns Transaction that need to be executed to update the ISM configuration. + */ + async createDefaultIsmUpdateTxs( + actualConfig: DerivedCoreConfig, + expectedConfig: CoreConfig, + ): Promise { + const updateTransactions: AnnotatedCosmJsNativeTransaction[] = []; + + const actualDefaultIsmConfig = actualConfig.defaultIsm as DerivedIsmConfig; + + // Try to update (may also deploy) Ism with the expected config + const { deployedIsm, ismUpdateTxs } = await this.deployOrUpdateIsm( + actualDefaultIsmConfig, + expectedConfig.defaultIsm, + ); + + if (ismUpdateTxs.length) { + updateTransactions.push(...ismUpdateTxs); + } + + const newIsmDeployed = actualDefaultIsmConfig.address !== deployedIsm; + if (newIsmDeployed) { + const { mailbox } = this.serialize(); + updateTransactions.push({ + annotation: `Updating default ISM of Mailbox from ${actualDefaultIsmConfig.address} to ${deployedIsm}`, + typeUrl: R.MsgSetMailbox.proto.type, + value: R.MsgSetMailbox.proto.converter.create({ + owner: actualConfig.owner, + mailbox_id: mailbox, + default_ism: deployedIsm, + }), + }); + } + + return updateTransactions; + } + + /** + * Updates or deploys the ISM using the provided configuration. + * + * @returns Object with deployedIsm address, and update Transactions + */ + public async deployOrUpdateIsm( + actualDefaultIsmConfig: DerivedIsmConfig, + expectDefaultIsmConfig: IsmConfig, + ): Promise<{ + deployedIsm: Address; + ismUpdateTxs: AnnotatedCosmJsNativeTransaction[]; + }> { + const { mailbox } = this.serialize(); + + const ismModule = new CosmosNativeIsmModule( + this.multiProvider, + { + addresses: { + mailbox: mailbox, + deployedIsm: actualDefaultIsmConfig.address, + }, + chain: this.chainName, + config: actualDefaultIsmConfig.address, + }, + this.signer, + ); + this.logger.info( + `Comparing target ISM config with ${this.args.chain} chain`, + ); + const ismUpdateTxs = await ismModule.update(expectDefaultIsmConfig); + const { deployedIsm } = ismModule.serialize(); + + return { deployedIsm, ismUpdateTxs }; + } + + /** + * Create a transaction to update an existing Hook config, or deploy a new Hook and return a tx to setDefaultHook + * + * @param actualConfig - The on-chain router configuration, including the Hook configuration, and address. + * @param expectedConfig - The expected token router configuration, including the Hook configuration. + * @returns Transaction that need to be executed to update the Hook configuration. + */ + async createDefaultHookUpdateTxs( + actualConfig: DerivedCoreConfig, + expectedConfig: CoreConfig, + ): Promise { + const updateTransactions: AnnotatedCosmJsNativeTransaction[] = []; + + const actualDefaultHookConfig = + actualConfig.defaultHook as DerivedHookConfig; + + // Try to update (may also deploy) Hook with the expected config + const { deployedHook, hookUpdateTxs } = await this.deployOrUpdateHook( + actualDefaultHookConfig, + expectedConfig.defaultHook, + ); + + if (hookUpdateTxs.length) { + updateTransactions.push(...hookUpdateTxs); + } + + const newHookDeployed = actualDefaultHookConfig.address !== deployedHook; + if (newHookDeployed) { + const { mailbox } = this.serialize(); + updateTransactions.push({ + annotation: `Updating default Hook of Mailbox from ${actualDefaultHookConfig.address} to ${deployedHook}`, + typeUrl: R.MsgSetMailbox.proto.type, + value: R.MsgSetMailbox.proto.converter.create({ + owner: actualConfig.owner, + mailbox_id: mailbox, + default_hook: deployedHook, + }), + }); + } + + return updateTransactions; + } + + /** + * Create a transaction to update an existing Hook config, or deploy a new Hook and return a tx to setRequiredHook + * + * @param actualConfig - The on-chain router configuration, including the Hook configuration, and address. + * @param expectedConfig - The expected token router configuration, including the Hook configuration. + * @returns Transaction that need to be executed to update the Hook configuration. + */ + async createRequiredHookUpdateTxs( + actualConfig: DerivedCoreConfig, + expectedConfig: CoreConfig, + ): Promise { + const updateTransactions: AnnotatedCosmJsNativeTransaction[] = []; + + const actualRequiredHookConfig = + actualConfig.requiredHook as DerivedHookConfig; + + // Try to update (may also deploy) Hook with the expected config + const { deployedHook, hookUpdateTxs } = await this.deployOrUpdateHook( + actualRequiredHookConfig, + expectedConfig.requiredHook, + ); + + if (hookUpdateTxs.length) { + updateTransactions.push(...hookUpdateTxs); + } + + const newHookDeployed = actualRequiredHookConfig.address !== deployedHook; + if (newHookDeployed) { + const { mailbox } = this.serialize(); + updateTransactions.push({ + annotation: `Updating required Hook of Mailbox from ${actualRequiredHookConfig.address} to ${deployedHook}`, + typeUrl: R.MsgSetMailbox.proto.type, + value: R.MsgSetMailbox.proto.converter.create({ + owner: actualConfig.owner, + mailbox_id: mailbox, + required_hook: deployedHook, + }), + }); + } + + return updateTransactions; + } + + /** + * Updates or deploys the Hook using the provided configuration. + * + * @returns Object with deployedHook address, and update Transactions + */ + public async deployOrUpdateHook( + actualHookConfig: DerivedHookConfig, + expectHookConfig: HookConfig, + ): Promise<{ + deployedHook: Address; + hookUpdateTxs: AnnotatedCosmJsNativeTransaction[]; + }> { + const { mailbox } = this.serialize(); + + const hookModule = new CosmosNativeHookModule( + this.multiProvider, + { + addresses: { + mailbox: mailbox, + deployedHook: actualHookConfig.address, + }, + chain: this.chainName, + config: actualHookConfig.address, + }, + this.signer, + ); + this.logger.info( + `Comparing target Hook config with ${this.args.chain} chain`, + ); + const hookUpdateTxs = await hookModule.update(expectHookConfig); + const { deployedHook } = hookModule.serialize(); + + return { deployedHook, hookUpdateTxs }; + } +} diff --git a/typescript/sdk/src/core/CosmosNativeCoreReader.ts b/typescript/sdk/src/core/CosmosNativeCoreReader.ts new file mode 100644 index 00000000000..681d5087418 --- /dev/null +++ b/typescript/sdk/src/core/CosmosNativeCoreReader.ts @@ -0,0 +1,51 @@ +import { + HyperlaneModuleClient, + SigningHyperlaneModuleClient, +} from '@hyperlane-xyz/cosmos-sdk'; +import { Address, rootLogger } from '@hyperlane-xyz/utils'; + +import { CosmosNativeHookReader } from '../hook/CosmosNativeHookReader.js'; +import { CosmosNativeIsmReader } from '../ism/CosmosNativeIsmReader.js'; +import { MultiProvider } from '../providers/MultiProvider.js'; + +import { DerivedCoreConfig } from './types.js'; + +export class CosmosNativeCoreReader { + protected readonly logger = rootLogger.child({ + module: 'CosmosNativeCoreReader', + }); + protected ismReader: CosmosNativeIsmReader; + protected hookReader: CosmosNativeHookReader; + + constructor( + protected readonly multiProvider: MultiProvider, + protected readonly signer: + | HyperlaneModuleClient + | SigningHyperlaneModuleClient, + ) { + this.ismReader = new CosmosNativeIsmReader(this.signer); + this.hookReader = new CosmosNativeHookReader( + this.multiProvider, + this.signer, + ); + } + + async deriveCoreConfig(mailboxAddress: Address): Promise { + const { mailbox } = await this.signer.query.core.Mailbox({ + id: mailboxAddress, + }); + + if (!mailbox) { + throw new Error(`Mailbox not found for address ${mailboxAddress}`); + } + + return { + owner: mailbox.owner, + defaultIsm: await this.ismReader.deriveIsmConfig(mailbox.default_ism), + defaultHook: await this.hookReader.deriveHookConfig(mailbox.default_hook), + requiredHook: await this.hookReader.deriveHookConfig( + mailbox.required_hook, + ), + }; + } +} diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 517aa926a63..97f3e5d6d87 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -467,6 +467,8 @@ export { GcpValidator } from './gcp/validator.js'; export { EvmHookModule } from './hook/EvmHookModule.js'; export { CosmosNativeHookModule } from './hook/CosmosNativeHookModule.js'; export { CosmosNativeHookReader } from './hook/CosmosNativeHookReader.js'; +export { CosmosNativeCoreModule } from './core/CosmosNativeCoreModule.js'; +export { CosmosNativeCoreReader } from './core/CosmosNativeCoreReader.js'; export { DerivedIcaRouterConfig, DerivedIcaRouterConfigSchema, From bb47b67251391a2db229775969fabe272e4e17f0 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 16 May 2025 16:05:34 +0100 Subject: [PATCH 194/223] feat: add apechain to solana igp (#6246) ### Description feat: add apechain to solana igp ### Drive-by changes - tidied up ordering of igp connected chains ### Related issues ### Backward compatibility ### Testing see the output file --- .../mainnet3/gas-oracle-configs.json | 24 ++++++++++++------- .../sealevel-helpers/print-gas-oracles.ts | 8 ++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json index 41edc7e89c9..7d948d81aab 100644 --- a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json +++ b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json @@ -32,6 +32,22 @@ }, "overhead": 333774 }, + "abstract": { + "oracleConfig": { + "tokenExchangeRate": "187056373229658428214", + "gasPrice": "193418836", + "tokenDecimals": 18 + }, + "overhead": 333774 + }, + "apechain": { + "oracleConfig": { + "tokenExchangeRate": "49999132185504026", + "gasPrice": "1280415238976", + "tokenDecimals": 18 + }, + "overhead": 166887 + }, "bsc": { "oracleConfig": { "tokenExchangeRate": "62110871980005554012", @@ -56,14 +72,6 @@ }, "overhead": 600000 }, - "abstract": { - "oracleConfig": { - "tokenExchangeRate": "187056373229658428214", - "gasPrice": "193418836", - "tokenDecimals": 18 - }, - "overhead": 333774 - }, "artela": { "oracleConfig": { "tokenExchangeRate": "152446195501249", diff --git a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts index 1a6c46ba7a9..e36b8888da3 100644 --- a/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts +++ b/typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts @@ -103,16 +103,18 @@ function getChainConnections( ['solanamainnet', 'everclear'], ['solanamainnet', 'infinityvmmainnet'], ['solanamainnet', 'sophon'], + ['solanamainnet', 'abstract'], + ['solanamainnet', 'apechain'], // for svmBNB routes solana<>bsc<>svmbnb<>soon ['solanamainnet', 'bsc'], ['svmbnb', 'solanamainnet'], ['svmbnb', 'bsc'], ['svmbnb', 'soon'], - ['sonicsvm', 'eclipsemainnet'], ['soon', 'solanamainnet'], ['soon', 'bsc'], - ['soon', 'eclipsemainnet'], - ['abstract', 'solanamainnet'], + // for eclipse routes + ['eclipsemainnet', 'sonicsvm'], + ['eclipsemainnet', 'soon'], // All warp routes ...Object.values(WarpRouteIds).map(getWarpChains), ]; From 03eebc3f838b5532bb43c71e26ebe61fd5366fa7 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 16 May 2025 11:53:09 -0400 Subject: [PATCH 195/223] =?UTF-8?q?feat:=20Make=20the=20validator's=20pani?= =?UTF-8?q?c=20message=20scarier=20if=20there's=20a=20detecte=E2=80=A6=20(?= =?UTF-8?q?#5901)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …d reorg ### Description - make errors more scary ### Related issues - fixes https://linear.app/hyperlane-xyz/issue/BACK-75/make-the-validators-panic-message-scarier-if-theres-a-detected-reorg ### Backward compatibility Yes ### Testing None --- rust/main/agents/validator/src/submit.rs | 6 ++++-- rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs index 6f7de570b76..9198cdcccfa 100644 --- a/rust/main/agents/validator/src/submit.rs +++ b/rust/main/agents/validator/src/submit.rs @@ -236,7 +236,7 @@ impl ValidatorSubmitter { "Incorrect tree root, something went wrong" ); - let mut panic_message = "Incorrect tree root, something went wrong.".to_owned(); + let mut panic_message = "Incorrect tree root. Most likely a reorg has occurred. Please reach out for help, this is a potentially serious error impacting signed messages. Do NOT forcefully resume operation of this validator. Keep it crashlooping or shut down until receive support.".to_owned(); if let Err(e) = self .checkpoint_syncer .write_reorg_status(&reorg_event) @@ -680,7 +680,9 @@ mod test { } #[tokio::test] - #[should_panic(expected = "Incorrect tree root, something went wrong.")] + #[should_panic( + expected = "Incorrect tree root. Most likely a reorg has occurred. Please reach out for help, this is a potentially serious error impacting signed messages. Do NOT forcefully resume operation of this validator. Keep it crashlooping or shut down until receive support." + )] async fn reorg_is_detected_and_persisted_to_checkpoint_storage() { let unix_timestamp = chrono::Utc::now().timestamp() as u64; let expected_reorg_period = 12; diff --git a/rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs b/rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs index bd1af317ef9..7542c4aa323 100644 --- a/rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs +++ b/rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs @@ -46,7 +46,7 @@ pub enum CheckpointSyncerConf { #[derive(Debug, thiserror::Error)] pub enum CheckpointSyncerBuildError { /// A reorg event has been detected in the checkpoint syncer when building it - #[error("A reorg event has been detected: {0:?}")] + #[error("Fatal: A reorg event has been detected. Please reach out for help, this is a potentially serious error impacting signed messages. Do NOT forcefully resume operation of this validator. Keep it crashlooping or shut down until receive support. {0:?}")] ReorgEvent(ReorgEvent), /// Error communicating with the chain #[error(transparent)] From 84f5b21f8b5052000f35e4ad589f93db92edb569 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Fri, 16 May 2025 18:09:56 -0400 Subject: [PATCH 196/223] chore: updated registry rc to update docker images (#6252) ### Description updated registry rc commit ### Drive-by changes - No ### Related issues ### Backward compatibility Yes ### Testing None --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 6 +++--- rust/main/config/testnet_config.json | 6 +++--- typescript/infra/src/warp/helm.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.registryrc b/.registryrc index ce08f975f64..7a5ce92a9aa 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -4d879c24b4bed89a770570dd7d54d3423edab496 +ea6268432101949d2f3b8d304b7aea9be841bf99 diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 3edd7009417..01c315c42e4 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -8592,10 +8592,10 @@ "plume": { "blockExplorers": [ { - "apiUrl": "https://phoenix-explorer.plumenetwork.xyz/api", + "apiUrl": "https://explorer.plume.org/api", "family": "blockscout", "name": "Plume Explorer", - "url": "https://phoenix-explorer.plumenetwork.xyz" + "url": "https://explorer.plume.org" } ], "blocks": { @@ -8623,7 +8623,7 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://phoenix-rpc.plumenetwork.xyz" + "http": "https://rpc.plume.org" } ], "technicalStack": "arbitrumnitro", diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index 4e923a6b649..7da0d4c1486 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -2707,10 +2707,10 @@ "plumetestnet2": { "blockExplorers": [ { - "apiUrl": "https://testnet-explorer.plumenetwork.xyz/api", + "apiUrl": "https://testnet-explorer.plume.org/api", "family": "blockscout", "name": "Plume Explorer", - "url": "https://testnet-explorer.plumenetwork.xyz" + "url": "https://testnet-explorer.plume.org" } ], "blocks": { @@ -2739,7 +2739,7 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://testnet-rpc.plumenetwork.xyz" + "http": "https://testnet-rpc.plume.org" } ], "technicalStack": "arbitrumnitro", diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 7d04a32b080..11786b95587 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -73,7 +73,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '5ad1aed-20250509-141037', + tag: 'a015285-20250516-204210', }, warpRouteId: this.warpRouteId, fullnameOverride: this.helmReleaseName, From 66dac254b404c0fe9d46c35d8230e670731ff6ad Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Sat, 17 May 2025 23:12:53 +0100 Subject: [PATCH 197/223] chore: default to only amd64 docker builds (#6256) ### Description chore: default to only amd64 docker builds - default agent/monorepo images are only for amd64 - this saves time, cost of each image build - if required for local runs, devs can manually trigger the workflow and say they want an arm64 build - allowing manual runs also means that you don't need to have a PR open just to trigger image builds anymore ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .github/workflows/monorepo-docker.yml | 17 ++++++++++++++++- .github/workflows/rust-docker.yml | 16 +++++++++++++++- README.md | 20 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/.github/workflows/monorepo-docker.yml b/.github/workflows/monorepo-docker.yml index 1eae7ae8ce5..36dad0dee26 100644 --- a/.github/workflows/monorepo-docker.yml +++ b/.github/workflows/monorepo-docker.yml @@ -13,6 +13,12 @@ on: - 'docker-entrypoint.sh' - '.dockerignore' - '.github/workflows/monorepo-docker.yml' + workflow_dispatch: + inputs: + include_arm64: + description: 'Include arm64 in the build' + required: false + default: 'false' concurrency: group: build-push-monorepo-${{ github.ref }} @@ -81,6 +87,15 @@ jobs: REGISTRY_VERSION=$(cat .registryrc) echo "REGISTRY_VERSION=$REGISTRY_VERSION" >> $GITHUB_ENV + - name: Determine platforms + id: determine-platforms + run: | + if [ "${{ github.event.inputs.include_arm64 }}" == "true" ]; then + echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT + else + echo "platforms=linux/amd64" >> $GITHUB_OUTPUT + fi + - name: Build and push uses: depot/build-push-action@v1 with: @@ -92,4 +107,4 @@ jobs: labels: ${{ steps.meta.outputs.labels }} build-args: | REGISTRY_COMMIT=${{ env.REGISTRY_VERSION }} - platforms: linux/amd64,linux/arm64 + platforms: ${{ steps.determine-platforms.outputs.platforms }} diff --git a/.github/workflows/rust-docker.yml b/.github/workflows/rust-docker.yml index 588205ce283..24d3f3b3009 100644 --- a/.github/workflows/rust-docker.yml +++ b/.github/workflows/rust-docker.yml @@ -8,6 +8,12 @@ on: paths: - 'rust/**' - '.github/workflows/rust-docker.yml' + workflow_dispatch: + inputs: + include_arm64: + description: 'Include arm64 in the build' + required: false + default: 'false' concurrency: group: build-push-agents-${{ github.ref }} cancel-in-progress: true @@ -64,6 +70,14 @@ jobs: registry: gcr.io username: _json_key password: ${{ secrets.GCLOUD_SERVICE_KEY }} + - name: Determine platforms + id: determine-platforms + run: | + if [ "${{ github.event.inputs.include_arm64 }}" == "true" ]; then + echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT + else + echo "platforms=linux/amd64" >> $GITHUB_OUTPUT + fi - name: Build and push uses: depot/build-push-action@v1 with: @@ -73,4 +87,4 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 + platforms: ${{ steps.determine-platforms.outputs.platforms }} diff --git a/README.md b/README.md index d2c8215e372..33b789b3939 100644 --- a/README.md +++ b/README.md @@ -119,3 +119,23 @@ See [`rust/README.md`](rust/README.md) 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). + +### Manually Triggering Docker Builds in CI + +To manually trigger Agent or Monorepo Docker builds in CI, you can use the workflows provided in the repository. Here are the steps to do so: + +1. **Navigate to the workflow:** + + - For agents, go to the [Rust Docker Workflow](https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/workflows/rust-docker.yml). + - For the monorepo, go to the [Monorepo Docker Workflow](https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/workflows/monorepo-docker.yml). + +2. **Trigger the workflow:** + + - On the workflow page, click on the "Run workflow" button. + - You may need to select a branch and decide whether to trigger builds for the `arm64` platform. + +3. **Wait for the build to complete:** + - Once triggered, monitor the progress of the build by opening the new workflow run. + - You may have to refresh the page for it to appear. + - Check the logs for any errors or issues during the build process. + - Wait for the `build-and-push-to-gcr` step to complete successfully before using the new image. From 2724559c1cf650191fd3cbc2068a54bac57e717f Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Mon, 19 May 2025 10:28:09 +0200 Subject: [PATCH 198/223] chore: add routing ism to cosmos-types and sdk (#6244) ### Description This PR adds the newly added Routing ISM of the Hyperlane Cosmos Module released in [v1.0.0](https://github.com/bcp-innovations/hyperlane-cosmos/releases/tag/v1.0.0) to the cosmos-types and cosmos-sdk package. Furthermore, e2e tests have been added to test the newly added transactions. Note that the majority of code changes are from the autogenerated code from the cosmos-types package. Only the changes in the cosmos-sdk package should be reviewed. ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing E2E tests --- .changeset/sharp-doors-decide.md | 7 + typescript/cosmos-sdk/Dockerfile | 2 +- typescript/cosmos-sdk/compose.yaml | 2 +- .../cosmos-sdk/src/clients/signingClient.ts | 64 ++ .../hyperlane/interchain_security/messages.ts | 20 + .../hyperlane/interchain_security/query.ts | 6 +- typescript/cosmos-sdk/src/registry.ts | 60 ++ .../tests/1_interchain_security.e2e-test.ts | 130 +++ .../cosmos-sdk/src/tests/2_core.e2e-test.ts | 1 + .../src/tests/3_post_dispatch.e2e-test.ts | 1 + .../cosmos-sdk/src/tests/4_warp.e2e-test.ts | 2 + typescript/cosmos-types/src/index.ts | 2 + .../src/types/cosmos/base/v1beta1/coin.ts | 138 +++ .../core/interchain_security/v1/events.ts | 963 ++++++++++++++++++ .../core/interchain_security/v1/tx.ts | 760 ++++++++++++++ .../core/interchain_security/v1/types.ts | 196 ++++ .../hyperlane/core/post_dispatch/v1/events.ts | 607 ++++++++++- .../hyperlane/core/post_dispatch/v1/tx.ts | 21 +- .../src/types/hyperlane/core/v1/events.ts | 410 +++++++- .../src/types/hyperlane/core/v1/query.ts | 497 ++++++++- .../src/types/hyperlane/core/v1/tx.ts | 20 + .../src/types/hyperlane/warp/v1/events.ts | 934 ++++++++++++++++- .../src/types/hyperlane/warp/v1/tx.ts | 26 +- .../sdk/src/core/CosmosNativeCoreModule.ts | 1 + .../sdk/src/hook/CosmosNativeHookModule.ts | 4 +- 25 files changed, 4764 insertions(+), 110 deletions(-) create mode 100644 .changeset/sharp-doors-decide.md create mode 100644 typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/events.ts diff --git a/.changeset/sharp-doors-decide.md b/.changeset/sharp-doors-decide.md new file mode 100644 index 00000000000..061ad6cf3e7 --- /dev/null +++ b/.changeset/sharp-doors-decide.md @@ -0,0 +1,7 @@ +--- +'@hyperlane-xyz/cosmos-types': minor +'@hyperlane-xyz/cosmos-sdk': minor +'@hyperlane-xyz/sdk': minor +--- + +add cosmos native routing ism cosmos-sdk and types diff --git a/typescript/cosmos-sdk/Dockerfile b/typescript/cosmos-sdk/Dockerfile index 9c880958796..3e0dddb0019 100644 --- a/typescript/cosmos-sdk/Dockerfile +++ b/typescript/cosmos-sdk/Dockerfile @@ -8,7 +8,7 @@ RUN apt update && apt upgrade -y \ && rm -rf /var/lib/apt/lists/* # Define a build argument for the branch name -ARG BRANCH_NAME=v1.0.0-beta0 +ARG BRANCH_NAME=v1.0.0 # install hypd from the specified branch RUN git clone --depth 1 --branch $BRANCH_NAME https://github.com/bcp-innovations/hyperlane-cosmos.git \ diff --git a/typescript/cosmos-sdk/compose.yaml b/typescript/cosmos-sdk/compose.yaml index 98efb10438c..77587d2bfb2 100644 --- a/typescript/cosmos-sdk/compose.yaml +++ b/typescript/cosmos-sdk/compose.yaml @@ -1,6 +1,6 @@ services: hyperlane-cosmos-simapp: - image: gcr.io/abacus-labs-dev/hyperlane-cosmos-simapp:v1.0.0-beta0 + image: gcr.io/abacus-labs-dev/hyperlane-cosmos-simapp:v1.0.0 ports: - 26657:26657 - 1317:1317 diff --git a/typescript/cosmos-sdk/src/clients/signingClient.ts b/typescript/cosmos-sdk/src/clients/signingClient.ts index 1b31f8953df..ab6f9ad1ed7 100644 --- a/typescript/cosmos-sdk/src/clients/signingClient.ts +++ b/typescript/cosmos-sdk/src/clients/signingClient.ts @@ -29,6 +29,10 @@ import { MsgCreateMerkleRootMultisigIsmEncodeObject, MsgCreateMessageIdMultisigIsmEncodeObject, MsgCreateNoopIsmEncodeObject, + MsgCreateRoutingIsmEncodeObject, + MsgRemoveRoutingIsmDomainEncodeObject, + MsgSetRoutingIsmDomainEncodeObject, + MsgUpdateRoutingIsmOwnerEncodeObject, } from '../hyperlane/interchain_security/messages.js'; import { setupInterchainSecurityExtension } from '../hyperlane/interchain_security/query.js'; import { @@ -261,6 +265,66 @@ export class SigningHyperlaneModuleClient extends SigningStargateClient { return this.submitTx(msg, options); } + public async createRoutingIsm( + value: Omit, + options?: TxOptions, + ): Promise> { + const msg: MsgCreateRoutingIsmEncodeObject = { + typeUrl: R.MsgCreateRoutingIsm.proto.type, + value: R.MsgCreateRoutingIsm.proto.converter.create({ + ...value, + creator: this.account.address, + }), + }; + + return this.submitTx(msg, options); + } + + public async setRoutingIsmDomain( + value: Omit, + options?: TxOptions, + ): Promise> { + const msg: MsgSetRoutingIsmDomainEncodeObject = { + typeUrl: R.MsgSetRoutingIsmDomain.proto.type, + value: R.MsgSetRoutingIsmDomain.proto.converter.create({ + ...value, + owner: this.account.address, + }), + }; + + return this.submitTx(msg, options); + } + + public async removeRoutingIsmDomain( + value: Omit, + options?: TxOptions, + ): Promise> { + const msg: MsgRemoveRoutingIsmDomainEncodeObject = { + typeUrl: R.MsgRemoveRoutingIsmDomain.proto.type, + value: R.MsgRemoveRoutingIsmDomain.proto.converter.create({ + ...value, + owner: this.account.address, + }), + }; + + return this.submitTx(msg, options); + } + + public async updateRoutingIsmOwner( + value: Omit, + options?: TxOptions, + ): Promise> { + const msg: MsgUpdateRoutingIsmOwnerEncodeObject = { + typeUrl: R.MsgUpdateRoutingIsmOwner.proto.type, + value: R.MsgUpdateRoutingIsmOwner.proto.converter.create({ + ...value, + owner: this.account.address, + }), + }; + + return this.submitTx(msg, options); + } + public async createIgp( value: Omit, options?: TxOptions, diff --git a/typescript/cosmos-sdk/src/hyperlane/interchain_security/messages.ts b/typescript/cosmos-sdk/src/hyperlane/interchain_security/messages.ts index 84fecc72b6f..3434e90ab38 100644 --- a/typescript/cosmos-sdk/src/hyperlane/interchain_security/messages.ts +++ b/typescript/cosmos-sdk/src/hyperlane/interchain_security/messages.ts @@ -25,3 +25,23 @@ export interface MsgAnnounceValidatorEncodeObject extends EncodeObject { readonly typeUrl: typeof R.MsgAnnounceValidator.proto.type; readonly value: Partial; } + +export interface MsgCreateRoutingIsmEncodeObject extends EncodeObject { + readonly typeUrl: typeof R.MsgCreateRoutingIsm.proto.type; + readonly value: Partial; +} + +export interface MsgSetRoutingIsmDomainEncodeObject extends EncodeObject { + readonly typeUrl: typeof R.MsgSetRoutingIsmDomain.proto.type; + readonly value: Partial; +} + +export interface MsgRemoveRoutingIsmDomainEncodeObject extends EncodeObject { + readonly typeUrl: typeof R.MsgRemoveRoutingIsmDomain.proto.type; + readonly value: Partial; +} + +export interface MsgUpdateRoutingIsmOwnerEncodeObject extends EncodeObject { + readonly typeUrl: typeof R.MsgUpdateRoutingIsmOwner.proto.type; + readonly value: Partial; +} diff --git a/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts b/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts index 83a30b6e836..2bc5ba901be 100644 --- a/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts +++ b/typescript/cosmos-sdk/src/hyperlane/interchain_security/query.ts @@ -5,7 +5,8 @@ import { any, isQuery, isTypes, pagination } from '@hyperlane-xyz/cosmos-types'; type ISM = | isTypes.NoopISM | isTypes.MerkleRootMultisigISM - | isTypes.MessageIdMultisigISM; + | isTypes.MessageIdMultisigISM + | isTypes.RoutingISM; type QueryDecodedIsmResponse = { ism: T; @@ -20,6 +21,7 @@ export enum IsmTypes { NoopISM = '/hyperlane.core.interchain_security.v1.NoopISM', MerkleRootMultisigISM = '/hyperlane.core.interchain_security.v1.MerkleRootMultisigISM', MessageIdMultisigISM = '/hyperlane.core.interchain_security.v1.MessageIdMultisigISM', + RoutingISM = '/hyperlane.core.interchain_security.v1.RoutingISM', } export const decodeIsm = (ism: any.Any | undefined): ISM => { @@ -30,6 +32,8 @@ export const decodeIsm = (ism: any.Any | undefined): ISM => { return isTypes.MerkleRootMultisigISM.decode(ism.value); case IsmTypes.MessageIdMultisigISM: return isTypes.MessageIdMultisigISM.decode(ism.value); + case IsmTypes.RoutingISM: + return isTypes.RoutingISM.decode(ism.value); default: throw new Error(`can not decode ISM with type url ${ism?.type_url}`); } diff --git a/typescript/cosmos-sdk/src/registry.ts b/typescript/cosmos-sdk/src/registry.ts index 1ef25f71d9a..3b3316ef012 100644 --- a/typescript/cosmos-sdk/src/registry.ts +++ b/typescript/cosmos-sdk/src/registry.ts @@ -147,6 +147,66 @@ export const COSMOS_MODULE_MESSAGE_REGISTRY: CosmosModuleMessageRegistry = { converter: types.isTx.MsgAnnounceValidatorResponse, }, }, + MsgCreateRoutingIsm: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgCreateRoutingIsm' as const, + converter: types.isTx.MsgCreateRoutingIsm, + }, + amino: { + type: 'hyperlane/v1/MsgCreateRoutingIsm' as const, + }, + }, + MsgCreateRoutingIsmResponse: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgCreateRoutingIsmResponse' as const, + converter: types.isTx.MsgCreateRoutingIsmResponse, + }, + }, + MsgSetRoutingIsmDomain: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgSetRoutingIsmDomain' as const, + converter: types.isTx.MsgSetRoutingIsmDomain, + }, + amino: { + type: 'hyperlane/v1/MsgSetRoutingIsmDomain' as const, + }, + }, + MsgSetRoutingIsmDomainResponse: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgSetRoutingIsmDomainResponse' as const, + converter: types.isTx.MsgSetRoutingIsmDomainResponse, + }, + }, + MsgRemoveRoutingIsmDomain: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgRemoveRoutingIsmDomain' as const, + converter: types.isTx.MsgRemoveRoutingIsmDomain, + }, + amino: { + type: 'hyperlane/v1/MsgRemoveRoutingIsmDomain' as const, + }, + }, + MsgRemoveRoutingIsmDomainResponse: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgRemoveRoutingIsmDomainResponse' as const, + converter: types.isTx.MsgRemoveRoutingIsmDomainResponse, + }, + }, + MsgUpdateRoutingIsmOwner: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgUpdateRoutingIsmOwner' as const, + converter: types.isTx.MsgUpdateRoutingIsmOwner, + }, + amino: { + type: 'hyperlane/v1/MsgUpdateRoutingIsmOwner' as const, + }, + }, + MsgUpdateRoutingIsmOwnerResponse: { + proto: { + type: '/hyperlane.core.interchain_security.v1.MsgUpdateRoutingIsmOwnerResponse' as const, + converter: types.isTx.MsgUpdateRoutingIsmOwnerResponse, + }, + }, // Post dispatch transactions MsgCreateIgp: { diff --git a/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts b/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts index 85a9d347cb3..841530d3319 100644 --- a/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts +++ b/typescript/cosmos-sdk/src/tests/1_interchain_security.e2e-test.ts @@ -4,6 +4,7 @@ import { step } from 'mocha-steps'; import { MerkleRootMultisigISM, MessageIdMultisigISM, + RoutingISM, } from '../../../cosmos-types/dist/types/hyperlane/core/interchain_security/v1/types.js'; import { bytes32ToAddress, @@ -157,4 +158,133 @@ describe('1. cosmos sdk interchain security e2e tests', async function () { expect(decodedIsm.ism.threshold).to.equal(threshold); expect(decodedIsm.ism.validators).deep.equal(validators); }); + + step('create new Routing ISM', async () => { + // ARRANGE + let isms = await signer.query.interchainSecurity.Isms({}); + expect(isms.isms).to.have.lengthOf(3); + + // ACT + const txResponse = await signer.createRoutingIsm({ + routes: [], + }); + + // ASSERT + expect(txResponse.code).to.equal(0); + + const routingIsm = txResponse.response; + + expect(routingIsm.id).to.be.not.empty; + expect(isValidAddressEvm(bytes32ToAddress(routingIsm.id))).to.be.true; + + isms = await signer.query.interchainSecurity.Isms({}); + expect(isms.isms).to.have.lengthOf(4); + + let ism = await signer.query.interchainSecurity.Ism({ + id: routingIsm.id, + }); + expect(ism.ism?.type_url).to.equal( + '/hyperlane.core.interchain_security.v1.RoutingISM', + ); + + let decodedIsm = await signer.query.interchainSecurity.DecodedIsm({ + id: routingIsm.id, + }); + + expect(decodedIsm.ism.id).to.equal(routingIsm.id); + expect(decodedIsm.ism.owner).to.equal(signer.account.address); + + expect((decodedIsm.ism as RoutingISM).routes).to.be.empty; + }); + + step('set Routing Ism domain', async () => { + // ARRANGE + let isms = await signer.query.interchainSecurity.DecodedIsms({}); + expect(isms.isms).to.have.lengthOf(4); + + const ism = isms.isms[isms.isms.length - 1]; + expect((ism as RoutingISM).routes).to.be.empty; + + // ACT + const txResponse = await signer.setRoutingIsmDomain({ + ism_id: ism.id, + route: { + ism: isms.isms[0].id, + domain: 1234, + }, + }); + + // ASSERT + expect(txResponse.code).to.equal(0); + + isms = await signer.query.interchainSecurity.DecodedIsms({}); + expect(isms.isms).to.have.lengthOf(4); + + let decodedIsm = await signer.query.interchainSecurity.DecodedIsm({ + id: ism.id, + }); + + expect((decodedIsm.ism as RoutingISM).routes).to.have.lengthOf(1); + expect((decodedIsm.ism as RoutingISM).routes[0]).to.deep.equal({ + ism: isms.isms[0].id, + domain: 1234, + }); + }); + + step('remove Routing Ism domain', async () => { + // ARRANGE + let isms = await signer.query.interchainSecurity.DecodedIsms({}); + expect(isms.isms).to.have.lengthOf(4); + + const ism = isms.isms[isms.isms.length - 1]; + expect((ism as RoutingISM).routes).to.have.lengthOf(1); + + // ACT + const txResponse = await signer.removeRoutingIsmDomain({ + ism_id: ism.id, + domain: 1234, + }); + + // ASSERT + expect(txResponse.code).to.equal(0); + + isms = await signer.query.interchainSecurity.DecodedIsms({}); + expect(isms.isms).to.have.lengthOf(4); + + let decodedIsm = await signer.query.interchainSecurity.DecodedIsm({ + id: ism.id, + }); + + expect((decodedIsm.ism as RoutingISM).routes).to.be.empty; + }); + + step('update Routing Ism owner', async () => { + // ARRANGE + let isms = await signer.query.interchainSecurity.DecodedIsms({}); + expect(isms.isms).to.have.lengthOf(4); + + const ism = isms.isms[isms.isms.length - 1]; + expect(ism.owner).to.equal(signer.account.address); + + const bobSigner = await createSigner('bob'); + + // ACT + const txResponse = await signer.updateRoutingIsmOwner({ + ism_id: ism.id, + new_owner: bobSigner.account.address, + renounce_ownership: false, + }); + + // ASSERT + expect(txResponse.code).to.equal(0); + + isms = await signer.query.interchainSecurity.DecodedIsms({}); + expect(isms.isms).to.have.lengthOf(4); + + let decodedIsm = await signer.query.interchainSecurity.DecodedIsm({ + id: ism.id, + }); + + expect(decodedIsm.ism.owner).to.equal(bobSigner.account.address); + }); }); diff --git a/typescript/cosmos-sdk/src/tests/2_core.e2e-test.ts b/typescript/cosmos-sdk/src/tests/2_core.e2e-test.ts index 5479d96edf4..1c345c7fd35 100644 --- a/typescript/cosmos-sdk/src/tests/2_core.e2e-test.ts +++ b/typescript/cosmos-sdk/src/tests/2_core.e2e-test.ts @@ -93,6 +93,7 @@ describe('2. cosmos sdk core e2e tests', async function () { default_hook: '', required_hook: '', new_owner: newOwner, + renounce_ownership: false, }); // ASSERT diff --git a/typescript/cosmos-sdk/src/tests/3_post_dispatch.e2e-test.ts b/typescript/cosmos-sdk/src/tests/3_post_dispatch.e2e-test.ts index f01365799f4..563388f357a 100644 --- a/typescript/cosmos-sdk/src/tests/3_post_dispatch.e2e-test.ts +++ b/typescript/cosmos-sdk/src/tests/3_post_dispatch.e2e-test.ts @@ -265,6 +265,7 @@ describe('3. cosmos sdk post dispatch e2e tests', async function () { const txResponse = await signer.setIgpOwner({ igp_id: igpBefore.id, new_owner: newOwner, + renounce_ownership: false, }); // ASSERT diff --git a/typescript/cosmos-sdk/src/tests/4_warp.e2e-test.ts b/typescript/cosmos-sdk/src/tests/4_warp.e2e-test.ts index aba04fa4579..aa68c6a8130 100644 --- a/typescript/cosmos-sdk/src/tests/4_warp.e2e-test.ts +++ b/typescript/cosmos-sdk/src/tests/4_warp.e2e-test.ts @@ -171,6 +171,7 @@ describe('4. cosmos sdk warp e2e tests', async function () { default_hook: igps.igps[0].id, required_hook: merkleTreeHooks.merkle_tree_hooks[0].id, new_owner: '', + renounce_ownership: false, }); expect(mailboxTxResponse.code).to.equal(0); @@ -311,6 +312,7 @@ describe('4. cosmos sdk warp e2e tests', async function () { token_id: tokenBefore.id, ism_id: '', new_owner: newOwner, + renounce_ownership: false, }); // ASSERT diff --git a/typescript/cosmos-types/src/index.ts b/typescript/cosmos-types/src/index.ts index 56788303764..1bea1793335 100644 --- a/typescript/cosmos-types/src/index.ts +++ b/typescript/cosmos-types/src/index.ts @@ -4,6 +4,7 @@ export * as coreQuery from './types/hyperlane/core/v1/query.js'; export * as coreTx from './types/hyperlane/core/v1/tx.js'; export * as coreTypes from './types/hyperlane/core/v1/types.js'; +export * as isEvents from './types/hyperlane/core/interchain_security/v1/events.js'; export * as isGenesis from './types/hyperlane/core/interchain_security/v1/genesis.js'; export * as isQuery from './types/hyperlane/core/interchain_security/v1/query.js'; export * as isTx from './types/hyperlane/core/interchain_security/v1/tx.js'; @@ -16,6 +17,7 @@ export * as pdTx from './types/hyperlane/core/post_dispatch/v1/tx.js'; export * as pdTypes from './types/hyperlane/core/post_dispatch/v1/types.js'; export * as warpEvents from './types/hyperlane/warp/v1/events.js'; +export * as warpGenesis from './types/hyperlane/warp/v1/genesis.js'; export * as warpQuery from './types/hyperlane/warp/v1/query.js'; export * as warpTx from './types/hyperlane/warp/v1/tx.js'; export * as warpTypes from './types/hyperlane/warp/v1/types.js'; diff --git a/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts b/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts index 19c271743fe..17d1a57d10c 100644 --- a/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts +++ b/typescript/cosmos-types/src/types/cosmos/base/v1beta1/coin.ts @@ -30,6 +30,22 @@ export interface DecCoin { amount: string; } +/** + * IntProto defines a Protobuf wrapper around an Int object. + * Deprecated: Prefer to use math.Int directly. It supports binary Marshal and Unmarshal. + */ +export interface IntProto { + int: string; +} + +/** + * DecProto defines a Protobuf wrapper around a Dec object. + * Deprecated: Prefer to use math.LegacyDec directly. It supports binary Marshal and Unmarshal. + */ +export interface DecProto { + dec: string; +} + function createBaseCoin(): Coin { return { denom: '', amount: '' }; } @@ -183,6 +199,128 @@ export const DecCoin = { }, }; +function createBaseIntProto(): IntProto { + return { int: '' }; +} + +export const IntProto = { + encode( + message: IntProto, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.int !== '') { + writer.uint32(10).string(message.int); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): IntProto { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseIntProto(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.int = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): IntProto { + return { int: isSet(object.int) ? globalThis.String(object.int) : '' }; + }, + + toJSON(message: IntProto): unknown { + const obj: any = {}; + if (message.int !== '') { + obj.int = message.int; + } + return obj; + }, + + create, I>>(base?: I): IntProto { + return IntProto.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): IntProto { + const message = createBaseIntProto(); + message.int = object.int ?? ''; + return message; + }, +}; + +function createBaseDecProto(): DecProto { + return { dec: '' }; +} + +export const DecProto = { + encode( + message: DecProto, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.dec !== '') { + writer.uint32(10).string(message.dec); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): DecProto { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDecProto(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.dec = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): DecProto { + return { dec: isSet(object.dec) ? globalThis.String(object.dec) : '' }; + }, + + toJSON(message: DecProto): unknown { + const obj: any = {}; + if (message.dec !== '') { + obj.dec = message.dec; + } + return obj; + }, + + create, I>>(base?: I): DecProto { + return DecProto.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): DecProto { + const message = createBaseDecProto(); + message.dec = object.dec ?? ''; + return message; + }, +}; + type Builtin = | Date | Function diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/events.ts new file mode 100644 index 00000000000..8823acb314e --- /dev/null +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/events.ts @@ -0,0 +1,963 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.181.2 +// protoc unknown +// source: hyperlane/core/interchain_security/v1/events.proto +/* eslint-disable */ +import _m0 from 'protobufjs/minimal.js'; + +export const protobufPackage = 'hyperlane.core.interchain_security.v1'; + +/** EventCreateNoopIsm ... */ +export interface EventCreateNoopIsm { + /** ism_id ... */ + ism_id: string; + /** owner ... */ + owner: string; +} + +/** EventCreateMerkleRootMultisigIsm ... */ +export interface EventCreateMerkleRootMultisigIsm { + ism_id: string; + owner: string; + validators: string[]; + threshold: number; +} + +/** EventCreateMessageIdMultisigIsm ... */ +export interface EventCreateMessageIdMultisigIsm { + ism_id: string; + owner: string; + validators: string[]; + threshold: number; +} + +/** EventCreateMessageIdMultisigIsm ... */ +export interface EventAnnounceStorageLocation { + mailbox_id: string; + sender: string; + validator: string; + storage_location: string; +} + +/** EventSetRoutingIsmDomain ... */ +export interface EventSetRoutingIsmDomain { + ism_id: string; + owner: string; + route_ism_id: string; + route_domain: number; +} + +/** EventRemoveRoutingIsmDomain ... */ +export interface EventRemoveRoutingIsmDomain { + ism_id: string; + owner: string; + route_domain: number; +} + +/** EventRemoveRoutingIsmDomain ... */ +export interface EventSetRoutingIsm { + ism_id: string; + owner: string; + new_owner: string; + renounce_ownership: boolean; +} + +/** EventCreateMessageIdMultisigIsm ... */ +export interface EventCreateRoutingIsm { + ism_id: string; + owner: string; +} + +function createBaseEventCreateNoopIsm(): EventCreateNoopIsm { + return { ism_id: '', owner: '' }; +} + +export const EventCreateNoopIsm = { + encode( + message: EventCreateNoopIsm, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventCreateNoopIsm { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateNoopIsm(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateNoopIsm { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + }; + }, + + toJSON(message: EventCreateNoopIsm): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateNoopIsm { + return EventCreateNoopIsm.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateNoopIsm { + const message = createBaseEventCreateNoopIsm(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + return message; + }, +}; + +function createBaseEventCreateMerkleRootMultisigIsm(): EventCreateMerkleRootMultisigIsm { + return { ism_id: '', owner: '', validators: [], threshold: 0 }; +} + +export const EventCreateMerkleRootMultisigIsm = { + encode( + message: EventCreateMerkleRootMultisigIsm, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + for (const v of message.validators) { + writer.uint32(26).string(v!); + } + if (message.threshold !== 0) { + writer.uint32(32).uint32(message.threshold); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventCreateMerkleRootMultisigIsm { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateMerkleRootMultisigIsm(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.validators.push(reader.string()); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.threshold = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateMerkleRootMultisigIsm { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + validators: globalThis.Array.isArray(object?.validators) + ? object.validators.map((e: any) => globalThis.String(e)) + : [], + threshold: isSet(object.threshold) + ? globalThis.Number(object.threshold) + : 0, + }; + }, + + toJSON(message: EventCreateMerkleRootMultisigIsm): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.validators?.length) { + obj.validators = message.validators; + } + if (message.threshold !== 0) { + obj.threshold = Math.round(message.threshold); + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateMerkleRootMultisigIsm { + return EventCreateMerkleRootMultisigIsm.fromPartial(base ?? ({} as any)); + }, + fromPartial< + I extends Exact, I>, + >(object: I): EventCreateMerkleRootMultisigIsm { + const message = createBaseEventCreateMerkleRootMultisigIsm(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + message.validators = object.validators?.map((e) => e) || []; + message.threshold = object.threshold ?? 0; + return message; + }, +}; + +function createBaseEventCreateMessageIdMultisigIsm(): EventCreateMessageIdMultisigIsm { + return { ism_id: '', owner: '', validators: [], threshold: 0 }; +} + +export const EventCreateMessageIdMultisigIsm = { + encode( + message: EventCreateMessageIdMultisigIsm, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + for (const v of message.validators) { + writer.uint32(26).string(v!); + } + if (message.threshold !== 0) { + writer.uint32(32).uint32(message.threshold); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventCreateMessageIdMultisigIsm { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateMessageIdMultisigIsm(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.validators.push(reader.string()); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.threshold = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateMessageIdMultisigIsm { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + validators: globalThis.Array.isArray(object?.validators) + ? object.validators.map((e: any) => globalThis.String(e)) + : [], + threshold: isSet(object.threshold) + ? globalThis.Number(object.threshold) + : 0, + }; + }, + + toJSON(message: EventCreateMessageIdMultisigIsm): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.validators?.length) { + obj.validators = message.validators; + } + if (message.threshold !== 0) { + obj.threshold = Math.round(message.threshold); + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateMessageIdMultisigIsm { + return EventCreateMessageIdMultisigIsm.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateMessageIdMultisigIsm { + const message = createBaseEventCreateMessageIdMultisigIsm(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + message.validators = object.validators?.map((e) => e) || []; + message.threshold = object.threshold ?? 0; + return message; + }, +}; + +function createBaseEventAnnounceStorageLocation(): EventAnnounceStorageLocation { + return { mailbox_id: '', sender: '', validator: '', storage_location: '' }; +} + +export const EventAnnounceStorageLocation = { + encode( + message: EventAnnounceStorageLocation, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.mailbox_id !== '') { + writer.uint32(10).string(message.mailbox_id); + } + if (message.sender !== '') { + writer.uint32(18).string(message.sender); + } + if (message.validator !== '') { + writer.uint32(26).string(message.validator); + } + if (message.storage_location !== '') { + writer.uint32(34).string(message.storage_location); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventAnnounceStorageLocation { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventAnnounceStorageLocation(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.mailbox_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.sender = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.validator = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.storage_location = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventAnnounceStorageLocation { + return { + mailbox_id: isSet(object.mailbox_id) + ? globalThis.String(object.mailbox_id) + : '', + sender: isSet(object.sender) ? globalThis.String(object.sender) : '', + validator: isSet(object.validator) + ? globalThis.String(object.validator) + : '', + storage_location: isSet(object.storage_location) + ? globalThis.String(object.storage_location) + : '', + }; + }, + + toJSON(message: EventAnnounceStorageLocation): unknown { + const obj: any = {}; + if (message.mailbox_id !== '') { + obj.mailbox_id = message.mailbox_id; + } + if (message.sender !== '') { + obj.sender = message.sender; + } + if (message.validator !== '') { + obj.validator = message.validator; + } + if (message.storage_location !== '') { + obj.storage_location = message.storage_location; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventAnnounceStorageLocation { + return EventAnnounceStorageLocation.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventAnnounceStorageLocation { + const message = createBaseEventAnnounceStorageLocation(); + message.mailbox_id = object.mailbox_id ?? ''; + message.sender = object.sender ?? ''; + message.validator = object.validator ?? ''; + message.storage_location = object.storage_location ?? ''; + return message; + }, +}; + +function createBaseEventSetRoutingIsmDomain(): EventSetRoutingIsmDomain { + return { ism_id: '', owner: '', route_ism_id: '', route_domain: 0 }; +} + +export const EventSetRoutingIsmDomain = { + encode( + message: EventSetRoutingIsmDomain, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.route_ism_id !== '') { + writer.uint32(26).string(message.route_ism_id); + } + if (message.route_domain !== 0) { + writer.uint32(32).uint32(message.route_domain); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventSetRoutingIsmDomain { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventSetRoutingIsmDomain(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.route_ism_id = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.route_domain = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventSetRoutingIsmDomain { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + route_ism_id: isSet(object.route_ism_id) + ? globalThis.String(object.route_ism_id) + : '', + route_domain: isSet(object.route_domain) + ? globalThis.Number(object.route_domain) + : 0, + }; + }, + + toJSON(message: EventSetRoutingIsmDomain): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.route_ism_id !== '') { + obj.route_ism_id = message.route_ism_id; + } + if (message.route_domain !== 0) { + obj.route_domain = Math.round(message.route_domain); + } + return obj; + }, + + create, I>>( + base?: I, + ): EventSetRoutingIsmDomain { + return EventSetRoutingIsmDomain.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventSetRoutingIsmDomain { + const message = createBaseEventSetRoutingIsmDomain(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + message.route_ism_id = object.route_ism_id ?? ''; + message.route_domain = object.route_domain ?? 0; + return message; + }, +}; + +function createBaseEventRemoveRoutingIsmDomain(): EventRemoveRoutingIsmDomain { + return { ism_id: '', owner: '', route_domain: 0 }; +} + +export const EventRemoveRoutingIsmDomain = { + encode( + message: EventRemoveRoutingIsmDomain, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.route_domain !== 0) { + writer.uint32(24).uint32(message.route_domain); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventRemoveRoutingIsmDomain { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventRemoveRoutingIsmDomain(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.route_domain = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventRemoveRoutingIsmDomain { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + route_domain: isSet(object.route_domain) + ? globalThis.Number(object.route_domain) + : 0, + }; + }, + + toJSON(message: EventRemoveRoutingIsmDomain): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.route_domain !== 0) { + obj.route_domain = Math.round(message.route_domain); + } + return obj; + }, + + create, I>>( + base?: I, + ): EventRemoveRoutingIsmDomain { + return EventRemoveRoutingIsmDomain.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventRemoveRoutingIsmDomain { + const message = createBaseEventRemoveRoutingIsmDomain(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + message.route_domain = object.route_domain ?? 0; + return message; + }, +}; + +function createBaseEventSetRoutingIsm(): EventSetRoutingIsm { + return { ism_id: '', owner: '', new_owner: '', renounce_ownership: false }; +} + +export const EventSetRoutingIsm = { + encode( + message: EventSetRoutingIsm, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.new_owner !== '') { + writer.uint32(26).string(message.new_owner); + } + if (message.renounce_ownership !== false) { + writer.uint32(32).bool(message.renounce_ownership); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventSetRoutingIsm { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventSetRoutingIsm(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.new_owner = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventSetRoutingIsm { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + new_owner: isSet(object.new_owner) + ? globalThis.String(object.new_owner) + : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, + }; + }, + + toJSON(message: EventSetRoutingIsm): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.new_owner !== '') { + obj.new_owner = message.new_owner; + } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventSetRoutingIsm { + return EventSetRoutingIsm.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventSetRoutingIsm { + const message = createBaseEventSetRoutingIsm(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; + return message; + }, +}; + +function createBaseEventCreateRoutingIsm(): EventCreateRoutingIsm { + return { ism_id: '', owner: '' }; +} + +export const EventCreateRoutingIsm = { + encode( + message: EventCreateRoutingIsm, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventCreateRoutingIsm { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateRoutingIsm(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateRoutingIsm { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + }; + }, + + toJSON(message: EventCreateRoutingIsm): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateRoutingIsm { + return EventCreateRoutingIsm.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateRoutingIsm { + const message = createBaseEventCreateRoutingIsm(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + return message; + }, +}; + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends globalThis.Array + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts index 73dd27dd984..40e3b67f048 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/tx.ts @@ -6,6 +6,8 @@ /* eslint-disable */ import _m0 from 'protobufjs/minimal.js'; +import { Route } from './types.js'; + export const protobufPackage = 'hyperlane.core.interchain_security.v1'; /** MsgCreateMessageIdMultisigIsm ... */ @@ -72,6 +74,60 @@ export interface MsgAnnounceValidator { /** MsgAnnounceValidatorResponse ... */ export interface MsgAnnounceValidatorResponse {} +/** MsgCreateRoutingIsm ... */ +export interface MsgCreateRoutingIsm { + /** creator ... */ + creator: string; + /** routes ... */ + routes: Route[]; +} + +/** MsgCreateRoutingIsmResponse ... */ +export interface MsgCreateRoutingIsmResponse { + id: string; +} + +/** MsgSetRoutingIsmDomain ... */ +export interface MsgSetRoutingIsmDomain { + /** ism_id ... */ + ism_id: string; + /** route ... */ + route?: Route | undefined; + /** owner ... */ + owner: string; +} + +/** MsgSetRoutingIsmDomainResponse ... */ +export interface MsgSetRoutingIsmDomainResponse {} + +/** MsgRemoveRoutingIsmDomain ... */ +export interface MsgRemoveRoutingIsmDomain { + /** ism_id ... */ + ism_id: string; + /** domain ... */ + domain: number; + /** owner ... */ + owner: string; +} + +/** MsgRemoveRoutingIsmDomainResponse ... */ +export interface MsgRemoveRoutingIsmDomainResponse {} + +/** MsgUpdateRoutingIsmOwner ... */ +export interface MsgUpdateRoutingIsmOwner { + /** ism_id ... */ + ism_id: string; + /** owner ... */ + owner: string; + /** new owner */ + new_owner: string; + /** renounce_ownership */ + renounce_ownership: boolean; +} + +/** MsgUpdateRoutingIsmOwnerResponse ... */ +export interface MsgUpdateRoutingIsmOwnerResponse {} + function createBaseMsgCreateMessageIdMultisigIsm(): MsgCreateMessageIdMultisigIsm { return { creator: '', validators: [], threshold: 0 }; } @@ -753,6 +809,642 @@ export const MsgAnnounceValidatorResponse = { }, }; +function createBaseMsgCreateRoutingIsm(): MsgCreateRoutingIsm { + return { creator: '', routes: [] }; +} + +export const MsgCreateRoutingIsm = { + encode( + message: MsgCreateRoutingIsm, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.creator !== '') { + writer.uint32(10).string(message.creator); + } + for (const v of message.routes) { + Route.encode(v!, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgCreateRoutingIsm { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgCreateRoutingIsm(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.creator = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.routes.push(Route.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MsgCreateRoutingIsm { + return { + creator: isSet(object.creator) ? globalThis.String(object.creator) : '', + routes: globalThis.Array.isArray(object?.routes) + ? object.routes.map((e: any) => Route.fromJSON(e)) + : [], + }; + }, + + toJSON(message: MsgCreateRoutingIsm): unknown { + const obj: any = {}; + if (message.creator !== '') { + obj.creator = message.creator; + } + if (message.routes?.length) { + obj.routes = message.routes.map((e) => Route.toJSON(e)); + } + return obj; + }, + + create, I>>( + base?: I, + ): MsgCreateRoutingIsm { + return MsgCreateRoutingIsm.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MsgCreateRoutingIsm { + const message = createBaseMsgCreateRoutingIsm(); + message.creator = object.creator ?? ''; + message.routes = object.routes?.map((e) => Route.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseMsgCreateRoutingIsmResponse(): MsgCreateRoutingIsmResponse { + return { id: '' }; +} + +export const MsgCreateRoutingIsmResponse = { + encode( + message: MsgCreateRoutingIsmResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.id !== '') { + writer.uint32(10).string(message.id); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgCreateRoutingIsmResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgCreateRoutingIsmResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MsgCreateRoutingIsmResponse { + return { id: isSet(object.id) ? globalThis.String(object.id) : '' }; + }, + + toJSON(message: MsgCreateRoutingIsmResponse): unknown { + const obj: any = {}; + if (message.id !== '') { + obj.id = message.id; + } + return obj; + }, + + create, I>>( + base?: I, + ): MsgCreateRoutingIsmResponse { + return MsgCreateRoutingIsmResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MsgCreateRoutingIsmResponse { + const message = createBaseMsgCreateRoutingIsmResponse(); + message.id = object.id ?? ''; + return message; + }, +}; + +function createBaseMsgSetRoutingIsmDomain(): MsgSetRoutingIsmDomain { + return { ism_id: '', route: undefined, owner: '' }; +} + +export const MsgSetRoutingIsmDomain = { + encode( + message: MsgSetRoutingIsmDomain, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.route !== undefined) { + Route.encode(message.route, writer.uint32(18).fork()).ldelim(); + } + if (message.owner !== '') { + writer.uint32(26).string(message.owner); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgSetRoutingIsmDomain { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgSetRoutingIsmDomain(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.route = Route.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.owner = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MsgSetRoutingIsmDomain { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + route: isSet(object.route) ? Route.fromJSON(object.route) : undefined, + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + }; + }, + + toJSON(message: MsgSetRoutingIsmDomain): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.route !== undefined) { + obj.route = Route.toJSON(message.route); + } + if (message.owner !== '') { + obj.owner = message.owner; + } + return obj; + }, + + create, I>>( + base?: I, + ): MsgSetRoutingIsmDomain { + return MsgSetRoutingIsmDomain.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MsgSetRoutingIsmDomain { + const message = createBaseMsgSetRoutingIsmDomain(); + message.ism_id = object.ism_id ?? ''; + message.route = + object.route !== undefined && object.route !== null + ? Route.fromPartial(object.route) + : undefined; + message.owner = object.owner ?? ''; + return message; + }, +}; + +function createBaseMsgSetRoutingIsmDomainResponse(): MsgSetRoutingIsmDomainResponse { + return {}; +} + +export const MsgSetRoutingIsmDomainResponse = { + encode( + _: MsgSetRoutingIsmDomainResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgSetRoutingIsmDomainResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgSetRoutingIsmDomainResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): MsgSetRoutingIsmDomainResponse { + return {}; + }, + + toJSON(_: MsgSetRoutingIsmDomainResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): MsgSetRoutingIsmDomainResponse { + return MsgSetRoutingIsmDomainResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + _: I, + ): MsgSetRoutingIsmDomainResponse { + const message = createBaseMsgSetRoutingIsmDomainResponse(); + return message; + }, +}; + +function createBaseMsgRemoveRoutingIsmDomain(): MsgRemoveRoutingIsmDomain { + return { ism_id: '', domain: 0, owner: '' }; +} + +export const MsgRemoveRoutingIsmDomain = { + encode( + message: MsgRemoveRoutingIsmDomain, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.domain !== 0) { + writer.uint32(16).uint32(message.domain); + } + if (message.owner !== '') { + writer.uint32(26).string(message.owner); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgRemoveRoutingIsmDomain { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgRemoveRoutingIsmDomain(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.domain = reader.uint32(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.owner = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MsgRemoveRoutingIsmDomain { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + domain: isSet(object.domain) ? globalThis.Number(object.domain) : 0, + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + }; + }, + + toJSON(message: MsgRemoveRoutingIsmDomain): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.domain !== 0) { + obj.domain = Math.round(message.domain); + } + if (message.owner !== '') { + obj.owner = message.owner; + } + return obj; + }, + + create, I>>( + base?: I, + ): MsgRemoveRoutingIsmDomain { + return MsgRemoveRoutingIsmDomain.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MsgRemoveRoutingIsmDomain { + const message = createBaseMsgRemoveRoutingIsmDomain(); + message.ism_id = object.ism_id ?? ''; + message.domain = object.domain ?? 0; + message.owner = object.owner ?? ''; + return message; + }, +}; + +function createBaseMsgRemoveRoutingIsmDomainResponse(): MsgRemoveRoutingIsmDomainResponse { + return {}; +} + +export const MsgRemoveRoutingIsmDomainResponse = { + encode( + _: MsgRemoveRoutingIsmDomainResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgRemoveRoutingIsmDomainResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgRemoveRoutingIsmDomainResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): MsgRemoveRoutingIsmDomainResponse { + return {}; + }, + + toJSON(_: MsgRemoveRoutingIsmDomainResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): MsgRemoveRoutingIsmDomainResponse { + return MsgRemoveRoutingIsmDomainResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial< + I extends Exact, I>, + >(_: I): MsgRemoveRoutingIsmDomainResponse { + const message = createBaseMsgRemoveRoutingIsmDomainResponse(); + return message; + }, +}; + +function createBaseMsgUpdateRoutingIsmOwner(): MsgUpdateRoutingIsmOwner { + return { ism_id: '', owner: '', new_owner: '', renounce_ownership: false }; +} + +export const MsgUpdateRoutingIsmOwner = { + encode( + message: MsgUpdateRoutingIsmOwner, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.ism_id !== '') { + writer.uint32(10).string(message.ism_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.new_owner !== '') { + writer.uint32(26).string(message.new_owner); + } + if (message.renounce_ownership !== false) { + writer.uint32(32).bool(message.renounce_ownership); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgUpdateRoutingIsmOwner { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateRoutingIsmOwner(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.new_owner = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MsgUpdateRoutingIsmOwner { + return { + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + new_owner: isSet(object.new_owner) + ? globalThis.String(object.new_owner) + : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, + }; + }, + + toJSON(message: MsgUpdateRoutingIsmOwner): unknown { + const obj: any = {}; + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.new_owner !== '') { + obj.new_owner = message.new_owner; + } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } + return obj; + }, + + create, I>>( + base?: I, + ): MsgUpdateRoutingIsmOwner { + return MsgUpdateRoutingIsmOwner.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MsgUpdateRoutingIsmOwner { + const message = createBaseMsgUpdateRoutingIsmOwner(); + message.ism_id = object.ism_id ?? ''; + message.owner = object.owner ?? ''; + message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; + return message; + }, +}; + +function createBaseMsgUpdateRoutingIsmOwnerResponse(): MsgUpdateRoutingIsmOwnerResponse { + return {}; +} + +export const MsgUpdateRoutingIsmOwnerResponse = { + encode( + _: MsgUpdateRoutingIsmOwnerResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): MsgUpdateRoutingIsmOwnerResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateRoutingIsmOwnerResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): MsgUpdateRoutingIsmOwnerResponse { + return {}; + }, + + toJSON(_: MsgUpdateRoutingIsmOwnerResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): MsgUpdateRoutingIsmOwnerResponse { + return MsgUpdateRoutingIsmOwnerResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial< + I extends Exact, I>, + >(_: I): MsgUpdateRoutingIsmOwnerResponse { + const message = createBaseMsgUpdateRoutingIsmOwnerResponse(); + return message; + }, +}; + /** Msg defines the module Msg service. */ export interface Msg { /** CreateMessageIdMultisigIsm ... */ @@ -765,6 +1457,22 @@ export interface Msg { ): Promise; /** CreateNoopIsm ... */ CreateNoopIsm(request: MsgCreateNoopIsm): Promise; + /** CreateRoutingIsm ... */ + CreateRoutingIsm( + request: MsgCreateRoutingIsm, + ): Promise; + /** SetRoutingIsmDomain ... */ + SetRoutingIsmDomain( + request: MsgSetRoutingIsmDomain, + ): Promise; + /** RemoveRoutingIsmDomain ... */ + RemoveRoutingIsmDomain( + request: MsgRemoveRoutingIsmDomain, + ): Promise; + /** UpdateRoutingIsmOwner ... */ + UpdateRoutingIsmOwner( + request: MsgUpdateRoutingIsmOwner, + ): Promise; /** AnnounceValidator ... */ AnnounceValidator( request: MsgAnnounceValidator, @@ -783,6 +1491,10 @@ export class MsgClientImpl implements Msg { this.CreateMerkleRootMultisigIsm = this.CreateMerkleRootMultisigIsm.bind(this); this.CreateNoopIsm = this.CreateNoopIsm.bind(this); + this.CreateRoutingIsm = this.CreateRoutingIsm.bind(this); + this.SetRoutingIsmDomain = this.SetRoutingIsmDomain.bind(this); + this.RemoveRoutingIsmDomain = this.RemoveRoutingIsmDomain.bind(this); + this.UpdateRoutingIsmOwner = this.UpdateRoutingIsmOwner.bind(this); this.AnnounceValidator = this.AnnounceValidator.bind(this); } CreateMessageIdMultisigIsm( @@ -821,6 +1533,54 @@ export class MsgClientImpl implements Msg { ); } + CreateRoutingIsm( + request: MsgCreateRoutingIsm, + ): Promise { + const data = MsgCreateRoutingIsm.encode(request).finish(); + const promise = this.rpc.request(this.service, 'CreateRoutingIsm', data); + return promise.then((data) => + MsgCreateRoutingIsmResponse.decode(_m0.Reader.create(data)), + ); + } + + SetRoutingIsmDomain( + request: MsgSetRoutingIsmDomain, + ): Promise { + const data = MsgSetRoutingIsmDomain.encode(request).finish(); + const promise = this.rpc.request(this.service, 'SetRoutingIsmDomain', data); + return promise.then((data) => + MsgSetRoutingIsmDomainResponse.decode(_m0.Reader.create(data)), + ); + } + + RemoveRoutingIsmDomain( + request: MsgRemoveRoutingIsmDomain, + ): Promise { + const data = MsgRemoveRoutingIsmDomain.encode(request).finish(); + const promise = this.rpc.request( + this.service, + 'RemoveRoutingIsmDomain', + data, + ); + return promise.then((data) => + MsgRemoveRoutingIsmDomainResponse.decode(_m0.Reader.create(data)), + ); + } + + UpdateRoutingIsmOwner( + request: MsgUpdateRoutingIsmOwner, + ): Promise { + const data = MsgUpdateRoutingIsmOwner.encode(request).finish(); + const promise = this.rpc.request( + this.service, + 'UpdateRoutingIsmOwner', + data, + ); + return promise.then((data) => + MsgUpdateRoutingIsmOwnerResponse.decode(_m0.Reader.create(data)), + ); + } + AnnounceValidator( request: MsgAnnounceValidator, ): Promise { diff --git a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts index 29e8a944bad..7df644fd913 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/interchain_security/v1/types.ts @@ -8,6 +8,30 @@ import _m0 from 'protobufjs/minimal.js'; export const protobufPackage = 'hyperlane.core.interchain_security.v1'; +/** Route */ +export interface Route { + /** ism ... */ + ism: string; + /** domain ... */ + domain: number; +} + +/** Routing ISM ... */ +export interface RoutingISM { + /** id ... */ + id: string; + /** owner ... */ + owner: string; + /** + * Routes associated with the Routing ISM. + * These are stored directly within the ISM to simplify the design, + * as the number of routes is expected to remain small. + * This approach avoids the added complexity of managing a separate + * collection. + */ + routes: Route[]; +} + /** MessageIdMultisigISM ... */ export interface MessageIdMultisigISM { /** id ... */ @@ -46,6 +70,178 @@ export interface NoopISM { owner: string; } +function createBaseRoute(): Route { + return { ism: '', domain: 0 }; +} + +export const Route = { + encode(message: Route, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.ism !== '') { + writer.uint32(10).string(message.ism); + } + if (message.domain !== 0) { + writer.uint32(16).uint32(message.domain); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Route { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRoute(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.ism = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.domain = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Route { + return { + ism: isSet(object.ism) ? globalThis.String(object.ism) : '', + domain: isSet(object.domain) ? globalThis.Number(object.domain) : 0, + }; + }, + + toJSON(message: Route): unknown { + const obj: any = {}; + if (message.ism !== '') { + obj.ism = message.ism; + } + if (message.domain !== 0) { + obj.domain = Math.round(message.domain); + } + return obj; + }, + + create, I>>(base?: I): Route { + return Route.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Route { + const message = createBaseRoute(); + message.ism = object.ism ?? ''; + message.domain = object.domain ?? 0; + return message; + }, +}; + +function createBaseRoutingISM(): RoutingISM { + return { id: '', owner: '', routes: [] }; +} + +export const RoutingISM = { + encode( + message: RoutingISM, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.id !== '') { + writer.uint32(10).string(message.id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + for (const v of message.routes) { + Route.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): RoutingISM { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRoutingISM(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.routes.push(Route.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): RoutingISM { + return { + id: isSet(object.id) ? globalThis.String(object.id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + routes: globalThis.Array.isArray(object?.routes) + ? object.routes.map((e: any) => Route.fromJSON(e)) + : [], + }; + }, + + toJSON(message: RoutingISM): unknown { + const obj: any = {}; + if (message.id !== '') { + obj.id = message.id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.routes?.length) { + obj.routes = message.routes.map((e) => Route.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): RoutingISM { + return RoutingISM.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): RoutingISM { + const message = createBaseRoutingISM(); + message.id = object.id ?? ''; + message.owner = object.owner ?? ''; + message.routes = object.routes?.map((e) => Route.fromPartial(e)) || []; + return message; + }, +}; + function createBaseMessageIdMultisigISM(): MessageIdMultisigISM { return { id: '', owner: '', validators: [], threshold: 0 }; } diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts index 0e9a114f107..1d916674459 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/events.ts @@ -8,17 +8,17 @@ import _m0 from 'protobufjs/minimal.js'; export const protobufPackage = 'hyperlane.core.post_dispatch.v1'; -/** InsertedIntoTree ... */ +/** EventCreateMerkleTreeHook ... */ export interface EventCreateMerkleTreeHook { /** id ... */ - id: string; + merkle_tree_hook_id: string; /** mailbox_id ... */ mailbox_id: string; owner: string; } -/** InsertedIntoTree ... */ -export interface InsertedIntoTree { +/** EventInsertedIntoTree ... */ +export interface EventInsertedIntoTree { /** message_id ... */ message_id: string; /** index ... */ @@ -27,8 +27,8 @@ export interface InsertedIntoTree { merkle_tree_hook_id: string; } -/** GasPayment ... */ -export interface GasPayment { +/** EventGasPayment ... */ +export interface EventGasPayment { /** message_id ... */ message_id: string; /** destination ... */ @@ -41,16 +41,48 @@ export interface GasPayment { igp_id: string; } -/** InsertedIntoTree ... */ +/** EventCreateNoopHook ... */ export interface EventCreateNoopHook { /** id ... */ - id: string; + noop_hook_id: string; /** owner ... */ owner: string; } +/** EventCreateIgp ... */ +export interface EventCreateIgp { + igp_id: string; + owner: string; + denom: string; +} + +/** EventSetIgp ... */ +export interface EventSetIgp { + igp_id: string; + owner: string; + new_owner: string; + renounce_ownership: boolean; +} + +/** EventSetDestinationGasConfig ... */ +export interface EventSetDestinationGasConfig { + igp_id: string; + owner: string; + remote_domain: number; + gas_overhead: string; + gas_price: string; + token_exchange_rate: string; +} + +/** EventClaimIgp ... */ +export interface EventClaimIgp { + igp_id: string; + owner: string; + amount: string; +} + function createBaseEventCreateMerkleTreeHook(): EventCreateMerkleTreeHook { - return { id: '', mailbox_id: '', owner: '' }; + return { merkle_tree_hook_id: '', mailbox_id: '', owner: '' }; } export const EventCreateMerkleTreeHook = { @@ -58,8 +90,8 @@ export const EventCreateMerkleTreeHook = { message: EventCreateMerkleTreeHook, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { - if (message.id !== '') { - writer.uint32(10).string(message.id); + if (message.merkle_tree_hook_id !== '') { + writer.uint32(10).string(message.merkle_tree_hook_id); } if (message.mailbox_id !== '') { writer.uint32(18).string(message.mailbox_id); @@ -86,7 +118,7 @@ export const EventCreateMerkleTreeHook = { break; } - message.id = reader.string(); + message.merkle_tree_hook_id = reader.string(); continue; case 2: if (tag !== 18) { @@ -113,7 +145,9 @@ export const EventCreateMerkleTreeHook = { fromJSON(object: any): EventCreateMerkleTreeHook { return { - id: isSet(object.id) ? globalThis.String(object.id) : '', + merkle_tree_hook_id: isSet(object.merkle_tree_hook_id) + ? globalThis.String(object.merkle_tree_hook_id) + : '', mailbox_id: isSet(object.mailbox_id) ? globalThis.String(object.mailbox_id) : '', @@ -123,8 +157,8 @@ export const EventCreateMerkleTreeHook = { toJSON(message: EventCreateMerkleTreeHook): unknown { const obj: any = {}; - if (message.id !== '') { - obj.id = message.id; + if (message.merkle_tree_hook_id !== '') { + obj.merkle_tree_hook_id = message.merkle_tree_hook_id; } if (message.mailbox_id !== '') { obj.mailbox_id = message.mailbox_id; @@ -144,20 +178,20 @@ export const EventCreateMerkleTreeHook = { object: I, ): EventCreateMerkleTreeHook { const message = createBaseEventCreateMerkleTreeHook(); - message.id = object.id ?? ''; + message.merkle_tree_hook_id = object.merkle_tree_hook_id ?? ''; message.mailbox_id = object.mailbox_id ?? ''; message.owner = object.owner ?? ''; return message; }, }; -function createBaseInsertedIntoTree(): InsertedIntoTree { +function createBaseEventInsertedIntoTree(): EventInsertedIntoTree { return { message_id: '', index: 0, merkle_tree_hook_id: '' }; } -export const InsertedIntoTree = { +export const EventInsertedIntoTree = { encode( - message: InsertedIntoTree, + message: EventInsertedIntoTree, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { if (message.message_id !== '') { @@ -172,11 +206,14 @@ export const InsertedIntoTree = { return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): InsertedIntoTree { + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventInsertedIntoTree { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseInsertedIntoTree(); + const message = createBaseEventInsertedIntoTree(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -210,7 +247,7 @@ export const InsertedIntoTree = { return message; }, - fromJSON(object: any): InsertedIntoTree { + fromJSON(object: any): EventInsertedIntoTree { return { message_id: isSet(object.message_id) ? globalThis.String(object.message_id) @@ -222,7 +259,7 @@ export const InsertedIntoTree = { }; }, - toJSON(message: InsertedIntoTree): unknown { + toJSON(message: EventInsertedIntoTree): unknown { const obj: any = {}; if (message.message_id !== '') { obj.message_id = message.message_id; @@ -236,15 +273,15 @@ export const InsertedIntoTree = { return obj; }, - create, I>>( + create, I>>( base?: I, - ): InsertedIntoTree { - return InsertedIntoTree.fromPartial(base ?? ({} as any)); + ): EventInsertedIntoTree { + return EventInsertedIntoTree.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>( + fromPartial, I>>( object: I, - ): InsertedIntoTree { - const message = createBaseInsertedIntoTree(); + ): EventInsertedIntoTree { + const message = createBaseEventInsertedIntoTree(); message.message_id = object.message_id ?? ''; message.index = object.index ?? 0; message.merkle_tree_hook_id = object.merkle_tree_hook_id ?? ''; @@ -252,7 +289,7 @@ export const InsertedIntoTree = { }, }; -function createBaseGasPayment(): GasPayment { +function createBaseEventGasPayment(): EventGasPayment { return { message_id: '', destination: 0, @@ -262,9 +299,9 @@ function createBaseGasPayment(): GasPayment { }; } -export const GasPayment = { +export const EventGasPayment = { encode( - message: GasPayment, + message: EventGasPayment, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { if (message.message_id !== '') { @@ -285,11 +322,11 @@ export const GasPayment = { return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): GasPayment { + decode(input: _m0.Reader | Uint8Array, length?: number): EventGasPayment { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseGasPayment(); + const message = createBaseEventGasPayment(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -337,7 +374,7 @@ export const GasPayment = { return message; }, - fromJSON(object: any): GasPayment { + fromJSON(object: any): EventGasPayment { return { message_id: isSet(object.message_id) ? globalThis.String(object.message_id) @@ -353,7 +390,7 @@ export const GasPayment = { }; }, - toJSON(message: GasPayment): unknown { + toJSON(message: EventGasPayment): unknown { const obj: any = {}; if (message.message_id !== '') { obj.message_id = message.message_id; @@ -373,13 +410,15 @@ export const GasPayment = { return obj; }, - create, I>>(base?: I): GasPayment { - return GasPayment.fromPartial(base ?? ({} as any)); + create, I>>( + base?: I, + ): EventGasPayment { + return EventGasPayment.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>( + fromPartial, I>>( object: I, - ): GasPayment { - const message = createBaseGasPayment(); + ): EventGasPayment { + const message = createBaseEventGasPayment(); message.message_id = object.message_id ?? ''; message.destination = object.destination ?? 0; message.gas_amount = object.gas_amount ?? ''; @@ -390,7 +429,7 @@ export const GasPayment = { }; function createBaseEventCreateNoopHook(): EventCreateNoopHook { - return { id: '', owner: '' }; + return { noop_hook_id: '', owner: '' }; } export const EventCreateNoopHook = { @@ -398,8 +437,8 @@ export const EventCreateNoopHook = { message: EventCreateNoopHook, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { - if (message.id !== '') { - writer.uint32(10).string(message.id); + if (message.noop_hook_id !== '') { + writer.uint32(10).string(message.noop_hook_id); } if (message.owner !== '') { writer.uint32(18).string(message.owner); @@ -420,7 +459,7 @@ export const EventCreateNoopHook = { break; } - message.id = reader.string(); + message.noop_hook_id = reader.string(); continue; case 2: if (tag !== 18) { @@ -440,15 +479,17 @@ export const EventCreateNoopHook = { fromJSON(object: any): EventCreateNoopHook { return { - id: isSet(object.id) ? globalThis.String(object.id) : '', + noop_hook_id: isSet(object.noop_hook_id) + ? globalThis.String(object.noop_hook_id) + : '', owner: isSet(object.owner) ? globalThis.String(object.owner) : '', }; }, toJSON(message: EventCreateNoopHook): unknown { const obj: any = {}; - if (message.id !== '') { - obj.id = message.id; + if (message.noop_hook_id !== '') { + obj.noop_hook_id = message.noop_hook_id; } if (message.owner !== '') { obj.owner = message.owner; @@ -465,8 +506,476 @@ export const EventCreateNoopHook = { object: I, ): EventCreateNoopHook { const message = createBaseEventCreateNoopHook(); - message.id = object.id ?? ''; + message.noop_hook_id = object.noop_hook_id ?? ''; + message.owner = object.owner ?? ''; + return message; + }, +}; + +function createBaseEventCreateIgp(): EventCreateIgp { + return { igp_id: '', owner: '', denom: '' }; +} + +export const EventCreateIgp = { + encode( + message: EventCreateIgp, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.igp_id !== '') { + writer.uint32(10).string(message.igp_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.denom !== '') { + writer.uint32(26).string(message.denom); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventCreateIgp { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateIgp(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.igp_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.denom = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateIgp { + return { + igp_id: isSet(object.igp_id) ? globalThis.String(object.igp_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + denom: isSet(object.denom) ? globalThis.String(object.denom) : '', + }; + }, + + toJSON(message: EventCreateIgp): unknown { + const obj: any = {}; + if (message.igp_id !== '') { + obj.igp_id = message.igp_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.denom !== '') { + obj.denom = message.denom; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateIgp { + return EventCreateIgp.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateIgp { + const message = createBaseEventCreateIgp(); + message.igp_id = object.igp_id ?? ''; + message.owner = object.owner ?? ''; + message.denom = object.denom ?? ''; + return message; + }, +}; + +function createBaseEventSetIgp(): EventSetIgp { + return { igp_id: '', owner: '', new_owner: '', renounce_ownership: false }; +} + +export const EventSetIgp = { + encode( + message: EventSetIgp, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.igp_id !== '') { + writer.uint32(10).string(message.igp_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.new_owner !== '') { + writer.uint32(26).string(message.new_owner); + } + if (message.renounce_ownership !== false) { + writer.uint32(32).bool(message.renounce_ownership); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventSetIgp { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventSetIgp(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.igp_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.new_owner = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventSetIgp { + return { + igp_id: isSet(object.igp_id) ? globalThis.String(object.igp_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + new_owner: isSet(object.new_owner) + ? globalThis.String(object.new_owner) + : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, + }; + }, + + toJSON(message: EventSetIgp): unknown { + const obj: any = {}; + if (message.igp_id !== '') { + obj.igp_id = message.igp_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.new_owner !== '') { + obj.new_owner = message.new_owner; + } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } + return obj; + }, + + create, I>>(base?: I): EventSetIgp { + return EventSetIgp.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventSetIgp { + const message = createBaseEventSetIgp(); + message.igp_id = object.igp_id ?? ''; + message.owner = object.owner ?? ''; + message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; + return message; + }, +}; + +function createBaseEventSetDestinationGasConfig(): EventSetDestinationGasConfig { + return { + igp_id: '', + owner: '', + remote_domain: 0, + gas_overhead: '', + gas_price: '', + token_exchange_rate: '', + }; +} + +export const EventSetDestinationGasConfig = { + encode( + message: EventSetDestinationGasConfig, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.igp_id !== '') { + writer.uint32(10).string(message.igp_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.remote_domain !== 0) { + writer.uint32(32).uint32(message.remote_domain); + } + if (message.gas_overhead !== '') { + writer.uint32(42).string(message.gas_overhead); + } + if (message.gas_price !== '') { + writer.uint32(50).string(message.gas_price); + } + if (message.token_exchange_rate !== '') { + writer.uint32(58).string(message.token_exchange_rate); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventSetDestinationGasConfig { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventSetDestinationGasConfig(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.igp_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.remote_domain = reader.uint32(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.gas_overhead = reader.string(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.gas_price = reader.string(); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.token_exchange_rate = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventSetDestinationGasConfig { + return { + igp_id: isSet(object.igp_id) ? globalThis.String(object.igp_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + remote_domain: isSet(object.remote_domain) + ? globalThis.Number(object.remote_domain) + : 0, + gas_overhead: isSet(object.gas_overhead) + ? globalThis.String(object.gas_overhead) + : '', + gas_price: isSet(object.gas_price) + ? globalThis.String(object.gas_price) + : '', + token_exchange_rate: isSet(object.token_exchange_rate) + ? globalThis.String(object.token_exchange_rate) + : '', + }; + }, + + toJSON(message: EventSetDestinationGasConfig): unknown { + const obj: any = {}; + if (message.igp_id !== '') { + obj.igp_id = message.igp_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.remote_domain !== 0) { + obj.remote_domain = Math.round(message.remote_domain); + } + if (message.gas_overhead !== '') { + obj.gas_overhead = message.gas_overhead; + } + if (message.gas_price !== '') { + obj.gas_price = message.gas_price; + } + if (message.token_exchange_rate !== '') { + obj.token_exchange_rate = message.token_exchange_rate; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventSetDestinationGasConfig { + return EventSetDestinationGasConfig.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventSetDestinationGasConfig { + const message = createBaseEventSetDestinationGasConfig(); + message.igp_id = object.igp_id ?? ''; + message.owner = object.owner ?? ''; + message.remote_domain = object.remote_domain ?? 0; + message.gas_overhead = object.gas_overhead ?? ''; + message.gas_price = object.gas_price ?? ''; + message.token_exchange_rate = object.token_exchange_rate ?? ''; + return message; + }, +}; + +function createBaseEventClaimIgp(): EventClaimIgp { + return { igp_id: '', owner: '', amount: '' }; +} + +export const EventClaimIgp = { + encode( + message: EventClaimIgp, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.igp_id !== '') { + writer.uint32(10).string(message.igp_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.amount !== '') { + writer.uint32(26).string(message.amount); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventClaimIgp { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventClaimIgp(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.igp_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.amount = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventClaimIgp { + return { + igp_id: isSet(object.igp_id) ? globalThis.String(object.igp_id) : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + amount: isSet(object.amount) ? globalThis.String(object.amount) : '', + }; + }, + + toJSON(message: EventClaimIgp): unknown { + const obj: any = {}; + if (message.igp_id !== '') { + obj.igp_id = message.igp_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.amount !== '') { + obj.amount = message.amount; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventClaimIgp { + return EventClaimIgp.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventClaimIgp { + const message = createBaseEventClaimIgp(); + message.igp_id = object.igp_id ?? ''; message.owner = object.owner ?? ''; + message.amount = object.amount ?? ''; return message; }, }; diff --git a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts index 59008794b13..0ecb9b0bb1e 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/post_dispatch/v1/tx.ts @@ -33,6 +33,8 @@ export interface MsgSetIgpOwner { igp_id: string; /** new_owner */ new_owner: string; + /** renounce_ownership */ + renounce_ownership: boolean; } /** MsgCreateIgpResponse ... */ @@ -255,7 +257,7 @@ export const MsgCreateIgpResponse = { }; function createBaseMsgSetIgpOwner(): MsgSetIgpOwner { - return { owner: '', igp_id: '', new_owner: '' }; + return { owner: '', igp_id: '', new_owner: '', renounce_ownership: false }; } export const MsgSetIgpOwner = { @@ -272,6 +274,9 @@ export const MsgSetIgpOwner = { if (message.new_owner !== '') { writer.uint32(26).string(message.new_owner); } + if (message.renounce_ownership !== false) { + writer.uint32(32).bool(message.renounce_ownership); + } return writer; }, @@ -304,6 +309,13 @@ export const MsgSetIgpOwner = { message.new_owner = reader.string(); continue; + case 4: + if (tag !== 32) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -320,6 +332,9 @@ export const MsgSetIgpOwner = { new_owner: isSet(object.new_owner) ? globalThis.String(object.new_owner) : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, }; }, @@ -334,6 +349,9 @@ export const MsgSetIgpOwner = { if (message.new_owner !== '') { obj.new_owner = message.new_owner; } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } return obj; }, @@ -349,6 +367,7 @@ export const MsgSetIgpOwner = { message.owner = object.owner ?? ''; message.igp_id = object.igp_id ?? ''; message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; return message; }, }; diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts index dccda5b49ec..978f3c567e9 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/events.ts @@ -8,8 +8,8 @@ import _m0 from 'protobufjs/minimal.js'; export const protobufPackage = 'hyperlane.core.v1'; -/** Dispatch ... */ -export interface Dispatch { +/** EventDispatch ... */ +export interface EventDispatch { /** origin_mailbox_id ... */ origin_mailbox_id: string; /** sender ... */ @@ -22,8 +22,8 @@ export interface Dispatch { message: string; } -/** Process ... */ -export interface Process { +/** EventProcess ... */ +export interface EventProcess { /** origin_mailbox_id ... */ origin_mailbox_id: string; /** origin ... */ @@ -38,7 +38,39 @@ export interface Process { message: string; } -function createBaseDispatch(): Dispatch { +/** EventCreateMailbox ... */ +export interface EventCreateMailbox { + /** mailbox_id ... */ + mailbox_id: string; + /** owner ... */ + owner: string; + /** default_ism ... */ + default_ism: string; + /** default_hook ... */ + default_hook: string; + /** required_hook ... */ + required_hook: string; + /** local_domain ... */ + local_domain: number; +} + +/** EventSetMailbox ... */ +export interface EventSetMailbox { + /** mailbox_id ... */ + mailbox_id: string; + /** owner ... */ + owner: string; + /** default_ism ... */ + default_ism: string; + /** default_hook ... */ + default_hook: string; + /** new_owner ... */ + new_owner: string; + /** renounce_ownership ... */ + renounce_ownership: boolean; +} + +function createBaseEventDispatch(): EventDispatch { return { origin_mailbox_id: '', sender: '', @@ -48,9 +80,9 @@ function createBaseDispatch(): Dispatch { }; } -export const Dispatch = { +export const EventDispatch = { encode( - message: Dispatch, + message: EventDispatch, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { if (message.origin_mailbox_id !== '') { @@ -71,11 +103,11 @@ export const Dispatch = { return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): Dispatch { + decode(input: _m0.Reader | Uint8Array, length?: number): EventDispatch { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseDispatch(); + const message = createBaseEventDispatch(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -123,7 +155,7 @@ export const Dispatch = { return message; }, - fromJSON(object: any): Dispatch { + fromJSON(object: any): EventDispatch { return { origin_mailbox_id: isSet(object.origin_mailbox_id) ? globalThis.String(object.origin_mailbox_id) @@ -139,7 +171,7 @@ export const Dispatch = { }; }, - toJSON(message: Dispatch): unknown { + toJSON(message: EventDispatch): unknown { const obj: any = {}; if (message.origin_mailbox_id !== '') { obj.origin_mailbox_id = message.origin_mailbox_id; @@ -159,11 +191,15 @@ export const Dispatch = { return obj; }, - create, I>>(base?: I): Dispatch { - return Dispatch.fromPartial(base ?? ({} as any)); + create, I>>( + base?: I, + ): EventDispatch { + return EventDispatch.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>(object: I): Dispatch { - const message = createBaseDispatch(); + fromPartial, I>>( + object: I, + ): EventDispatch { + const message = createBaseEventDispatch(); message.origin_mailbox_id = object.origin_mailbox_id ?? ''; message.sender = object.sender ?? ''; message.destination = object.destination ?? 0; @@ -173,7 +209,7 @@ export const Dispatch = { }, }; -function createBaseProcess(): Process { +function createBaseEventProcess(): EventProcess { return { origin_mailbox_id: '', origin: 0, @@ -184,9 +220,9 @@ function createBaseProcess(): Process { }; } -export const Process = { +export const EventProcess = { encode( - message: Process, + message: EventProcess, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { if (message.origin_mailbox_id !== '') { @@ -210,11 +246,11 @@ export const Process = { return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): Process { + decode(input: _m0.Reader | Uint8Array, length?: number): EventProcess { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseProcess(); + const message = createBaseEventProcess(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -269,7 +305,7 @@ export const Process = { return message; }, - fromJSON(object: any): Process { + fromJSON(object: any): EventProcess { return { origin_mailbox_id: isSet(object.origin_mailbox_id) ? globalThis.String(object.origin_mailbox_id) @@ -286,7 +322,7 @@ export const Process = { }; }, - toJSON(message: Process): unknown { + toJSON(message: EventProcess): unknown { const obj: any = {}; if (message.origin_mailbox_id !== '') { obj.origin_mailbox_id = message.origin_mailbox_id; @@ -309,11 +345,15 @@ export const Process = { return obj; }, - create, I>>(base?: I): Process { - return Process.fromPartial(base ?? ({} as any)); + create, I>>( + base?: I, + ): EventProcess { + return EventProcess.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>(object: I): Process { - const message = createBaseProcess(); + fromPartial, I>>( + object: I, + ): EventProcess { + const message = createBaseEventProcess(); message.origin_mailbox_id = object.origin_mailbox_id ?? ''; message.origin = object.origin ?? 0; message.sender = object.sender ?? ''; @@ -324,6 +364,324 @@ export const Process = { }, }; +function createBaseEventCreateMailbox(): EventCreateMailbox { + return { + mailbox_id: '', + owner: '', + default_ism: '', + default_hook: '', + required_hook: '', + local_domain: 0, + }; +} + +export const EventCreateMailbox = { + encode( + message: EventCreateMailbox, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.mailbox_id !== '') { + writer.uint32(10).string(message.mailbox_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.default_ism !== '') { + writer.uint32(26).string(message.default_ism); + } + if (message.default_hook !== '') { + writer.uint32(34).string(message.default_hook); + } + if (message.required_hook !== '') { + writer.uint32(42).string(message.required_hook); + } + if (message.local_domain !== 0) { + writer.uint32(48).uint32(message.local_domain); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventCreateMailbox { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateMailbox(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.mailbox_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.default_ism = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.default_hook = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.required_hook = reader.string(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.local_domain = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateMailbox { + return { + mailbox_id: isSet(object.mailbox_id) + ? globalThis.String(object.mailbox_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + default_ism: isSet(object.default_ism) + ? globalThis.String(object.default_ism) + : '', + default_hook: isSet(object.default_hook) + ? globalThis.String(object.default_hook) + : '', + required_hook: isSet(object.required_hook) + ? globalThis.String(object.required_hook) + : '', + local_domain: isSet(object.local_domain) + ? globalThis.Number(object.local_domain) + : 0, + }; + }, + + toJSON(message: EventCreateMailbox): unknown { + const obj: any = {}; + if (message.mailbox_id !== '') { + obj.mailbox_id = message.mailbox_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.default_ism !== '') { + obj.default_ism = message.default_ism; + } + if (message.default_hook !== '') { + obj.default_hook = message.default_hook; + } + if (message.required_hook !== '') { + obj.required_hook = message.required_hook; + } + if (message.local_domain !== 0) { + obj.local_domain = Math.round(message.local_domain); + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateMailbox { + return EventCreateMailbox.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateMailbox { + const message = createBaseEventCreateMailbox(); + message.mailbox_id = object.mailbox_id ?? ''; + message.owner = object.owner ?? ''; + message.default_ism = object.default_ism ?? ''; + message.default_hook = object.default_hook ?? ''; + message.required_hook = object.required_hook ?? ''; + message.local_domain = object.local_domain ?? 0; + return message; + }, +}; + +function createBaseEventSetMailbox(): EventSetMailbox { + return { + mailbox_id: '', + owner: '', + default_ism: '', + default_hook: '', + new_owner: '', + renounce_ownership: false, + }; +} + +export const EventSetMailbox = { + encode( + message: EventSetMailbox, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.mailbox_id !== '') { + writer.uint32(10).string(message.mailbox_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.default_ism !== '') { + writer.uint32(26).string(message.default_ism); + } + if (message.default_hook !== '') { + writer.uint32(34).string(message.default_hook); + } + if (message.new_owner !== '') { + writer.uint32(42).string(message.new_owner); + } + if (message.renounce_ownership !== false) { + writer.uint32(48).bool(message.renounce_ownership); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventSetMailbox { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventSetMailbox(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.mailbox_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.default_ism = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.default_hook = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.new_owner = reader.string(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventSetMailbox { + return { + mailbox_id: isSet(object.mailbox_id) + ? globalThis.String(object.mailbox_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + default_ism: isSet(object.default_ism) + ? globalThis.String(object.default_ism) + : '', + default_hook: isSet(object.default_hook) + ? globalThis.String(object.default_hook) + : '', + new_owner: isSet(object.new_owner) + ? globalThis.String(object.new_owner) + : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, + }; + }, + + toJSON(message: EventSetMailbox): unknown { + const obj: any = {}; + if (message.mailbox_id !== '') { + obj.mailbox_id = message.mailbox_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.default_ism !== '') { + obj.default_ism = message.default_ism; + } + if (message.default_hook !== '') { + obj.default_hook = message.default_hook; + } + if (message.new_owner !== '') { + obj.new_owner = message.new_owner; + } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventSetMailbox { + return EventSetMailbox.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventSetMailbox { + const message = createBaseEventSetMailbox(); + message.mailbox_id = object.mailbox_id ?? ''; + message.owner = object.owner ?? ''; + message.default_ism = object.default_ism ?? ''; + message.default_hook = object.default_hook ?? ''; + message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; + return message; + }, +}; + type Builtin = | Date | Function diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts index 0c0868a9400..4c4df99ccb6 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/query.ts @@ -64,6 +64,7 @@ export interface QueryVerifyDryRunRequest { ism_id: string; message: string; metadata: string; + gas_limit: string; } /** QueryVerifyDryRunResponse ... */ @@ -71,6 +72,30 @@ export interface QueryVerifyDryRunResponse { verified: boolean; } +/** QueryRegisteredISMs ... */ +export interface QueryRegisteredISMs {} + +/** QueryRegisteredISMsResponse ... */ +export interface QueryRegisteredISMsResponse { + ids: number[]; +} + +/** QueryRegisteredHooks ... */ +export interface QueryRegisteredHooks {} + +/** QueryRegisteredHooksResponse ... */ +export interface QueryRegisteredHooksResponse { + ids: number[]; +} + +/** QueryRegisteredApps ... */ +export interface QueryRegisteredApps {} + +/** QueryRegisteredAppsResponse ... */ +export interface QueryRegisteredAppsResponse { + ids: number[]; +} + function createBaseQueryMailboxesRequest(): QueryMailboxesRequest { return { pagination: undefined }; } @@ -684,7 +709,7 @@ export const QueryRecipientIsmResponse = { }; function createBaseQueryVerifyDryRunRequest(): QueryVerifyDryRunRequest { - return { ism_id: '', message: '', metadata: '' }; + return { ism_id: '', message: '', metadata: '', gas_limit: '' }; } export const QueryVerifyDryRunRequest = { @@ -701,6 +726,9 @@ export const QueryVerifyDryRunRequest = { if (message.metadata !== '') { writer.uint32(26).string(message.metadata); } + if (message.gas_limit !== '') { + writer.uint32(34).string(message.gas_limit); + } return writer; }, @@ -736,6 +764,13 @@ export const QueryVerifyDryRunRequest = { message.metadata = reader.string(); continue; + case 4: + if (tag !== 34) { + break; + } + + message.gas_limit = reader.string(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -752,6 +787,9 @@ export const QueryVerifyDryRunRequest = { metadata: isSet(object.metadata) ? globalThis.String(object.metadata) : '', + gas_limit: isSet(object.gas_limit) + ? globalThis.String(object.gas_limit) + : '', }; }, @@ -766,6 +804,9 @@ export const QueryVerifyDryRunRequest = { if (message.metadata !== '') { obj.metadata = message.metadata; } + if (message.gas_limit !== '') { + obj.gas_limit = message.gas_limit; + } return obj; }, @@ -781,6 +822,7 @@ export const QueryVerifyDryRunRequest = { message.ism_id = object.ism_id ?? ''; message.message = object.message ?? ''; message.metadata = object.metadata ?? ''; + message.gas_limit = object.gas_limit ?? ''; return message; }, }; @@ -857,6 +899,414 @@ export const QueryVerifyDryRunResponse = { }, }; +function createBaseQueryRegisteredISMs(): QueryRegisteredISMs { + return {}; +} + +export const QueryRegisteredISMs = { + encode( + _: QueryRegisteredISMs, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): QueryRegisteredISMs { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryRegisteredISMs(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): QueryRegisteredISMs { + return {}; + }, + + toJSON(_: QueryRegisteredISMs): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): QueryRegisteredISMs { + return QueryRegisteredISMs.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + _: I, + ): QueryRegisteredISMs { + const message = createBaseQueryRegisteredISMs(); + return message; + }, +}; + +function createBaseQueryRegisteredISMsResponse(): QueryRegisteredISMsResponse { + return { ids: [] }; +} + +export const QueryRegisteredISMsResponse = { + encode( + message: QueryRegisteredISMsResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.ids) { + writer.uint32(v); + } + writer.ldelim(); + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): QueryRegisteredISMsResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryRegisteredISMsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.ids.push(reader.uint32()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.ids.push(reader.uint32()); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): QueryRegisteredISMsResponse { + return { + ids: globalThis.Array.isArray(object?.ids) + ? object.ids.map((e: any) => globalThis.Number(e)) + : [], + }; + }, + + toJSON(message: QueryRegisteredISMsResponse): unknown { + const obj: any = {}; + if (message.ids?.length) { + obj.ids = message.ids.map((e) => Math.round(e)); + } + return obj; + }, + + create, I>>( + base?: I, + ): QueryRegisteredISMsResponse { + return QueryRegisteredISMsResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): QueryRegisteredISMsResponse { + const message = createBaseQueryRegisteredISMsResponse(); + message.ids = object.ids?.map((e) => e) || []; + return message; + }, +}; + +function createBaseQueryRegisteredHooks(): QueryRegisteredHooks { + return {}; +} + +export const QueryRegisteredHooks = { + encode( + _: QueryRegisteredHooks, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): QueryRegisteredHooks { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryRegisteredHooks(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): QueryRegisteredHooks { + return {}; + }, + + toJSON(_: QueryRegisteredHooks): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): QueryRegisteredHooks { + return QueryRegisteredHooks.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + _: I, + ): QueryRegisteredHooks { + const message = createBaseQueryRegisteredHooks(); + return message; + }, +}; + +function createBaseQueryRegisteredHooksResponse(): QueryRegisteredHooksResponse { + return { ids: [] }; +} + +export const QueryRegisteredHooksResponse = { + encode( + message: QueryRegisteredHooksResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.ids) { + writer.uint32(v); + } + writer.ldelim(); + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): QueryRegisteredHooksResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryRegisteredHooksResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.ids.push(reader.uint32()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.ids.push(reader.uint32()); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): QueryRegisteredHooksResponse { + return { + ids: globalThis.Array.isArray(object?.ids) + ? object.ids.map((e: any) => globalThis.Number(e)) + : [], + }; + }, + + toJSON(message: QueryRegisteredHooksResponse): unknown { + const obj: any = {}; + if (message.ids?.length) { + obj.ids = message.ids.map((e) => Math.round(e)); + } + return obj; + }, + + create, I>>( + base?: I, + ): QueryRegisteredHooksResponse { + return QueryRegisteredHooksResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): QueryRegisteredHooksResponse { + const message = createBaseQueryRegisteredHooksResponse(); + message.ids = object.ids?.map((e) => e) || []; + return message; + }, +}; + +function createBaseQueryRegisteredApps(): QueryRegisteredApps { + return {}; +} + +export const QueryRegisteredApps = { + encode( + _: QueryRegisteredApps, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): QueryRegisteredApps { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryRegisteredApps(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): QueryRegisteredApps { + return {}; + }, + + toJSON(_: QueryRegisteredApps): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): QueryRegisteredApps { + return QueryRegisteredApps.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + _: I, + ): QueryRegisteredApps { + const message = createBaseQueryRegisteredApps(); + return message; + }, +}; + +function createBaseQueryRegisteredAppsResponse(): QueryRegisteredAppsResponse { + return { ids: [] }; +} + +export const QueryRegisteredAppsResponse = { + encode( + message: QueryRegisteredAppsResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.ids) { + writer.uint32(v); + } + writer.ldelim(); + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): QueryRegisteredAppsResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryRegisteredAppsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.ids.push(reader.uint32()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.ids.push(reader.uint32()); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): QueryRegisteredAppsResponse { + return { + ids: globalThis.Array.isArray(object?.ids) + ? object.ids.map((e: any) => globalThis.Number(e)) + : [], + }; + }, + + toJSON(message: QueryRegisteredAppsResponse): unknown { + const obj: any = {}; + if (message.ids?.length) { + obj.ids = message.ids.map((e) => Math.round(e)); + } + return obj; + }, + + create, I>>( + base?: I, + ): QueryRegisteredAppsResponse { + return QueryRegisteredAppsResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): QueryRegisteredAppsResponse { + const message = createBaseQueryRegisteredAppsResponse(); + message.ids = object.ids?.map((e) => e) || []; + return message; + }, +}; + /** Query defines the module Query service. */ export interface Query { /** Mailboxes ... */ @@ -879,6 +1329,18 @@ export interface Query { VerifyDryRun( request: QueryVerifyDryRunRequest, ): Promise; + /** RegisteredISMs ... */ + RegisteredISMs( + request: QueryRegisteredISMs, + ): Promise; + /** RegisteredHooks ... */ + RegisteredHooks( + request: QueryRegisteredHooks, + ): Promise; + /** RegisteredApps ... */ + RegisteredApps( + request: QueryRegisteredApps, + ): Promise; } export const QueryServiceName = 'hyperlane.core.v1.Query'; @@ -893,6 +1355,9 @@ export class QueryClientImpl implements Query { this.Delivered = this.Delivered.bind(this); this.RecipientIsm = this.RecipientIsm.bind(this); this.VerifyDryRun = this.VerifyDryRun.bind(this); + this.RegisteredISMs = this.RegisteredISMs.bind(this); + this.RegisteredHooks = this.RegisteredHooks.bind(this); + this.RegisteredApps = this.RegisteredApps.bind(this); } Mailboxes(request: QueryMailboxesRequest): Promise { const data = QueryMailboxesRequest.encode(request).finish(); @@ -937,6 +1402,36 @@ export class QueryClientImpl implements Query { QueryVerifyDryRunResponse.decode(_m0.Reader.create(data)), ); } + + RegisteredISMs( + request: QueryRegisteredISMs, + ): Promise { + const data = QueryRegisteredISMs.encode(request).finish(); + const promise = this.rpc.request(this.service, 'RegisteredISMs', data); + return promise.then((data) => + QueryRegisteredISMsResponse.decode(_m0.Reader.create(data)), + ); + } + + RegisteredHooks( + request: QueryRegisteredHooks, + ): Promise { + const data = QueryRegisteredHooks.encode(request).finish(); + const promise = this.rpc.request(this.service, 'RegisteredHooks', data); + return promise.then((data) => + QueryRegisteredHooksResponse.decode(_m0.Reader.create(data)), + ); + } + + RegisteredApps( + request: QueryRegisteredApps, + ): Promise { + const data = QueryRegisteredApps.encode(request).finish(); + const promise = this.rpc.request(this.service, 'RegisteredApps', data); + return promise.then((data) => + QueryRegisteredAppsResponse.decode(_m0.Reader.create(data)), + ); + } } interface Rpc { diff --git a/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts index 6a705b8f4e0..faca55890a8 100644 --- a/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/core/v1/tx.ts @@ -40,6 +40,8 @@ export interface MsgSetMailbox { required_hook: string; /** new_owner ... */ new_owner: string; + /** renounce_ownership */ + renounce_ownership: boolean; } /** MsgSetMailboxResponse ... */ @@ -277,6 +279,7 @@ function createBaseMsgSetMailbox(): MsgSetMailbox { default_hook: '', required_hook: '', new_owner: '', + renounce_ownership: false, }; } @@ -303,6 +306,9 @@ export const MsgSetMailbox = { if (message.new_owner !== '') { writer.uint32(50).string(message.new_owner); } + if (message.renounce_ownership !== false) { + writer.uint32(56).bool(message.renounce_ownership); + } return writer; }, @@ -356,6 +362,13 @@ export const MsgSetMailbox = { message.new_owner = reader.string(); continue; + case 7: + if (tag !== 56) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -383,6 +396,9 @@ export const MsgSetMailbox = { new_owner: isSet(object.new_owner) ? globalThis.String(object.new_owner) : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, }; }, @@ -406,6 +422,9 @@ export const MsgSetMailbox = { if (message.new_owner !== '') { obj.new_owner = message.new_owner; } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } return obj; }, @@ -424,6 +443,7 @@ export const MsgSetMailbox = { message.default_hook = object.default_hook ?? ''; message.required_hook = object.required_hook ?? ''; message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; return message; }, }; diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts index 24a727f745d..4fc9c0d21a7 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/events.ts @@ -8,51 +8,770 @@ import _m0 from 'protobufjs/minimal.js'; export const protobufPackage = 'hyperlane.warp.v1'; -/** RemoteTransfer ... */ -export interface RemoteTransfer { +/** EventCreateSyntheticToken ... */ +export interface EventCreateSyntheticToken { + token_id: string; + owner: string; + origin_mailbox: string; + origin_denom: string; +} + +/** EventCreateCollateralToken ... */ +export interface EventCreateCollateralToken { + token_id: string; + owner: string; + origin_mailbox: string; + origin_denom: string; +} + +/** EventSetToken ... */ +export interface EventSetToken { + token_id: string; + owner: string; + ism_id: string; + new_owner: string; + renounce_ownership: boolean; +} + +/** EventEnrollRemoteRouter ... */ +export interface EventEnrollRemoteRouter { + token_id: string; + owner: string; + receiver_domain: number; + receiver_contract: string; + gas: string; +} + +/** EventUnrollRemoteRouter ... */ +export interface EventUnrollRemoteRouter { + token_id: string; + owner: string; + receiver_domain: number; +} + +/** EventSendRemoteTransfer ... */ +export interface EventSendRemoteTransfer { + token_id: string; + sender: string; destination_domain: number; - recipient_address: string; + recipient: string; + amount: string; +} + +/** EventReceiveRemoteTransfer ... */ +export interface EventReceiveRemoteTransfer { + token_id: string; + sender: string; + origin_domain: number; + recipient: string; + amount: string; +} + +function createBaseEventCreateSyntheticToken(): EventCreateSyntheticToken { + return { token_id: '', owner: '', origin_mailbox: '', origin_denom: '' }; +} + +export const EventCreateSyntheticToken = { + encode( + message: EventCreateSyntheticToken, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.origin_mailbox !== '') { + writer.uint32(26).string(message.origin_mailbox); + } + if (message.origin_denom !== '') { + writer.uint32(34).string(message.origin_denom); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventCreateSyntheticToken { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateSyntheticToken(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.token_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.origin_mailbox = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.origin_denom = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateSyntheticToken { + return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + origin_mailbox: isSet(object.origin_mailbox) + ? globalThis.String(object.origin_mailbox) + : '', + origin_denom: isSet(object.origin_denom) + ? globalThis.String(object.origin_denom) + : '', + }; + }, + + toJSON(message: EventCreateSyntheticToken): unknown { + const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.origin_mailbox !== '') { + obj.origin_mailbox = message.origin_mailbox; + } + if (message.origin_denom !== '') { + obj.origin_denom = message.origin_denom; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateSyntheticToken { + return EventCreateSyntheticToken.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateSyntheticToken { + const message = createBaseEventCreateSyntheticToken(); + message.token_id = object.token_id ?? ''; + message.owner = object.owner ?? ''; + message.origin_mailbox = object.origin_mailbox ?? ''; + message.origin_denom = object.origin_denom ?? ''; + return message; + }, +}; + +function createBaseEventCreateCollateralToken(): EventCreateCollateralToken { + return { token_id: '', owner: '', origin_mailbox: '', origin_denom: '' }; +} + +export const EventCreateCollateralToken = { + encode( + message: EventCreateCollateralToken, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.origin_mailbox !== '') { + writer.uint32(26).string(message.origin_mailbox); + } + if (message.origin_denom !== '') { + writer.uint32(34).string(message.origin_denom); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventCreateCollateralToken { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventCreateCollateralToken(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.token_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.origin_mailbox = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.origin_denom = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventCreateCollateralToken { + return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + origin_mailbox: isSet(object.origin_mailbox) + ? globalThis.String(object.origin_mailbox) + : '', + origin_denom: isSet(object.origin_denom) + ? globalThis.String(object.origin_denom) + : '', + }; + }, + + toJSON(message: EventCreateCollateralToken): unknown { + const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.origin_mailbox !== '') { + obj.origin_mailbox = message.origin_mailbox; + } + if (message.origin_denom !== '') { + obj.origin_denom = message.origin_denom; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventCreateCollateralToken { + return EventCreateCollateralToken.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventCreateCollateralToken { + const message = createBaseEventCreateCollateralToken(); + message.token_id = object.token_id ?? ''; + message.owner = object.owner ?? ''; + message.origin_mailbox = object.origin_mailbox ?? ''; + message.origin_denom = object.origin_denom ?? ''; + return message; + }, +}; + +function createBaseEventSetToken(): EventSetToken { + return { + token_id: '', + owner: '', + ism_id: '', + new_owner: '', + renounce_ownership: false, + }; } -function createBaseRemoteTransfer(): RemoteTransfer { - return { destination_domain: 0, recipient_address: '' }; +export const EventSetToken = { + encode( + message: EventSetToken, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.ism_id !== '') { + writer.uint32(26).string(message.ism_id); + } + if (message.new_owner !== '') { + writer.uint32(34).string(message.new_owner); + } + if (message.renounce_ownership !== false) { + writer.uint32(40).bool(message.renounce_ownership); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EventSetToken { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventSetToken(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.token_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.ism_id = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.new_owner = reader.string(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventSetToken { + return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + new_owner: isSet(object.new_owner) + ? globalThis.String(object.new_owner) + : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, + }; + }, + + toJSON(message: EventSetToken): unknown { + const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.ism_id !== '') { + obj.ism_id = message.ism_id; + } + if (message.new_owner !== '') { + obj.new_owner = message.new_owner; + } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventSetToken { + return EventSetToken.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventSetToken { + const message = createBaseEventSetToken(); + message.token_id = object.token_id ?? ''; + message.owner = object.owner ?? ''; + message.ism_id = object.ism_id ?? ''; + message.new_owner = object.new_owner ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; + return message; + }, +}; + +function createBaseEventEnrollRemoteRouter(): EventEnrollRemoteRouter { + return { + token_id: '', + owner: '', + receiver_domain: 0, + receiver_contract: '', + gas: '', + }; } -export const RemoteTransfer = { +export const EventEnrollRemoteRouter = { encode( - message: RemoteTransfer, + message: EventEnrollRemoteRouter, writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.receiver_domain !== 0) { + writer.uint32(24).uint32(message.receiver_domain); + } + if (message.receiver_contract !== '') { + writer.uint32(34).string(message.receiver_contract); + } + if (message.gas !== '') { + writer.uint32(42).string(message.gas); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventEnrollRemoteRouter { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventEnrollRemoteRouter(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.token_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.receiver_domain = reader.uint32(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.receiver_contract = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.gas = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventEnrollRemoteRouter { + return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + receiver_domain: isSet(object.receiver_domain) + ? globalThis.Number(object.receiver_domain) + : 0, + receiver_contract: isSet(object.receiver_contract) + ? globalThis.String(object.receiver_contract) + : '', + gas: isSet(object.gas) ? globalThis.String(object.gas) : '', + }; + }, + + toJSON(message: EventEnrollRemoteRouter): unknown { + const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.receiver_domain !== 0) { + obj.receiver_domain = Math.round(message.receiver_domain); + } + if (message.receiver_contract !== '') { + obj.receiver_contract = message.receiver_contract; + } + if (message.gas !== '') { + obj.gas = message.gas; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventEnrollRemoteRouter { + return EventEnrollRemoteRouter.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventEnrollRemoteRouter { + const message = createBaseEventEnrollRemoteRouter(); + message.token_id = object.token_id ?? ''; + message.owner = object.owner ?? ''; + message.receiver_domain = object.receiver_domain ?? 0; + message.receiver_contract = object.receiver_contract ?? ''; + message.gas = object.gas ?? ''; + return message; + }, +}; + +function createBaseEventUnrollRemoteRouter(): EventUnrollRemoteRouter { + return { token_id: '', owner: '', receiver_domain: 0 }; +} + +export const EventUnrollRemoteRouter = { + encode( + message: EventUnrollRemoteRouter, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.owner !== '') { + writer.uint32(18).string(message.owner); + } + if (message.receiver_domain !== 0) { + writer.uint32(24).uint32(message.receiver_domain); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventUnrollRemoteRouter { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventUnrollRemoteRouter(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.token_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.owner = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.receiver_domain = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventUnrollRemoteRouter { + return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + owner: isSet(object.owner) ? globalThis.String(object.owner) : '', + receiver_domain: isSet(object.receiver_domain) + ? globalThis.Number(object.receiver_domain) + : 0, + }; + }, + + toJSON(message: EventUnrollRemoteRouter): unknown { + const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.owner !== '') { + obj.owner = message.owner; + } + if (message.receiver_domain !== 0) { + obj.receiver_domain = Math.round(message.receiver_domain); + } + return obj; + }, + + create, I>>( + base?: I, + ): EventUnrollRemoteRouter { + return EventUnrollRemoteRouter.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventUnrollRemoteRouter { + const message = createBaseEventUnrollRemoteRouter(); + message.token_id = object.token_id ?? ''; + message.owner = object.owner ?? ''; + message.receiver_domain = object.receiver_domain ?? 0; + return message; + }, +}; + +function createBaseEventSendRemoteTransfer(): EventSendRemoteTransfer { + return { + token_id: '', + sender: '', + destination_domain: 0, + recipient: '', + amount: '', + }; +} + +export const EventSendRemoteTransfer = { + encode( + message: EventSendRemoteTransfer, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.sender !== '') { + writer.uint32(18).string(message.sender); + } if (message.destination_domain !== 0) { - writer.uint32(8).uint32(message.destination_domain); + writer.uint32(24).uint32(message.destination_domain); + } + if (message.recipient !== '') { + writer.uint32(34).string(message.recipient); } - if (message.recipient_address !== '') { - writer.uint32(18).string(message.recipient_address); + if (message.amount !== '') { + writer.uint32(42).string(message.amount); } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): RemoteTransfer { + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventSendRemoteTransfer { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseRemoteTransfer(); + const message = createBaseEventSendRemoteTransfer(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - if (tag !== 8) { + if (tag !== 10) { break; } - message.destination_domain = reader.uint32(); + message.token_id = reader.string(); continue; case 2: if (tag !== 18) { break; } - message.recipient_address = reader.string(); + message.sender = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.destination_domain = reader.uint32(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.recipient = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.amount = reader.string(); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -63,39 +782,198 @@ export const RemoteTransfer = { return message; }, - fromJSON(object: any): RemoteTransfer { + fromJSON(object: any): EventSendRemoteTransfer { return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + sender: isSet(object.sender) ? globalThis.String(object.sender) : '', destination_domain: isSet(object.destination_domain) ? globalThis.Number(object.destination_domain) : 0, - recipient_address: isSet(object.recipient_address) - ? globalThis.String(object.recipient_address) + recipient: isSet(object.recipient) + ? globalThis.String(object.recipient) : '', + amount: isSet(object.amount) ? globalThis.String(object.amount) : '', }; }, - toJSON(message: RemoteTransfer): unknown { + toJSON(message: EventSendRemoteTransfer): unknown { const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.sender !== '') { + obj.sender = message.sender; + } if (message.destination_domain !== 0) { obj.destination_domain = Math.round(message.destination_domain); } - if (message.recipient_address !== '') { - obj.recipient_address = message.recipient_address; + if (message.recipient !== '') { + obj.recipient = message.recipient; + } + if (message.amount !== '') { + obj.amount = message.amount; } return obj; }, - create, I>>( + create, I>>( base?: I, - ): RemoteTransfer { - return RemoteTransfer.fromPartial(base ?? ({} as any)); + ): EventSendRemoteTransfer { + return EventSendRemoteTransfer.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>( + fromPartial, I>>( object: I, - ): RemoteTransfer { - const message = createBaseRemoteTransfer(); + ): EventSendRemoteTransfer { + const message = createBaseEventSendRemoteTransfer(); + message.token_id = object.token_id ?? ''; + message.sender = object.sender ?? ''; message.destination_domain = object.destination_domain ?? 0; - message.recipient_address = object.recipient_address ?? ''; + message.recipient = object.recipient ?? ''; + message.amount = object.amount ?? ''; + return message; + }, +}; + +function createBaseEventReceiveRemoteTransfer(): EventReceiveRemoteTransfer { + return { + token_id: '', + sender: '', + origin_domain: 0, + recipient: '', + amount: '', + }; +} + +export const EventReceiveRemoteTransfer = { + encode( + message: EventReceiveRemoteTransfer, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.token_id !== '') { + writer.uint32(10).string(message.token_id); + } + if (message.sender !== '') { + writer.uint32(18).string(message.sender); + } + if (message.origin_domain !== 0) { + writer.uint32(24).uint32(message.origin_domain); + } + if (message.recipient !== '') { + writer.uint32(34).string(message.recipient); + } + if (message.amount !== '') { + writer.uint32(42).string(message.amount); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): EventReceiveRemoteTransfer { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEventReceiveRemoteTransfer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.token_id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.sender = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.origin_domain = reader.uint32(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.recipient = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.amount = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EventReceiveRemoteTransfer { + return { + token_id: isSet(object.token_id) + ? globalThis.String(object.token_id) + : '', + sender: isSet(object.sender) ? globalThis.String(object.sender) : '', + origin_domain: isSet(object.origin_domain) + ? globalThis.Number(object.origin_domain) + : 0, + recipient: isSet(object.recipient) + ? globalThis.String(object.recipient) + : '', + amount: isSet(object.amount) ? globalThis.String(object.amount) : '', + }; + }, + + toJSON(message: EventReceiveRemoteTransfer): unknown { + const obj: any = {}; + if (message.token_id !== '') { + obj.token_id = message.token_id; + } + if (message.sender !== '') { + obj.sender = message.sender; + } + if (message.origin_domain !== 0) { + obj.origin_domain = Math.round(message.origin_domain); + } + if (message.recipient !== '') { + obj.recipient = message.recipient; + } + if (message.amount !== '') { + obj.amount = message.amount; + } + return obj; + }, + + create, I>>( + base?: I, + ): EventReceiveRemoteTransfer { + return EventReceiveRemoteTransfer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): EventReceiveRemoteTransfer { + const message = createBaseEventReceiveRemoteTransfer(); + message.token_id = object.token_id ?? ''; + message.sender = object.sender ?? ''; + message.origin_domain = object.origin_domain ?? 0; + message.recipient = object.recipient ?? ''; + message.amount = object.amount ?? ''; return message; }, }; diff --git a/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts b/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts index b2d5b74b974..b8711a60da1 100644 --- a/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts +++ b/typescript/cosmos-types/src/types/hyperlane/warp/v1/tx.ts @@ -44,6 +44,7 @@ export interface MsgSetToken { token_id: string; new_owner: string; ism_id: string; + renounce_ownership: boolean; } /** MsgSetTokenResponse ... */ @@ -418,7 +419,13 @@ export const MsgCreateSyntheticTokenResponse = { }; function createBaseMsgSetToken(): MsgSetToken { - return { owner: '', token_id: '', new_owner: '', ism_id: '' }; + return { + owner: '', + token_id: '', + new_owner: '', + ism_id: '', + renounce_ownership: false, + }; } export const MsgSetToken = { @@ -438,6 +445,9 @@ export const MsgSetToken = { if (message.ism_id !== '') { writer.uint32(34).string(message.ism_id); } + if (message.renounce_ownership !== false) { + writer.uint32(56).bool(message.renounce_ownership); + } return writer; }, @@ -477,6 +487,13 @@ export const MsgSetToken = { message.ism_id = reader.string(); continue; + case 7: + if (tag !== 56) { + break; + } + + message.renounce_ownership = reader.bool(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -496,6 +513,9 @@ export const MsgSetToken = { ? globalThis.String(object.new_owner) : '', ism_id: isSet(object.ism_id) ? globalThis.String(object.ism_id) : '', + renounce_ownership: isSet(object.renounce_ownership) + ? globalThis.Boolean(object.renounce_ownership) + : false, }; }, @@ -513,6 +533,9 @@ export const MsgSetToken = { if (message.ism_id !== '') { obj.ism_id = message.ism_id; } + if (message.renounce_ownership !== false) { + obj.renounce_ownership = message.renounce_ownership; + } return obj; }, @@ -527,6 +550,7 @@ export const MsgSetToken = { message.token_id = object.token_id ?? ''; message.new_owner = object.new_owner ?? ''; message.ism_id = object.ism_id ?? ''; + message.renounce_ownership = object.renounce_ownership ?? false; return message; }, }; diff --git a/typescript/sdk/src/core/CosmosNativeCoreModule.ts b/typescript/sdk/src/core/CosmosNativeCoreModule.ts index 9d724fc7ec2..43b9fa7240e 100644 --- a/typescript/sdk/src/core/CosmosNativeCoreModule.ts +++ b/typescript/sdk/src/core/CosmosNativeCoreModule.ts @@ -161,6 +161,7 @@ export class CosmosNativeCoreModule extends HyperlaneModule< default_hook: defaultHook, required_hook: requiredHook, new_owner: config.owner || '', + renounce_ownership: !config.owner, // if owner is empty we renounce the ownership }); const addresses: DeployedCoreAddresses = { diff --git a/typescript/sdk/src/hook/CosmosNativeHookModule.ts b/typescript/sdk/src/hook/CosmosNativeHookModule.ts index 112e6a448b5..bc5949ad74c 100644 --- a/typescript/sdk/src/hook/CosmosNativeHookModule.ts +++ b/typescript/sdk/src/hook/CosmosNativeHookModule.ts @@ -8,6 +8,7 @@ import { ProtocolType, assert, deepEquals, + eqAddress, rootLogger, } from '@hyperlane-xyz/utils'; @@ -184,10 +185,11 @@ export class CosmosNativeHookModule extends HyperlaneModule< }); } - if (config.owner && this.signer.account.address !== config.owner) { + if (!eqAddress(this.signer.account.address, config.owner)) { await this.signer.setIgpOwner({ igp_id: igp.id, new_owner: config.owner, + renounce_ownership: !config.owner, // if owner is empty we renounce the ownership }); } From 1a7222b24af1d0549c2e6a2e3f2f5bf58b5faa3f Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Mon, 19 May 2025 10:34:13 +0200 Subject: [PATCH 199/223] refactor: Compress Starknet logo in widgets (#5857) ### Description This PR compresses the size of Starknet logo ### Drive-by changes None ### Related issues None ### Backward compatibility Yes ### Testing None --- .changeset/dry-dots-sin.md | 5 +++ typescript/widgets/src/logos/Starknet.tsx | 50 ++++++++++------------- 2 files changed, 27 insertions(+), 28 deletions(-) create mode 100644 .changeset/dry-dots-sin.md diff --git a/.changeset/dry-dots-sin.md b/.changeset/dry-dots-sin.md new file mode 100644 index 00000000000..9d5a5335d98 --- /dev/null +++ b/.changeset/dry-dots-sin.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/widgets': patch +--- + +Compress Starknet logo diff --git a/typescript/widgets/src/logos/Starknet.tsx b/typescript/widgets/src/logos/Starknet.tsx index ff7efda1c59..c3f1f46b0c8 100644 --- a/typescript/widgets/src/logos/Starknet.tsx +++ b/typescript/widgets/src/logos/Starknet.tsx @@ -9,35 +9,29 @@ function _StarknetLogo(props: SVGProps) { {...props} > - - - - + + + + + + ); } From e193176fef521d28f15f60e4140a2e219b719ac9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 09:33:59 +0000 Subject: [PATCH 200/223] Version Packages (#6237) 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 publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). 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/starknet-core@13.0.0 ### Major Changes - f8696c7: feat: Add Starknet contract ABI fetching and contract artifact generation ## @hyperlane-xyz/cosmos-sdk@13.0.0 ### Minor Changes - 2724559: add cosmos native routing ism cosmos-sdk and types ### Patch Changes - Updated dependencies [2724559] - @hyperlane-xyz/cosmos-types@13.0.0 ## @hyperlane-xyz/cosmos-types@13.0.0 ### Minor Changes - 2724559: add cosmos native routing ism cosmos-sdk and types ## @hyperlane-xyz/sdk@13.0.0 ### Minor Changes - 72b90f8: add cosmos native core module & reader - bc58283: feat: Starknet SDK logic integration - 2724559: add cosmos native routing ism cosmos-sdk and types ### Patch Changes - Updated dependencies [0de63e0] - Updated dependencies [f8696c7] - Updated dependencies [2724559] - @hyperlane-xyz/utils@13.0.0 - @hyperlane-xyz/starknet-core@13.0.0 - @hyperlane-xyz/cosmos-sdk@13.0.0 - @hyperlane-xyz/core@7.1.6 ## @hyperlane-xyz/utils@13.0.0 ### Minor Changes - 0de63e0: Add Starknet address and tx utils ## @hyperlane-xyz/core@7.1.6 ### Patch Changes - Updated dependencies [0de63e0] - @hyperlane-xyz/utils@13.0.0 ## @hyperlane-xyz/helloworld@13.0.0 ### Patch Changes - Updated dependencies [72b90f8] - Updated dependencies [bc58283] - Updated dependencies [2724559] - @hyperlane-xyz/sdk@13.0.0 - @hyperlane-xyz/core@7.1.6 ## @hyperlane-xyz/widgets@13.0.0 ### Patch Changes - 1a7222b: Compress Starknet logo - Updated dependencies [72b90f8] - Updated dependencies [bc58283] - Updated dependencies [0de63e0] - Updated dependencies [2724559] - @hyperlane-xyz/sdk@13.0.0 - @hyperlane-xyz/utils@13.0.0 - @hyperlane-xyz/cosmos-sdk@13.0.0 ## @hyperlane-xyz/cli@13.0.0 ## @hyperlane-xyz/infra@13.0.0 ### Patch Changes - Updated dependencies [72b90f8] - Updated dependencies [bc58283] - Updated dependencies [0de63e0] - Updated dependencies [2724559] - @hyperlane-xyz/sdk@13.0.0 - @hyperlane-xyz/utils@13.0.0 - @hyperlane-xyz/helloworld@13.0.0 ## @hyperlane-xyz/ccip-server@13.0.0 ## @hyperlane-xyz/github-proxy@13.0.0 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/better-carrots-tell.md | 5 --- .changeset/dry-dots-sin.md | 5 --- .changeset/hip-papayas-kiss.md | 5 --- .changeset/metal-coats-remain.md | 5 --- .changeset/popular-trains-repair.md | 5 --- .changeset/sharp-doors-decide.md | 7 ---- solidity/CHANGELOG.md | 7 ++++ solidity/contracts/PackageVersioned.sol | 2 +- solidity/package.json | 4 +-- starknet/CHANGELOG.md | 7 ++++ starknet/package.json | 2 +- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 2 ++ typescript/cli/package.json | 8 ++--- typescript/cli/src/version.ts | 2 +- typescript/cosmos-sdk/CHANGELOG.md | 11 ++++++ typescript/cosmos-sdk/package.json | 4 +-- typescript/cosmos-types/CHANGELOG.md | 6 ++++ typescript/cosmos-types/package.json | 2 +- typescript/github-proxy/CHANGELOG.md | 2 ++ typescript/github-proxy/package.json | 2 +- typescript/helloworld/CHANGELOG.md | 10 ++++++ typescript/helloworld/package.json | 6 ++-- typescript/infra/CHANGELOG.md | 12 +++++++ typescript/infra/package.json | 8 ++--- typescript/sdk/CHANGELOG.md | 18 ++++++++++ typescript/sdk/package.json | 10 +++--- typescript/utils/CHANGELOG.md | 6 ++++ typescript/utils/package.json | 2 +- typescript/widgets/CHANGELOG.md | 13 +++++++ typescript/widgets/package.json | 8 ++--- yarn.lock | 48 ++++++++++++------------- 33 files changed, 151 insertions(+), 87 deletions(-) delete mode 100644 .changeset/better-carrots-tell.md delete mode 100644 .changeset/dry-dots-sin.md delete mode 100644 .changeset/hip-papayas-kiss.md delete mode 100644 .changeset/metal-coats-remain.md delete mode 100644 .changeset/popular-trains-repair.md delete mode 100644 .changeset/sharp-doors-decide.md create mode 100644 starknet/CHANGELOG.md diff --git a/.changeset/better-carrots-tell.md b/.changeset/better-carrots-tell.md deleted file mode 100644 index d0167a92cc3..00000000000 --- a/.changeset/better-carrots-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -add cosmos native core module & reader diff --git a/.changeset/dry-dots-sin.md b/.changeset/dry-dots-sin.md deleted file mode 100644 index 9d5a5335d98..00000000000 --- a/.changeset/dry-dots-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/widgets': patch ---- - -Compress Starknet logo diff --git a/.changeset/hip-papayas-kiss.md b/.changeset/hip-papayas-kiss.md deleted file mode 100644 index 8ed6b86f2d6..00000000000 --- a/.changeset/hip-papayas-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -feat: Starknet SDK logic integration diff --git a/.changeset/metal-coats-remain.md b/.changeset/metal-coats-remain.md deleted file mode 100644 index bda7ba34165..00000000000 --- a/.changeset/metal-coats-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/utils': minor ---- - -Add Starknet address and tx utils diff --git a/.changeset/popular-trains-repair.md b/.changeset/popular-trains-repair.md deleted file mode 100644 index d0cab3f6198..00000000000 --- a/.changeset/popular-trains-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/starknet-core': major ---- - -feat: Add Starknet contract ABI fetching and contract artifact generation diff --git a/.changeset/sharp-doors-decide.md b/.changeset/sharp-doors-decide.md deleted file mode 100644 index 061ad6cf3e7..00000000000 --- a/.changeset/sharp-doors-decide.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/cosmos-types': minor -'@hyperlane-xyz/cosmos-sdk': minor -'@hyperlane-xyz/sdk': minor ---- - -add cosmos native routing ism cosmos-sdk and types diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index 08f0de83f5b..5f4245a751c 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperlane-xyz/core +## 7.1.6 + +### Patch Changes + +- Updated dependencies [0de63e0] + - @hyperlane-xyz/utils@13.0.0 + ## 7.1.5 ### Patch Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index fab433e1594..48a5c7e4470 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "7.1.5"; + string public constant PACKAGE_VERSION = "7.1.6"; } diff --git a/solidity/package.json b/solidity/package.json index 7400be33fce..cfdd0f1d13b 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "7.1.5", + "version": "7.1.6", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@chainlink/contracts-ccip": "^1.5.0", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "12.6.0", + "@hyperlane-xyz/utils": "13.0.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@matterlabs/hardhat-zksync-solc": "1.2.5", "@matterlabs/hardhat-zksync-verify": "1.7.1", diff --git a/starknet/CHANGELOG.md b/starknet/CHANGELOG.md new file mode 100644 index 00000000000..1f9e594de39 --- /dev/null +++ b/starknet/CHANGELOG.md @@ -0,0 +1,7 @@ +# @hyperlane-xyz/starknet-core + +## 13.0.0 + +### Major Changes + +- f8696c7: feat: Add Starknet contract ABI fetching and contract artifact generation diff --git a/starknet/package.json b/starknet/package.json index 9a979e8ec01..5f0057fc3e5 100644 --- a/starknet/package.json +++ b/starknet/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/starknet-core", "description": "Core cairo contracts for Hyperlane", - "version": "1.0.0", + "version": "13.0.0", "type": "module", "homepage": "https://www.hyperlane.xyz", "license": "Apache-2.0", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 061e999d1e6..c741fbeb823 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 13.0.0 + ## 12.6.0 ## 12.5.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index ab5661e8ee0..a107357961e 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "12.6.0", + "version": "13.0.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 d5759fc53f7..4eebd557171 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/cli +## 13.0.0 + ## 12.6.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 7f0d71d5cee..082141c62d2 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cli", - "version": "12.6.0", + "version": "13.0.0", "description": "A command-line utility for common Hyperlane operations", "devDependencies": { "@aws-sdk/client-kms": "^3.577.0", @@ -9,10 +9,10 @@ "@eslint/js": "^9.15.0", "@ethersproject/abi": "*", "@ethersproject/providers": "*", - "@hyperlane-xyz/cosmos-sdk": "12.6.0", + "@hyperlane-xyz/cosmos-sdk": "13.0.0", "@hyperlane-xyz/registry": "15.0.0", - "@hyperlane-xyz/sdk": "12.6.0", - "@hyperlane-xyz/utils": "12.6.0", + "@hyperlane-xyz/sdk": "13.0.0", + "@hyperlane-xyz/utils": "13.0.0", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index d3fb2e95e65..61a355df044 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '12.6.0'; +export const VERSION = '13.0.0'; diff --git a/typescript/cosmos-sdk/CHANGELOG.md b/typescript/cosmos-sdk/CHANGELOG.md index f7d7a30ab09..4a9caaa5371 100644 --- a/typescript/cosmos-sdk/CHANGELOG.md +++ b/typescript/cosmos-sdk/CHANGELOG.md @@ -1,5 +1,16 @@ # @hyperlane-xyz/cosmos-sdk +## 13.0.0 + +### Minor Changes + +- 2724559: add cosmos native routing ism cosmos-sdk and types + +### Patch Changes + +- Updated dependencies [2724559] + - @hyperlane-xyz/cosmos-types@13.0.0 + ## 12.6.0 ### Minor Changes diff --git a/typescript/cosmos-sdk/package.json b/typescript/cosmos-sdk/package.json index 07f1af432a0..7f70f838ccb 100644 --- a/typescript/cosmos-sdk/package.json +++ b/typescript/cosmos-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-sdk", - "version": "12.6.0", + "version": "13.0.0", "description": "Hyperlane TypeScript SDK for the Cosmos Hyperlane SDK module", "type": "module", "exports": { @@ -46,6 +46,6 @@ }, "dependencies": { "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/cosmos-types": "12.6.0" + "@hyperlane-xyz/cosmos-types": "13.0.0" } } diff --git a/typescript/cosmos-types/CHANGELOG.md b/typescript/cosmos-types/CHANGELOG.md index df6e0515081..2596f93cb39 100644 --- a/typescript/cosmos-types/CHANGELOG.md +++ b/typescript/cosmos-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/cosmos-types +## 13.0.0 + +### Minor Changes + +- 2724559: add cosmos native routing ism cosmos-sdk and types + ## 12.6.0 ## 12.5.0 diff --git a/typescript/cosmos-types/package.json b/typescript/cosmos-types/package.json index 7db3c37d3f3..69194422d7c 100644 --- a/typescript/cosmos-types/package.json +++ b/typescript/cosmos-types/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/cosmos-types", - "version": "12.6.0", + "version": "13.0.0", "description": "Hyperlane TypeScript SDK types for the Cosmos Hyperlane SDK module", "type": "module", "exports": { diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index 24528718db5..1fd90a49a6c 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 13.0.0 + ## 12.6.0 ## 12.5.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index 0c09010baad..45370ea57e5 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "12.6.0", + "version": "13.0.0", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 6d79dd7b4fc..44ef18902b8 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/helloworld +## 13.0.0 + +### Patch Changes + +- Updated dependencies [72b90f8] +- Updated dependencies [bc58283] +- Updated dependencies [2724559] + - @hyperlane-xyz/sdk@13.0.0 + - @hyperlane-xyz/core@7.1.6 + ## 12.6.0 ### Minor Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index b7f199ba0d8..78b8a25f8c2 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": "12.6.0", + "version": "13.0.0", "dependencies": { - "@hyperlane-xyz/core": "7.1.5", + "@hyperlane-xyz/core": "7.1.6", "@hyperlane-xyz/registry": "15.0.0", - "@hyperlane-xyz/sdk": "12.6.0", + "@hyperlane-xyz/sdk": "13.0.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index bd0ebcd720b..ccf244fe7b8 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,17 @@ # @hyperlane-xyz/infra +## 13.0.0 + +### Patch Changes + +- Updated dependencies [72b90f8] +- Updated dependencies [bc58283] +- Updated dependencies [0de63e0] +- Updated dependencies [2724559] + - @hyperlane-xyz/sdk@13.0.0 + - @hyperlane-xyz/utils@13.0.0 + - @hyperlane-xyz/helloworld@13.0.0 + ## 12.6.0 ### Minor Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 932ce8d4cd7..014a2aaffc3 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": "12.6.0", + "version": "13.0.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "*", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "12.6.0", + "@hyperlane-xyz/helloworld": "13.0.0", "@hyperlane-xyz/registry": "15.0.0", - "@hyperlane-xyz/sdk": "12.6.0", - "@hyperlane-xyz/utils": "12.6.0", + "@hyperlane-xyz/sdk": "13.0.0", + "@hyperlane-xyz/utils": "13.0.0", "@inquirer/prompts": "3.3.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index b4d385a5754..799a86c276e 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,23 @@ # @hyperlane-xyz/sdk +## 13.0.0 + +### Minor Changes + +- 72b90f8: add cosmos native core module & reader +- bc58283: feat: Starknet SDK logic integration +- 2724559: add cosmos native routing ism cosmos-sdk and types + +### Patch Changes + +- Updated dependencies [0de63e0] +- Updated dependencies [f8696c7] +- Updated dependencies [2724559] + - @hyperlane-xyz/utils@13.0.0 + - @hyperlane-xyz/starknet-core@13.0.0 + - @hyperlane-xyz/cosmos-sdk@13.0.0 + - @hyperlane-xyz/core@7.1.6 + ## 12.6.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 9ba90d65260..094b9d0bb28 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,17 +1,17 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "12.6.0", + "version": "13.0.0", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.577.0", "@chain-registry/types": "^0.50.122", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "7.1.5", - "@hyperlane-xyz/cosmos-sdk": "12.6.0", - "@hyperlane-xyz/starknet-core": "1.0.0", - "@hyperlane-xyz/utils": "12.6.0", + "@hyperlane-xyz/core": "7.1.6", + "@hyperlane-xyz/cosmos-sdk": "13.0.0", + "@hyperlane-xyz/starknet-core": "13.0.0", + "@hyperlane-xyz/utils": "13.0.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.23", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index b111af6886f..930f4bc4ddc 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/utils +## 13.0.0 + +### Minor Changes + +- 0de63e0: Add Starknet address and tx utils + ## 12.6.0 ### Minor Changes diff --git a/typescript/utils/package.json b/typescript/utils/package.json index fbf537b863f..4826d74a9b6 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": "12.6.0", + "version": "13.0.0", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.95.4", diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index e6c17f07a0b..24260febc39 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,18 @@ # @hyperlane-xyz/widgets +## 13.0.0 + +### Patch Changes + +- 1a7222b: Compress Starknet logo +- Updated dependencies [72b90f8] +- Updated dependencies [bc58283] +- Updated dependencies [0de63e0] +- Updated dependencies [2724559] + - @hyperlane-xyz/sdk@13.0.0 + - @hyperlane-xyz/utils@13.0.0 + - @hyperlane-xyz/cosmos-sdk@13.0.0 + ## 12.6.0 ### Minor Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 583d2c38ec4..92451008730 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "12.6.0", + "version": "13.0.0", "peerDependencies": { "react": "^18", "react-dom": "^18" @@ -10,9 +10,9 @@ "@cosmjs/stargate": "^0.32.4", "@cosmos-kit/react": "^2.18.0", "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/cosmos-sdk": "12.6.0", - "@hyperlane-xyz/sdk": "12.6.0", - "@hyperlane-xyz/utils": "12.6.0", + "@hyperlane-xyz/cosmos-sdk": "13.0.0", + "@hyperlane-xyz/sdk": "13.0.0", + "@hyperlane-xyz/utils": "13.0.0", "@interchain-ui/react": "^1.23.28", "@rainbow-me/rainbowkit": "^2.2.0", "@solana/wallet-adapter-react": "^0.15.32", diff --git a/yarn.lock b/yarn.lock index 5b1406d5b24..d85bf67574f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7645,10 +7645,10 @@ __metadata: "@eslint/js": "npm:^9.15.0" "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" - "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" + "@hyperlane-xyz/cosmos-sdk": "npm:13.0.0" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.6.0" - "@hyperlane-xyz/utils": "npm:12.6.0" + "@hyperlane-xyz/sdk": "npm:13.0.0" + "@hyperlane-xyz/utils": "npm:13.0.0" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" @@ -7688,14 +7688,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:7.1.5, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:7.1.6, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@chainlink/contracts-ccip": "npm:^1.5.0" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:12.6.0" + "@hyperlane-xyz/utils": "npm:13.0.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@matterlabs/hardhat-zksync-solc": "npm:1.2.5" @@ -7735,13 +7735,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-sdk@npm:12.6.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": +"@hyperlane-xyz/cosmos-sdk@npm:13.0.0, @hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-sdk@workspace:typescript/cosmos-sdk" dependencies: "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/cosmos-types": "npm:12.6.0" + "@hyperlane-xyz/cosmos-types": "npm:13.0.0" "@types/mocha": "npm:^10.0.1" "@typescript-eslint/eslint-plugin": "npm:^8.1.6" "@typescript-eslint/parser": "npm:^8.1.6" @@ -7757,7 +7757,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/cosmos-types@npm:12.6.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": +"@hyperlane-xyz/cosmos-types@npm:13.0.0, @hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cosmos-types@workspace:typescript/cosmos-types" dependencies: @@ -7791,14 +7791,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:12.6.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:13.0.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.5" + "@hyperlane-xyz/core": "npm:7.1.6" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.6.0" + "@hyperlane-xyz/sdk": "npm:13.0.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7847,10 +7847,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:*" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:12.6.0" + "@hyperlane-xyz/helloworld": "npm:13.0.0" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.6.0" - "@hyperlane-xyz/utils": "npm:12.6.0" + "@hyperlane-xyz/sdk": "npm:13.0.0" + "@hyperlane-xyz/utils": "npm:13.0.0" "@inquirer/prompts": "npm:3.3.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7922,7 +7922,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:12.6.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:13.0.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7932,10 +7932,10 @@ __metadata: "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" "@eslint/js": "npm:^9.15.0" - "@hyperlane-xyz/core": "npm:7.1.5" - "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" - "@hyperlane-xyz/starknet-core": "npm:1.0.0" - "@hyperlane-xyz/utils": "npm:12.6.0" + "@hyperlane-xyz/core": "npm:7.1.6" + "@hyperlane-xyz/cosmos-sdk": "npm:13.0.0" + "@hyperlane-xyz/starknet-core": "npm:13.0.0" + "@hyperlane-xyz/utils": "npm:13.0.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -7980,7 +7980,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/starknet-core@npm:1.0.0, @hyperlane-xyz/starknet-core@workspace:starknet": +"@hyperlane-xyz/starknet-core@npm:13.0.0, @hyperlane-xyz/starknet-core@workspace:starknet": version: 0.0.0-use.local resolution: "@hyperlane-xyz/starknet-core@workspace:starknet" dependencies: @@ -7999,7 +7999,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:12.6.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:13.0.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8042,10 +8042,10 @@ __metadata: "@emotion/styled": "npm:^11.13.0" "@eslint/js": "npm:^9.15.0" "@headlessui/react": "npm:^2.1.8" - "@hyperlane-xyz/cosmos-sdk": "npm:12.6.0" + "@hyperlane-xyz/cosmos-sdk": "npm:13.0.0" "@hyperlane-xyz/registry": "npm:15.0.0" - "@hyperlane-xyz/sdk": "npm:12.6.0" - "@hyperlane-xyz/utils": "npm:12.6.0" + "@hyperlane-xyz/sdk": "npm:13.0.0" + "@hyperlane-xyz/utils": "npm:13.0.0" "@interchain-ui/react": "npm:^1.23.28" "@rainbow-me/rainbowkit": "npm:^2.2.0" "@solana/wallet-adapter-react": "npm:^0.15.32" From 93155a47457cb6ffabdd6f26b8c5a85d120ef604 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 19 May 2025 12:38:52 +0100 Subject: [PATCH 201/223] fix: special-case darwin in fetch-contracts-release.sh (#6266) ### Description fix: special-case mac in fetch-contracts-release ### Drive-by changes ### Related issues ### Backward compatibility ### Testing - ubuntu test is fine https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/runs/15111483420/job/42471852313?pr=6266#step:4:13 - macos test is fine https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/runs/15111483420/job/42471853847?pr=6266#step:4:13 - windows test is fine https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/runs/15111483420/job/42471854670?pr=6266#step:4:13 --- starknet/scripts/fetch-contracts-release.sh | 34 ++++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/starknet/scripts/fetch-contracts-release.sh b/starknet/scripts/fetch-contracts-release.sh index d79af81ad3e..4fb1ffdf01f 100755 --- a/starknet/scripts/fetch-contracts-release.sh +++ b/starknet/scripts/fetch-contracts-release.sh @@ -25,7 +25,7 @@ log_success() { check_dependencies() { local -r required_tools=("curl" "jq" "unzip") - + for tool in "${required_tools[@]}"; do if ! command -v "$tool" &> /dev/null; then log_error "$tool is not installed" @@ -53,7 +53,7 @@ verify_version_exists() { get_release_info() { local version=$1 local release_info - + release_info=$(curl -sf "${GITHUB_RELEASES_API}/tags/${version}") || { log_error "Failed to fetch release information for version ${version}" exit 1 @@ -66,14 +66,14 @@ download_and_extract() { local download_url=$2 local base_url="${download_url%/*}" local filename="${download_url##*/}" - + if ! mkdir -p "$TARGET_DIR"; then log_error "Failed to create target directory" exit 1 fi log_success "Downloading version ${version} from ${download_url}" - + if ! curl -L "$download_url" -o "${TARGET_DIR}/release.zip"; then log_error "Download failed" exit 1 @@ -83,7 +83,7 @@ download_and_extract() { rm -f "${TARGET_DIR}/release.zip" exit 1 fi - + if ! unzip -o "${TARGET_DIR}/release.zip" -d "${TARGET_DIR}"; then log_error "Extraction failed" exit 1 @@ -96,11 +96,15 @@ verify_checksum() { local filename="$3" local checksum_filename checksum_filename="${filename%.zip}.CHECKSUM" - + local downloaded_checksum - downloaded_checksum="$(sha256sum "$file_path" | cut -d' ' -f1)" + if [[ "$OSTYPE" == "darwin"* ]]; then + downloaded_checksum="$(shasum -a 256 "$file_path" | cut -d' ' -f1)" + else + downloaded_checksum="$(sha256sum "$file_path" | cut -d' ' -f1)" + fi log_success "File checksum: ${downloaded_checksum}" - + local expected_checksum if ! expected_checksum="$(curl -sL "${base_url}/${checksum_filename}")"; then log_error "Failed to fetch checksum file" @@ -111,7 +115,7 @@ verify_checksum() { log_error "Checksum verification failed" return 1 fi - + return 0 } @@ -123,9 +127,9 @@ cleanup() { main() { trap cleanup EXIT - + check_dependencies - + # Skip if contracts already exist if check_if_contracts_exist; then exit 0 @@ -136,18 +140,18 @@ main() { local release_info release_info=$(get_release_info "$VERSION") - + local download_url download_url=$(echo "$release_info" | jq -r '.assets[] | select(.name | startswith("hyperlane-starknet") and endswith(".zip")) | .browser_download_url') - + if [[ -z "$download_url" ]]; then log_error "Could not find ZIP download URL for release" exit 1 fi - + # Process download and file checksum verification and extraction download_and_extract "$VERSION" "$download_url" - + log_success "Successfully downloaded and extracted version ${VERSION}" } From 82be0180b08697f43accbba910a6669fc5045689 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 19 May 2025 12:39:17 +0100 Subject: [PATCH 202/223] chore: update print-latest-checkpoints to support all chains (#6254) ### Description chore: update print-latest-checkpoints to support all chains ### Drive-by changes ### Related issues ### Backward compatibility ### Testing tested with forma, which is not in the core supported chains list but we can read checkpoints for now! image --- .../validators/print-latest-checkpoints.ts | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/typescript/infra/scripts/validators/print-latest-checkpoints.ts b/typescript/infra/scripts/validators/print-latest-checkpoints.ts index e9d06f1f38a..f0e54992582 100644 --- a/typescript/infra/scripts/validators/print-latest-checkpoints.ts +++ b/typescript/infra/scripts/validators/print-latest-checkpoints.ts @@ -13,9 +13,12 @@ import { rootLogger, } from '@hyperlane-xyz/utils'; +import { Contexts } from '../../config/contexts.js'; +import { getChainAddresses } from '../../config/registry.js'; +import { Role } from '../../src/roles.js'; import { isEthereumProtocolChain } from '../../src/utils/utils.js'; import { getArgs, withChainsRequired } from '../agent-utils.js'; -import { getHyperlaneCore } from '../core-utils.js'; +import { getEnvironmentConfig } from '../core-utils.js'; async function main() { configureRootLogger(LogFormat.Pretty, LogLevel.Info); @@ -53,19 +56,33 @@ async function main() { > > = {}; - // Manually add validator announce for OG Lumia chain deployment - const { core, multiProvider } = await getHyperlaneCore(environment); - const lumiaValidatorAnnounce = ValidatorAnnounce__factory.connect( - '0x989B7307d266151BE763935C856493D968b2affF', - multiProvider.getProvider('lumia'), + // Filter to only include target networks + const chainAddresses = Object.fromEntries( + Object.entries(getChainAddresses()).filter(([chain, _]) => + targetNetworks.includes(chain), + ), + ); + + // Get multiprovider for target networks + const envConfig = getEnvironmentConfig(environment); + const multiProvider = await envConfig.getMultiProvider( + Contexts.Hyperlane, + Role.Deployer, + true, + targetNetworks, ); await Promise.all( targetNetworks.map(async (chain) => { - const validatorAnnounce = - chain === 'lumia' - ? lumiaValidatorAnnounce - : core.getContracts(chain).validatorAnnounce; + if (chain === 'lumia') { + rootLogger.info('Skipping deprecated chain: lumia'); + return; + } + + const validatorAnnounce = ValidatorAnnounce__factory.connect( + chainAddresses[chain]['validatorAnnounce'], + multiProvider.getProvider(chain), + ); const announcedValidators = await validatorAnnounce.getAnnouncedValidators(); @@ -75,7 +92,7 @@ async function main() { ); const defaultIsmValidators = - defaultMultisigConfigs[chain].validators || []; + defaultMultisigConfigs[chain]?.validators || []; const findDefaultValidatorAlias = (address: Address): string => { const validator = defaultIsmValidators.find((v) => @@ -90,8 +107,9 @@ async function main() { const location = storageLocations[i][storageLocations[i].length - 1]; // Skip validators not in default ISM unless --all flag is set + // If it's not a core chain, then we'll want to check all announced validators const isDefaultIsmValidator = findDefaultValidatorAlias(validator); - if (!isDefaultIsmValidator && !all) { + if (defaultIsmValidators.length > 0 && !isDefaultIsmValidator && !all) { continue; } From 4f03dad8083d04de0aec5cf3a6b991cfc4e7e17b Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 19 May 2025 14:16:07 +0100 Subject: [PATCH 203/223] fix: add repository to starknet package.json (#6270) --- starknet/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/starknet/package.json b/starknet/package.json index 5f0057fc3e5..eabd291c6bc 100644 --- a/starknet/package.json +++ b/starknet/package.json @@ -5,6 +5,7 @@ "type": "module", "homepage": "https://www.hyperlane.xyz", "license": "Apache-2.0", + "repository": "https://github.com/hyperlane-xyz/hyperlane-monorepo", "scripts": { "fetch-contracts": "./scripts/fetch-contracts-release.sh", "generate-artifacts": "tsx ./scripts/generate-artifacts.ts", From 736dc1935e426456fb27647cafad3cd92d1e574e Mon Sep 17 00:00:00 2001 From: Christopher Brumm <97845034+christopherbrumm@users.noreply.github.com> Date: Mon, 19 May 2025 16:44:18 +0200 Subject: [PATCH 204/223] chore: add `MIRAI/abstract-bsc-solanamainnet` (#6238) ### Description Add `MIRAI/abstract-bsc-solanamainnet` token config and program IDs. ### Drive-by changes N/A ### Backward compatibility Yes ### Testing N/A --------- Co-authored-by: xeno097 --- .../program-ids.json | 14 +++++++++++ .../token-config.json | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/token-config.json diff --git a/rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/program-ids.json new file mode 100644 index 00000000000..7218f048323 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/program-ids.json @@ -0,0 +1,14 @@ +{ + "solanamainnet": { + "hex": "0x1071f5fcc751c8ef4c77456b617b76e17375cddf8364fe417b403f74c43e5d7c", + "base58": "27CK2NMbkSTTddgbBDvujccpqNrfGztr9WggXusNmnD1" + }, + "bsc": { + "hex": "0x000000000000000000000000cb508877ffe5a085b2474641dd3b28f8bc22a57c", + "base58": "1111111111113qHaSkbVC6n27M5jBCfJURoVVszj" + }, + "abstract": { + "hex": "0x000000000000000000000000bcda9ae6a148bc4fb411979ffa883c9d1df08f43", + "base58": "1111111111113dbrwMrFnehbw2Yo2ZaCdnbEJBSz" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/token-config.json new file mode 100644 index 00000000000..be9da83949e --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/MIRAI-abstract-bsc-solanamainnet/token-config.json @@ -0,0 +1,25 @@ +{ + "abstract": { + "type": "synthetic", + "decimals": 6, + "name": "Project Mirai", + "symbol": "MIRAI", + "owner": "", + "foreignDeployment": "0xbCdA9Ae6A148Bc4fb411979fFA883c9D1DF08F43" + }, + "bsc": { + "type": "synthetic", + "decimals": 6, + "name": "Project Mirai", + "symbol": "MIRAI", + "owner": "", + "foreignDeployment": "0xcb508877ffe5a085B2474641dD3B28F8Bc22A57c" + }, + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "token": "5evN2exivZXJfLaA1KhHfiJKWfwH8znqyH36w1SFz89Y", + "owner": "GpYhR9WY1i2V2jE7amN4RsSNSfzu4JeMkPEeWrCprFPS" + } +} \ No newline at end of file From 8601b168fc63e9a4e2962979bf6ca51a496ffbc1 Mon Sep 17 00:00:00 2001 From: Lee <6251863+ltyu@users.noreply.github.com> Date: Mon, 19 May 2025 18:19:37 -0400 Subject: [PATCH 205/223] feat: subtensor USDC (#6277) ### Description This PR adds USDC to subtensor ### Testing checker --- .../program-ids.json | 18 +++++++++++ .../token-config.json | 31 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/token-config.json diff --git a/rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/program-ids.json new file mode 100644 index 00000000000..71ac4ba881c --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/program-ids.json @@ -0,0 +1,18 @@ +{ + "base": { + "hex": "0x00000000000000000000000026af973a5b256f9b9bc0b1a3c566de1566568a87", + "base58": "111111111111YG4VUEpTecUfFXDssQfKUpCGdZG" + }, + "ethereum": { + "hex": "0x0000000000000000000000003c43c421f08e2a48889ea3f75a747b7a7a366a0b", + "base58": "111111111111qhNqRUzcpvPZ4XypQPtGsRtHjR4" + }, + "subtensor": { + "hex": "0x000000000000000000000000b833e8137fedf80de7e908dc6fea43a029142f20", + "base58": "1111111111113Zqsxh77mETmH4DKhM9r8bPJonHm" + }, + "solanamainnet": { + "hex": "0x03f0d1b0ebbc3caa2a101111e26b6ee3ac2a971ae2dc841f14eda9b3cb9cf5b7", + "base58": "GPCsiXvm9NaFjrxB6sThscap6akyvRgD5V6decCk25c" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/token-config.json new file mode 100644 index 00000000000..73d9472e270 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/USDC-base-ethereum-solana-subtensor/token-config.json @@ -0,0 +1,31 @@ +{ + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "owner": "BNGDJ1h9brgt6FFVd8No1TVAH48Fp44d7jkuydr1URwJ", + "name": "USD Coin", + "symbol": "USDC", + "token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/63ae6c0a0415d480c00880e64ec8a9c3724b4e37/deployments/warp_routes/USDC/metadata.json" + }, + "base": { + "type": "collateral", + "decimals": 6, + "foreignDeployment": "0x26af973A5b256F9B9bc0B1A3c566de1566568a87", + "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" + }, + "ethereum": { + "type": "collateral", + "decimals": 6, + "foreignDeployment": "0x3C43C421f08e2a48889eA3F75a747b7a7a366A0b", + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "subtensor": { + "type": "synthetic", + "decimals": 6, + "name": "USD Coin", + "symbol": "USDC", + "foreignDeployment": "0xB833E8137FEDf80de7E908dc6fea43a029142F20" + } +} \ No newline at end of file From 409e325fb3e0fb40b40adb605ad555b7ccd2abe2 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Tue, 20 May 2025 11:26:06 +0100 Subject: [PATCH 206/223] fix: Avoid holding lock on nonce manager with a remote call (#6268) ### Description Avoid holding lock on nonce manager with a remote call. Now we are using internal mutability to update the number of non finalized transactions in nonce manager ### Backward compatibility Yes ### Testing None Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../chains/ethereum/adapter.rs | 12 ++++-------- .../chains/ethereum/nonce/manager.rs | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs index b460e326492..8eedc325eca 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -37,7 +37,7 @@ pub struct EthereumTxAdapter { _raw_conf: RawChainConf, provider: Box, reorg_period: EthereumReorgPeriod, - nonce_manager: Arc>, + nonce_manager: NonceManager, } impl EthereumTxAdapter { @@ -60,7 +60,7 @@ impl EthereumTxAdapter { ) .await?; let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; - let nonce_manager = Arc::new(Mutex::new(NonceManager::new())); + let nonce_manager = NonceManager::new(); Ok(Self { conf, @@ -125,11 +125,7 @@ impl AdaptsChain for EthereumTxAdapter { info!(?tx, "submitting transaction"); - self.nonce_manager - .lock() - .await - .set_nonce(tx, &self.provider) - .await?; + self.nonce_manager.set_nonce(tx, &self.provider).await?; let precursor = tx.precursor(); let hash = self @@ -185,6 +181,6 @@ impl AdaptsChain for EthereumTxAdapter { } async fn set_unfinalized_tx_count(&self, count: usize) { - self.nonce_manager.lock().await.tx_in_finality_count = count; + self.nonce_manager.set_tx_in_finality_count(count).await; } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs index 037371f8d02..06ba13bb306 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs @@ -2,6 +2,10 @@ // implementing the trait for the boxed type would require a lot of boilerplate code. #![allow(clippy::borrowed_box)] +use std::sync::Arc; + +use tokio::sync::Mutex; + use hyperlane_ethereum::EvmProviderForSubmitter; use crate::transaction::Transaction; @@ -10,13 +14,13 @@ use crate::SubmitterError; use super::super::transaction::Precursor; pub struct NonceManager { - pub tx_in_finality_count: usize, + pub tx_in_finality_count: Arc>, } impl NonceManager { pub fn new() -> Self { Self { - tx_in_finality_count: 0, + tx_in_finality_count: Arc::new(Mutex::new(0usize)), } } @@ -38,10 +42,18 @@ impl NonceManager { "Transaction missing address".to_string(), ))?; let nonce = provider.get_next_nonce_on_finalized_block(address).await?; - let next_nonce = nonce + self.tx_in_finality_count; + let next_nonce = nonce + self.get_tx_in_finality_count().await as u64; precursor.tx.set_nonce(next_nonce); Ok(()) } + + pub async fn set_tx_in_finality_count(&self, count: usize) { + *self.tx_in_finality_count.lock().await = count; + } + + async fn get_tx_in_finality_count(&self) -> usize { + *self.tx_in_finality_count.lock().await + } } From 1a597e95bb0c1e96ba4432be82259004fb5dee89 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 20 May 2025 15:42:37 +0100 Subject: [PATCH 207/223] chore: evm e2e with new submitter (#6196) ### Description ### Drive-by changes ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/6147 ### Backward compatibility ### Testing --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../agents/relayer/src/msg/op_submitter.rs | 12 +- .../src/contracts/mailbox.rs | 3 +- .../src/rpc_clients/provider.rs | 6 +- .../chains/ethereum/adapter.rs | 60 ++++++- .../ethereum/adapter/gas_price_estimator.rs | 3 +- .../chains/ethereum/nonce/manager.rs | 16 +- .../chains/ethereum/precursor.rs | 25 ++- rust/main/submitter/src/error.rs | 7 +- rust/main/submitter/src/payload/types.rs | 11 +- .../src/ethereum/termination_invariants.rs | 3 + .../src/invariants/termination_invariants.rs | 146 +++++++++++++++++- rust/main/utils/run-locally/src/main.rs | 9 +- .../src/sealevel/termination_invariants.rs | 145 +---------------- 13 files changed, 272 insertions(+), 174 deletions(-) diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index 243e180efb0..44f09152e22 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -171,20 +171,14 @@ impl SerialSubmitter { let entrypoint = self.payload_dispatcher_entrypoint.take().map(Arc::new); - let prepare_task = match &entrypoint { - None => self.create_classic_prepare_task(), - Some(entrypoint) => self.create_lander_prepare_task(entrypoint.clone()), - }; + let prepare_task = self.create_classic_prepare_task(); let submit_task = match &entrypoint { None => self.create_classic_submit_task(), Some(entrypoint) => self.create_lander_submit_task(entrypoint.clone()), }; - let confirm_task = match &entrypoint { - None => self.create_classic_confirm_task(), - Some(entrypoint) => self.create_lander_confirm_task(entrypoint.clone()), - }; + let confirm_task = self.create_classic_confirm_task(); let tasks = [ self.create_receive_task(rx_prepare), @@ -270,6 +264,7 @@ impl SerialSubmitter { .expect("spawning tokio task from Builder is infallible") } + #[allow(unused)] fn create_lander_prepare_task( &self, entrypoint: Arc, @@ -317,6 +312,7 @@ impl SerialSubmitter { .expect("spawning tokio task from Builder is infallible") } + #[allow(unused)] fn create_lander_confirm_task( &self, entrypoint: Arc, diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index b11133660fd..32273448693 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -685,10 +685,11 @@ where message: &HyperlaneMessage, metadata: &[u8], ) -> ChainResult> { - let contract_call = self.contract.process( + let mut contract_call = self.contract.process( metadata.to_vec().into(), RawHyperlaneMessage::from(message).to_vec().into(), ); + contract_call.tx.set_chain_id(self.domain.id()); let data = (contract_call.tx, contract_call.function); serde_json::to_vec(&data).map_err(Into::into) } diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index 9987cf28841..36d50624041 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -6,7 +6,7 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; -use ethers::types::{Block, H256 as EthersH256}; +use ethers::types::{Block, H160, H256 as EthersH256}; use ethers::{prelude::Middleware, types::TransactionReceipt}; use ethers_contract::builders::ContractCall; use ethers_core::abi::Function; @@ -134,7 +134,7 @@ pub trait EvmProviderForSubmitter: Send + Sync { ) -> ChainResult; /// Get default sender - fn default_sender(&self) -> Option
; + fn get_signer(&self) -> Option; } #[async_trait] @@ -231,7 +231,7 @@ where .map_err(ChainCommunicationError::from_other) } - fn default_sender(&self) -> Option
{ + fn get_signer(&self) -> Option { self.provider.default_sender() } } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs index 8eedc325eca..8e4aa9e2786 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter.rs @@ -1,13 +1,15 @@ use std::marker::PhantomData; use std::sync::Arc; +use std::time::Duration; use async_trait::async_trait; use ethers::contract::builders::ContractCall; use ethers::prelude::U64; use ethers::providers::Middleware; use ethers::types::H256; +use eyre::eyre; use tokio::sync::Mutex; -use tracing::{info, warn}; +use tracing::{error, info, warn}; use uuid::Uuid; use hyperlane_base::settings::parser::h_eth::{BuildableWithProvider, ConnectionConf}; @@ -32,6 +34,8 @@ mod gas_price_estimator; mod tx_status_checker; pub struct EthereumTxAdapter { + estimated_block_time: Duration, + max_batch_size: u32, conf: ChainConf, connection_conf: ConnectionConf, _raw_conf: RawChainConf, @@ -61,8 +65,12 @@ impl EthereumTxAdapter { .await?; let reorg_period = EthereumReorgPeriod::try_from(&conf.reorg_period)?; let nonce_manager = NonceManager::new(); + let estimated_block_time = conf.estimated_block_time; + let max_batch_size = Self::batch_size(&conf)?; Ok(Self { + estimated_block_time, + max_batch_size, conf, connection_conf, _raw_conf: raw_conf, @@ -71,6 +79,40 @@ impl EthereumTxAdapter { nonce_manager, }) } + + fn batch_size(conf: &ChainConf) -> eyre::Result { + Ok(conf + .connection + .operation_submission_config() + .ok_or_else(|| eyre!("no operation batch config"))? + .max_batch_size) + } + + async fn set_nonce_if_needed(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { + self.nonce_manager.set_nonce(tx, &self.provider).await?; + Ok(()) + } + + async fn set_gas_limit_if_needed(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { + if tx.precursor().tx.gas().is_none() { + self.estimate_tx(tx).await?; + } + Ok(()) + } + + async fn set_gas_price(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { + if tx.precursor().tx.gas_price().is_none() { + gas_price_estimator::estimate_gas_price( + &self.provider, + tx.precursor_mut(), + &self.connection_conf.transaction_overrides, + &self.conf.domain, + ) + .await?; + info!(?tx, "estimated gas price for transaction"); + } + Ok(()) + } } #[async_trait] @@ -86,9 +128,13 @@ impl AdaptsChain for EthereumTxAdapter { use super::transaction::TransactionFactory; info!(?payloads, "building transactions for payloads"); + let Some(signer) = self.provider.get_signer() else { + error!("No signer found! Cannot build transactions"); + return vec![]; + }; let payloads_and_precursors = payloads .iter() - .map(|payload| (EthereumTxPrecursor::from_payload(payload), payload)) + .map(|payload| (EthereumTxPrecursor::from_payload(payload, signer), payload)) .collect::>(); let mut transactions = Vec::new(); @@ -123,9 +169,11 @@ impl AdaptsChain for EthereumTxAdapter { async fn submit(&self, tx: &mut Transaction) -> Result<(), SubmitterError> { use super::transaction::Precursor; - info!(?tx, "submitting transaction"); + self.set_nonce_if_needed(tx).await?; + self.set_gas_limit_if_needed(tx).await?; + self.set_gas_price(tx).await?; - self.nonce_manager.set_nonce(tx, &self.provider).await?; + info!(?tx, "submitting transaction"); let precursor = tx.precursor(); let hash = self @@ -173,11 +221,11 @@ impl AdaptsChain for EthereumTxAdapter { } fn estimated_block_time(&self) -> &std::time::Duration { - todo!() + &self.estimated_block_time } fn max_batch_size(&self) -> u32 { - todo!() + self.max_batch_size } async fn set_unfinalized_tx_count(&self, count: usize) { diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs index 75e846e8556..024734f3a90 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs @@ -40,7 +40,6 @@ pub type Eip1559Fee = ( EthersU256, // max priority fee ); -#[allow(unused)] pub async fn estimate_gas_price( provider: &Box, tx_precursor: &mut EthereumTxPrecursor, @@ -205,7 +204,7 @@ async fn zksync_estimate_fee( tx.set_from( // use the sender in the provider if one is set, otherwise default to the EVM relayer address provider - .default_sender() + .get_signer() .unwrap_or(H160::from_str(EVM_RELAYER_ADDRESS).unwrap()), ); diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs index 06ba13bb306..87ca9dec190 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/nonce/manager.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use tokio::sync::Mutex; +use tracing::info; use hyperlane_ethereum::EvmProviderForSubmitter; @@ -14,7 +15,7 @@ use crate::SubmitterError; use super::super::transaction::Precursor; pub struct NonceManager { - pub tx_in_finality_count: Arc>, + tx_in_finality_count: Arc>, } impl NonceManager { @@ -29,22 +30,31 @@ impl NonceManager { tx: &mut Transaction, provider: &Box, ) -> Result<(), SubmitterError> { + let tx_id = tx.id.to_string(); let precursor = tx.precursor_mut(); if precursor.tx.nonce().is_some() { return Ok(()); } - let address = precursor + let address = *precursor .tx .from() .ok_or(SubmitterError::TxSubmissionError( "Transaction missing address".to_string(), ))?; - let nonce = provider.get_next_nonce_on_finalized_block(address).await?; + let nonce = provider.get_next_nonce_on_finalized_block(&address).await?; + let next_nonce = nonce + self.get_tx_in_finality_count().await as u64; precursor.tx.set_nonce(next_nonce); + info!( + nonce = next_nonce.to_string(), + address = ?address, + ?tx_id, + precursor = ?precursor, + "Set nonce for transaction" + ); Ok(()) } diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs index 9dc68b9f9d6..d34fbf80be9 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/precursor.rs @@ -1,16 +1,31 @@ use std::fmt::Debug; -use ethers::abi::Function; use ethers::types::transaction::eip2718::TypedTransaction; +use ethers::{abi::Function, types::H160}; use crate::payload::{FullPayload, PayloadDetails}; -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[derive(Clone, serde::Deserialize, serde::Serialize)] pub struct EthereumTxPrecursor { pub tx: TypedTransaction, pub function: Function, } +impl Debug for EthereumTxPrecursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EthereumTxPrecursor") + .field("tx.from", &self.tx.from()) + .field("tx.to", &self.tx.to()) + .field("tx.nonce", &self.tx.nonce()) + .field("tx.gas_limit", &self.tx.gas()) + .field("tx.gas_price", &self.tx.gas_price()) + .field("tx.chain_id", &self.tx.chain_id()) + .field("tx.value", &self.tx.value()) + .field("function.name", &self.function.name) + .finish() + } +} + impl PartialEq for EthereumTxPrecursor { fn eq(&self, other: &Self) -> bool { self.tx == other.tx @@ -28,10 +43,12 @@ impl EthereumTxPrecursor { Self { tx, function } } - pub fn from_payload(payload: &FullPayload) -> Self { + pub fn from_payload(payload: &FullPayload, signer: H160) -> Self { use super::payload::parse_data; - let (tx, function) = parse_data(payload); + let (mut tx, function) = parse_data(payload); + tx.set_from(signer); + EthereumTxPrecursor::new(tx, function) } diff --git a/rust/main/submitter/src/error.rs b/rust/main/submitter/src/error.rs index 4f788307528..a688b3be6a1 100644 --- a/rust/main/submitter/src/error.rs +++ b/rust/main/submitter/src/error.rs @@ -68,7 +68,12 @@ impl IsRetryable for SubmitterError { // TODO: add logic to classify based on the error message false } - SubmitterError::ChainCommunicationError(_) => { + SubmitterError::ChainCommunicationError(err) => { + // this error is returned randomly by the `TestTokenRecipient`, + // to simulate delivery errors + if err.to_string().contains("block hash ends in 0") { + return true; + } // TODO: add logic to classify based on the error message false } diff --git a/rust/main/submitter/src/payload/types.rs b/rust/main/submitter/src/payload/types.rs index 72c7b7220ee..a020d3dd112 100644 --- a/rust/main/submitter/src/payload/types.rs +++ b/rust/main/submitter/src/payload/types.rs @@ -13,7 +13,7 @@ pub type PayloadId = UniqueIdentifier; type Address = H256; /// Struct needed to keep lightweight references to payloads, such that when included in logs there's no noise. -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq, Default)] +#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq, Default)] pub struct PayloadDetails { /// unique payload identifier pub id: PayloadId, @@ -26,6 +26,15 @@ pub struct PayloadDetails { pub success_criteria: Option>, } +impl Debug for PayloadDetails { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PayloadDetails") + .field("id", &self.id) + .field("metadata", &self.metadata) + .finish() + } +} + impl PayloadDetails { pub fn new( id: PayloadId, diff --git a/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs b/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs index 5ff0ef39663..3de0f6451bb 100644 --- a/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/ethereum/termination_invariants.rs @@ -1,3 +1,4 @@ +use hyperlane_core::SubmitterType; use maplit::hashmap; use crate::config::Config; @@ -16,6 +17,7 @@ use crate::{FAILED_MESSAGE_COUNT, RELAYER_METRICS_PORT, ZERO_MERKLE_INSERTION_KA pub fn termination_invariants_met( config: &Config, starting_relayer_balance: f64, + submitter_type: SubmitterType, ) -> eyre::Result { let eth_messages_expected = (config.kathy_messages / 2) as u32 * 2; @@ -40,6 +42,7 @@ pub fn termination_invariants_met( non_matching_igp_message_count: 0, double_insertion_message_count: (config.kathy_messages as u32 / 4) * 2, sealevel_tx_id_indexing: false, + submitter_type, }; if !relayer_termination_invariants_met(params)? { return Ok(false); diff --git a/rust/main/utils/run-locally/src/invariants/termination_invariants.rs b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs index 68af608213f..eaebd056192 100644 --- a/rust/main/utils/run-locally/src/invariants/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs @@ -1,13 +1,15 @@ use std::collections::HashMap; use std::fs::File; -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 hyperlane_core::SubmitterType; + +use crate::config::Config; use crate::logging::log; +use crate::metrics::agent_balance_sum; +use crate::utils::get_matching_lines; use crate::{fetch_metric, AGENT_LOGGING_DIR, RELAYER_METRICS_PORT, SCRAPER_METRICS_PORT}; #[derive(Clone)] @@ -23,6 +25,7 @@ pub struct RelayerTerminationInvariantParams<'a> { pub non_matching_igp_message_count: u32, pub double_insertion_message_count: u32, pub sealevel_tx_id_indexing: bool, + pub submitter_type: SubmitterType, } /// returns false if invariants are not met @@ -42,7 +45,8 @@ pub fn relayer_termination_invariants_met( non_matching_igp_message_count, double_insertion_message_count, sealevel_tx_id_indexing, - } = params; + submitter_type, + } = params.clone(); log!("Checking relayer termination invariants"); @@ -51,7 +55,10 @@ pub fn relayer_termination_invariants_met( "hyperlane_submitter_queue_length", &hashmap! {}, )?; - assert!(!lengths.is_empty(), "Could not find queue length metric"); + if lengths.is_empty() { + log!("No submitter queues found"); + return Ok(false); + } if lengths.iter().sum::() != submitter_queue_length_expected { log!( "Relayer queues contain more messages than expected. Lengths: {:?}, expected {}", @@ -204,6 +211,13 @@ pub fn relayer_termination_invariants_met( return Ok(false); } + if matches!(submitter_type, SubmitterType::Lander) + && !submitter_metrics_invariants_met(params, RELAYER_METRICS_PORT, &hashmap! {})? + { + log!("Submitter metrics invariants not met"); + return Ok(false); + } + Ok(true) } @@ -292,6 +306,128 @@ pub fn relayer_balance_check(starting_relayer_balance: f64) -> eyre::Result, +) -> eyre::Result { + let finalized_transactions = fetch_metric( + relayer_port, + "hyperlane_lander_finalized_transactions", + filter_hashmap, + )? + .iter() + .sum::(); + + let building_stage_queue_length = fetch_metric( + relayer_port, + "hyperlane_lander_building_stage_queue_length", + filter_hashmap, + )? + .iter() + .sum::(); + + let inclusion_stage_pool_length = fetch_metric( + relayer_port, + "hyperlane_lander_inclusion_stage_pool_length", + filter_hashmap, + )? + .iter() + .sum::(); + let finality_stage_pool_length = fetch_metric( + relayer_port, + "hyperlane_lander_finality_stage_pool_length", + filter_hashmap, + )? + .iter() + .sum::(); + let dropped_payloads = fetch_metric( + relayer_port, + "hyperlane_lander_dropped_payloads", + filter_hashmap, + )? + .iter() + .sum::(); + let dropped_transactions = fetch_metric( + relayer_port, + "hyperlane_lander_dropped_transactions", + filter_hashmap, + )? + .iter() + .sum::(); + + let transaction_submissions = fetch_metric( + relayer_port, + "hyperlane_lander_transaction_submissions", + filter_hashmap, + )? + .iter() + .sum::(); + + if finalized_transactions < params.total_messages_expected { + log!( + "hyperlane_lander_finalized_transactions {} count, expected {}", + finalized_transactions, + params.total_messages_expected + ); + return Ok(false); + } + if building_stage_queue_length != 0 { + log!( + "hyperlane_lander_building_stage_queue_length {} count, expected {}", + building_stage_queue_length, + 0 + ); + return Ok(false); + } + if inclusion_stage_pool_length != 0 { + log!( + "hyperlane_lander_inclusion_stage_pool_length {} count, expected {}", + inclusion_stage_pool_length, + 0 + ); + return Ok(false); + } + if finality_stage_pool_length != 0 { + log!( + "hyperlane_lander_finality_stage_pool_length {} count, expected {}", + finality_stage_pool_length, + 0 + ); + return Ok(false); + } + if dropped_payloads != 0 { + log!( + "hyperlane_lander_dropped_payloads {} count, expected {}", + dropped_payloads, + 0 + ); + return Ok(false); + } + if dropped_transactions != 0 { + log!( + "hyperlane_lander_dropped_transactions {} count, expected {}", + dropped_transactions, + 0 + ); + return Ok(false); + } + + // resubmissions are possible because it takes a while for the local + // solana validator to report a tx hash as included once broadcast + // but no more than 2 submissions are expected per message + if transaction_submissions > 2 * params.total_messages_expected { + log!( + "hyperlane_lander_transaction_submissions {} count, expected {}", + transaction_submissions, + params.total_messages_expected + ); + return Ok(false); + } + + Ok(true) +} + #[allow(dead_code)] pub fn provider_metrics_invariant_met( relayer_port: &str, diff --git a/rust/main/utils/run-locally/src/main.rs b/rust/main/utils/run-locally/src/main.rs index 068e2ea43ea..733ca23e6fc 100644 --- a/rust/main/utils/run-locally/src/main.rs +++ b/rust/main/utils/run-locally/src/main.rs @@ -29,7 +29,7 @@ use std::{ }; use ethers_contract::MULTICALL_ADDRESS; -use hyperlane_core::{PendingOperationStatus, ReorgEvent, ReprepareReason}; +use hyperlane_core::{PendingOperationStatus, ReorgEvent, ReprepareReason, SubmitterType}; use logging::log; pub use metrics::fetch_metric; use once_cell::sync::Lazy; @@ -97,6 +97,8 @@ const FAILED_MESSAGE_COUNT: u32 = 1; const RELAYER_METRICS_PORT: &str = "9092"; const SCRAPER_METRICS_PORT: &str = "9093"; +pub const SUBMITTER_TYPE: SubmitterType = SubmitterType::Lander; + type DynPath = Box>; static RUN_LOG_WATCHERS: AtomicBool = AtomicBool::new(true); @@ -386,7 +388,7 @@ fn main() -> ExitCode { let mut test_passed = wait_for_condition( &config, loop_start, - || termination_invariants_met(&config, starting_relayer_balance), + || termination_invariants_met(&config, starting_relayer_balance, SUBMITTER_TYPE), || !SHUTDOWN.load(Ordering::Relaxed), || long_running_processes_exited_check(&mut state), ); @@ -491,6 +493,9 @@ fn create_relayer(rocks_db_dir: &TempDir) -> Program { .hyp_env("DB", relayer_db.to_str().unwrap()) .hyp_env("CHAINS_TEST1_SIGNER_KEY", RELAYER_KEYS[0]) .hyp_env("CHAINS_TEST2_SIGNER_KEY", RELAYER_KEYS[1]) + .hyp_env("CHAINS_TEST1_SUBMITTER", SUBMITTER_TYPE.to_string()) + .hyp_env("CHAINS_TEST2_SUBMITTER", SUBMITTER_TYPE.to_string()) + .hyp_env("CHAINS_TEST3_SUBMITTER", SUBMITTER_TYPE.to_string()) .hyp_env("RELAYCHAINS", "invalidchain,otherinvalid") .hyp_env("ALLOWLOCALCHECKPOINTSYNCERS", "true") .hyp_env( diff --git a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs index 583b9d76cac..c24292cf1b3 100644 --- a/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/sealevel/termination_invariants.rs @@ -1,15 +1,14 @@ -use std::{collections::HashMap, path::Path}; +use std::path::Path; use hyperlane_core::SubmitterType; use maplit::hashmap; use crate::{ config::Config, - fetch_metric, invariants::{ provider_metrics_invariant_met, relayer_termination_invariants_met, - scraper_termination_invariants_met, RelayerTerminationInvariantParams, - ScraperTerminationInvariantParams, + scraper_termination_invariants_met, submitter_metrics_invariants_met, + RelayerTerminationInvariantParams, ScraperTerminationInvariantParams, }, logging::log, sealevel::{solana::*, SOL_MESSAGES_EXPECTED, SOL_MESSAGES_WITH_NON_MATCHING_IGP}, @@ -52,6 +51,7 @@ pub fn termination_invariants_met( non_matching_igp_message_count: 0, double_insertion_message_count: sol_messages_with_non_matching_igp, sealevel_tx_id_indexing: true, + submitter_type, }; if !relayer_termination_invariants_met(relayer_invariant_params.clone())? { log!("Relayer termination invariants not met"); @@ -84,145 +84,13 @@ pub fn termination_invariants_met( return Ok(false); } - if matches!(submitter_type, SubmitterType::Lander) - && !submitter_metrics_invariants_met( - relayer_invariant_params, - RELAYER_METRICS_PORT, - &hashmap! {"destination" => "sealeveltest2"}, - )? - { - log!("Submitter metrics invariants not met"); - return Ok(false); - } - log!("Termination invariants have been meet"); Ok(true) } -fn submitter_metrics_invariants_met( - params: RelayerTerminationInvariantParams, - relayer_port: &str, - filter_hashmap: &HashMap<&str, &str>, -) -> eyre::Result { - let finalized_transactions = fetch_metric( - relayer_port, - "hyperlane_lander_finalized_transactions", - filter_hashmap, - )? - .iter() - .sum::(); - - let building_stage_queue_length = fetch_metric( - relayer_port, - "hyperlane_lander_building_stage_queue_length", - filter_hashmap, - )? - .iter() - .sum::(); - - let inclusion_stage_pool_length = fetch_metric( - relayer_port, - "hyperlane_lander_inclusion_stage_pool_length", - filter_hashmap, - )? - .iter() - .sum::(); - let finality_stage_pool_length = fetch_metric( - relayer_port, - "hyperlane_lander_finality_stage_pool_length", - filter_hashmap, - )? - .iter() - .sum::(); - let dropped_payloads = fetch_metric( - relayer_port, - "hyperlane_lander_dropped_payloads", - filter_hashmap, - )? - .iter() - .sum::(); - let dropped_transactions = fetch_metric( - relayer_port, - "hyperlane_lander_dropped_transactions", - filter_hashmap, - )? - .iter() - .sum::(); - - let transaction_submissions = fetch_metric( - relayer_port, - "hyperlane_lander_transaction_submissions", - filter_hashmap, - )? - .iter() - .sum::(); - - if finalized_transactions < params.total_messages_expected { - log!( - "hyperlane_lander_finalized_transactions {} count, expected {}", - finalized_transactions, - params.total_messages_expected - ); - return Ok(false); - } - if building_stage_queue_length != 0 { - log!( - "hyperlane_lander_building_stage_queue_length {} count, expected {}", - building_stage_queue_length, - 0 - ); - return Ok(false); - } - if inclusion_stage_pool_length != 0 { - log!( - "hyperlane_lander_inclusion_stage_pool_length {} count, expected {}", - inclusion_stage_pool_length, - 0 - ); - return Ok(false); - } - if finality_stage_pool_length != 0 { - log!( - "hyperlane_lander_finality_stage_pool_length {} count, expected {}", - finality_stage_pool_length, - 0 - ); - return Ok(false); - } - if dropped_payloads != 0 { - log!( - "hyperlane_lander_dropped_payloads {} count, expected {}", - dropped_payloads, - 0 - ); - return Ok(false); - } - if dropped_transactions != 0 { - log!( - "hyperlane_lander_dropped_transactions {} count, expected {}", - dropped_transactions, - 0 - ); - return Ok(false); - } - - // resubmissions are possible because it takes a while for the local - // solana validator to report a tx hash as included once broadcast - // but no more than 2 submissions are expected per message - if transaction_submissions > 2 * params.total_messages_expected { - log!( - "hyperlane_lander_transaction_submissions {} count, expected {}", - transaction_submissions, - params.total_messages_expected - ); - return Ok(false); - } - - Ok(true) -} - #[cfg(test)] mod tests { + use super::*; use maplit::hashmap; #[test] @@ -231,7 +99,7 @@ mod tests { let filter_hashmap = hashmap! { "destination" => "sealeveltest2", }; - let params = super::RelayerTerminationInvariantParams { + let params = RelayerTerminationInvariantParams { total_messages_expected: 10, // the rest are not used config: &crate::config::Config::load(), @@ -244,6 +112,7 @@ mod tests { non_matching_igp_message_count: 0, double_insertion_message_count: 0, sealevel_tx_id_indexing: true, + submitter_type: SubmitterType::Lander, }; assert_eq!( super::submitter_metrics_invariants_met( From 2faf54edd961f7c738699333f3aefbf765cfb6ef Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 20 May 2025 19:45:48 +0100 Subject: [PATCH 208/223] fix: storage diff-check (#6290) ### Description fix: storage diff-check ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- solidity/storage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/storage.sh b/solidity/storage.sh index 7662e252fbb..54b28510180 100755 --- a/solidity/storage.sh +++ b/solidity/storage.sh @@ -17,5 +17,5 @@ do contract=$(basename "$file" .sol) echo "Generating storage layout of $contract" - forge inspect "$contract" storage --pretty > "$OUTPUT_PATH/$contract.md" + forge inspect "$contract" storage > "$OUTPUT_PATH/$contract.md" done From d03a8ff73c59edcb41f9aecdeb4becfdb077f529 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 21 May 2025 10:56:48 +0100 Subject: [PATCH 209/223] chore: depot docker recommendations (#6258) ### Description chore: depot docker recommendations - update dockerfiles based on the depot recommendations ### Drive-by changes ### Related issues ### Backward compatibility ### Testing - [x] ci - [x] monorepo image on keyfunder - [x] agent image on rc --- Dockerfile | 5 ++--- rust/Dockerfile | 7 +++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index edbe7038b0c..09e82ef1d18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,8 @@ FROM node:20-alpine WORKDIR /hyperlane-monorepo -RUN apk add --update --no-cache git g++ make py3-pip jq bash curl - -RUN yarn set version 4.5.1 +RUN apk add --update --no-cache git g++ make py3-pip jq bash curl && \ + yarn set version 4.5.1 # Copy package.json and friends COPY package.json yarn.lock .yarnrc.yml ./ diff --git a/rust/Dockerfile b/rust/Dockerfile index 5b7ab65bcc5..2a741bcf081 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -7,7 +7,9 @@ # Base image containing all necessary build tools and dependencies FROM rust:1.81.0 AS base RUN apt-get update && \ - apt-get install -y musl-tools clang && \ + apt-get install -y --no-install-recommends musl-tools clang && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* && \ rustup target add x86_64-unknown-linux-musl && \ cargo install --locked sccache @@ -67,7 +69,8 @@ COPY --from=builder /release/* . # Install runtime dependencies # remove /var/lib/apt/lists/* to clean up the package lists RUN apt-get update && \ - apt-get install -y openssl ca-certificates tini && \ + apt-get install -y --no-install-recommends openssl ca-certificates tini && \ + apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ chmod 777 /app && \ mkdir -p /usr/share/hyperlane && chmod 1000 /usr/share/hyperlane && \ From 7aae0ec85f7087d84137849aa6701d1f4d48404f Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 21 May 2025 11:21:56 +0100 Subject: [PATCH 210/223] feat: setup turborepo (#3899) - use [turbo](https://turbo.build/repo/docs) for faster build times - automatic build task scheduling/dependency ordering - automatic build caching - faster build times - see turbo docs for more info - better integration with depot cache - use depot's turbo cache https://depot.dev/docs/cache/reference/turbo - use more depot runners to speed up recovery of yarn-build-with-cache action - depot runners will make use of depot's caching backend over github-native or buildjet --- .github/workflows/release.yml | 11 ++++- .github/workflows/test.yml | 34 ++++++++------- .gitignore | 2 + Dockerfile | 1 + package.json | 19 +++++---- solidity/eslint.config.mjs | 1 + solidity/turbo.json | 14 +++++++ starknet/turbo.json | 8 ++++ turbo.json | 25 +++++++++++ typescript/helloworld/turbo.json | 8 ++++ yarn.lock | 72 ++++++++++++++++++++++++++++++++ 11 files changed, 169 insertions(+), 26 deletions(-) create mode 100644 solidity/turbo.json create mode 100644 starknet/turbo.json create mode 100644 turbo.json create mode 100644 typescript/helloworld/turbo.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 02db7267c43..8640e67ff54 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,13 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} +env: + LOG_FORMAT: PRETTY + TURBO_TELEMETRY_DISABLED: 1 + TURBO_API: https://cache.depot.dev + TURBO_TOKEN: ${{ secrets.DEPOT_TURBO_TOKEN }} + TURBO_TEAM: ${{ secrets.DEPOT_ORG_ID }} + jobs: # This job prepares the release by creating or updating a release PR. # Notice the omission of the `publish` flag in the changesets action. @@ -19,7 +26,7 @@ jobs: id-token: write contents: write pull-requests: write - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -84,7 +91,7 @@ jobs: id-token: write contents: write pull-requests: write - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e4a396b315..baa7d95ec3d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,10 +21,14 @@ env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full RUSTC_WRAPPER: sccache + TURBO_TELEMETRY_DISABLED: 1 + TURBO_API: https://cache.depot.dev + TURBO_TOKEN: ${{ secrets.DEPOT_TURBO_TOKEN }} + TURBO_TEAM: ${{ secrets.DEPOT_ORG_ID }} jobs: yarn-install: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -60,7 +64,7 @@ jobs: run: yarn syncpack list-mismatches lint-prettier: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -75,6 +79,7 @@ jobs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + cache-provider: github - name: lint run: yarn lint @@ -169,7 +174,7 @@ jobs: echo "Push event to main detected. Setting only_rust to false." yarn-test-run: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest needs: [rust-only] timeout-minutes: 10 if: needs.rust-only.outputs.only_rust == 'false' @@ -187,7 +192,7 @@ jobs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - + cache-provider: github - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -207,7 +212,7 @@ jobs: result: ${{ needs.yarn-test-run.result }} infra-test: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest needs: [yarn-install, set-base-sha] env: GRAFANA_SERVICE_ACCOUNT_TOKEN: ${{ secrets.GRAFANA_SERVICE_ACCOUNT_TOKEN }} @@ -234,6 +239,7 @@ jobs: if: env.BALANCE_CHANGES == 'true' || env.WARP_CONFIG_CHANGES == 'true' with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + cache-provider: github - name: Checkout registry if: env.BALANCE_CHANGES == 'true' || env.WARP_CONFIG_CHANGES == 'true' @@ -278,7 +284,7 @@ jobs: result: ${{ needs.cli-install-test-run.result }} cli-e2e-matrix: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest needs: [rust-only] if: needs.rust-only.outputs.only_rust == 'false' strategy: @@ -314,16 +320,12 @@ jobs: - name: foundry-install uses: foundry-rs/foundry-toolchain@v1 - - name: yarn-build - uses: ./.github/actions/yarn-build-with-cache - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: install-hyperlane-cli id: install-hyperlane-cli uses: ./.github/actions/install-cli with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + cache-provider: github - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -346,7 +348,7 @@ jobs: result: ${{ needs.cli-e2e-matrix.result }} cosmos-sdk-e2e-run: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest needs: [rust-only] if: needs.rust-only.outputs.only_rust == 'false' steps: @@ -360,6 +362,7 @@ jobs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + cache-provider: github - name: Cosmos SDK e2e tests run: yarn --cwd typescript/cosmos-sdk test:e2e @@ -377,7 +380,7 @@ jobs: result: ${{ needs.cosmos-sdk-e2e-run.result }} agent-configs: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest strategy: fail-fast: false matrix: @@ -392,6 +395,7 @@ jobs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + cache-provider: github - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -581,7 +585,7 @@ jobs: exit 1 env-test-matrix: - runs-on: ubuntu-latest + runs-on: depot-ubuntu-latest needs: [rust-only] if: needs.rust-only.outputs.only_rust == 'false' env: @@ -614,6 +618,7 @@ jobs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + cache-provider: github - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -654,7 +659,6 @@ jobs: uses: ./.github/actions/yarn-build-with-cache with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: foundry-install uses: foundry-rs/foundry-toolchain@v1 diff --git a/.gitignore b/.gitignore index 82e68e79dd2..26a42fdf9a6 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ solidity/artifacts .yarn/cache yarn-error.log +.turbo + .idea **/*.ignore diff --git a/Dockerfile b/Dockerfile index 09e82ef1d18..4084a79b5ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ COPY starknet/package.json ./starknet/ RUN yarn install && yarn cache clean # Copy everything else +COPY turbo.json ./ COPY tsconfig.json ./ COPY typescript ./typescript COPY solidity ./solidity diff --git a/package.json b/package.json index f8ddfc9ae65..2845d6f0ef2 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "prettier": "^3.5.3", "prettier-plugin-solidity": "^1.4.2", "syncpack": "^13.0.0", - "tsx": "^4.19.1" + "tsx": "^4.19.1", + "turbo": "^2.5.3" }, "dependencies": { "@changesets/cli": "^2.26.2" @@ -26,14 +27,14 @@ "private": true, "scripts": { "agent-configs": "yarn --cwd typescript/infra/ update-agent-config:mainnet3 && yarn --cwd typescript/infra/ update-agent-config:testnet4 && yarn prettier", - "build": "yarn workspaces foreach --all --parallel --topological-dev run build", - "clean": "yarn workspaces foreach --all --parallel run clean", - "prettier": "yarn workspaces foreach --since --parallel run prettier", - "lint": "yarn workspaces foreach --all --parallel run lint", - "test": "yarn workspaces foreach --all --parallel run test", - "test:ci": "yarn workspaces foreach --all --topological-dev run test:ci", - "coverage": "yarn workspaces foreach --all --parallel run coverage", - "version:prepare": "yarn changeset version && yarn workspaces foreach --all --parallel run version:update && yarn install --no-immutable", + "build": "turbo run build", + "clean": "turbo run clean", + "prettier": "turbo run prettier", + "lint": "turbo run lint", + "test": "turbo run test --continue", + "test:ci": "turbo run test:ci", + "coverage": "turbo run coverage", + "version:prepare": "yarn changeset version && turbo run version:update && yarn install --no-immutable", "version:check": "yarn changeset status", "release": "yarn build && yarn changeset publish", "postinstall": "husky install" diff --git a/solidity/eslint.config.mjs b/solidity/eslint.config.mjs index 44a7ba33fca..9bde7706e9f 100644 --- a/solidity/eslint.config.mjs +++ b/solidity/eslint.config.mjs @@ -6,6 +6,7 @@ export default [ ignores: [ '**/test/**/*', '**/dist/**/*', + '**/lib/**/*', '**/typechain/**/*', '.solcover.js', 'generate-artifact-exports.mjs', diff --git a/solidity/turbo.json b/solidity/turbo.json new file mode 100644 index 00000000000..372d01e57e8 --- /dev/null +++ b/solidity/turbo.json @@ -0,0 +1,14 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "outputs": [ + "artifacts/**", + "cache/**", + "core-utils/**", + "dist/**", + "types/**" + ] + } + } +} diff --git a/starknet/turbo.json b/starknet/turbo.json new file mode 100644 index 00000000000..5ec5222d675 --- /dev/null +++ b/starknet/turbo.json @@ -0,0 +1,8 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "outputs": ["dist/**", "release/**"] + } + } +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 00000000000..6a734130119 --- /dev/null +++ b/turbo.json @@ -0,0 +1,25 @@ +{ + "ui": "stream", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "cache/**", "types/**"] + }, + "test:ci": { + "dependsOn": ["build", "^test:ci"] + }, + "test": { + "dependsOn": ["build"] + }, + "clean": { + "cache": false + }, + "prettier": {}, + "lint": {}, + "version:update": {}, + "coverage": { + "dependsOn": ["build"], + "outputs": ["coverage/**", "fixtures/**"] + } + } +} diff --git a/typescript/helloworld/turbo.json b/typescript/helloworld/turbo.json new file mode 100644 index 00000000000..cf097a6d80b --- /dev/null +++ b/typescript/helloworld/turbo.json @@ -0,0 +1,8 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "outputs": ["artifacts/**", "cache/**", "dist/**", "src/types/**"] + } + } +} diff --git a/yarn.lock b/yarn.lock index d85bf67574f..11598676d8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7909,6 +7909,7 @@ __metadata: prettier-plugin-solidity: "npm:^1.4.2" syncpack: "npm:^13.0.0" tsx: "npm:^4.19.1" + turbo: "npm:^2.5.3" languageName: unknown linkType: soft @@ -36908,6 +36909,77 @@ __metadata: languageName: node linkType: hard +"turbo-darwin-64@npm:2.5.3": + version: 2.5.3 + resolution: "turbo-darwin-64@npm:2.5.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"turbo-darwin-arm64@npm:2.5.3": + version: 2.5.3 + resolution: "turbo-darwin-arm64@npm:2.5.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"turbo-linux-64@npm:2.5.3": + version: 2.5.3 + resolution: "turbo-linux-64@npm:2.5.3" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"turbo-linux-arm64@npm:2.5.3": + version: 2.5.3 + resolution: "turbo-linux-arm64@npm:2.5.3" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"turbo-windows-64@npm:2.5.3": + version: 2.5.3 + resolution: "turbo-windows-64@npm:2.5.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"turbo-windows-arm64@npm:2.5.3": + version: 2.5.3 + resolution: "turbo-windows-arm64@npm:2.5.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"turbo@npm:^2.5.3": + version: 2.5.3 + resolution: "turbo@npm:2.5.3" + dependencies: + turbo-darwin-64: "npm:2.5.3" + turbo-darwin-arm64: "npm:2.5.3" + turbo-linux-64: "npm:2.5.3" + turbo-linux-arm64: "npm:2.5.3" + turbo-windows-64: "npm:2.5.3" + turbo-windows-arm64: "npm:2.5.3" + dependenciesMeta: + turbo-darwin-64: + optional: true + turbo-darwin-arm64: + optional: true + turbo-linux-64: + optional: true + turbo-linux-arm64: + optional: true + turbo-windows-64: + optional: true + turbo-windows-arm64: + optional: true + bin: + turbo: bin/turbo + checksum: 10/7459eeae711992a23644dc658b40ca50b5f9a6db5a0f30d6663294ea509d18921b683060a5553771727e2c8d5c6381e0d95871c19cc869271be833109d06e8d0 + languageName: node + linkType: hard + "tween-functions@npm:^1.2.0": version: 1.2.0 resolution: "tween-functions@npm:1.2.0" From ac13dde47dbd656fcbdd1f004f7f202b6fb3fb1b Mon Sep 17 00:00:00 2001 From: Troy Kessler <43882936+troykessler@users.noreply.github.com> Date: Wed, 21 May 2025 15:34:03 +0200 Subject: [PATCH 211/223] chore: add cosmos native core cli commands (#6263) ### Description This PR finally adds support for Cosmos Native Core Init, Read, Deploy & Apply. Note that this is the fifth and last PR of https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6050 which is being split up in order to make it easier to review ### Drive-by changes - ### Related issues - ### Backward compatibility Yes ### Testing Manual Tests --- .changeset/ninety-keys-brush.md | 5 + typescript/cli/src/commands/core.ts | 11 +- typescript/cli/src/config/hooks.ts | 2 +- .../strategies/chain/MultiChainResolver.ts | 15 +- typescript/cli/src/deploy/core.ts | 189 +++++++++++++----- typescript/cli/src/read/core.ts | 62 ++++-- 6 files changed, 212 insertions(+), 72 deletions(-) create mode 100644 .changeset/ninety-keys-brush.md diff --git a/.changeset/ninety-keys-brush.md b/.changeset/ninety-keys-brush.md new file mode 100644 index 00000000000..5f7288345df --- /dev/null +++ b/.changeset/ninety-keys-brush.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +add cosmos native core cli commands diff --git a/typescript/cli/src/commands/core.ts b/typescript/cli/src/commands/core.ts index 51801d9db01..ff0d03743ac 100644 --- a/typescript/cli/src/commands/core.ts +++ b/typescript/cli/src/commands/core.ts @@ -13,6 +13,7 @@ import { createCoreDeployConfig, readCoreDeployConfigs, } from '../config/core.js'; +import { MultiProtocolSignerManager } from '../context/strategies/signer/MultiProtocolSignerManager.js'; import { CommandModuleWithContext, CommandModuleWithWriteContext, @@ -104,6 +105,7 @@ export const deploy: CommandModuleWithWriteContext<{ config: string; dryRun: string; fromAddress: string; + multiProtocolSigner?: MultiProtocolSignerManager; }> = { command: 'deploy', describe: 'Deploy Hyperlane contracts', @@ -117,7 +119,13 @@ export const deploy: CommandModuleWithWriteContext<{ 'dry-run': dryRunCommandOption, 'from-address': fromAddressCommandOption, }, - handler: async ({ context, chain, config: configFilePath, dryRun }) => { + handler: async ({ + context, + chain, + config: configFilePath, + dryRun, + multiProtocolSigner, + }) => { logCommandHeader(`Hyperlane Core deployment${dryRun ? ' dry-run' : ''}`); try { @@ -125,6 +133,7 @@ export const deploy: CommandModuleWithWriteContext<{ context, chain, config: readYamlOrJson(configFilePath), + multiProtocolSigner, }); } catch (error: any) { evaluateIfDryRunFailure(error, dryRun); diff --git a/typescript/cli/src/config/hooks.ts b/typescript/cli/src/config/hooks.ts index a66822f95a1..ada9680bc3e 100644 --- a/typescript/cli/src/config/hooks.ts +++ b/typescript/cli/src/config/hooks.ts @@ -309,7 +309,7 @@ async function getIgpTokenPrices( chainMetadata: filteredMetadata, }); const results = await tokenPriceGetter.getAllTokenPrices(); - fetchedPrices = objMap(results, (v) => v.toString()); + fetchedPrices = objMap(results, (_, v) => v.toString()); } logBlue( diff --git a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts index f2305dc908a..5dd191728bc 100644 --- a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts +++ b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts @@ -146,9 +146,12 @@ export class MultiChainResolver implements ChainResolver { return Array.from(new Set([...chains, ...additionalChains])); } - // If no destination is specified, return all EVM chains + // If no destination is specified, return all EVM and Cosmos Native chains if (!argv.destination) { - return Array.from(this.getEvmChains(multiProvider)); + return [ + ...this.getEvmChains(multiProvider), + ...this.getCosmosNativeChains(multiProvider), + ]; } chains.add(argv.destination); @@ -198,6 +201,14 @@ export class MultiChainResolver implements ChainResolver { ); } + private getCosmosNativeChains(multiProvider: MultiProvider): ChainName[] { + const chains = multiProvider.getKnownChainNames(); + + return chains.filter( + (chain) => multiProvider.getProtocol(chain) === ProtocolType.CosmosNative, + ); + } + static forAgentKurtosis(): MultiChainResolver { return new MultiChainResolver(ChainSelectionMode.AGENT_KURTOSIS); } diff --git a/typescript/cli/src/deploy/core.ts b/typescript/cli/src/deploy/core.ts index 7ce8a0247c7..ad1dc0f5a0a 100644 --- a/typescript/cli/src/deploy/core.ts +++ b/typescript/cli/src/deploy/core.ts @@ -1,18 +1,22 @@ import { stringify as yamlStringify } from 'yaml'; import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; +import { ChainAddresses } from '@hyperlane-xyz/registry'; import { ChainMap, ChainName, ContractVerifier, CoreConfig, + CosmosNativeCoreModule, DeployedCoreAddresses, EvmCoreModule, ExplorerLicenseType, } from '@hyperlane-xyz/sdk'; +import { ProtocolType, assert } from '@hyperlane-xyz/utils'; import { MINIMUM_CORE_DEPLOY_GAS } from '../consts.js'; import { requestAndSaveApiKeys } from '../context/context.js'; +import { MultiProtocolSignerManager } from '../context/strategies/signer/MultiProtocolSignerManager.js'; import { WriteCommandContext } from '../context/types.js'; import { log, logBlue, logGray, logGreen } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; @@ -29,6 +33,7 @@ interface DeployParams { context: WriteCommandContext; chain: ChainName; config: CoreConfig; + multiProtocolSigner?: MultiProtocolSignerManager; } interface ApplyParams extends DeployParams { @@ -41,7 +46,6 @@ interface ApplyParams extends DeployParams { export async function runCoreDeploy(params: DeployParams) { const { context, config } = params; let chain = params.chain; - const { isDryRun, chainMetadata, @@ -49,6 +53,7 @@ export async function runCoreDeploy(params: DeployParams) { registry, skipConfirmation, multiProvider, + multiProtocolSigner, } = context; // Select a dry-run chain if it's not supplied @@ -65,42 +70,75 @@ export async function runCoreDeploy(params: DeployParams) { if (!skipConfirmation) apiKeys = await requestAndSaveApiKeys([chain], chainMetadata, registry); - const signer = multiProvider.getSigner(chain); - const deploymentParams: DeployParams = { - context: { ...context, signer }, + context: { ...context }, chain, config, }; - await runDeployPlanStep(deploymentParams); - await runPreflightChecksForChains({ - ...deploymentParams, - chains: [chain], - minGas: MINIMUM_CORE_DEPLOY_GAS, - }); - - const userAddress = await signer.getAddress(); - - const initialBalances = await prepareDeploy(context, userAddress, [chain]); - - const contractVerifier = new ContractVerifier( - multiProvider, - apiKeys, - coreBuildArtifact, - ExplorerLicenseType.MIT, - ); - - logBlue('🚀 All systems ready, captain! Beginning deployment...'); - const evmCoreModule = await EvmCoreModule.create({ - chain, - config, - multiProvider, - contractVerifier, - }); - - await completeDeploy(context, 'core', initialBalances, userAddress, [chain]); - const deployedAddresses = evmCoreModule.serialize(); + let deployedAddresses: ChainAddresses; + switch (multiProvider.getProtocol(chain)) { + case ProtocolType.Ethereum: + { + const signer = multiProvider.getSigner(chain); + await runDeployPlanStep(deploymentParams); + + await runPreflightChecksForChains({ + ...deploymentParams, + chains: [chain], + minGas: MINIMUM_CORE_DEPLOY_GAS, + }); + + const userAddress = await signer.getAddress(); + + const initialBalances = await prepareDeploy(context, userAddress, [ + chain, + ]); + + const contractVerifier = new ContractVerifier( + multiProvider, + apiKeys, + coreBuildArtifact, + ExplorerLicenseType.MIT, + ); + + logBlue('🚀 All systems ready, captain! Beginning deployment...'); + const evmCoreModule = await EvmCoreModule.create({ + chain, + config, + multiProvider, + contractVerifier, + }); + + await completeDeploy(context, 'core', initialBalances, userAddress, [ + chain, + ]); + deployedAddresses = evmCoreModule.serialize(); + } + break; + + case ProtocolType.CosmosNative: + { + const signer = + multiProtocolSigner?.getCosmosNativeSigner(chain) ?? null; + assert(signer, 'Cosmos Native signer failed!'); + + logBlue('🚀 All systems ready, captain! Beginning deployment...'); + + const cosmosNativeCoreModule = await CosmosNativeCoreModule.create({ + chain, + config, + multiProvider, + signer, + }); + + deployedAddresses = cosmosNativeCoreModule.serialize(); + } + break; + + default: + throw new Error('Chain protocol is not supported yet!'); + } if (!isDryRun) { await registry.updateChain({ @@ -115,29 +153,72 @@ export async function runCoreDeploy(params: DeployParams) { export async function runCoreApply(params: ApplyParams) { const { context, chain, deployedCoreAddresses, config } = params; - const { multiProvider } = context; - const evmCoreModule = new EvmCoreModule(multiProvider, { - chain, - config, - addresses: deployedCoreAddresses, - }); - - const transactions = await evmCoreModule.update(config); - - if (transactions.length) { - logGray('Updating deployed core contracts'); - for (const transaction of transactions) { - await multiProvider.sendTransaction( - // Using the provided chain id because there might be remote chain transactions included in the batch - transaction.chainId ?? chain, - transaction, - ); + const { multiProvider, multiProtocolSigner } = context; + + switch (multiProvider.getProtocol(chain)) { + case ProtocolType.Ethereum: { + const evmCoreModule = new EvmCoreModule(multiProvider, { + chain, + config, + addresses: deployedCoreAddresses, + }); + + const transactions = await evmCoreModule.update(config); + + if (transactions.length) { + logGray('Updating deployed core contracts'); + for (const transaction of transactions) { + await multiProvider.sendTransaction( + // Using the provided chain id because there might be remote chain transactions included in the batch + transaction.chainId ?? chain, + transaction, + ); + } + + logGreen(`Core config updated on ${chain}.`); + } else { + logGreen( + `Core config on ${chain} is the same as target. No updates needed.`, + ); + } + break; } + case ProtocolType.CosmosNative: { + const signer = multiProtocolSigner?.getCosmosNativeSigner(chain) ?? null; + assert(signer, 'Cosmos Native signer failed!'); + + const cosmosNativeCoreModule = new CosmosNativeCoreModule( + multiProvider, + signer, + { + chain, + config, + addresses: deployedCoreAddresses, + }, + ); - logGreen(`Core config updated on ${chain}.`); - } else { - logGreen( - `Core config on ${chain} is the same as target. No updates needed.`, - ); + const transactions = await cosmosNativeCoreModule.update(config); + + if (transactions.length) { + logGray('Updating deployed core contracts'); + const response = await signer.signAndBroadcast( + signer.account.address, + transactions, + 2, + ); + + assert( + response.code === 0, + `Transaction failed with status code ${response.code}`, + ); + + logGreen(`Core config updated on ${chain}.`); + } else { + logGreen( + `Core config on ${chain} is the same as target. No updates needed.`, + ); + } + break; + } } } diff --git a/typescript/cli/src/read/core.ts b/typescript/cli/src/read/core.ts index 76daa8e9189..e0c845a7542 100644 --- a/typescript/cli/src/read/core.ts +++ b/typescript/cli/src/read/core.ts @@ -1,5 +1,10 @@ -import { ChainName, CoreConfig, EvmCoreReader } from '@hyperlane-xyz/sdk'; -import { Address, assert } from '@hyperlane-xyz/utils'; +import { + ChainName, + CoreConfig, + CosmosNativeCoreReader, + EvmCoreReader, +} from '@hyperlane-xyz/sdk'; +import { Address, ProtocolType, assert } from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; import { errorRed } from '../logger.js'; @@ -23,17 +28,46 @@ export async function executeCoreRead({ ); } - const evmCoreReader = new EvmCoreReader(context.multiProvider, chain); - try { - return evmCoreReader.deriveCoreConfig({ - mailbox, - interchainAccountRouter: addresses?.interchainAccountRouter, - }); - } catch (e: any) { - errorRed( - `❌ Failed to read core config for mailbox ${mailbox} on ${chain}:`, - e, - ); - process.exit(1); + const protocolType = context.multiProvider.getProtocol(chain); + + switch (protocolType) { + case ProtocolType.Ethereum: { + const evmCoreReader = new EvmCoreReader(context.multiProvider, chain); + try { + return evmCoreReader.deriveCoreConfig({ + mailbox, + interchainAccountRouter: addresses?.interchainAccountRouter, + }); + } catch (e: any) { + errorRed( + `❌ Failed to read core config for mailbox ${mailbox} on ${chain}:`, + e, + ); + process.exit(1); + } + break; + } + case ProtocolType.CosmosNative: { + const cosmosProvider = + await context.multiProtocolProvider!.getCosmJsNativeProvider(chain); + const cosmosCoreReader = new CosmosNativeCoreReader( + context.multiProvider, + cosmosProvider, + ); + try { + return cosmosCoreReader.deriveCoreConfig(mailbox); + } catch (e: any) { + errorRed( + `❌ Failed to read core config for mailbox ${mailbox} on ${chain}:`, + e, + ); + process.exit(1); + } + break; + } + default: { + errorRed(`❌ Core Read not supported for protocol type ${protocolType}:`); + process.exit(1); + } } } From 33f88c25cb2c4b07a4760a26c679e0760ab8d6a3 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 21 May 2025 14:46:23 +0100 Subject: [PATCH 212/223] feat(relayer): optimize ccip-read initialization (#6299) ### Description https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/6144 made relayer initialization very slow. It creates a signing provider within nested for loops, spending quadratic time (based on chain count) to create message contexts. At 110 chains and 200ms per network call that's ~40 mins (`110*110*200/(1000*60)`). since the origin and destination chains are identical in the relayer, the first commit optimizes this by only initializing the provider in the upper for-loop, making it linear. This got us down to ~30s of initialization overhead added by the ccip read initialization. the second commit moves out the provider building out of the for loops and just awaits the futures in parallel. AWS KMS rate limits shouldn't be a concern since it's just `chain_count` calls. After releasing to RC, the initialization overhead dropped to near zero see the three logs [here](https://cloudlogging.app.goo.gl/H7LmsVr5hgzJSSdk7) for performance comparison: - first log is without the CCIP-read feature (30 sec baseline) - second log is with linear provider building (60 sec, so 30s overhead) - third log is with the parallelized provider building (30 sec, back to the baseline) ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/agents/relayer/src/relayer.rs | 49 +++++++++++++++++-------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index ac07f0e4fa5..93abbd0541e 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -8,6 +8,7 @@ use std::{ use async_trait::async_trait; use derive_more::AsRef; use eyre::Result; +use futures::future::join_all; use futures_util::future::try_join_all; use tokio::{ sync::{ @@ -31,8 +32,8 @@ use hyperlane_base::{ }; use hyperlane_core::{ rpc_clients::call_and_retry_n_times, ChainCommunicationError, ChainResult, ContractSyncCursor, - HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, Mailbox, MerkleTreeInsertion, - QueueOperation, SubmitterType, ValidatorAnnounce, H512, U256, + HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, InterchainGasPayment, Mailbox, + MerkleTreeInsertion, QueueOperation, SubmitterType, ValidatorAnnounce, H512, U256, }; use hyperlane_operation_verifier::ApplicationOperationVerifier; use submitter::{ @@ -329,6 +330,35 @@ impl BaseAgent for Relayer { // only iterate through destination chains that were successfully instantiated start_entity_init = Instant::now(); + let ccip_signer_futures = mailboxes + .keys() + .map(|destination| { + let destination_chain_setup = + core.settings.chain_setup(destination).unwrap().clone(); + let signer = destination_chain_setup.signer.clone(); + async move { + if !matches!(destination.domain_protocol(), HyperlaneDomainProtocol::Ethereum) { + return (destination, None); + } + let signer = if let Some(builder) = signer { + match builder.build::().await { + Ok(signer) => Some(signer), + Err(err) => { + warn!(error = ?err, "Failed to build Ethereum signer for CCIP-read ISM. "); + None + } + } + } else { + None + }; + (destination, signer) + } + }) + .collect::>(); + let ccip_signers = join_all(ccip_signer_futures) + .await + .into_iter() + .collect::>(); for (destination, dest_mailbox) in mailboxes.iter() { let destination_chain_setup = core.settings.chain_setup(destination).unwrap().clone(); destination_chains.insert(destination.clone(), destination_chain_setup.clone()); @@ -346,19 +376,6 @@ impl BaseAgent for Relayer { let db = dbs.get(origin).unwrap().clone(); let default_ism_getter = DefaultIsmCache::new(dest_mailbox.clone()); // Extract optional Ethereum signer for CCIP-read authentication - let eth_signer: Option = if let Some(builder) = - &destination_chain_setup.signer - { - match builder.build().await { - Ok(s) => Some(s), - Err(err) => { - warn!(error = ?err, "Failed to build Ethereum signer for CCIP-read ISM"); - None - } - } - } else { - None - }; let metadata_builder = BaseMetadataBuilder::new( origin.clone(), destination_chain_setup.clone(), @@ -376,7 +393,7 @@ impl BaseAgent for Relayer { default_ism_getter.clone(), settings.ism_cache_configs.clone(), ), - eth_signer, + ccip_signers.get(destination).cloned().flatten(), ); msg_ctxs.insert( From 0c4037ffcfd31a72d322eccf5a1cc33f918fe13b Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 21 May 2025 14:50:26 +0100 Subject: [PATCH 213/223] fix: require ssl for connection (#6285) ### Description require ssl for connection ### Testing Manual --- .../infra/scripts/funding/calculate-relayer-daily-burn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts b/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts index af32de95330..ecaae4392b1 100644 --- a/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts +++ b/typescript/infra/scripts/funding/calculate-relayer-daily-burn.ts @@ -83,7 +83,7 @@ async function getReadOnlyScraperDb() { const credentialsUrl = await fetchLatestGCPSecret( SCRAPER_READ_ONLY_DB_SECRET_NAME, ); - return postgres(credentialsUrl); + return postgres(credentialsUrl, { ssl: 'require' }); } async function getDailyRelayerBurnScraperDB( From 61cc9638621f7db357f36bff903f9e927e15cd0f Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 21 May 2025 14:52:42 +0100 Subject: [PATCH 214/223] fix: priority gas price overpayments (#6295) ### Description Applies changes from https://github.com/hyperlane-xyz/ethers-rs/pull/35 - tested on Base and we now pay 80x less --- rust/main/Cargo.lock | 20 ++++++++++---------- rust/main/Cargo.toml | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 94c3cfb0e01..7840fb14dfd 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -3604,7 +3604,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3618,7 +3618,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "ethers-core", "once_cell", @@ -3629,7 +3629,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -3647,7 +3647,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "Inflector", "cfg-if", @@ -3671,7 +3671,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -3685,7 +3685,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "arrayvec", "bytes", @@ -3715,7 +3715,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3731,7 +3731,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3782,7 +3782,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3818,7 +3818,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-07#2716287694ab2cbfa472be8fc3d26344a176f1b0" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2025-05-21#f0440ebedff16b68ef2d7d6faab9e18e1f364e0f" dependencies = [ "async-trait", "coins-bip32 0.7.0", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 612f053953a..e4cc478d1c5 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -220,27 +220,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-07" +tag = "2025-05-21" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-07" +tag = "2025-05-21" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-07" +tag = "2025-05-21" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-07" +tag = "2025-05-21" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2025-05-07" +tag = "2025-05-21" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" From 5840ad1e4e933816dba9ee542e7fcbc4ac4f6bf7 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 21 May 2025 14:59:32 +0100 Subject: [PATCH 215/223] chore: update monitor image to include new token standard handling (#6242) ### Description - update monitor image to include new token standard handling --- typescript/infra/src/warp/helm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 11786b95587..9db0824470f 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -73,7 +73,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'a015285-20250516-204210', + tag: '7aae0ec-20250521-103450', }, warpRouteId: this.warpRouteId, fullnameOverride: this.helmReleaseName, From db5744ee8bd19392bf39da00c4dee1085a0d95ac Mon Sep 17 00:00:00 2001 From: Christopher Brumm <97845034+christopherbrumm@users.noreply.github.com> Date: Wed, 21 May 2025 15:59:58 +0200 Subject: [PATCH 216/223] fix: token metadata handling (#6175) ### Description This PR fixes the token metadata handling by changing the return type of `deriveTokenMetadata` to TokenMetadataMap`, enabling support for multiple different token metadata. ### Drive-by changes - `assertWarpRouteConfig` expects the mailbox address to verify the correct deployed mailbox. - remove `./test-configs/anvil/deployments` before running e2e-tests ### Backward compatibility Yes ### Testing Added E2E test to verify that for `collateralFiat` and `collateral` with two different metadata, token is deployed with two different metadata. --------- Co-authored-by: Morteza Shojaei --- typescript/cli/scripts/run-e2e-test.sh | 1 + typescript/cli/src/config/warp.ts | 9 +- typescript/cli/src/deploy/warp.ts | 36 +-- typescript/cli/src/tests/commands/helpers.ts | 8 +- .../cli/src/tests/warp/warp-bridge-utils.ts | 2 + .../src/tests/warp/warp-deploy.e2e-test.ts | 208 +++++++++++++++++- typescript/sdk/src/index.ts | 2 +- typescript/sdk/src/token/TokenMetadataMap.ts | 92 ++++++++ typescript/sdk/src/token/configUtils.ts | 12 +- typescript/sdk/src/token/deploy.ts | 69 ++++-- 10 files changed, 382 insertions(+), 57 deletions(-) create mode 100644 typescript/sdk/src/token/TokenMetadataMap.ts diff --git a/typescript/cli/scripts/run-e2e-test.sh b/typescript/cli/scripts/run-e2e-test.sh index c98fc9158f6..bbeaed19fab 100755 --- a/typescript/cli/scripts/run-e2e-test.sh +++ b/typescript/cli/scripts/run-e2e-test.sh @@ -4,6 +4,7 @@ function cleanup() { set +e pkill -f anvil rm -rf ./tmp + rm -rf ./test-configs/anvil/deployments rm -f ./test-configs/anvil/chains/anvil2/addresses.yaml rm -f ./test-configs/anvil/chains/anvil3/addresses.yaml set -e diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 13fb3bdfa5e..7610ea18258 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -274,12 +274,11 @@ export async function createWarpRouteDeployConfig({ context.multiProvider, warpRouteDeployConfig, ); - assert( - tokenMetadata?.symbol, - 'Error deriving token metadata, please check the provided token addresses', - ); + + const symbol: string = tokenMetadata.getDefaultSymbol(); + await context.registry.addWarpRouteConfig(warpRouteDeployConfig, { - symbol: tokenMetadata.symbol, + symbol: symbol, }); } logGreen('✅ Successfully created new warp route deployment config.'); diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index bdbe53cce31..fb75adab64e 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -28,6 +28,7 @@ import { SubmissionStrategy, TOKEN_TYPE_TO_STANDARD, TokenFactories, + TokenMetadataMap, TrustedRelayerIsmConfig, TxSubmitterBuilder, TxSubmitterType, @@ -44,7 +45,6 @@ import { getTokenConnectionId, hypERC20factories, isCollateralTokenConfig, - isTokenMetadata, isXERC20TokenConfig, splitWarpCoreAndExtendedConfigs, } from '@hyperlane-xyz/sdk'; @@ -193,28 +193,23 @@ async function getWarpCoreConfig( const warpCoreConfig: WarpCoreConfig = { tokens: [] }; // TODO: replace with warp read - const tokenMetadata = await HypERC20Deployer.deriveTokenMetadata( - params.context.multiProvider, - params.warpDeployConfig, - ); - assert( - tokenMetadata && isTokenMetadata(tokenMetadata), - 'Missing required token metadata', - ); - const { decimals, symbol, name } = tokenMetadata; - assert(decimals, 'Missing decimals on token metadata'); + const tokenMetadataMap: TokenMetadataMap = + await HypERC20Deployer.deriveTokenMetadata( + params.context.multiProvider, + params.warpDeployConfig, + ); generateTokenConfigs( warpCoreConfig, params.warpDeployConfig, contracts, - symbol, - name, - decimals, + tokenMetadataMap, ); fullyConnectTokens(warpCoreConfig); + const symbol = tokenMetadataMap.getDefaultSymbol(); + return { warpCoreConfig, addWarpRouteOptions: { symbol } }; } @@ -225,9 +220,7 @@ function generateTokenConfigs( warpCoreConfig: WarpCoreConfig, warpDeployConfig: WarpRouteDeployConfigMailboxRequired, contracts: HyperlaneContractsMap, - symbol: string, - name: string, - decimals: number, + tokenMetadataMap: TokenMetadataMap, ): void { for (const [chainName, contract] of Object.entries(contracts)) { const config = warpDeployConfig[chainName]; @@ -236,6 +229,13 @@ function generateTokenConfigs( ? config.token // gets set in the above deriveTokenMetadata() : undefined; + const decimals: number | undefined = + tokenMetadataMap.getDecimals(chainName); + const name: any = tokenMetadataMap.getName(chainName); + const symbol: any = tokenMetadataMap.getSymbol(chainName); + + assert(decimals, `Decimals for ${chainName} doesn't exist`); + warpCoreConfig.tokens.push({ chainName, standard: TOKEN_TYPE_TO_STANDARD[config.type], @@ -513,7 +513,7 @@ async function deriveMetadataFromExisting( return objMap(extendedConfigs, (_chain, extendedConfig) => { return { - ...existingTokenMetadata, + ...existingTokenMetadata.getMetadataForChain(_chain), ...extendedConfig, }; }); diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 1b6fd7fe545..03e40cf02ce 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -374,6 +374,7 @@ export async function deployToken( chain: string, decimals = 18, symbol = 'TOKEN', + name = 'token', ): Promise { const { multiProvider } = await getContext({ registryUris: [REGISTRY_PATH], @@ -385,12 +386,7 @@ export async function deployToken( const token = await new ERC20Test__factory( multiProvider.getSigner(chain), - ).deploy( - 'token', - symbol.toLocaleUpperCase(), - '100000000000000000000', - decimals, - ); + ).deploy(name, symbol.toLocaleUpperCase(), '100000000000000000000', decimals); await token.deployed(); return token; diff --git a/typescript/cli/src/tests/warp/warp-bridge-utils.ts b/typescript/cli/src/tests/warp/warp-bridge-utils.ts index c636865759a..ec28e969afe 100644 --- a/typescript/cli/src/tests/warp/warp-bridge-utils.ts +++ b/typescript/cli/src/tests/warp/warp-bridge-utils.ts @@ -206,6 +206,8 @@ export function getTokenSymbolFromDeployment( let symbol: string; if (warpConfig[CHAIN_NAME_2].type.match(/.*vault.*/i)) { symbol = tokenVaultChain2Symbol; + } else if (warpConfig[CHAIN_NAME_3].type.match(/.*native.*/i)) { + symbol = 'ETH'; } else if (warpConfig[CHAIN_NAME_2].type.match(/.*collateral.*/i)) { symbol = tokenChain2Symbol; } else if (warpConfig[CHAIN_NAME_3].type.match(/.*vault.*/i)) { diff --git a/typescript/cli/src/tests/warp/warp-deploy.e2e-test.ts b/typescript/cli/src/tests/warp/warp-deploy.e2e-test.ts index b32b2f1cc87..10931c966fa 100644 --- a/typescript/cli/src/tests/warp/warp-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/warp/warp-deploy.e2e-test.ts @@ -77,6 +77,7 @@ describe('hyperlane warp deploy e2e tests', async function () { warpCoreConfigPath: string, chainName: ChainName, expectedMetadata: { decimals: number; symbol: string }, + expectedMailboxAddress: string, ): Promise { const currentWarpDeployConfig = await readWarpConfig( chainName, @@ -94,7 +95,7 @@ describe('hyperlane warp deploy e2e tests', async function () { warpDeployConfig[chainName].symbol ?? expectedMetadata.symbol, ); expect(currentWarpDeployConfig[chainName].mailbox).to.equal( - chain2Addresses.mailbox, + expectedMailboxAddress, ); } @@ -137,6 +138,82 @@ describe('hyperlane warp deploy e2e tests', async function () { ).to.be.true; }); + it(`should exit early when the provided scale is incorrect`, async function () { + const tokenFiat = await deployToken( + ANVIL_KEY, + CHAIN_NAME_2, + 9, + 'TOKEN.E', + 'FIAT TOKEN', + ); + const token = await deployToken( + ANVIL_KEY, + CHAIN_NAME_3, + 18, + 'TOKEN', + 'TOKEN', + ); + + const warpConfig: WarpRouteDeployConfig = { + [CHAIN_NAME_2]: { + type: TokenType.collateralFiat, + token: tokenFiat.address, + mailbox: chain2Addresses.mailbox, + owner: ownerAddress, + decimals: 9, + scale: 1, + }, + [CHAIN_NAME_3]: { + type: TokenType.collateral, + token: token.address, + mailbox: chain3Addresses.mailbox, + owner: ownerAddress, + decimals: 18, + scale: 5, + }, + }; + + writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); + + const steps: TestPromptAction[] = [ + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Is this deployment plan correct?'), + input: KeyBoardKeys.ENTER, + }, + ]; + + // Deploy + const output = hyperlaneWarpDeployRaw({ + warpCorePath: WARP_DEPLOY_OUTPUT_PATH, + }) + .stdio('pipe') + .nothrow(); + + const finalOutput = await handlePrompts(output, steps); + + // Assertions + expect(finalOutput.exitCode).to.equal(1); + + expect( + finalOutput + .text() + .includes( + `Failed to derive token metadata Error: Scale is not correct for ${CHAIN_NAME_3}`, + ), + ).to.be.true; + }); + it(`should successfully deploy a ${TokenType.collateral} -> ${TokenType.synthetic} warp route`, async function () { const token = await deployToken(ANVIL_KEY, CHAIN_NAME_2); @@ -195,14 +272,137 @@ describe('hyperlane warp deploy e2e tests', async function () { // Assertions expect(finalOutput.exitCode).to.equal(0); for (const chainName of [CHAIN_NAME_2, CHAIN_NAME_3]) { + const mailboxAddress = + chainName === CHAIN_NAME_3 + ? chain3Addresses.mailbox + : chain2Addresses.mailbox; + await assertWarpRouteConfig( warpConfig, COMBINED_WARP_CORE_CONFIG_PATH, chainName, { decimals: expectedTokenDecimals, symbol: expectedTokenSymbol }, + mailboxAddress, ); } }); + + it(`should successfully deploy a ${TokenType.collateralFiat} -> ${TokenType.collateral} warp route`, async function () { + const tokenFiat = await deployToken( + ANVIL_KEY, + CHAIN_NAME_2, + 9, + 'TOKEN.E', + 'FIAT TOKEN', + ); + const token = await deployToken( + ANVIL_KEY, + CHAIN_NAME_3, + 9, + 'TOKEN', + 'TOKEN', + ); + + const [ + expectedTokenSymbol, + expectedTokenDecimals, + expectedCollateralFiatTokenSymbol, + ] = await Promise.all([ + token.symbol(), + tokenFiat.decimals(), + tokenFiat.symbol(), + ]); + + const COMBINED_WARP_CORE_CONFIG_PATH = getCombinedWarpRoutePath( + expectedTokenSymbol, + [CHAIN_NAME_2, CHAIN_NAME_3], + ); + + const warpConfig: WarpRouteDeployConfig = { + [CHAIN_NAME_2]: { + type: TokenType.collateralFiat, + token: tokenFiat.address, + mailbox: chain2Addresses.mailbox, + owner: ownerAddress, + }, + [CHAIN_NAME_3]: { + type: TokenType.collateral, + token: token.address, + mailbox: chain3Addresses.mailbox, + owner: ownerAddress, + }, + }; + + writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); + + const steps: TestPromptAction[] = [ + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Is this deployment plan correct?'), + input: KeyBoardKeys.ENTER, + }, + ]; + + // Deploy + const output = hyperlaneWarpDeployRaw({ + warpCorePath: WARP_DEPLOY_OUTPUT_PATH, + }) + .stdio('pipe') + .nothrow(); + + const finalOutput = await handlePrompts(output, steps); + + // Assertions + expect(finalOutput.exitCode).to.equal(0); + + const collateralFiatWarpDeployConfig = await readWarpConfig( + CHAIN_NAME_2, + COMBINED_WARP_CORE_CONFIG_PATH, + WARP_DEPLOY_OUTPUT_PATH, + ); + + const collateralWarpDeployConfig = await readWarpConfig( + CHAIN_NAME_3, + COMBINED_WARP_CORE_CONFIG_PATH, + WARP_DEPLOY_OUTPUT_PATH, + ); + + // Used collateral type to deploy, which is why this check is skipped + // expect(collateralFiatWarpDeployConfig[CHAIN_NAME_2].type).to.equal( + // warpConfig[CHAIN_NAME_2].type, + // ); + expect(collateralWarpDeployConfig[CHAIN_NAME_3].type).to.equal( + warpConfig[CHAIN_NAME_3].type, + ); + expect(collateralFiatWarpDeployConfig[CHAIN_NAME_2].decimals).to.equal( + warpConfig[CHAIN_NAME_2].decimals ?? expectedTokenDecimals, + ); + expect(collateralWarpDeployConfig[CHAIN_NAME_3].decimals).to.equal( + warpConfig[CHAIN_NAME_3].decimals ?? expectedTokenDecimals, + ); + expect(collateralFiatWarpDeployConfig[CHAIN_NAME_2].symbol).to.equal( + warpConfig[CHAIN_NAME_2].symbol ?? expectedCollateralFiatTokenSymbol, + ); + expect(collateralWarpDeployConfig[CHAIN_NAME_3].symbol).to.equal( + warpConfig[CHAIN_NAME_3].symbol ?? expectedTokenSymbol, + ); + expect(collateralFiatWarpDeployConfig[CHAIN_NAME_2].mailbox).to.equal( + chain2Addresses.mailbox, + ); + expect(collateralWarpDeployConfig[CHAIN_NAME_3].mailbox).to.equal( + chain3Addresses.mailbox, + ); + }); }); describe('hyperlane warp deploy --config ... --yes', () => { @@ -296,11 +496,17 @@ describe('hyperlane warp deploy e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); for (const chainName of [CHAIN_NAME_2, CHAIN_NAME_3]) { + const mailboxAddress = + chainName === CHAIN_NAME_3 + ? chain3Addresses.mailbox + : chain2Addresses.mailbox; + await assertWarpRouteConfig( warpConfig, COMBINED_WARP_CORE_CONFIG_PATH, chainName, { decimals: expectedTokenDecimals, symbol: expectedTokenSymbol }, + mailboxAddress, ); } }); diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 97f3e5d6d87..4ec988fa770 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -719,7 +719,7 @@ export { CCIPContractCache, } from './ccip/utils.js'; export { HyperlaneCCIPDeployer } from './ccip/HyperlaneCCIPDeployer.js'; - +export { TokenMetadataMap } from './token/TokenMetadataMap.js'; export { StarknetContractName, getStarknetContract, diff --git a/typescript/sdk/src/token/TokenMetadataMap.ts b/typescript/sdk/src/token/TokenMetadataMap.ts new file mode 100644 index 00000000000..5f456fa027d --- /dev/null +++ b/typescript/sdk/src/token/TokenMetadataMap.ts @@ -0,0 +1,92 @@ +import { assert } from '@hyperlane-xyz/utils'; + +import { TokenMetadata } from './types.js'; + +export class TokenMetadataMap { + private tokenMetadataMap: Map; + + constructor() { + this.tokenMetadataMap = new Map(); + } + + set(chain: string, metadata: TokenMetadata): void { + this.tokenMetadataMap.set(chain, metadata); + } + + getDecimals(chain: string): number | undefined { + const config = this.tokenMetadataMap.get(chain); + if (config) return config.decimals!; + return [...this.tokenMetadataMap.values()].find( + (config) => config?.decimals, + )?.decimals; + } + + getMetadataForChain(chain: string): TokenMetadata | undefined { + return this.tokenMetadataMap.get(chain); + } + + getName(chain: string): string | undefined { + const config = this.tokenMetadataMap.get(chain); + if (config?.name) return config.name; + + for (const [, meta] of this.tokenMetadataMap) { + if (meta.name) return meta.name; + } + return undefined; + } + + getScale(chain: string): number | undefined { + return this.tokenMetadataMap.get(chain)?.scale; + } + + getSymbol(chain: string): string { + const symbol = this.tokenMetadataMap.get(chain)?.symbol; + if (symbol) return symbol; + + return this.getDefaultSymbol(); + } + + getDefaultSymbol(): string { + for (const [, metadata] of this.tokenMetadataMap) { + if (metadata.symbol) return metadata.symbol; + } + throw new Error('No symbol found in token metadata map.'); + } + + areDecimalsUniform(): boolean { + const values = [...this.tokenMetadataMap.values()]; + const [first, ...rest] = values; + for (const d of rest) { + if (d.decimals !== first.decimals) { + return false; + } + } + return true; + } + + finalize(): void { + assert( + [...this.tokenMetadataMap.values()].every((config) => !!config.decimals), + 'All decimals must be defined', + ); + + if (!this.areDecimalsUniform()) { + const maxDecimals = Math.max( + ...[...this.tokenMetadataMap.values()].map( + (config) => config.decimals!, + ), + ); + + for (const [chain, config] of this.tokenMetadataMap.entries()) { + if (config.decimals) { + const scale = 10 ** (maxDecimals - config.decimals); + assert( + config.scale && scale !== config.scale, + `Scale is not correct for ${chain}`, + ); + config.scale = scale; + } + } + } + } +} diff --git a/typescript/sdk/src/token/configUtils.ts b/typescript/sdk/src/token/configUtils.ts index 1d876f90ff0..5f113295d8b 100644 --- a/typescript/sdk/src/token/configUtils.ts +++ b/typescript/sdk/src/token/configUtils.ts @@ -24,6 +24,7 @@ import { ChainMap } from '../types.js'; import { WarpCoreConfig } from '../warp/types.js'; import { EvmERC20WarpRouteReader } from './EvmERC20WarpRouteReader.js'; +import { TokenMetadataMap } from './TokenMetadataMap.js'; import { gasOverhead } from './config.js'; import { HypERC20Deployer } from './deploy.js'; import { @@ -116,10 +117,8 @@ export async function expandWarpDeployConfig(params: { expandedOnChainWarpConfig, } = params; - const derivedTokenMetadata = await HypERC20Deployer.deriveTokenMetadata( - multiProvider, - warpDeployConfig, - ); + const derivedTokenMetadata: TokenMetadataMap = + await HypERC20Deployer.deriveTokenMetadata(multiProvider, warpDeployConfig); // If the token is on an EVM chain check if it is deployed as a proxy // to expand the proxy config too @@ -146,7 +145,10 @@ export async function expandWarpDeployConfig(params: { const chainConfig: WarpRouteDeployConfigMailboxRequired[string] & Partial = { // Default Expansion - ...derivedTokenMetadata, + name: derivedTokenMetadata.getName(chain), + symbol: derivedTokenMetadata.getSymbol(chain), + decimals: derivedTokenMetadata.getDecimals(chain), + scale: derivedTokenMetadata.getScale(chain), remoteRouters, destinationGas, hook: zeroAddress, diff --git a/typescript/sdk/src/token/deploy.ts b/typescript/sdk/src/token/deploy.ts index 3aa67b9c5e8..658c4b34f4e 100644 --- a/typescript/sdk/src/token/deploy.ts +++ b/typescript/sdk/src/token/deploy.ts @@ -22,6 +22,7 @@ import { MultiProvider } from '../providers/MultiProvider.js'; import { GasRouterDeployer } from '../router/GasRouterDeployer.js'; import { ChainName } from '../types.js'; +import { TokenMetadataMap } from './TokenMetadataMap.js'; import { TokenType, gasOverhead } from './config.js'; import { HypERC20Factories, @@ -34,7 +35,6 @@ import { } from './contracts.js'; import { HypTokenRouterConfig, - TokenMetadata, TokenMetadataSchema, WarpRouteDeployConfig, WarpRouteDeployConfigMailboxRequired, @@ -123,10 +123,20 @@ abstract class TokenDeployer< static async deriveTokenMetadata( multiProvider: MultiProvider, configMap: WarpRouteDeployConfig, - ): Promise { - for (const [chain, config] of Object.entries(configMap)) { + ): Promise { + const metadataMap = new TokenMetadataMap(); + + const priorityGetter = (type: string) => { + return ['collateral', 'native'].indexOf(type); + }; + + const sortedEntries = Object.entries(configMap).sort( + ([, a], [, b]) => priorityGetter(b.type) - priorityGetter(a.type), + ); + + for (const [chain, config] of sortedEntries) { if (isTokenMetadata(config)) { - return TokenMetadataSchema.parse(config); + metadataMap.set(chain, TokenMetadataSchema.parse(config)); } else if (multiProvider.getProtocol(chain) !== ProtocolType.Ethereum) { // If the config didn't specify the token metadata, we can only now // derive it for Ethereum chains. So here we skip non-Ethereum chains. @@ -136,9 +146,13 @@ abstract class TokenDeployer< if (isNativeTokenConfig(config)) { const nativeToken = multiProvider.getChainMetadata(chain).nativeToken; if (nativeToken) { - return TokenMetadataSchema.parse({ - ...nativeToken, - }); + metadataMap.set( + chain, + TokenMetadataSchema.parse({ + ...nativeToken, + }), + ); + continue; } } @@ -154,10 +168,14 @@ abstract class TokenDeployer< erc721.name(), erc721.symbol(), ]); - return TokenMetadataSchema.parse({ - name, - symbol, - }); + metadataMap.set( + chain, + TokenMetadataSchema.parse({ + name, + symbol, + }), + ); + continue; } let token: string; @@ -186,21 +204,25 @@ abstract class TokenDeployer< erc20.decimals(), ]); - return TokenMetadataSchema.parse({ - name, - symbol, - decimals, - }); + metadataMap.set( + chain, + TokenMetadataSchema.parse({ + name, + symbol, + decimals, + }), + ); } } - return undefined; + metadataMap.finalize(); + return metadataMap; } async deploy(configMap: WarpRouteDeployConfigMailboxRequired) { - let tokenMetadata: TokenMetadata | undefined; + let tokenMetadataMap: TokenMetadataMap; try { - tokenMetadata = await TokenDeployer.deriveTokenMetadata( + tokenMetadataMap = await TokenDeployer.deriveTokenMetadata( this.multiProvider, configMap, ); @@ -209,8 +231,13 @@ abstract class TokenDeployer< throw err; } - const resolvedConfigMap = objMap(configMap, (_, config) => ({ - ...tokenMetadata, + const resolvedConfigMap = objMap(configMap, (chain, config) => ({ + name: tokenMetadataMap.getName(chain), + decimals: tokenMetadataMap.getDecimals(chain), + symbol: + tokenMetadataMap.getSymbol(chain) || + tokenMetadataMap.getDefaultSymbol(), + scale: tokenMetadataMap.getScale(chain), gas: gasOverhead(config.type), ...config, })); From 1c01a4f035173d60884827a885af4f70a2fb8113 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 21 May 2025 15:05:23 +0100 Subject: [PATCH 217/223] fix: Scraper: Fix issue with missing enum invariant for Sealevel (#6298) ### Description Scraper is failing to index some message dispatches due to obsolete Solana SDK we are using. We pinned the dependency version to 1.14.13. Since then enum `TransactionError` added a few enum invariants. New tag of the Solana SDK dependencies brings these invariants from upstream. ### Backward compatibility Yes ### Testing Tested with experimental image Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- rust/main/Cargo.lock | 50 ++++++++++++++-------------- rust/main/Cargo.toml | 16 ++++----- rust/sealevel/Cargo.lock | 72 ++++++++++++++++++++-------------------- rust/sealevel/Cargo.toml | 68 ++++++++++++++++++------------------- 4 files changed, 103 insertions(+), 103 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 7840fb14dfd..08d35cf910f 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -9655,7 +9655,7 @@ dependencies = [ [[package]] name = "solana-account-decoder" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "Inflector", "base64 0.13.1", @@ -9679,7 +9679,7 @@ dependencies = [ [[package]] name = "solana-address-lookup-table-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "bytemuck", @@ -9699,7 +9699,7 @@ dependencies = [ [[package]] name = "solana-clap-utils" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "chrono", "clap 2.34.0", @@ -9716,7 +9716,7 @@ dependencies = [ [[package]] name = "solana-cli-config" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "dirs-next", "lazy_static", @@ -9731,7 +9731,7 @@ dependencies = [ [[package]] name = "solana-client" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "async-mutex", "async-trait", @@ -9784,7 +9784,7 @@ dependencies = [ [[package]] name = "solana-config-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "chrono", @@ -9797,7 +9797,7 @@ dependencies = [ [[package]] name = "solana-faucet" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "byteorder", @@ -9820,7 +9820,7 @@ dependencies = [ [[package]] name = "solana-frozen-abi" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "ahash 0.7.8", "blake3", @@ -9853,7 +9853,7 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "proc-macro2 1.0.93", "quote 1.0.37", @@ -9864,7 +9864,7 @@ dependencies = [ [[package]] name = "solana-logger" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "env_logger", "lazy_static", @@ -9874,7 +9874,7 @@ dependencies = [ [[package]] name = "solana-measure" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "log", "solana-sdk", @@ -9883,7 +9883,7 @@ dependencies = [ [[package]] name = "solana-metrics" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "crossbeam-channel", "gethostname", @@ -9896,7 +9896,7 @@ dependencies = [ [[package]] name = "solana-net-utils" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "clap 3.2.25", @@ -9917,7 +9917,7 @@ dependencies = [ [[package]] name = "solana-perf" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "ahash 0.7.8", "bincode", @@ -9943,7 +9943,7 @@ dependencies = [ [[package]] name = "solana-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "base64 0.13.1", "bincode", @@ -9991,7 +9991,7 @@ dependencies = [ [[package]] name = "solana-program-runtime" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "base64 0.13.1", "bincode", @@ -10017,7 +10017,7 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "lazy_static", "num_cpus", @@ -10026,7 +10026,7 @@ dependencies = [ [[package]] name = "solana-remote-wallet" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "console", "dialoguer", @@ -10044,7 +10044,7 @@ dependencies = [ [[package]] name = "solana-sdk" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "assert_matches", "base64 0.13.1", @@ -10094,7 +10094,7 @@ dependencies = [ [[package]] name = "solana-sdk-macro" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bs58 0.4.0", "proc-macro2 1.0.93", @@ -10106,7 +10106,7 @@ dependencies = [ [[package]] name = "solana-streamer" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "crossbeam-channel", "futures-util", @@ -10134,7 +10134,7 @@ dependencies = [ [[package]] name = "solana-transaction-status" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "Inflector", "base64 0.13.1", @@ -10162,7 +10162,7 @@ dependencies = [ [[package]] name = "solana-version" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "log", "rustc_version", @@ -10177,7 +10177,7 @@ dependencies = [ [[package]] name = "solana-vote-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "log", @@ -10197,7 +10197,7 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-02-14#b94add305fc3232797f518ec7d9919c8528b381c" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "aes-gcm-siv", "arrayref", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index e4cc478d1c5..a86126a003f 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -259,42 +259,42 @@ version = "=0.5.2" [patch.crates-io.solana-account-decoder] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-clap-utils] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-cli-config] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-client] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-program] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-sdk] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-transaction-status] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-zk-token-sdk] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2025-02-14" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.spl-associated-token-account] diff --git a/rust/sealevel/Cargo.lock b/rust/sealevel/Cargo.lock index 9fd71b04378..a88b4c8d054 100644 --- a/rust/sealevel/Cargo.lock +++ b/rust/sealevel/Cargo.lock @@ -4946,7 +4946,7 @@ dependencies = [ [[package]] name = "solana-account-decoder" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "Inflector", "base64 0.13.1", @@ -4970,7 +4970,7 @@ dependencies = [ [[package]] name = "solana-address-lookup-table-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "bytemuck", @@ -4990,7 +4990,7 @@ dependencies = [ [[package]] name = "solana-banks-client" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "borsh", "futures", @@ -5006,7 +5006,7 @@ dependencies = [ [[package]] name = "solana-banks-interface" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "serde", "solana-sdk", @@ -5016,7 +5016,7 @@ dependencies = [ [[package]] name = "solana-banks-server" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "crossbeam-channel", @@ -5035,7 +5035,7 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "byteorder", @@ -5053,7 +5053,7 @@ dependencies = [ [[package]] name = "solana-bucket-map" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "log", "memmap2", @@ -5067,7 +5067,7 @@ dependencies = [ [[package]] name = "solana-clap-utils" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "chrono", "clap 2.34.0", @@ -5084,7 +5084,7 @@ dependencies = [ [[package]] name = "solana-cli-config" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "dirs-next", "lazy_static", @@ -5099,7 +5099,7 @@ dependencies = [ [[package]] name = "solana-client" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "async-mutex", "async-trait", @@ -5152,7 +5152,7 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "solana-program-runtime", "solana-sdk", @@ -5161,7 +5161,7 @@ dependencies = [ [[package]] name = "solana-config-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "chrono", @@ -5174,7 +5174,7 @@ dependencies = [ [[package]] name = "solana-faucet" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "byteorder", @@ -5197,7 +5197,7 @@ dependencies = [ [[package]] name = "solana-frozen-abi" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "ahash", "blake3", @@ -5230,7 +5230,7 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", @@ -5241,7 +5241,7 @@ dependencies = [ [[package]] name = "solana-logger" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "env_logger 0.9.3", "lazy_static", @@ -5251,7 +5251,7 @@ dependencies = [ [[package]] name = "solana-measure" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "log", "solana-sdk", @@ -5260,7 +5260,7 @@ dependencies = [ [[package]] name = "solana-metrics" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "crossbeam-channel", "gethostname", @@ -5273,7 +5273,7 @@ dependencies = [ [[package]] name = "solana-net-utils" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "clap 3.2.25", @@ -5294,7 +5294,7 @@ dependencies = [ [[package]] name = "solana-perf" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "ahash", "bincode", @@ -5320,7 +5320,7 @@ dependencies = [ [[package]] name = "solana-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "base64 0.13.1", "bincode", @@ -5368,7 +5368,7 @@ dependencies = [ [[package]] name = "solana-program-runtime" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "base64 0.13.1", "bincode", @@ -5394,7 +5394,7 @@ dependencies = [ [[package]] name = "solana-program-test" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "assert_matches", "async-trait", @@ -5418,7 +5418,7 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "lazy_static", "num_cpus", @@ -5427,7 +5427,7 @@ dependencies = [ [[package]] name = "solana-remote-wallet" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "console", "dialoguer", @@ -5445,7 +5445,7 @@ dependencies = [ [[package]] name = "solana-runtime" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "arrayref", "bincode", @@ -5505,7 +5505,7 @@ dependencies = [ [[package]] name = "solana-sdk" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "assert_matches", "base64 0.13.1", @@ -5555,7 +5555,7 @@ dependencies = [ [[package]] name = "solana-sdk-macro" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bs58 0.4.0", "proc-macro2 1.0.86", @@ -5567,7 +5567,7 @@ dependencies = [ [[package]] name = "solana-send-transaction-service" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "crossbeam-channel", "log", @@ -5581,7 +5581,7 @@ dependencies = [ [[package]] name = "solana-stake-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "log", @@ -5603,7 +5603,7 @@ dependencies = [ [[package]] name = "solana-streamer" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "crossbeam-channel", "futures-util", @@ -5631,7 +5631,7 @@ dependencies = [ [[package]] name = "solana-transaction-status" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "Inflector", "base64 0.13.1", @@ -5659,7 +5659,7 @@ dependencies = [ [[package]] name = "solana-version" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "log", "rustc_version", @@ -5674,7 +5674,7 @@ dependencies = [ [[package]] name = "solana-vote-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bincode", "log", @@ -5694,7 +5694,7 @@ dependencies = [ [[package]] name = "solana-zk-token-proof-program" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "bytemuck", "getrandom 0.1.16", @@ -5708,7 +5708,7 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2024-11-20#913db71a07f967f4c5c7e7f747cb48517cdbf09e" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2025-05-21#6cc4764f0e6482c6844e8062c55535a6934ea279" dependencies = [ "aes-gcm-siv", "arrayref", diff --git a/rust/sealevel/Cargo.toml b/rust/sealevel/Cargo.toml index ffc287a2165..1c7bb26f758 100644 --- a/rust/sealevel/Cargo.toml +++ b/rust/sealevel/Cargo.toml @@ -1,28 +1,28 @@ [workspace] members = [ - "client", - "libraries/access-control", - "libraries/account-utils", - "libraries/ecdsa-signature", - "libraries/hyperlane-sealevel-connection-client", - "libraries/hyperlane-sealevel-token", - "libraries/interchain-security-module-interface", - "libraries/message-recipient-interface", - "libraries/multisig-ism", - "libraries/serializable-account-meta", - "libraries/test-transaction-utils", - "libraries/test-utils", - "programs/hyperlane-sealevel-igp", - "programs/hyperlane-sealevel-igp-test", - "programs/hyperlane-sealevel-token", - "programs/hyperlane-sealevel-token-collateral", - "programs/hyperlane-sealevel-token-native", - "programs/ism/multisig-ism-message-id", - "programs/ism/test-ism", - "programs/mailbox", - "programs/mailbox-test", - "programs/test-send-receiver", - "programs/validator-announce", + "client", + "libraries/access-control", + "libraries/account-utils", + "libraries/ecdsa-signature", + "libraries/hyperlane-sealevel-connection-client", + "libraries/hyperlane-sealevel-token", + "libraries/interchain-security-module-interface", + "libraries/message-recipient-interface", + "libraries/multisig-ism", + "libraries/serializable-account-meta", + "libraries/test-transaction-utils", + "libraries/test-utils", + "programs/hyperlane-sealevel-igp", + "programs/hyperlane-sealevel-igp-test", + "programs/hyperlane-sealevel-token", + "programs/hyperlane-sealevel-token-collateral", + "programs/hyperlane-sealevel-token-native", + "programs/ism/multisig-ism-message-id", + "programs/ism/test-ism", + "programs/mailbox", + "programs/mailbox-test", + "programs/test-send-receiver", + "programs/validator-announce", ] [workspace.package] @@ -114,7 +114,7 @@ solana-sdk = "=1.14.13" solana-transaction-status = "=1.14.13" solana-zk-token-sdk = "=1.14.13" spl-associated-token-account = { version = "=1.1.2", features = [ - "no-entrypoint", + "no-entrypoint", ] } spl-noop = { version = "=0.1.3", features = ["no-entrypoint"] } spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } @@ -257,52 +257,52 @@ version = "=0.5.2" [patch.crates-io.solana-account-decoder] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-banks-client] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-clap-utils] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-cli-config] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-client] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-program] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-program-test] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-sdk] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-transaction-status] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.solana-zk-token-sdk] git = "https://github.com/hyperlane-xyz/solana.git" -tag = "hyperlane-1.14.13-2024-11-20" +tag = "hyperlane-1.14.13-2025-05-21" version = "=1.14.13" [patch.crates-io.spl-associated-token-account] From 197fd87278007061b11757d0906b54f71d6270d0 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 21 May 2025 15:35:57 +0100 Subject: [PATCH 218/223] chore: new relayer image (#6304) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index f6f965936ad..b4bfb77d2d7 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -855,7 +855,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '0fffbfb-20250514-130038', + tag: '1c01a4f-20250521-141620', }, blacklist, gasPaymentEnforcement: gasPaymentEnforcement, @@ -895,7 +895,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '0ad4122-20250514-154514', + tag: '4c678c5-20250521-125729', }, blacklist, // We're temporarily (ab)using the RC relayer as a way to increase From 689ed7c78d02f6a401518aac0b9054c81d247d20 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 21 May 2025 16:57:25 +0100 Subject: [PATCH 219/223] chore: reduce release-e2e-matrix frequency (#6296) ### Description chore: reduce release-e2e-matrix frequency - previously we would run the release cli test matrix if there were no changesets - no changesets _implied_ that a release is to be done, but in cases where packages are published but no new publishable changes have landed yet - we're still churning through these tests for no good reason - this PR changes the behaviour such that we always try and do prepare-release so that the Changesets PR is updated, but we also in parallel check if the _current_ package versions are published and only run the tests if we detect that there are packages that need to be published ### Drive-by changes ### Related issues ### Backward compatibility ### Testing tested here https://github.com/hyperlane-xyz/hyperlane-monorepo/actions/runs/15161126534 ![image](https://github.com/user-attachments/assets/984e16d7-ae0e-4cb6-ad0f-2a5adbfc5fd9) ![image](https://github.com/user-attachments/assets/61f960fe-3ee1-4250-b737-0c515c23bb73) --- .github/workflows/release.yml | 44 ++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8640e67ff54..4054c1645e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,8 +20,6 @@ jobs: # This job prepares the release by creating or updating a release PR. # Notice the omission of the `publish` flag in the changesets action. prepare-release: - outputs: - hasChangesets: ${{ steps.changesets.outputs.hasChangesets }} permissions: id-token: write contents: write @@ -53,15 +51,45 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - # The release PR removes individual changesets to prepare for a release. - # This means that once the release PR is merged, there are no changesets left. - # When there are no changesets left, we can run the cli-install-cross-platform-release-test - # workflow to verify that the CLI installs correctly on all platforms. + check-latest-published: + runs-on: ubuntu-latest + outputs: + all_latest: ${{ steps.check.outputs.all_latest }} + steps: + - uses: actions/checkout@v4 + + - name: Retrieve package versions + id: pkg + run: | + find . -name 'package.json' -print0 | while IFS= read -r -d '' pkg; do + jq -r 'select(.private != true) | .name + "@" + .version' "$pkg" + done | tee versions.txt + + - name: Compare package versions + id: check + run: | + all_latest=true + while read -r pkg; do + echo "Checking if $pkg is published..." + exists=$(npm view "$pkg" version 2>/dev/null || echo "N/A") + echo "npm returned: $exists" + if [ "$exists" = "N/A" ]; then + echo "$pkg is NOT published." + all_latest=false + break + else + echo "$pkg is published." + fi + done < versions.txt + echo "all_latest=$all_latest" >> $GITHUB_OUTPUT + + # If we detect that not all packages are published, we run the + # cli-install-cross-platform-release-test workflow to verify that the CLI installs correctly on all platforms. # In all other cases, we already have a barebones `cli-install` test on the default CI platform # which will catch most issues before any offending PR is merged. cli-install-cross-platform-release-test: - needs: prepare-release - if: needs.prepare-release.outputs.hasChangesets == 'false' + needs: [check-latest-published] + if: needs.check-latest-published.outputs.all_latest == 'false' strategy: matrix: os: [depot-ubuntu-latest, depot-macos-latest, depot-windows-2022] From 89a7c6fc5a6fe9cf2b51bff3678659fc16b56e32 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 21 May 2025 17:22:00 +0100 Subject: [PATCH 220/223] fix(submitter): Fix transaction fields (#6306) ### Description Required fields on EVM transaction are overwritten when we estimate gas price in EVM adapter for Lander. It means that these fields are not set when transaction is submitted into a chain. This change ensures that all the required fields are copied from old transaction to the new one during gas price estimations. E2E tests do not pass with fields set, so, we use classical submitter for the time being. ### Backward compatibility Yes ### Testing E2E test Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- .../chains/ethereum/adapter/gas_price_estimator.rs | 9 +++++++++ rust/main/utils/run-locally/src/main.rs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs index 024734f3a90..1d974315605 100644 --- a/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs +++ b/rust/main/submitter/src/chain_tx_adapter/chains/ethereum/adapter/gas_price_estimator.rs @@ -93,6 +93,15 @@ pub async fn estimate_gas_price( if let Some(value) = tx.value() { request = request.value(*value); } + if let Some(nonce) = tx.nonce() { + request = request.nonce(*nonce); + } + if let Some(gas_limit) = tx.gas() { + request = request.gas(*gas_limit); + } + if let Some(chain_id) = tx.chain_id() { + request = request.chain_id(chain_id); + } request = request.max_fee_per_gas(max_fee); request = request.max_priority_fee_per_gas(max_priority_fee); diff --git a/rust/main/utils/run-locally/src/main.rs b/rust/main/utils/run-locally/src/main.rs index 733ca23e6fc..c490e6d1b6d 100644 --- a/rust/main/utils/run-locally/src/main.rs +++ b/rust/main/utils/run-locally/src/main.rs @@ -97,7 +97,7 @@ const FAILED_MESSAGE_COUNT: u32 = 1; const RELAYER_METRICS_PORT: &str = "9092"; const SCRAPER_METRICS_PORT: &str = "9093"; -pub const SUBMITTER_TYPE: SubmitterType = SubmitterType::Lander; +pub const SUBMITTER_TYPE: SubmitterType = SubmitterType::Classic; type DynPath = Box>; From e67cb5fb85f67a3d1c59e0f0791e5063d4a584d6 Mon Sep 17 00:00:00 2001 From: Danil Nemirovsky Date: Wed, 21 May 2025 17:23:48 +0100 Subject: [PATCH 221/223] chore: Upgrade Scraper to latest (#6307) ### Description Upgrade Scraper to 197fd87-20250521-144619. ### Backward compatibility Yes ### Testing Tested in hyperlane Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com> --- typescript/infra/config/environments/mainnet3/agent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index b4bfb77d2d7..5f668fcb3ff 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -880,7 +880,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'abdf139-20250514-081104', + tag: '197fd87-20250521-144619', }, resources: scraperResources, }, From ec1ba921489f79e74c14576f02e7dded0ed5e856 Mon Sep 17 00:00:00 2001 From: Christopher Brumm <97845034+christopherbrumm@users.noreply.github.com> Date: Wed, 21 May 2025 19:15:11 +0200 Subject: [PATCH 222/223] chore: add `sol`, `ufd`, `fartcoin`& `pengu` (#6249) ### Description Add `sol`, `ufd`, `fartcoin` & `pengu` between Solana and Apechain. ### Backward compatibility Yes ### Testing N/A --------- Co-authored-by: xeno097 --- .../program-ids.json | 10 ++++++++++ .../token-config.json | 16 ++++++++++++++++ .../program-ids.json | 10 ++++++++++ .../token-config.json | 16 ++++++++++++++++ .../SOL-apechain-solanamainnet/program-ids.json | 10 ++++++++++ .../SOL-apechain-solanamainnet/token-config.json | 15 +++++++++++++++ .../UFD-apechain-solanamainnet/program-ids.json | 10 ++++++++++ .../UFD-apechain-solanamainnet/token-config.json | 16 ++++++++++++++++ 8 files changed, 103 insertions(+) create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/token-config.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/token-config.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/token-config.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/program-ids.json create mode 100644 rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/token-config.json diff --git a/rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/program-ids.json new file mode 100644 index 00000000000..73ec5080eac --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/program-ids.json @@ -0,0 +1,10 @@ +{ + "solanamainnet": { + "hex": "0x349563bf83a11275c9218fcf0ed756c2bbaee32957561461a786a2660738d5b0", + "base58": "4YGKdefcJMUVnzZFu124t7epCNWnAjmXbKXyC13HiAFR" + }, + "apechain": { + "hex": "0x0000000000000000000000005fc9b323013dacf2d56046f9ff0f61c95c6a466b", + "base58": "1111111111112LQDHtXE4PAZXpp89JaZSrrsK2qQ" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/token-config.json new file mode 100644 index 00000000000..84c9fdcddeb --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/Fartcoin-apechain-solanamainnet/token-config.json @@ -0,0 +1,16 @@ +{ + "apechain": { + "type": "synthetic", + "decimals": 6, + "name": "Fartcoin", + "symbol": "Fartcoin", + "foreignDeployment": "0x5FC9b323013DAcF2d56046F9ff0f61c95c6A466B" + }, + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "owner": "B1q2dsjCE4F6wi5ktn4anmFq9572JpziBUg2TqCS2dFn", + "token": "9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/program-ids.json new file mode 100644 index 00000000000..9ea569cf26c --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/program-ids.json @@ -0,0 +1,10 @@ +{ + "apechain": { + "hex": "0x0000000000000000000000009eaf39a97d56119236a356225b339fe7383549b3", + "base58": "1111111111113DDwBZG7ckGNZKzZ1B76penkvDPp" + }, + "solanamainnet": { + "hex": "0xa51f69e733636afcc8d2e1cf6983f89b200e339fc79cbab4ac402328c3b0e304", + "base58": "C7a43nYF9atxgJhfqoP2vc95exJcEQhvJqYbtCNkDf8f" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/token-config.json new file mode 100644 index 00000000000..a0c143559ec --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/PENGU-apechain-solanamainnet/token-config.json @@ -0,0 +1,16 @@ +{ + "apechain": { + "type": "synthetic", + "decimals": 6, + "name": "Pudgy Penguins", + "symbol": "PENGU", + "foreignDeployment": "0x9Eaf39A97d56119236a356225B339fE7383549B3" + }, + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "owner": "B1q2dsjCE4F6wi5ktn4anmFq9572JpziBUg2TqCS2dFn", + "token": "2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/program-ids.json new file mode 100644 index 00000000000..3dd59b21663 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/program-ids.json @@ -0,0 +1,10 @@ +{ + "solanamainnet": { + "hex": "0xa277cedf23775482c7ceae9805637398096cd638b5ea9b590f9e3e0785dbda0f", + "base58": "BwD1LvMvofVZAAWyG318S17zqKMXXeeNeVHQimaWG6qt" + }, + "apechain": { + "hex": "0x00000000000000000000000016ee589e237f2c70abe91f87a6c0712c836941bb", + "base58": "111111111111KXh2VivxW5ayEbWc54xmJyJ4duU" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/token-config.json new file mode 100644 index 00000000000..3a82b20b689 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/SOL-apechain-solanamainnet/token-config.json @@ -0,0 +1,15 @@ +{ + "apechain": { + "type": "synthetic", + "name": "Solana", + "symbol": "SOL", + "decimals": 9, + "foreignDeployment": "0x16eE589E237f2c70aBE91F87a6C0712C836941bb" + }, + "solanamainnet": { + "type": "native", + "decimals": 9, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "owner": "B1q2dsjCE4F6wi5ktn4anmFq9572JpziBUg2TqCS2dFn" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/program-ids.json new file mode 100644 index 00000000000..ab8a8457925 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/program-ids.json @@ -0,0 +1,10 @@ +{ + "solanamainnet": { + "hex": "0xb90d8a47529f0c98938baaf66bde02e10e5d91cb5fd0c132114f7765545a74db", + "base58": "DTNQ9T9KYbB2rpnJxdwcZbnxB17G9jAiiSqU8fAf6uSS" + }, + "apechain": { + "hex": "0x000000000000000000000000c58eec72352b04358b0b6979ba10462190f0d54c", + "base58": "1111111111113kdoVA3kUxTmzqvXbi7yKepjE5GF" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/token-config.json new file mode 100644 index 00000000000..22538af2a67 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/UFD-apechain-solanamainnet/token-config.json @@ -0,0 +1,16 @@ +{ + "apechain": { + "type": "synthetic", + "decimals": 6, + "name": "Unicorn Fart Dust", + "symbol": "UFD", + "foreignDeployment": "0xC58eeC72352b04358b0b6979ba10462190f0d54C" + }, + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "owner": "B1q2dsjCE4F6wi5ktn4anmFq9572JpziBUg2TqCS2dFn", + "token": "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump" + } +} \ No newline at end of file From 204323f5e914a38aa48f0855920827130a6e3fe9 Mon Sep 17 00:00:00 2001 From: Morteza Shojaei Date: Thu, 22 May 2025 18:03:35 +0330 Subject: [PATCH 223/223] refactor(PostDeploymentContractVerifier): streamline contract verification process --- .../deploy/verify/PostDeploymentContractVerifier.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts b/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts index bd2cb71112b..3d78956b1eb 100644 --- a/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts +++ b/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts @@ -37,12 +37,11 @@ export class PostDeploymentContractVerifier extends MultiGeneric { // can check explorer family here to avoid doing these checks for each input in verifier const { family } = this.multiProvider.getExplorerApi(chain); + let contractVerifier: BaseContractVerifier = this.contractVerifier; if (family === ExplorerFamily.ZkSync) { this.logger.debug('Using ZkSync verifier'); - this.contractVerifier = new ZKSyncContractVerifier( - this.multiProvider, - ); + contractVerifier = new ZKSyncContractVerifier(this.multiProvider); } if (family === ExplorerFamily.Other) { @@ -55,11 +54,7 @@ export class PostDeploymentContractVerifier extends MultiGeneric