Merkle Tree Hook Deployer changes (#2747)
### Description - Interceptor is the pair <hook, ism> - `MerkleTreeInterceptorDeployer` for deploying merkle root hook and the `StaticMerkleRootMultisigIsm` on chain ### Drive-by changes - None ### Related issues - addresses https://github.com/hyperlane-xyz/issues/issues/621 ### Backward compatibility Yes ### Testing Manualpull/2774/head
parent
8e4f2bbe18
commit
f0a45bd6b1
@ -1,35 +0,0 @@ |
||||
import { |
||||
ChainMap, |
||||
HookConfig, |
||||
HookContractType, |
||||
MessageHookConfig, |
||||
NoMetadataIsmConfig, |
||||
filterByChains, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { objMap } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { owners } from './owners'; |
||||
|
||||
const chainNameFilter = new Set(['ethereum', 'optimism']); |
||||
const filteredOwnersResult = filterByChains<string>(owners, chainNameFilter); |
||||
|
||||
export const hooks: ChainMap<HookConfig> = objMap( |
||||
filteredOwnersResult, |
||||
(chain) => { |
||||
if (chain === 'ethereum') { |
||||
const hookConfig: MessageHookConfig = { |
||||
hookContractType: HookContractType.HOOK, |
||||
nativeBridge: '0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1', |
||||
remoteIsm: '0x4c5859f0f772848b2d91f1d83e2fe57935348029', // dummy, remoteISM should be deployed first
|
||||
destination: 'optimism', |
||||
}; |
||||
return hookConfig; |
||||
} else { |
||||
const ismConfig: NoMetadataIsmConfig = { |
||||
hookContractType: HookContractType.ISM, |
||||
nativeBridge: '0x4200000000000000000000000000000000000007', |
||||
}; |
||||
return ismConfig; |
||||
} |
||||
}, |
||||
); |
@ -1,35 +1,16 @@ |
||||
import { |
||||
ChainMap, |
||||
HookConfig, |
||||
HookContractType, |
||||
MessageHookConfig, |
||||
NoMetadataIsmConfig, |
||||
filterByChains, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
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'; |
||||
|
||||
const chainNameFilter = new Set(['test1', 'test2']); |
||||
const filteredOwnersResult = filterByChains<string>(owners, chainNameFilter); |
||||
|
||||
export const hooks: ChainMap<HookConfig> = objMap( |
||||
filteredOwnersResult, |
||||
(chain) => { |
||||
if (chain === 'test1') { |
||||
const hookConfig: MessageHookConfig = { |
||||
hookContractType: HookContractType.HOOK, |
||||
nativeBridge: '0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1', |
||||
remoteIsm: '0x4c5859f0f772848b2d91f1d83e2fe57935348029', // dummy, remoteISM should be deployed first
|
||||
destination: 'test2', |
||||
}; |
||||
return hookConfig; |
||||
} else { |
||||
const ismConfig: NoMetadataIsmConfig = { |
||||
hookContractType: HookContractType.ISM, |
||||
nativeBridge: '0x4200000000000000000000000000000000000007', |
||||
}; |
||||
return ismConfig; |
||||
} |
||||
export const merkleTree: ChainMap<MerkleTreeHookConfig> = objMap( |
||||
owners, |
||||
(_, __) => { |
||||
const config: MerkleTreeHookConfig = { |
||||
type: HookType.MERKLE_TREE_HOOK, |
||||
}; |
||||
return config; |
||||
}, |
||||
); |
||||
|
@ -1,34 +0,0 @@ |
||||
import { |
||||
ChainMap, |
||||
HookConfig, |
||||
HookContractType, |
||||
MessageHookConfig, |
||||
NoMetadataIsmConfig, |
||||
filterByChains, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { objMap } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { owners } from './owners'; |
||||
|
||||
const chainNameFilter = new Set(['goerli', 'optimismgoerli']); |
||||
const filteredOwnersResult = filterByChains<string>(owners, chainNameFilter); |
||||
|
||||
export const hooks: ChainMap<HookConfig> = objMap( |
||||
filteredOwnersResult, |
||||
(chain) => { |
||||
if (chain === 'goerli') { |
||||
const hookConfig: MessageHookConfig = { |
||||
hookContractType: HookContractType.HOOK, |
||||
nativeBridge: '0x5086d1eEF304eb5284A0f6720f79403b4e9bE294', |
||||
destination: 'optimismgoerli', |
||||
}; |
||||
return hookConfig; |
||||
} else { |
||||
const ismConfig: NoMetadataIsmConfig = { |
||||
hookContractType: HookContractType.ISM, |
||||
nativeBridge: '0x4200000000000000000000000000000000000007', |
||||
}; |
||||
return ismConfig; |
||||
} |
||||
}, |
||||
); |
@ -1,96 +1,41 @@ |
||||
import { ChainMap, MultisigConfig } from '@hyperlane-xyz/sdk'; |
||||
import { |
||||
ChainMap, |
||||
ChainName, |
||||
MultisigIsmConfig, |
||||
defaultMultisigIsmConfigs, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { objFilter, objMap } from '@hyperlane-xyz/utils'; |
||||
|
||||
export const rcMultisigIsmConfigs: ChainMap<MultisigConfig> = { |
||||
// ----------------- Mainnets -----------------
|
||||
celo: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0xe7a82e210f512f8e9900d6bc2acbf7981c63e66e', // abacus
|
||||
], |
||||
}, |
||||
ethereum: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0xaea1adb1c687b061e5b60b9da84cb69e7b5fab44', // abacus
|
||||
], |
||||
}, |
||||
avalanche: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x706976391e23dea28152e0207936bd942aba01ce', // abacus
|
||||
], |
||||
}, |
||||
polygon: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0xef372f6ff7775989b3ac884506ee31c79638c989', // abacus
|
||||
], |
||||
}, |
||||
bsc: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x0823081031a4a6f97c6083775c191d17ca96d0ab', // abacus
|
||||
], |
||||
}, |
||||
arbitrum: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x1a95b35fb809d57faf1117c1cc29a6c5df289df1', // abacus
|
||||
], |
||||
}, |
||||
optimism: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x60e938bf280bbc21bacfd8bf435459d9003a8f98', // abacus
|
||||
], |
||||
}, |
||||
moonbeam: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x0df7140811e309dc69638352545151ebb9d5e0fd', // abacus
|
||||
], |
||||
}, |
||||
gnosis: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x15f48e78092a4f79febface509cfd76467c6cdbb', // abacus
|
||||
], |
||||
}, |
||||
// ----------------- Testnets -----------------
|
||||
alfajores: { |
||||
threshold: 1, |
||||
validators: ['0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de'], |
||||
}, |
||||
fuji: { |
||||
threshold: 1, |
||||
validators: ['0xd81ba169170a9b582812cf0e152d2c168572e21f'], |
||||
}, |
||||
mumbai: { |
||||
threshold: 1, |
||||
validators: ['0xb537c4ce34e1cad718be52aa30b095e416eae46a'], |
||||
}, |
||||
bsctestnet: { |
||||
threshold: 1, |
||||
validators: ['0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb'], |
||||
}, |
||||
goerli: { |
||||
threshold: 1, |
||||
validators: ['0x9597ddb4ad2af237665559574b820596bb77ae7a'], |
||||
}, |
||||
sepolia: { |
||||
threshold: 1, |
||||
validators: ['0x183f15924f3a464c54c9393e8d268eb44d2b208c'], |
||||
}, |
||||
moonbasealpha: { |
||||
threshold: 1, |
||||
validators: ['0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d'], |
||||
}, |
||||
optimismgoerli: { |
||||
threshold: 1, |
||||
validators: ['0x1d6798671ac532f2bf30c3a5230697a4695705e4'], |
||||
}, |
||||
arbitrumgoerli: { |
||||
threshold: 1, |
||||
validators: ['0x6d13367c7cd713a4ea79a2552adf824bf1ecdd5e'], |
||||
}, |
||||
import { DeployEnvironment } from '../src/config'; |
||||
|
||||
import { Contexts } from './contexts'; |
||||
import { supportedChainNames as mainnet2Chains } from './environments/mainnet2/chains'; |
||||
import { chainNames as testChains } from './environments/test/chains'; |
||||
import { supportedChainNames as testnet3Chains } from './environments/testnet3/chains'; |
||||
import { rcMultisigIsmConfigs } from './rcMultisigIsmConfigs'; |
||||
|
||||
const chains = { |
||||
mainnet2: mainnet2Chains, |
||||
testnet3: testnet3Chains, |
||||
test: testChains, |
||||
}; |
||||
|
||||
export const multisigIsms = ( |
||||
env: DeployEnvironment, |
||||
local: ChainName, |
||||
type: MultisigIsmConfig['type'], |
||||
context: Contexts, |
||||
): ChainMap<MultisigIsmConfig> => |
||||
objMap( |
||||
objFilter( |
||||
context === Contexts.ReleaseCandidate |
||||
? rcMultisigIsmConfigs |
||||
: defaultMultisigIsmConfigs, |
||||
(chain, config): config is MultisigIsmConfig => |
||||
chain !== local && chains[env].includes(chain), |
||||
), |
||||
(_, config) => ({ |
||||
...config, |
||||
type, |
||||
}), |
||||
); |
||||
|
@ -0,0 +1,96 @@ |
||||
import { ChainMap, MultisigConfig } from '@hyperlane-xyz/sdk'; |
||||
|
||||
export const rcMultisigIsmConfigs: ChainMap<MultisigConfig> = { |
||||
// ----------------- Mainnets -----------------
|
||||
celo: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0xe7a82e210f512f8e9900d6bc2acbf7981c63e66e', // abacus
|
||||
], |
||||
}, |
||||
ethereum: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0xaea1adb1c687b061e5b60b9da84cb69e7b5fab44', // abacus
|
||||
], |
||||
}, |
||||
avalanche: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x706976391e23dea28152e0207936bd942aba01ce', // abacus
|
||||
], |
||||
}, |
||||
polygon: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0xef372f6ff7775989b3ac884506ee31c79638c989', // abacus
|
||||
], |
||||
}, |
||||
bsc: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x0823081031a4a6f97c6083775c191d17ca96d0ab', // abacus
|
||||
], |
||||
}, |
||||
arbitrum: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x1a95b35fb809d57faf1117c1cc29a6c5df289df1', // abacus
|
||||
], |
||||
}, |
||||
optimism: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x60e938bf280bbc21bacfd8bf435459d9003a8f98', // abacus
|
||||
], |
||||
}, |
||||
moonbeam: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x0df7140811e309dc69638352545151ebb9d5e0fd', // abacus
|
||||
], |
||||
}, |
||||
gnosis: { |
||||
threshold: 1, |
||||
validators: [ |
||||
'0x15f48e78092a4f79febface509cfd76467c6cdbb', // abacus
|
||||
], |
||||
}, |
||||
// ----------------- Testnets -----------------
|
||||
alfajores: { |
||||
threshold: 1, |
||||
validators: ['0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de'], |
||||
}, |
||||
fuji: { |
||||
threshold: 1, |
||||
validators: ['0xd81ba169170a9b582812cf0e152d2c168572e21f'], |
||||
}, |
||||
mumbai: { |
||||
threshold: 1, |
||||
validators: ['0xb537c4ce34e1cad718be52aa30b095e416eae46a'], |
||||
}, |
||||
bsctestnet: { |
||||
threshold: 1, |
||||
validators: ['0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb'], |
||||
}, |
||||
goerli: { |
||||
threshold: 1, |
||||
validators: ['0x9597ddb4ad2af237665559574b820596bb77ae7a'], |
||||
}, |
||||
sepolia: { |
||||
threshold: 1, |
||||
validators: ['0x183f15924f3a464c54c9393e8d268eb44d2b208c'], |
||||
}, |
||||
moonbasealpha: { |
||||
threshold: 1, |
||||
validators: ['0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d'], |
||||
}, |
||||
optimismgoerli: { |
||||
threshold: 1, |
||||
validators: ['0x1d6798671ac532f2bf30c3a5230697a4695705e4'], |
||||
}, |
||||
arbitrumgoerli: { |
||||
threshold: 1, |
||||
validators: ['0x6d13367c7cd713a4ea79a2552adf824bf1ecdd5e'], |
||||
}, |
||||
}; |
@ -1,104 +1,55 @@ |
||||
import debug from 'debug'; |
||||
|
||||
import { objFilter, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; |
||||
import { Address } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { |
||||
HyperlaneAddressesMap, |
||||
HyperlaneContracts, |
||||
HyperlaneContractsMap, |
||||
} from '../contracts/types'; |
||||
import { CoreFactories } from '../core/contracts'; |
||||
import { HyperlaneContracts } from '../contracts/types'; |
||||
import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; |
||||
import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; |
||||
import { MultiProvider } from '../providers/MultiProvider'; |
||||
import { ChainMap, ChainName } from '../types'; |
||||
|
||||
import { isHookConfig, isISMConfig } from './config'; |
||||
import { OptimismHookFactories, optimismHookFactories } from './contracts'; |
||||
import { HookConfig, MessageHookConfig, NoMetadataIsmConfig } from './types'; |
||||
import { |
||||
HookFactories, |
||||
MerkleTreeHookFactory, |
||||
hookFactories, |
||||
} from './contracts'; |
||||
import { HookConfig, HookType } from './types'; |
||||
|
||||
// TODO: make generic from optimism hooks
|
||||
export class HyperlaneHookDeployer extends HyperlaneDeployer< |
||||
HookConfig, |
||||
OptimismHookFactories |
||||
HookFactories |
||||
> { |
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
public core: HyperlaneAddressesMap<CoreFactories>, |
||||
readonly ismFactory: HyperlaneIsmFactory, |
||||
readonly mailboxes: ChainMap<Address>, |
||||
) { |
||||
super(multiProvider, optimismHookFactories, { |
||||
logger: debug('hyperlane:HookDeployer'), |
||||
super(multiProvider, hookFactories, { |
||||
logger: debug('hyperlane:HyperlaneHookDeployer'), |
||||
}); |
||||
} |
||||
|
||||
async deploy( |
||||
configMap: ChainMap<HookConfig>, |
||||
): Promise<HyperlaneContractsMap<OptimismHookFactories>> { |
||||
// deploy ISMs first
|
||||
const ismConfigMap = objFilter( |
||||
configMap, |
||||
(_, config): config is NoMetadataIsmConfig => isISMConfig(config), |
||||
); |
||||
await super.deploy(ismConfigMap); |
||||
|
||||
// deploy Hooks next
|
||||
const hookConfigMap = objFilter( |
||||
configMap, |
||||
(_, config): config is MessageHookConfig => isHookConfig(config), |
||||
); |
||||
await super.deploy(hookConfigMap); |
||||
|
||||
// configure ISMs with authorized hooks
|
||||
await promiseObjAll( |
||||
objMap(hookConfigMap, (hookChain, hookConfig) => { |
||||
const hookAddress = this.deployedContracts[hookChain].hook.address; |
||||
const ism = this.deployedContracts[hookConfig.destination].ism; |
||||
return this.multiProvider.handleTx( |
||||
hookConfig.destination, |
||||
ism.setAuthorizedHook(hookAddress), |
||||
); |
||||
}), |
||||
); |
||||
|
||||
return this.deployedContracts; |
||||
} |
||||
|
||||
async deployContracts( |
||||
chain: ChainName, |
||||
config: HookConfig, |
||||
): Promise<HyperlaneContracts<OptimismHookFactories>> { |
||||
this.logger(`Deploying ${config.hookContractType} on ${chain}`); |
||||
if (isISMConfig(config)) { |
||||
const ism = await this.multiProvider.handleDeploy( |
||||
chain, |
||||
this.factories.ism, |
||||
[config.nativeBridge], |
||||
); |
||||
// @ts-ignore
|
||||
return { ism, hook: undefined }; |
||||
} else if (isHookConfig(config)) { |
||||
const remoteIsm = this.deployedContracts[config.destination].ism; |
||||
if (!remoteIsm) { |
||||
throw new Error(`Remote ISM not found for ${config.destination}`); |
||||
} |
||||
|
||||
const mailbox = this.core[chain].mailbox; |
||||
if (!mailbox) { |
||||
throw new Error(`Mailbox not found for ${chain}`); |
||||
} |
||||
const destinationDomain = this.multiProvider.getDomainId( |
||||
config.destination, |
||||
); |
||||
|
||||
const hook = await this.multiProvider.handleDeploy( |
||||
chain, |
||||
this.factories.hook, |
||||
[mailbox, destinationDomain, remoteIsm.address, config.nativeBridge], |
||||
); |
||||
|
||||
// @ts-ignore
|
||||
return { hook, ism: undefined }; |
||||
): Promise<HyperlaneContracts<HookFactories>> { |
||||
if (config.type === HookType.MERKLE_TREE_HOOK) { |
||||
return this.deployMerkleTreeHook(chain, config); |
||||
} else { |
||||
throw new Error(`Invalid config type: ${config}`); |
||||
throw new Error(`Unsupported hook type: ${config.type}`); |
||||
} |
||||
} |
||||
|
||||
async deployMerkleTreeHook( |
||||
chain: ChainName, |
||||
_: HookConfig, |
||||
): Promise<HyperlaneContracts<MerkleTreeHookFactory>> { |
||||
this.logger(`Deploying MerkleTreeHook to ${chain}`); |
||||
const merkleTreeHook = await this.deployContract(chain, 'merkleTreeHook', [ |
||||
this.mailboxes[chain], |
||||
]); |
||||
return { |
||||
merkleTreeHook: merkleTreeHook, |
||||
}; |
||||
} |
||||
} |
||||
|
@ -1,14 +0,0 @@ |
||||
import { |
||||
HookConfig, |
||||
HookContractType, |
||||
MessageHookConfig, |
||||
NoMetadataIsmConfig, |
||||
} from './types'; |
||||
|
||||
export const isISMConfig = ( |
||||
config: HookConfig, |
||||
): config is NoMetadataIsmConfig => |
||||
config.hookContractType === HookContractType.ISM; |
||||
|
||||
export const isHookConfig = (config: HookConfig): config is MessageHookConfig => |
||||
config.hookContractType === HookContractType.HOOK; |
@ -1,18 +1,9 @@ |
||||
import { |
||||
AbstractMessageIdAuthHook__factory, |
||||
AbstractMessageIdAuthorizedIsm__factory, |
||||
OPStackHook__factory, |
||||
OPStackIsm__factory, |
||||
} from '@hyperlane-xyz/core'; |
||||
import { MerkleTreeHook__factory } from '@hyperlane-xyz/core'; |
||||
|
||||
export type HookFactories = { |
||||
hook: AbstractMessageIdAuthHook__factory; |
||||
ism: AbstractMessageIdAuthorizedIsm__factory; |
||||
export const merkleTreeHookFactory = { |
||||
merkleTreeHook: new MerkleTreeHook__factory(), |
||||
}; |
||||
export const hookFactories = merkleTreeHookFactory; |
||||
export type MerkleTreeHookFactory = typeof merkleTreeHookFactory; |
||||
|
||||
export const optimismHookFactories = { |
||||
hook: new OPStackHook__factory(), |
||||
ism: new OPStackIsm__factory(), |
||||
}; |
||||
|
||||
export type OptimismHookFactories = typeof optimismHookFactories; |
||||
export type HookFactories = MerkleTreeHookFactory; |
||||
|
@ -1,22 +1,9 @@ |
||||
import type { Address } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { ChainName } from '../types'; |
||||
|
||||
export enum HookContractType { |
||||
HOOK = 'hook', |
||||
ISM = 'ism', |
||||
export enum HookType { |
||||
MERKLE_TREE_HOOK = 'merkleTreeHook', |
||||
} |
||||
|
||||
export type MessageHookConfig = { |
||||
hookContractType: HookContractType.HOOK; |
||||
nativeBridge: Address; |
||||
remoteIsm?: Address; |
||||
destination: ChainName; |
||||
}; |
||||
|
||||
export type NoMetadataIsmConfig = { |
||||
hookContractType: HookContractType.ISM; |
||||
nativeBridge: Address; |
||||
export type MerkleTreeHookConfig = { |
||||
type: HookType.MERKLE_TREE_HOOK; |
||||
}; |
||||
|
||||
export type HookConfig = MessageHookConfig | NoMetadataIsmConfig; |
||||
export type HookConfig = MerkleTreeHookConfig; |
||||
|
Loading…
Reference in new issue