From d69d76a73feabd61d653c99ae42075d733b74bee Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:26:39 -0400 Subject: [PATCH] Aggregation hook deployer (#2769) Co-authored-by: Yorke Rhodes fixes https://github.com/hyperlane-xyz/issues/issues/624 --- solidity/contracts/Mailbox.sol | 4 + solidity/hardhat.config.ts | 2 +- typescript/helloworld/hardhat.config.ts | 2 +- .../helloworld/src/test/helloworld.test.ts | 4 +- .../config/environments/mainnet2/core.ts | 44 +++++++-- .../infra/config/environments/mainnet2/igp.ts | 22 ++--- .../infra/config/environments/test/core.ts | 37 +++++-- .../infra/config/environments/test/hooks.ts | 16 --- .../infra/config/environments/test/igp.ts | 19 ++-- .../infra/config/environments/test/index.ts | 2 - .../config/environments/testnet3/core.ts | 44 +++++++-- typescript/infra/scripts/deploy.ts | 19 +--- typescript/infra/scripts/helloworld/utils.ts | 2 +- typescript/infra/scripts/utils.ts | 6 +- typescript/infra/src/config/environment.ts | 2 - .../sdk/src/core/CoreDeployer.hardhat-test.ts | 10 +- .../sdk/src/core/HyperlaneCoreDeployer.ts | 46 ++++----- typescript/sdk/src/core/TestCoreDeployer.ts | 4 +- .../sdk/src/deploy/HyperlaneDeployer.ts | 20 ++-- .../deploy/HyperlaneProxyFactoryDeployer.ts | 40 ++++++++ typescript/sdk/src/deploy/contracts.ts | 29 ++++++ typescript/sdk/src/gas/HyperlaneIgpChecker.ts | 10 -- .../sdk/src/gas/HyperlaneIgpDeployer.ts | 25 ----- typescript/sdk/src/gas/contracts.ts | 5 +- typescript/sdk/src/gas/types.ts | 2 - .../sdk/src/hook/HyperlaneHookDeployer.ts | 97 +++++++++++++++++-- typescript/sdk/src/hook/contracts.ts | 9 +- typescript/sdk/src/hook/types.ts | 29 +++++- typescript/sdk/src/index.ts | 25 +++-- .../ism/HyperlaneIsmFactory.hardhat-test.ts | 6 +- typescript/sdk/src/ism/HyperlaneIsmFactory.ts | 34 ++++--- .../src/ism/HyperlaneIsmFactoryDeployer.ts | 80 --------------- typescript/sdk/src/ism/contracts.ts | 6 +- typescript/sdk/src/test/testUtils.ts | 10 +- 34 files changed, 427 insertions(+), 285 deletions(-) delete mode 100644 typescript/infra/config/environments/test/hooks.ts create mode 100644 typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts create mode 100644 typescript/sdk/src/deploy/contracts.ts delete mode 100644 typescript/sdk/src/ism/HyperlaneIsmFactoryDeployer.ts diff --git a/solidity/contracts/Mailbox.sol b/solidity/contracts/Mailbox.sol index 59b0d63b18..a444d46c8d 100644 --- a/solidity/contracts/Mailbox.sol +++ b/solidity/contracts/Mailbox.sol @@ -297,6 +297,10 @@ contract Mailbox is IMailbox, Indexed, Versioned, OwnableUpgradeable { /// INTERACTIONS /// uint256 requiredValue = requiredHook.quoteDispatch(metadata, message); + // if underpaying, defer to required hook's reverting behavior + if (msg.value < requiredValue) { + requiredValue = msg.value; + } requiredHook.postDispatch{value: requiredValue}(metadata, message); hook.postDispatch{value: msg.value - requiredValue}(metadata, message); diff --git a/solidity/hardhat.config.ts b/solidity/hardhat.config.ts index f26c74ded9..d206cb8aed 100644 --- a/solidity/hardhat.config.ts +++ b/solidity/hardhat.config.ts @@ -27,7 +27,7 @@ module.exports = { typechain: { outDir: './types', target: 'ethers-v5', - alwaysGenerateOverloads: false, + alwaysGenerateOverloads: true, }, mocha: { bail: true, diff --git a/typescript/helloworld/hardhat.config.ts b/typescript/helloworld/hardhat.config.ts index 7c2e51c6fc..01d6077e4f 100644 --- a/typescript/helloworld/hardhat.config.ts +++ b/typescript/helloworld/hardhat.config.ts @@ -21,6 +21,6 @@ module.exports = { typechain: { outDir: './src/types', target: 'ethers-v5', - alwaysGenerateOverloads: false, // should overloads with full signatures like deposit(uint256) be generated always, even if there are no overloads? + alwaysGenerateOverloads: true, }, }; diff --git a/typescript/helloworld/src/test/helloworld.test.ts b/typescript/helloworld/src/test/helloworld.test.ts index 8e02d8a2e2..9ab3784de6 100644 --- a/typescript/helloworld/src/test/helloworld.test.ts +++ b/typescript/helloworld/src/test/helloworld.test.ts @@ -80,9 +80,9 @@ describe('HelloWorld', async () => { const body = 'Hello'; await expect( local.sendHelloWorld(remoteDomain, body, { - value: (await quoteGasPayment(body)).sub(1), + value: 0, }), - ).to.be.revertedWith('insufficient interchain gas payment'); + ).to.be.revertedWith('StaticProtocolFee: insufficient protocol fee'); }); it('handles a message', async () => { diff --git a/typescript/infra/config/environments/mainnet2/core.ts b/typescript/infra/config/environments/mainnet2/core.ts index a83839c4b6..ca40b0530f 100644 --- a/typescript/infra/config/environments/mainnet2/core.ts +++ b/typescript/infra/config/environments/mainnet2/core.ts @@ -1,9 +1,20 @@ -import { ChainMap, CoreConfig, HookType } from '@hyperlane-xyz/sdk'; +import { BigNumber, ethers } from 'ethers'; + +import { + AggregationHookConfig, + ChainMap, + CoreConfig, + HookType, + IgpHookConfig, + MerkleTreeHookConfig, + ProtocolFeeHookConfig, +} from '@hyperlane-xyz/sdk'; import { objMap } from '@hyperlane-xyz/utils'; import { aggregationIsm } from '../../aggregationIsm'; import { Contexts } from '../../contexts'; +import { igp } from './igp'; import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { @@ -23,15 +34,32 @@ export const core: ChainMap = objMap(owners, (local, owner) => { }; } + const merkleHook: MerkleTreeHookConfig = { + type: HookType.MERKLE_TREE, + }; + + const igpHook: IgpHookConfig = { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + ...igp[local], + }; + + const defaultHook: AggregationHookConfig = { + type: HookType.AGGREGATION, + hooks: [merkleHook, igpHook], + }; + + const requiredHook: ProtocolFeeHookConfig = { + type: HookType.PROTOCOL_FEE, + maxProtocolFee: ethers.utils.parseUnits('1', 'gwei'), // 1 gwei of native token + protocolFee: BigNumber.from(1), // 1 wei + beneficiary: owner, + owner, + }; + return { owner, - upgrade, defaultIsm, - defaultHook: { - type: HookType.INTERCHAIN_GAS_PAYMASTER, - }, - requiredHook: { - type: HookType.MERKLE_TREE, - }, + defaultHook, + requiredHook, }; }); diff --git a/typescript/infra/config/environments/mainnet2/igp.ts b/typescript/infra/config/environments/mainnet2/igp.ts index e2410fe5e5..59b1ab7e56 100644 --- a/typescript/infra/config/environments/mainnet2/igp.ts +++ b/typescript/infra/config/environments/mainnet2/igp.ts @@ -8,7 +8,6 @@ import { import { exclude, objMap } from '@hyperlane-xyz/utils'; import { MainnetChains, supportedChainNames } from './chains'; -import { core } from './core'; import { owners } from './owners'; // TODO: make this generic @@ -25,20 +24,21 @@ function getGasOracles(local: MainnetChains) { } export const igp: ChainMap = objMap(owners, (chain, owner) => { + const overhead = Object.fromEntries( + exclude(chain, supportedChainNames).map((remote) => [ + remote, + multisigIsmVerificationCost( + defaultMultisigIsmConfigs[remote].threshold, + defaultMultisigIsmConfigs[remote].validators.length, + ), + ]), + ); + return { owner, oracleKey: DEPLOYER_ADDRESS, beneficiary: KEY_FUNDER_ADDRESS, gasOracleType: getGasOracles(chain), - overhead: Object.fromEntries( - exclude(chain, supportedChainNames).map((remote) => [ - remote, - multisigIsmVerificationCost( - defaultMultisigIsmConfigs[remote].threshold, - defaultMultisigIsmConfigs[remote].validators.length, - ), - ]), - ), - upgrade: core[chain].upgrade, + overhead, }; }); diff --git a/typescript/infra/config/environments/test/core.ts b/typescript/infra/config/environments/test/core.ts index 1cfb5dc9d2..73370f214a 100644 --- a/typescript/infra/config/environments/test/core.ts +++ b/typescript/infra/config/environments/test/core.ts @@ -1,13 +1,20 @@ +import { BigNumber, ethers } from 'ethers'; + import { + AggregationHookConfig, ChainMap, CoreConfig, HookType, + IgpHookConfig, + MerkleTreeHookConfig, ModuleType, RoutingIsmConfig, } from '@hyperlane-xyz/sdk'; +import { ProtocolFeeHookConfig } from '@hyperlane-xyz/sdk/src/hook/types'; import { objMap } from '@hyperlane-xyz/utils'; import { aggregationIsm } from './aggregationIsm'; +import { igp } from './igp'; import { chainToValidator } from './multisigIsm'; import { owners } from './owners'; @@ -22,14 +29,32 @@ export const core: ChainMap = objMap(owners, (local, owner) => { ), }; + const merkleHook: MerkleTreeHookConfig = { + type: HookType.MERKLE_TREE, + }; + + const igpHook: IgpHookConfig = { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + ...igp[local], + }; + + const defaultHook: AggregationHookConfig = { + type: HookType.AGGREGATION, + hooks: [merkleHook, igpHook], + }; + + const requiredHook: ProtocolFeeHookConfig = { + type: HookType.PROTOCOL_FEE, + maxProtocolFee: ethers.utils.parseUnits('1', 'gwei'), // 1 gwei of native token + protocolFee: BigNumber.from(1), // 1 wei + beneficiary: owner, + owner, + }; + return { owner, defaultIsm, - defaultHook: { - type: HookType.INTERCHAIN_GAS_PAYMASTER, - }, - requiredHook: { - type: HookType.MERKLE_TREE, - }, + defaultHook, + requiredHook, }; }); diff --git a/typescript/infra/config/environments/test/hooks.ts b/typescript/infra/config/environments/test/hooks.ts deleted file mode 100644 index 5fd8296b4a..0000000000 --- a/typescript/infra/config/environments/test/hooks.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; -import { MerkleTreeHookConfig } from '@hyperlane-xyz/sdk/dist/hook/types'; -import { HookType } from '@hyperlane-xyz/sdk/src/hook/types'; -import { objMap } from '@hyperlane-xyz/utils'; - -import { owners } from './owners'; - -export const merkleTree: ChainMap = objMap( - owners, - (_, __) => { - const config: MerkleTreeHookConfig = { - type: HookType.MERKLE_TREE, - }; - return config; - }, -); diff --git a/typescript/infra/config/environments/test/igp.ts b/typescript/infra/config/environments/test/igp.ts index 833c86c24a..6590c933ab 100644 --- a/typescript/infra/config/environments/test/igp.ts +++ b/typescript/infra/config/environments/test/igp.ts @@ -20,19 +20,20 @@ function getGasOracles(local: TestChains) { } export const igp: ChainMap = objMap(owners, (chain, owner) => { + const overhead = Object.fromEntries( + exclude(chain, chainNames).map((remote) => [ + remote, + multisigIsmVerificationCost( + multisigIsm[remote].threshold, + multisigIsm[remote].validators.length, + ), + ]), + ); return { owner, oracleKey: owner, beneficiary: owner, gasOracleType: getGasOracles(chain), - overhead: Object.fromEntries( - exclude(chain, chainNames).map((remote) => [ - remote, - multisigIsmVerificationCost( - multisigIsm[remote].threshold, - multisigIsm[remote].validators.length, - ), - ]), - ), + overhead, }; }); diff --git a/typescript/infra/config/environments/test/index.ts b/typescript/infra/config/environments/test/index.ts index 6004ff82cd..a458f47a08 100644 --- a/typescript/infra/config/environments/test/index.ts +++ b/typescript/infra/config/environments/test/index.ts @@ -8,7 +8,6 @@ import { agents } from './agent'; import { testConfigs } from './chains'; import { core } from './core'; import { storageGasOracleConfig } from './gas-oracle'; -import { merkleTree } from './hooks'; import { igp } from './igp'; import { infra } from './infra'; import { owners } from './owners'; @@ -18,7 +17,6 @@ export const environment: EnvironmentConfig = { chainMetadataConfigs: testConfigs, agents, core, - hook: merkleTree, igp, owners, infra, diff --git a/typescript/infra/config/environments/testnet3/core.ts b/typescript/infra/config/environments/testnet3/core.ts index 82de2834a9..536fa771ff 100644 --- a/typescript/infra/config/environments/testnet3/core.ts +++ b/typescript/infra/config/environments/testnet3/core.ts @@ -1,21 +1,51 @@ -import { ChainMap, CoreConfig, HookType } from '@hyperlane-xyz/sdk'; +import { BigNumber, ethers } from 'ethers'; + +import { + AggregationHookConfig, + ChainMap, + CoreConfig, + HookType, + IgpHookConfig, + MerkleTreeHookConfig, + ProtocolFeeHookConfig, +} from '@hyperlane-xyz/sdk'; import { objMap } from '@hyperlane-xyz/utils'; import { aggregationIsm } from '../../aggregationIsm'; import { Contexts } from '../../contexts'; +import { igp } from './igp'; import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { const defaultIsm = aggregationIsm('testnet3', local, Contexts.Hyperlane); + + const merkleHook: MerkleTreeHookConfig = { + type: HookType.MERKLE_TREE, + }; + + const igpHook: IgpHookConfig = { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + ...igp[local], + }; + + const defaultHook: AggregationHookConfig = { + type: HookType.AGGREGATION, + hooks: [merkleHook, igpHook], + }; + + const requiredHook: ProtocolFeeHookConfig = { + type: HookType.PROTOCOL_FEE, + maxProtocolFee: ethers.utils.parseUnits('1', 'gwei'), // 1 gwei of native token + protocolFee: BigNumber.from(1), // 1 wei + beneficiary: owner, + owner, + }; + return { owner, defaultIsm, - defaultHook: { - type: HookType.INTERCHAIN_GAS_PAYMASTER, - }, - requiredHook: { - type: HookType.MERKLE_TREE, - }, + defaultHook, + requiredHook, }; }); diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index 8d0a800082..bd52c0625a 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -5,10 +5,9 @@ import { ChainMap, HyperlaneCoreDeployer, HyperlaneDeployer, - HyperlaneHookDeployer, HyperlaneIgpDeployer, HyperlaneIsmFactory, - HyperlaneIsmFactoryDeployer, + HyperlaneProxyFactoryDeployer, InterchainAccountDeployer, InterchainQueryDeployer, LiquidityLayerDeployer, @@ -61,24 +60,16 @@ async function main() { let config: ChainMap = {}; let deployer: HyperlaneDeployer; - if (module === Modules.ISM_FACTORY) { + if (module === Modules.PROXY_FACTORY) { config = objMap(envConfig.core, (_chain) => true); - deployer = new HyperlaneIsmFactoryDeployer(multiProvider); + deployer = new HyperlaneProxyFactoryDeployer(multiProvider); } else if (module === Modules.CORE) { config = envConfig.core; const ismFactory = HyperlaneIsmFactory.fromAddressesMap( - getAddresses(environment, Modules.ISM_FACTORY), + getAddresses(environment, Modules.PROXY_FACTORY), multiProvider, ); deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory); - } else if (module === Modules.HOOK) { - if (!envConfig.hook) { - throw new Error(`No hook config for ${environment}`); - } - config = envConfig.hook; - const core = getAddresses(environment, Modules.CORE); - const mailboxes = objMap(core, (_, contracts) => contracts.mailbox); - deployer = new HyperlaneHookDeployer(multiProvider, mailboxes); } else if (module === Modules.INTERCHAIN_GAS_PAYMASTER) { config = envConfig.igp; deployer = new HyperlaneIgpDeployer(multiProvider); @@ -123,7 +114,7 @@ async function main() { true, // use deployer as owner ); const ismFactory = HyperlaneIsmFactory.fromAddressesMap( - getAddresses(environment, Modules.ISM_FACTORY), + getAddresses(environment, Modules.PROXY_FACTORY), multiProvider, ); deployer = new HelloWorldDeployer(multiProvider, ismFactory); diff --git a/typescript/infra/scripts/helloworld/utils.ts b/typescript/infra/scripts/helloworld/utils.ts index e0a45ae0ba..d71480cd19 100644 --- a/typescript/infra/scripts/helloworld/utils.ts +++ b/typescript/infra/scripts/helloworld/utils.ts @@ -14,9 +14,9 @@ import { chainMetadata, filterAddressesToProtocol, hyperlaneEnvironments, + hyperlaneEnvironmentsWithSealevel, igpFactories, } from '@hyperlane-xyz/sdk'; -import { hyperlaneEnvironmentsWithSealevel } from '@hyperlane-xyz/sdk/src'; import { ProtocolType, objMerge } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; diff --git a/typescript/infra/scripts/utils.ts b/typescript/infra/scripts/utils.ts index c9abd0c6f6..d6975fbb6f 100644 --- a/typescript/infra/scripts/utils.ts +++ b/typescript/infra/scripts/utils.ts @@ -42,7 +42,8 @@ import { Role } from '../src/roles'; import { assertContext, assertRole, readJSON } from '../src/utils/utils'; export enum Modules { - ISM_FACTORY = 'ism', + // TODO: change + PROXY_FACTORY = 'ism', CORE = 'core', HOOK = 'hook', INTERCHAIN_GAS_PAYMASTER = 'igp', @@ -55,7 +56,7 @@ export enum Modules { } export const SDK_MODULES = [ - Modules.ISM_FACTORY, + Modules.PROXY_FACTORY, Modules.CORE, Modules.INTERCHAIN_GAS_PAYMASTER, Modules.INTERCHAIN_ACCOUNTS, @@ -323,6 +324,7 @@ export async function getRouterConfig( deployEnvToSdkEnv[environment], multiProvider, ); + // TODO: replace this with core.getRouterConfig const igp = HyperlaneIgp.fromEnvironment( deployEnvToSdkEnv[environment], multiProvider, diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index e02723dc6a..e702dabb56 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -4,7 +4,6 @@ import { ChainMetadata, ChainName, CoreConfig, - HookConfig, HyperlaneEnvironment, IgpConfig, MultiProvider, @@ -38,7 +37,6 @@ export type EnvironmentConfig = { // Each AgentConfig, keyed by the context agents: Partial>; core: ChainMap; - hook?: ChainMap; igp: ChainMap; owners: ChainMap
; infra: InfrastructureConfig; diff --git a/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts b/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts index 92934a52ce..8503847ea6 100644 --- a/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts +++ b/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts @@ -5,8 +5,8 @@ import sinon from 'sinon'; import { TestChains } from '../consts/chains'; import { HyperlaneContractsMap } from '../contracts/types'; +import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; -import { HyperlaneIsmFactoryDeployer } from '../ism/HyperlaneIsmFactoryDeployer'; import { MultiProvider } from '../providers/MultiProvider'; import { testCoreConfig } from '../test/testUtils'; import { ChainMap } from '../types'; @@ -27,8 +27,11 @@ describe('core', async () => { before(async () => { const [signer] = await ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); - const ismFactoryDeployer = new HyperlaneIsmFactoryDeployer(multiProvider); - const ismFactories = await ismFactoryDeployer.deploy(TestChains); + const proxyFactoryDeployer = new HyperlaneProxyFactoryDeployer( + multiProvider, + ); + coreConfig = testCoreConfig(TestChains); + const ismFactories = await proxyFactoryDeployer.deploy(coreConfig); ismFactory = new HyperlaneIsmFactory(ismFactories, multiProvider); }); @@ -36,7 +39,6 @@ describe('core', async () => { const [signer] = await ethers.getSigners(); // This is kind of awkward and really these tests shouldn't live here multiProvider = MultiProvider.createTestMultiProvider({ signer }); - coreConfig = testCoreConfig(TestChains); }); it('deploys', async () => { diff --git a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts index 52c99178c7..8913c162fc 100644 --- a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts +++ b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts @@ -1,19 +1,18 @@ import debug from 'debug'; import { Mailbox, ValidatorAnnounce } from '@hyperlane-xyz/core'; -import { Address, objMap } from '@hyperlane-xyz/utils'; +import { Address } from '@hyperlane-xyz/utils'; -import { HyperlaneContracts, HyperlaneContractsMap } from '../contracts/types'; +import { HyperlaneContracts } from '../contracts/types'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; import { HyperlaneHookDeployer } from '../hook/HyperlaneHookDeployer'; -import { HookFactories } from '../hook/contracts'; import { HookConfig } from '../hook/types'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; import { IsmConfig } from '../ism/types'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; -import { CoreFactories, coreFactories } from './contracts'; +import { CoreAddresses, CoreFactories, coreFactories } from './contracts'; import { CoreConfig } from './types'; export class HyperlaneCoreDeployer extends HyperlaneDeployer< @@ -21,23 +20,27 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< CoreFactories > { startingBlockNumbers: ChainMap = {}; - deployedHooks: HyperlaneContractsMap = {}; + hookDeployer: HyperlaneHookDeployer; constructor( multiProvider: MultiProvider, readonly ismFactory: HyperlaneIsmFactory, - readonly hookDeployer = new HyperlaneHookDeployer(multiProvider, {}), ) { super(multiProvider, coreFactories, { logger: debug('hyperlane:CoreDeployer'), chainTimeoutMs: 1000 * 60 * 10, // 10 minutes }); + this.hookDeployer = new HyperlaneHookDeployer( + multiProvider, + {}, + ismFactory, + ); } async deployMailbox( chain: ChainName, - proxyAdmin: Address, config: CoreConfig, + proxyAdmin: Address, ): Promise { const cachedMailbox = this.readCache( chain, @@ -60,15 +63,17 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< ); const defaultIsm = await this.deployIsm(chain, config.defaultIsm); + + const hookAddresses = { mailbox: mailbox.address, proxyAdmin }; const defaultHook = await this.deployHook( chain, config.defaultHook, - mailbox.address, + hookAddresses, ); const requiredHook = await this.deployHook( chain, config.requiredHook, - mailbox.address, + hookAddresses, ); // configure mailbox @@ -95,17 +100,14 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< async deployHook( chain: ChainName, config: HookConfig, - mailbox: Address, + coreAddresses: Partial, ): Promise
{ const hooks = await this.hookDeployer.deployContracts( chain, config, - mailbox, + coreAddresses, ); - this.deployedHooks[chain] = { - ...hooks, - ...this.deployedHooks[chain], - }; + this.addDeployedContracts(chain, hooks); return hooks[config.type].address; } @@ -125,8 +127,9 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< const proxyAdmin = await this.deployContract(chain, 'proxyAdmin', []); - const mailbox = await this.deployMailbox(chain, proxyAdmin.address, config); + const mailbox = await this.deployMailbox(chain, config, proxyAdmin.address); + // TODO: remove once agents fetch deployedBlock from mailbox const deployedBlock = await mailbox.deployedBlock(); this.startingBlockNumbers[chain] = deployedBlock.toNumber(); @@ -154,15 +157,4 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< validatorAnnounce, }; } - - async deploy( - configMap: ChainMap, - ): Promise> { - const contractsMap = await super.deploy(configMap); - this.deployedContracts = objMap(contractsMap, (chain, core) => ({ - ...core, - ...this.deployedHooks[chain], - })); - return contractsMap; - } } diff --git a/typescript/sdk/src/core/TestCoreDeployer.ts b/typescript/sdk/src/core/TestCoreDeployer.ts index 6e73a492fb..33b91c09c2 100644 --- a/typescript/sdk/src/core/TestCoreDeployer.ts +++ b/typescript/sdk/src/core/TestCoreDeployer.ts @@ -6,7 +6,6 @@ import { import { TestChains } from '../consts/chains'; import { HyperlaneContracts } from '../contracts/types'; -import { HyperlaneHookDeployer } from '../hook/HyperlaneHookDeployer'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; import { MultiProvider } from '../providers/MultiProvider'; import { testCoreConfig } from '../test/testUtils'; @@ -26,8 +25,7 @@ const testCoreFactories = { export class TestCoreDeployer extends HyperlaneCoreDeployer { constructor(public readonly multiProvider: MultiProvider) { const ismFactory = new HyperlaneIsmFactory({}, multiProvider); - const hookDeployer = new HyperlaneHookDeployer(multiProvider, {}); - super(multiProvider, ismFactory, hookDeployer); + super(multiProvider, ismFactory); } // deploy a test ISM instead of a real ISM diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index 86aa9177d5..1ea9c61973 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -94,15 +94,23 @@ export abstract class HyperlaneDeployer< .getProvider(chain) .getBlockNumber(); await runWithTimeout(this.chainTimeoutMs, async () => { - this.deployedContracts[chain] = await this.deployContracts( - chain, - configMap[chain], - ); + const contracts = await this.deployContracts(chain, configMap[chain]); + this.addDeployedContracts(chain, contracts); }); } return this.deployedContracts; } + protected addDeployedContracts( + chain: ChainName, + contracts: HyperlaneContracts, + ) { + this.deployedContracts[chain] = { + ...this.deployedContracts[chain], + ...contracts, + }; + } + protected async runIf( chain: ChainName, address: string, @@ -398,7 +406,7 @@ export abstract class HyperlaneDeployer< ); } - protected writeCache( + writeCache( chain: ChainName, contractName: K, address: Address, @@ -409,7 +417,7 @@ export abstract class HyperlaneDeployer< this.cachedAddresses[chain][contractName] = address; } - protected readCache( + readCache( chain: ChainName, factory: F, contractName: string, diff --git a/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts b/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts new file mode 100644 index 0000000000..f4afd51107 --- /dev/null +++ b/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts @@ -0,0 +1,40 @@ +import debug from 'debug'; + +import { HyperlaneContracts } from '../contracts/types'; +import { MultiProvider } from '../providers/MultiProvider'; +import { ChainName } from '../types'; + +import { HyperlaneDeployer } from './HyperlaneDeployer'; +import { + ProxyFactoryFactories, + proxyFactoryFactories, + proxyFactoryImplementations, +} from './contracts'; + +export class HyperlaneProxyFactoryDeployer extends HyperlaneDeployer< + any, + ProxyFactoryFactories +> { + constructor(multiProvider: MultiProvider) { + super(multiProvider, proxyFactoryFactories, { + logger: debug('hyperlane:IsmFactoryDeployer'), + }); + } + + async deployContracts( + chain: ChainName, + ): Promise> { + const contracts: any = {}; + for (const factoryName of Object.keys( + this.factories, + ) as (keyof ProxyFactoryFactories)[]) { + const factory = await this.deployContract(chain, factoryName, []); + this.verificationInputs[chain].push({ + name: proxyFactoryImplementations[factoryName], + address: await factory.implementation(), + }); + contracts[factoryName] = factory; + } + return contracts as HyperlaneContracts; + } +} diff --git a/typescript/sdk/src/deploy/contracts.ts b/typescript/sdk/src/deploy/contracts.ts new file mode 100644 index 0000000000..542c245c13 --- /dev/null +++ b/typescript/sdk/src/deploy/contracts.ts @@ -0,0 +1,29 @@ +import { + DomainRoutingIsmFactory__factory, + StaticAggregationHookFactory__factory, + StaticAggregationIsmFactory__factory, + StaticMerkleRootMultisigIsmFactory__factory, + StaticMessageIdMultisigIsmFactory__factory, +} from '@hyperlane-xyz/core'; + +export const proxyFactoryFactories = { + merkleRootMultisigIsmFactory: + new StaticMerkleRootMultisigIsmFactory__factory(), + messageIdMultisigIsmFactory: new StaticMessageIdMultisigIsmFactory__factory(), + aggregationIsmFactory: new StaticAggregationIsmFactory__factory(), + aggregationHookFactory: new StaticAggregationHookFactory__factory(), + routingIsmFactory: new DomainRoutingIsmFactory__factory(), +}; + +export type ProxyFactoryFactories = typeof proxyFactoryFactories; + +type ProxyFactoryImplementations = Record; + +// must match contract names for verification +export const proxyFactoryImplementations: ProxyFactoryImplementations = { + merkleRootMultisigIsmFactory: 'StaticMerkleRootMultisigIsm', + messageIdMultisigIsmFactory: 'StaticMessageIdMultisigIsm', + aggregationIsmFactory: 'StaticAggregationIsm', + aggregationHookFactory: 'StaticAggregationHook', + routingIsmFactory: 'DomaingRoutingIsm', +}; diff --git a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts index 6278ee2f6e..2e7f5e1e2d 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts @@ -27,11 +27,6 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< await this.checkBytecodes(chain); await this.checkOverheadInterchainGasPaymaster(chain); await this.checkInterchainGasPaymaster(chain); - - const config = this.configMap[chain]; - if (config.upgrade) { - await this.checkUpgrade(chain, config.upgrade); - } } async checkDomainOwnership(chain: ChainName): Promise { @@ -40,11 +35,6 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< const ownableOverrides: Record = { storageGasOracle: config.oracleKey, }; - if (config.upgrade) { - const timelockController = - this.app.getAddresses(chain).timelockController; - ownableOverrides['proxyAdmin'] = timelockController; - } await super.checkOwnership(chain, config.owner, ownableOverrides); } diff --git a/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts b/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts index 6fa1947edc..c8b6d85fd3 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts @@ -1,12 +1,9 @@ import debug from 'debug'; -import { ethers } from 'ethers'; import { InterchainGasPaymaster, ProxyAdmin, StorageGasOracle, - TimelockController, - TimelockController__factory, } from '@hyperlane-xyz/core'; import { eqAddress } from '@hyperlane-xyz/utils'; @@ -87,27 +84,6 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer< // NB: To share ProxyAdmins with HyperlaneCore, ensure the ProxyAdmin // is loaded into the contract cache. const proxyAdmin = await this.deployContract(chain, 'proxyAdmin', []); - let timelockController: TimelockController; - if (config.upgrade) { - timelockController = await this.deployTimelock( - chain, - config.upgrade.timelock, - ); - await this.transferOwnershipOfContracts( - chain, - timelockController.address, - { proxyAdmin }, - ); - } else { - // mock this for consistent serialization - timelockController = TimelockController__factory.connect( - ethers.constants.AddressZero, - this.multiProvider.getProvider(chain), - ); - await this.transferOwnershipOfContracts(chain, config.owner, { - proxyAdmin, - }); - } const storageGasOracle = await this.deployStorageGasOracle(chain); const interchainGasPaymaster = await this.deployInterchainGasPaymaster( @@ -128,7 +104,6 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer< return { proxyAdmin, - timelockController, storageGasOracle, interchainGasPaymaster, }; diff --git a/typescript/sdk/src/gas/contracts.ts b/typescript/sdk/src/gas/contracts.ts index e33bfb5ede..0dfe6d8f6e 100644 --- a/typescript/sdk/src/gas/contracts.ts +++ b/typescript/sdk/src/gas/contracts.ts @@ -1,14 +1,13 @@ import { InterchainGasPaymaster__factory, + ProxyAdmin__factory, StorageGasOracle__factory, } from '@hyperlane-xyz/core'; -import { proxiedFactories } from '../router/types'; - export const igpFactories = { interchainGasPaymaster: new InterchainGasPaymaster__factory(), storageGasOracle: new StorageGasOracle__factory(), - ...proxiedFactories, + proxyAdmin: new ProxyAdmin__factory(), }; export type IgpFactories = typeof igpFactories; diff --git a/typescript/sdk/src/gas/types.ts b/typescript/sdk/src/gas/types.ts index 65a64a0896..ca0c0485ab 100644 --- a/typescript/sdk/src/gas/types.ts +++ b/typescript/sdk/src/gas/types.ts @@ -3,7 +3,6 @@ import { BigNumber } from 'ethers'; import { InterchainGasPaymaster } from '@hyperlane-xyz/core'; import type { Address } from '@hyperlane-xyz/utils'; -import { UpgradeConfig } from '../deploy/proxy'; import type { CheckerViolation } from '../deploy/types'; import { ChainMap } from '../types'; @@ -16,7 +15,6 @@ export type IgpConfig = { beneficiary: Address; gasOracleType: ChainMap; oracleKey: Address; - upgrade?: UpgradeConfig; overhead: ChainMap; }; diff --git a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts index 614c4dac44..72ff2195eb 100644 --- a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts +++ b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts @@ -1,14 +1,27 @@ import debug from 'debug'; -import { Address } from '@hyperlane-xyz/utils'; +import { + StaticAggregationHook__factory, + StaticProtocolFee, +} from '@hyperlane-xyz/core'; import { HyperlaneContracts } from '../contracts/types'; +import { CoreAddresses } from '../core/contracts'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; +import { HyperlaneIgpDeployer } from '../gas/HyperlaneIgpDeployer'; +import { IgpFactories } from '../gas/contracts'; +import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; import { HookFactories, hookFactories } from './contracts'; -import { HookConfig, HookType } from './types'; +import { + AggregationHookConfig, + HookConfig, + HookType, + IgpHookConfig, + ProtocolFeeHookConfig, +} from './types'; export class HyperlaneHookDeployer extends HyperlaneDeployer< HookConfig, @@ -16,26 +29,94 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< > { constructor( multiProvider: MultiProvider, - readonly mailboxes: ChainMap
, + readonly core: ChainMap>, + readonly ismFactory: HyperlaneIsmFactory, + readonly igpDeployer = new HyperlaneIgpDeployer(multiProvider), ) { super(multiProvider, hookFactories, { - logger: debug('hyperlane:HyperlaneHookDeployer'), + logger: debug('hyperlane:HookDeployer'), }); } async deployContracts( chain: ChainName, config: HookConfig, - mailbox = this.mailboxes[chain], + coreAddresses = this.core[chain], ): Promise> { + // other simple hooks can go here if (config.type === HookType.MERKLE_TREE) { + const mailbox = coreAddresses.mailbox; + if (!mailbox) { + throw new Error(`Mailbox address is required for ${config.type}`); + } const hook = await this.deployContract(chain, config.type, [mailbox]); return { [config.type]: hook } as any; } else if (config.type === HookType.INTERCHAIN_GAS_PAYMASTER) { - const hook = await this.deployContract(chain, config.type, []); + return this.deployIgp(chain, config, coreAddresses) as any; + } else if (config.type === HookType.AGGREGATION) { + return this.deployAggregation(chain, config, coreAddresses); + } else if (config.type === HookType.PROTOCOL_FEE) { + const hook = await this.deployProtocolFee(chain, config); return { [config.type]: hook } as any; - } else { - throw new Error(`Unexpected hook type: ${JSON.stringify(config)}`); } + + throw new Error(`Unexpected hook type: ${JSON.stringify(config)}`); + } + + async deployProtocolFee( + chain: ChainName, + config: ProtocolFeeHookConfig, + ): Promise { + return this.deployContract(chain, HookType.PROTOCOL_FEE, [ + config.maxProtocolFee, + config.protocolFee, + config.beneficiary, + config.owner, + ]); + } + + async deployIgp( + chain: ChainName, + config: IgpHookConfig, + coreAddresses = this.core[chain], + ): Promise> { + if (coreAddresses.proxyAdmin) { + this.igpDeployer.writeCache( + chain, + 'proxyAdmin', + coreAddresses.proxyAdmin, + ); + } + const igpContracts = await this.igpDeployer.deployContracts(chain, config); + this.addDeployedContracts(chain, igpContracts); + return igpContracts; + } + + async deployAggregation( + chain: ChainName, + config: AggregationHookConfig, + coreAddresses = this.core[chain], + ): Promise> { + const aggregatedHooks: string[] = []; + let hooks: any = {}; + for (const hookConfig of config.hooks) { + const subhooks = await this.deployContracts( + chain, + hookConfig, + coreAddresses, + ); + aggregatedHooks.push(subhooks[hookConfig.type].address); + hooks = { ...hooks, ...subhooks }; + } + const address = await this.ismFactory.deployStaticAddressSet( + chain, + this.ismFactory.getContracts(chain).aggregationHookFactory, + aggregatedHooks, + ); + hooks[HookType.AGGREGATION] = StaticAggregationHook__factory.connect( + address, + this.multiProvider.getSignerOrProvider(chain), + ); + return hooks; } } diff --git a/typescript/sdk/src/hook/contracts.ts b/typescript/sdk/src/hook/contracts.ts index c5b3a32997..9e5782d6ec 100644 --- a/typescript/sdk/src/hook/contracts.ts +++ b/typescript/sdk/src/hook/contracts.ts @@ -1,14 +1,17 @@ import { + InterchainGasPaymaster__factory, MerkleTreeHook__factory, - TestInterchainGasPaymaster__factory, + StaticAggregationHook__factory, + StaticProtocolFee__factory, } from '@hyperlane-xyz/core'; import { HookType } from './types'; export const hookFactories = { [HookType.MERKLE_TREE]: new MerkleTreeHook__factory(), - [HookType.INTERCHAIN_GAS_PAYMASTER]: - new TestInterchainGasPaymaster__factory(), + [HookType.PROTOCOL_FEE]: new StaticProtocolFee__factory(), + [HookType.INTERCHAIN_GAS_PAYMASTER]: new InterchainGasPaymaster__factory(), // unused + [HookType.AGGREGATION]: new StaticAggregationHook__factory(), // unused }; export type HookFactories = typeof hookFactories; diff --git a/typescript/sdk/src/hook/types.ts b/typescript/sdk/src/hook/types.ts index 40d9c326b9..72d7a435c5 100644 --- a/typescript/sdk/src/hook/types.ts +++ b/typescript/sdk/src/hook/types.ts @@ -1,14 +1,39 @@ +import { BigNumber } from 'ethers'; + +import { Address } from '@hyperlane-xyz/utils'; + +import { IgpConfig } from '../gas/types'; + export enum HookType { MERKLE_TREE = 'merkleTreeHook', INTERCHAIN_GAS_PAYMASTER = 'interchainGasPaymaster', + AGGREGATION = 'aggregationHook', + PROTOCOL_FEE = 'protocolFee', } export type MerkleTreeHookConfig = { type: HookType.MERKLE_TREE; }; -export type IgpHookConfig = { +export type AggregationHookConfig = { + type: HookType.AGGREGATION; + hooks: Array; +}; + +export type IgpHookConfig = IgpConfig & { type: HookType.INTERCHAIN_GAS_PAYMASTER; }; -export type HookConfig = MerkleTreeHookConfig | IgpHookConfig; +export type ProtocolFeeHookConfig = { + type: HookType.PROTOCOL_FEE; + maxProtocolFee: BigNumber; + protocolFee: BigNumber; + beneficiary: Address; + owner: Address; +}; + +export type HookConfig = + | MerkleTreeHookConfig + | AggregationHookConfig + | IgpHookConfig + | ProtocolFeeHookConfig; diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 7b7cbc77a9..5202e0371e 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -73,6 +73,7 @@ export { } from './core/types'; export { HyperlaneAppChecker } from './deploy/HyperlaneAppChecker'; export { DeployerOptions, HyperlaneDeployer } from './deploy/HyperlaneDeployer'; +export { HyperlaneProxyFactoryDeployer } from './deploy/HyperlaneProxyFactoryDeployer'; export { CheckerViolation, OwnerViolation, @@ -108,12 +109,18 @@ export { IgpViolationType, } from './gas/types'; export { HyperlaneHookDeployer } from './hook/HyperlaneHookDeployer'; -export { HookConfig, HookType, MerkleTreeHookConfig } from './hook/types'; +export { + AggregationHookConfig, + HookConfig, + HookType, + IgpHookConfig, + MerkleTreeHookConfig, + ProtocolFeeHookConfig, +} from './hook/types'; export { HyperlaneIsmFactory, collectValidators, } from './ism/HyperlaneIsmFactory'; -export { HyperlaneIsmFactoryDeployer } from './ism/HyperlaneIsmFactoryDeployer'; export { AggregationIsmConfig, DeployedIsm, @@ -135,19 +142,18 @@ export { AgentLogFormat, AgentLogLevel, AgentSigner, - AgentSignerKeyType, - AgentSignerHexKey, AgentSignerAwsKey, + AgentSignerHexKey, + AgentSignerKeyType, AgentSignerNode, - buildAgentConfig, - RpcConsensusType, - ValidatorConfig, GasPaymentEnforcement, - RelayerConfig, GasPaymentEnforcementPolicyType, + RelayerConfig, + RpcConsensusType, ScraperConfig, + ValidatorConfig, + buildAgentConfig, } from './metadata/agentConfig'; -export { MatchingList } from './metadata/matchingList'; export { ChainMetadata, ChainMetadataSchema, @@ -162,6 +168,7 @@ export { HyperlaneDeploymentArtifacts, HyperlaneDeploymentArtifactsSchema, } from './metadata/deploymentArtifacts'; +export { MatchingList } from './metadata/matchingList'; export { InterchainAccount } from './middleware/account/InterchainAccount'; export { InterchainAccountChecker } from './middleware/account/InterchainAccountChecker'; export { diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts index 5e8cd0b446..cf12f7ee63 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts @@ -4,6 +4,7 @@ import { ethers } from 'hardhat'; import { error } from '@hyperlane-xyz/utils'; import { TestChains } from '../consts/chains'; +import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer'; import { MultiProvider } from '../providers/MultiProvider'; import { randomAddress, randomInt } from '../test/testUtils'; @@ -11,7 +12,6 @@ import { HyperlaneIsmFactory, moduleMatchesConfig, } from './HyperlaneIsmFactory'; -import { HyperlaneIsmFactoryDeployer } from './HyperlaneIsmFactoryDeployer'; import { AggregationIsmConfig, IsmConfig, @@ -79,8 +79,8 @@ describe('HyperlaneIsmFactory', async () => { const multiProvider = MultiProvider.createTestMultiProvider({ signer }); - const deployer = new HyperlaneIsmFactoryDeployer(multiProvider); - const contracts = await deployer.deploy([chain]); + const deployer = new HyperlaneProxyFactoryDeployer(multiProvider); + const contracts = await deployer.deploy({ [chain]: {} }); factory = new HyperlaneIsmFactory(contracts, multiProvider); }); diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts index a3ab7fe5c6..b44a2d6215 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts @@ -7,6 +7,7 @@ import { IInterchainSecurityModule__factory, IMultisigIsm__factory, IRoutingIsm__factory, + StaticAddressSetFactory, StaticAggregationIsm__factory, StaticThresholdAddressSetFactory, } from '@hyperlane-xyz/core'; @@ -22,7 +23,7 @@ import { HyperlaneAddressesMap, HyperlaneContracts } from '../contracts/types'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; -import { IsmFactoryFactories, ismFactoryFactories } from './contracts'; +import { FactoryFactories, factoryFactories } from './contracts'; import { AggregationIsmConfig, DeployedIsm, @@ -32,7 +33,7 @@ import { RoutingIsmConfig, } from './types'; -export class HyperlaneIsmFactory extends HyperlaneApp { +export class HyperlaneIsmFactory extends HyperlaneApp { static fromEnvironment( env: Env, multiProvider: MultiProvider, @@ -50,7 +51,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { ): HyperlaneIsmFactory { const helper = appFromAddressesMapHelper( addressesMap, - ismFactoryFactories, + factoryFactories, multiProvider, ); return new HyperlaneIsmFactory( @@ -112,7 +113,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { ? this.getContracts(chain).merkleRootMultisigIsmFactory : this.getContracts(chain).messageIdMultisigIsmFactory; - const address = await this.deployThresholdFactory( + const address = await this.deployStaticAddressSet( chain, multisigIsmFactory, config.validators, @@ -179,7 +180,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { for (const module of config.modules) { addresses.push((await this.deploy(chain, module)).address); } - const address = await this.deployThresholdFactory( + const address = await this.deployStaticAddressSet( chain, aggregationIsmFactory, addresses, @@ -188,24 +189,31 @@ export class HyperlaneIsmFactory extends HyperlaneApp { return IAggregationIsm__factory.connect(address, signer); } - private async deployThresholdFactory( + async deployStaticAddressSet( chain: ChainName, - factory: StaticThresholdAddressSetFactory, + factory: StaticThresholdAddressSetFactory | StaticAddressSetFactory, values: Address[], - threshold: number, + threshold = values.length, ): Promise
{ const sorted = [...values].sort(); - const address = await factory.getAddress(sorted, threshold); - const provider = this.multiProvider.getProvider(chain); - const code = await provider.getCode(address); + const address = await factory['getAddress(address[],uint8)']( + sorted, + threshold, + ); + const code = await this.multiProvider.getProvider(chain).getCode(address); if (code === '0x') { this.logger( `Deploying new ${threshold} of ${values.length} address set to ${chain}`, ); const overrides = this.multiProvider.getTransactionOverrides(chain); - const hash = await factory.deploy(sorted, threshold, overrides); + const hash = await factory['deploy(address[],uint8)']( + sorted, + threshold, + overrides, + ); await this.multiProvider.handleTx(chain, hash); + // TODO: add proxy verification artifact? } else { this.logger( `Recovered ${threshold} of ${values.length} address set on ${chain}`, @@ -331,7 +339,7 @@ export async function moduleMatchesConfig( moduleAddress: Address, config: IsmConfig, multiProvider: MultiProvider, - contracts: HyperlaneContracts, + contracts: HyperlaneContracts, _origin?: ChainName, ): Promise { if (typeof config === 'string') { diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactoryDeployer.ts b/typescript/sdk/src/ism/HyperlaneIsmFactoryDeployer.ts deleted file mode 100644 index 6afd8f2ade..0000000000 --- a/typescript/sdk/src/ism/HyperlaneIsmFactoryDeployer.ts +++ /dev/null @@ -1,80 +0,0 @@ -import debug from 'debug'; - -import { isObject } from '@hyperlane-xyz/utils'; - -import { HyperlaneContracts, HyperlaneContractsMap } from '../contracts/types'; -import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; -import { MultiProvider } from '../providers/MultiProvider'; -import { ChainMap, ChainName } from '../types'; - -import { IsmFactoryFactories, ismFactoryFactories } from './contracts'; - -export class HyperlaneIsmFactoryDeployer extends HyperlaneDeployer< - boolean, - IsmFactoryFactories -> { - constructor(multiProvider: MultiProvider) { - super(multiProvider, ismFactoryFactories, { - logger: debug('hyperlane:IsmFactoryDeployer'), - }); - } - - async deploy( - config: ChainName[] | ChainMap, - ): Promise> { - if (isObject(config)) { - return super.deploy(config as ChainMap); - } else { - return super.deploy( - Object.fromEntries((config as ChainName[]).map((c) => [c, true])), - ); - } - } - - async deployContracts( - chain: ChainName, - ): Promise> { - const merkleRootMultisigIsmFactory = await this.deployContract( - chain, - 'merkleRootMultisigIsmFactory', - [], - ); - this.verificationInputs[chain].push({ - name: 'StaticMerkleRootMultisigIsm', - address: await merkleRootMultisigIsmFactory.implementation(), - }); - const messageIdMultisigIsmFactory = await this.deployContract( - chain, - 'messageIdMultisigIsmFactory', - [], - ); - this.verificationInputs[chain].push({ - name: 'StaticMessageIdMultisigIsm', - address: await messageIdMultisigIsmFactory.implementation(), - }); - const aggregationIsmFactory = await this.deployContract( - chain, - 'aggregationIsmFactory', - [], - ); - this.verificationInputs[chain].push({ - name: 'StaticAggregationIsm', - address: await aggregationIsmFactory.implementation(), - }); - const routingIsmFactory = await this.deployContract( - chain, - 'routingIsmFactory', - [], - ); - this.verificationInputs[chain].push({ - name: 'DomaingRoutingIsm', - address: await routingIsmFactory.implementation(), - }); - return { - merkleRootMultisigIsmFactory, - messageIdMultisigIsmFactory, - aggregationIsmFactory, - routingIsmFactory, - }; - } -} diff --git a/typescript/sdk/src/ism/contracts.ts b/typescript/sdk/src/ism/contracts.ts index dc85245865..f5517dd462 100644 --- a/typescript/sdk/src/ism/contracts.ts +++ b/typescript/sdk/src/ism/contracts.ts @@ -1,16 +1,18 @@ import { DomainRoutingIsmFactory__factory, + StaticAggregationHookFactory__factory, StaticAggregationIsmFactory__factory, StaticMerkleRootMultisigIsmFactory__factory, StaticMessageIdMultisigIsmFactory__factory, } from '@hyperlane-xyz/core'; -export const ismFactoryFactories = { +export const factoryFactories = { merkleRootMultisigIsmFactory: new StaticMerkleRootMultisigIsmFactory__factory(), messageIdMultisigIsmFactory: new StaticMessageIdMultisigIsmFactory__factory(), aggregationIsmFactory: new StaticAggregationIsmFactory__factory(), + aggregationHookFactory: new StaticAggregationHookFactory__factory(), routingIsmFactory: new DomainRoutingIsmFactory__factory(), }; -export type IsmFactoryFactories = typeof ismFactoryFactories; +export type FactoryFactories = typeof factoryFactories; diff --git a/typescript/sdk/src/test/testUtils.ts b/typescript/sdk/src/test/testUtils.ts index 33880ae64d..1f78cc7d37 100644 --- a/typescript/sdk/src/test/testUtils.ts +++ b/typescript/sdk/src/test/testUtils.ts @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { BigNumber, ethers } from 'ethers'; import { Address, objMap } from '@hyperlane-xyz/utils'; @@ -66,10 +66,14 @@ export function testCoreConfig(chains: ChainName[]): ChainMap { ), }, defaultHook: { - type: HookType.INTERCHAIN_GAS_PAYMASTER, + type: HookType.MERKLE_TREE, }, requiredHook: { - type: HookType.MERKLE_TREE, + type: HookType.PROTOCOL_FEE, + maxProtocolFee: ethers.utils.parseUnits('1', 'gwei'), // 1 gwei of native token + protocolFee: BigNumber.from(1), // 1 wei + beneficiary: nonZeroAddress, + owner: nonZeroAddress, }, }, ]),