Add checkers for middlewares (#2030)
### Description This PR adds apps and checkers for the ICA and IQS middlewares. ### Drive-by changes - Remove redundant lines from writeJSON - Automatically detect ownables in checkers - Automatically detect proxied contracts in checkers - Deploy ICA and IQS to testnet3 ### Backward compatibility _Are these changes backward compatible?_ Yes _Are there any infrastructure implications, e.g. changes that would prohibit deploying older commits using this infra tooling?_ None ### Testing _What kind of testing have these changes undergone?_ Unit Tests --------- Co-authored-by: Yorke Rhodes <yorke@hyperlane.xyz>test-sol-fixes
parent
08f3b9b32b
commit
17b400806e
@ -1 +1 @@ |
||||
Subproject commit 3b443c79bcb7671ef3d49ba7d6765c9adb139789 |
||||
Subproject commit 591edb07f44e67d95220e6c89d669f11cde68501 |
@ -0,0 +1,25 @@ |
||||
import { |
||||
ChainMap, |
||||
InterchainAccount, |
||||
InterchainAccountChecker, |
||||
InterchainAccountConfig, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { types } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { HyperlaneAppGovernor } from '../../govern/HyperlaneAppGovernor'; |
||||
|
||||
export class InterchainAccountGovernor extends HyperlaneAppGovernor< |
||||
InterchainAccount, |
||||
InterchainAccountConfig |
||||
> { |
||||
constructor( |
||||
checker: InterchainAccountChecker, |
||||
owners: ChainMap<types.Address>, |
||||
) { |
||||
super(checker, owners); |
||||
} |
||||
|
||||
protected async mapViolationsToCalls() { |
||||
throw new Error('governor not implemented for account middleware'); |
||||
} |
||||
} |
@ -0,0 +1,25 @@ |
||||
import { |
||||
ChainMap, |
||||
InterchainQuery, |
||||
InterchainQueryChecker, |
||||
InterchainQueryConfig, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { types } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { HyperlaneAppGovernor } from '../../govern/HyperlaneAppGovernor'; |
||||
|
||||
export class InterchainQueryGovernor extends HyperlaneAppGovernor< |
||||
InterchainQuery, |
||||
InterchainQueryConfig |
||||
> { |
||||
constructor( |
||||
checker: InterchainQueryChecker, |
||||
owners: ChainMap<types.Address>, |
||||
) { |
||||
super(checker, owners); |
||||
} |
||||
|
||||
protected async mapViolationsToCalls() { |
||||
throw new Error('governor not implemented for query middleware'); |
||||
} |
||||
} |
@ -1,53 +1,53 @@ |
||||
{ |
||||
"test1": { |
||||
"storageGasOracle": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", |
||||
"validatorAnnounce": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", |
||||
"proxyAdmin": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", |
||||
"storageGasOracle": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1", |
||||
"validatorAnnounce": "0x0165878A594ca255338adfa4d48449f69242Eb8F", |
||||
"proxyAdmin": "0x59b670e9fA9D0A427751Af201D676719a970857b", |
||||
"mailbox": { |
||||
"kind": "Transparent", |
||||
"proxy": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", |
||||
"implementation": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" |
||||
"proxy": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", |
||||
"implementation": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" |
||||
}, |
||||
"interchainGasPaymaster": { |
||||
"kind": "Transparent", |
||||
"proxy": "0x0165878A594ca255338adfa4d48449f69242Eb8F", |
||||
"implementation": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" |
||||
"proxy": "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f", |
||||
"implementation": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" |
||||
}, |
||||
"defaultIsmInterchainGasPaymaster": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", |
||||
"defaultIsmInterchainGasPaymaster": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", |
||||
"multisigIsm": "0x5FbDB2315678afecb367f032d93F642f64180aa3" |
||||
}, |
||||
"test2": { |
||||
"storageGasOracle": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed", |
||||
"validatorAnnounce": "0x09635F643e140090A9A8Dcd712eD6285858ceBef", |
||||
"proxyAdmin": "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE", |
||||
"storageGasOracle": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E", |
||||
"validatorAnnounce": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", |
||||
"proxyAdmin": "0x67d269191c92Caf3cD7723F116c85e6E9bf55933", |
||||
"mailbox": { |
||||
"kind": "Transparent", |
||||
"proxy": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", |
||||
"implementation": "0x4A679253410272dd5232B3Ff7cF5dbB88f295319" |
||||
"proxy": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", |
||||
"implementation": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" |
||||
}, |
||||
"interchainGasPaymaster": { |
||||
"kind": "Transparent", |
||||
"proxy": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", |
||||
"implementation": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c" |
||||
"proxy": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB", |
||||
"implementation": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" |
||||
}, |
||||
"defaultIsmInterchainGasPaymaster": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1", |
||||
"multisigIsm": "0x9A676e781A523b5d0C0e43731313A708CB607508" |
||||
"defaultIsmInterchainGasPaymaster": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", |
||||
"multisigIsm": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" |
||||
}, |
||||
"test3": { |
||||
"storageGasOracle": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB", |
||||
"validatorAnnounce": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528", |
||||
"proxyAdmin": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690", |
||||
"storageGasOracle": "0x95401dc811bb5740090279Ba06cfA8fcF6113778", |
||||
"validatorAnnounce": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", |
||||
"proxyAdmin": "0xf5059a5D33d5853360D16C683c16e67980206f36", |
||||
"mailbox": { |
||||
"kind": "Transparent", |
||||
"proxy": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49", |
||||
"implementation": "0x998abeb3E57409262aE5b751f60747921B33613E" |
||||
"proxy": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c", |
||||
"implementation": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed" |
||||
}, |
||||
"interchainGasPaymaster": { |
||||
"kind": "Transparent", |
||||
"proxy": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", |
||||
"implementation": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" |
||||
"proxy": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49", |
||||
"implementation": "0x998abeb3E57409262aE5b751f60747921B33613E" |
||||
}, |
||||
"defaultIsmInterchainGasPaymaster": "0x851356ae760d987E095750cCeb3bC6014560891C", |
||||
"multisigIsm": "0xc5a5C42992dECbae36851359345FE25997F5C42d" |
||||
"defaultIsmInterchainGasPaymaster": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", |
||||
"multisigIsm": "0x9A676e781A523b5d0C0e43731313A708CB607508" |
||||
} |
||||
} |
||||
|
@ -0,0 +1,20 @@ |
||||
import { HyperlaneContracts } from '../contracts'; |
||||
import { HyperlaneRouterChecker } from '../router/HyperlaneRouterChecker'; |
||||
import { RouterApp } from '../router/RouterApps'; |
||||
import { RouterConfig } from '../router/types'; |
||||
import { ChainName } from '../types'; |
||||
|
||||
export abstract class MiddlewareRouterChecker< |
||||
MiddlewareRouterApp extends RouterApp<MiddlewareRouterContracts>, |
||||
MiddlewareRouterConfig extends RouterConfig, |
||||
MiddlewareRouterContracts extends HyperlaneContracts, |
||||
> extends HyperlaneRouterChecker< |
||||
MiddlewareRouterApp, |
||||
MiddlewareRouterConfig, |
||||
MiddlewareRouterContracts |
||||
> { |
||||
async checkChain(chain: ChainName): Promise<void> { |
||||
await super.checkChain(chain); |
||||
await this.checkProxiedContracts(chain); |
||||
} |
||||
} |
@ -0,0 +1,96 @@ |
||||
import { ContractFactory, ethers } from 'ethers'; |
||||
|
||||
import { ProxyAdmin, Router } from '@hyperlane-xyz/core'; |
||||
|
||||
import { MultiProvider } from '../providers/MultiProvider'; |
||||
import { HyperlaneRouterDeployer } from '../router/HyperlaneRouterDeployer'; |
||||
import { |
||||
ProxiedContracts, |
||||
ProxiedFactories, |
||||
RouterConfig, |
||||
} from '../router/types'; |
||||
import { ChainMap, ChainName } from '../types'; |
||||
|
||||
export abstract class MiddlewareRouterDeployer< |
||||
MiddlewareRouterConfig extends RouterConfig, |
||||
MiddlewareRouterContracts extends ProxiedContracts, |
||||
MiddlewareFactories extends ProxiedFactories, |
||||
RouterFactory extends ContractFactory, |
||||
> extends HyperlaneRouterDeployer< |
||||
MiddlewareRouterConfig, |
||||
MiddlewareRouterContracts, |
||||
MiddlewareFactories |
||||
> { |
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<MiddlewareRouterConfig>, |
||||
factories: MiddlewareFactories, |
||||
protected create2salt = 'middlewarerouter', |
||||
) { |
||||
super(multiProvider, configMap, factories); |
||||
} |
||||
|
||||
constructorArgs( |
||||
_chain: ChainName, |
||||
_config: MiddlewareRouterConfig, |
||||
): Parameters<RouterFactory['deploy']> { |
||||
return [] as any; |
||||
} |
||||
|
||||
abstract readonly routerContractName: string; |
||||
|
||||
router(contracts: MiddlewareRouterContracts): Router { |
||||
return contracts[this.routerContractName].contract; |
||||
} |
||||
|
||||
async initializeArgs( |
||||
chain: ChainName, |
||||
config: MiddlewareRouterConfig, |
||||
): Promise<[string, string, string, string]> { |
||||
// configure owner as signer for additional initialization steps
|
||||
// ownership is transferred to config.owner in HyperlaneRouterDeployer.deploy
|
||||
const owner = await this.multiProvider.getSignerAddress(chain); |
||||
return [ |
||||
config.mailbox, |
||||
config.interchainGasPaymaster, |
||||
config.interchainSecurityModule ?? ethers.constants.AddressZero, |
||||
owner, |
||||
]; |
||||
} |
||||
|
||||
async deployContracts( |
||||
chain: ChainName, |
||||
config: MiddlewareRouterConfig, |
||||
): Promise<MiddlewareRouterContracts> { |
||||
const proxyAdmin = (await this.deployContract( |
||||
chain, |
||||
'proxyAdmin', |
||||
[] as any, // generic type inference fails here
|
||||
)) as ProxyAdmin; |
||||
|
||||
const initArgs = await this.initializeArgs(chain, config); |
||||
const proxiedRouter = await this.deployProxiedContract( |
||||
chain, |
||||
this.routerContractName, |
||||
this.constructorArgs(chain, config), |
||||
initArgs as any, // generic type inference fails here
|
||||
proxyAdmin.address, |
||||
{ |
||||
create2Salt: this.create2salt, |
||||
}, |
||||
); |
||||
|
||||
this.logger(`Transferring ownership of proxy admin to ${config.owner}`); |
||||
await super.runIfOwner(chain, proxyAdmin, () => |
||||
this.multiProvider.handleTx( |
||||
chain, |
||||
proxyAdmin.transferOwnership(config.owner), |
||||
), |
||||
); |
||||
const ret: MiddlewareRouterContracts = { |
||||
[this.routerContractName]: proxiedRouter, |
||||
proxyAdmin, |
||||
} as MiddlewareRouterContracts; |
||||
return ret; |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
import { InterchainAccountRouter } from '@hyperlane-xyz/core'; |
||||
|
||||
import { |
||||
HyperlaneEnvironment, |
||||
hyperlaneEnvironments, |
||||
} from '../../consts/environments'; |
||||
import { HyperlaneAddresses } from '../../contracts'; |
||||
import { MultiProvider } from '../../providers/MultiProvider'; |
||||
import { RouterApp } from '../../router/RouterApps'; |
||||
import { ChainMap, ChainName } from '../../types'; |
||||
|
||||
import { |
||||
InterchainAccountContracts, |
||||
interchainAccountFactories, |
||||
} from './contracts'; |
||||
|
||||
export type InterchainAccountContractsMap = |
||||
ChainMap<InterchainAccountContracts>; |
||||
|
||||
export class InterchainAccount extends RouterApp<InterchainAccountContracts> { |
||||
constructor( |
||||
contractsMap: InterchainAccountContractsMap, |
||||
multiProvider: MultiProvider, |
||||
) { |
||||
super(contractsMap, multiProvider); |
||||
} |
||||
|
||||
router(contracts: InterchainAccountContracts): InterchainAccountRouter { |
||||
return contracts.interchainAccountRouter.contract; |
||||
} |
||||
|
||||
static fromAddresses( |
||||
addresses: ChainMap<HyperlaneAddresses>, |
||||
multiProvider: MultiProvider, |
||||
): InterchainAccount { |
||||
const { contracts, intersectionProvider } = |
||||
this.buildContracts<InterchainAccountContracts>( |
||||
addresses, |
||||
interchainAccountFactories, |
||||
multiProvider, |
||||
); |
||||
return new InterchainAccount(contracts, intersectionProvider); |
||||
} |
||||
|
||||
static fromEnvironment<Env extends HyperlaneEnvironment>( |
||||
env: Env, |
||||
multiProvider: MultiProvider, |
||||
): InterchainAccount { |
||||
const envAddresses = hyperlaneEnvironments[env]; |
||||
if (!envAddresses) { |
||||
throw new Error(`No addresses found for ${env}`); |
||||
} |
||||
return InterchainAccount.fromAddresses(envAddresses, multiProvider); |
||||
} |
||||
|
||||
getContracts(chain: ChainName): InterchainAccountContracts { |
||||
return super.getContracts(chain); |
||||
} |
||||
} |
@ -0,0 +1,11 @@ |
||||
import { MiddlewareRouterChecker } from '../MiddlewareRouterChecker'; |
||||
|
||||
import { InterchainAccount } from './InterchainAccount'; |
||||
import { InterchainAccountConfig } from './InterchainAccountDeployer'; |
||||
import { InterchainAccountContracts } from './contracts'; |
||||
|
||||
export class InterchainAccountChecker extends MiddlewareRouterChecker< |
||||
InterchainAccount, |
||||
InterchainAccountConfig, |
||||
InterchainAccountContracts |
||||
> {} |
@ -0,0 +1,113 @@ |
||||
import { |
||||
InterchainAccountRouter, |
||||
InterchainAccountRouter__factory, |
||||
ProxyAdmin, |
||||
TransparentUpgradeableProxy__factory, |
||||
} from '@hyperlane-xyz/core'; |
||||
|
||||
import { MultiProvider } from '../../providers/MultiProvider'; |
||||
import { ProxiedContract, ProxyKind } from '../../proxy'; |
||||
import { RouterConfig } from '../../router/types'; |
||||
import { ChainMap, ChainName } from '../../types'; |
||||
import { MiddlewareRouterDeployer } from '../MiddlewareRouterDeployer'; |
||||
|
||||
import { |
||||
InterchainAccountContracts, |
||||
InterchainAccountFactories, |
||||
interchainAccountFactories, |
||||
} from './contracts'; |
||||
|
||||
export type InterchainAccountConfig = RouterConfig; |
||||
|
||||
export class InterchainAccountDeployer extends MiddlewareRouterDeployer< |
||||
InterchainAccountConfig, |
||||
InterchainAccountContracts, |
||||
InterchainAccountFactories, |
||||
InterchainAccountRouter__factory |
||||
> { |
||||
readonly routerContractName = 'interchainAccountRouter'; |
||||
|
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<InterchainAccountConfig>, |
||||
create2salt = 'accountsrouter', |
||||
) { |
||||
super(multiProvider, configMap, interchainAccountFactories, create2salt); |
||||
} |
||||
|
||||
// The OwnableMulticall implementation has an immutable owner address that
|
||||
// must be set to the InterchainAccountRouter proxy address. To achieve this, we
|
||||
// 1. deploy the proxy first with a dummy implementation
|
||||
// 2. deploy the real InterchainAccountRouter and OwnableMulticall implementation with proxy address
|
||||
// 3. upgrade the proxy to the real implementation and initialize
|
||||
async deployContracts( |
||||
chain: ChainName, |
||||
config: InterchainAccountConfig, |
||||
): Promise<InterchainAccountContracts> { |
||||
const proxyAdmin = (await this.deployContract( |
||||
chain, |
||||
'proxyAdmin', |
||||
[], |
||||
)) as ProxyAdmin; |
||||
|
||||
// adapted from HyperlaneDeployer.deployProxiedContract
|
||||
const cached = this.deployedContracts[chain] |
||||
?.interchainAccountRouter as ProxiedContract<InterchainAccountRouter>; |
||||
if (cached && cached.addresses.proxy && cached.addresses.implementation) { |
||||
this.logger('Recovered full InterchainAccountRouter'); |
||||
return { |
||||
proxyAdmin, |
||||
interchainAccountRouter: cached, |
||||
}; |
||||
} |
||||
|
||||
const deployer = await this.multiProvider.getSignerAddress(chain); |
||||
|
||||
// 1. deploy the proxy first with a dummy implementation (proxy admin contract)
|
||||
const proxy = await this.deployContractFromFactory( |
||||
chain, |
||||
new TransparentUpgradeableProxy__factory(), |
||||
'TransparentUpgradeableProxy', |
||||
[proxyAdmin.address, deployer, '0x'], |
||||
{ create2Salt: this.create2salt }, |
||||
); |
||||
|
||||
// 2. deploy the real InterchainAccountRouter and OwnableMulticall implementation with proxy address
|
||||
const domainId = this.multiProvider.getDomainId(chain); |
||||
const implementation = await this.deployContract( |
||||
chain, |
||||
'interchainAccountRouter', |
||||
[domainId, proxy.address], |
||||
); |
||||
|
||||
// 3. upgrade the proxy to the real implementation and initialize
|
||||
// adapted from HyperlaneDeployer.deployProxy.useCreate2
|
||||
const initArgs = await this.initializeArgs(chain, config); |
||||
const initData = |
||||
this.factories.interchainAccountRouter.interface.encodeFunctionData( |
||||
'initialize', |
||||
initArgs, |
||||
); |
||||
await super.upgradeAndInitialize( |
||||
chain, |
||||
proxy, |
||||
implementation.address, |
||||
initData, |
||||
); |
||||
await super.changeAdmin(chain, proxy, proxyAdmin.address); |
||||
|
||||
const proxiedRouter = new ProxiedContract( |
||||
implementation.attach(proxy.address), |
||||
{ |
||||
kind: ProxyKind.Transparent, |
||||
implementation: implementation.address, |
||||
proxy: proxy.address, |
||||
}, |
||||
); |
||||
|
||||
return { |
||||
proxyAdmin, |
||||
interchainAccountRouter: proxiedRouter, // for serialization
|
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
import { |
||||
InterchainAccountRouter, |
||||
InterchainAccountRouter__factory, |
||||
ProxyAdmin, |
||||
ProxyAdmin__factory, |
||||
} from '@hyperlane-xyz/core'; |
||||
|
||||
import { ProxiedContract } from '../../proxy'; |
||||
|
||||
export type InterchainAccountFactories = { |
||||
interchainAccountRouter: InterchainAccountRouter__factory; |
||||
proxyAdmin: ProxyAdmin__factory; |
||||
}; |
||||
|
||||
export const interchainAccountFactories = { |
||||
interchainAccountRouter: new InterchainAccountRouter__factory(), |
||||
proxyAdmin: new ProxyAdmin__factory(), |
||||
}; |
||||
|
||||
export type InterchainAccountContracts = { |
||||
interchainAccountRouter: ProxiedContract<InterchainAccountRouter>; |
||||
proxyAdmin: ProxyAdmin; |
||||
}; |
@ -1,207 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
|
||||
import { |
||||
InterchainAccountRouter, |
||||
InterchainAccountRouter__factory, |
||||
InterchainQueryRouter, |
||||
InterchainQueryRouter__factory, |
||||
ProxyAdmin__factory, |
||||
TransparentUpgradeableProxy__factory, |
||||
} from '@hyperlane-xyz/core'; |
||||
|
||||
import { MultiProvider } from '../providers/MultiProvider'; |
||||
import { ProxiedContract, ProxyKind } from '../proxy'; |
||||
import { HyperlaneRouterDeployer } from '../router/HyperlaneRouterDeployer'; |
||||
import { |
||||
ProxiedRouterContracts, |
||||
ProxiedRouterFactories, |
||||
RouterConfig, |
||||
} from '../router/types'; |
||||
import { ChainMap, ChainName } from '../types'; |
||||
|
||||
export type InterchainAccountFactories = |
||||
ProxiedRouterFactories<InterchainAccountRouter>; |
||||
|
||||
export const interchainAccountFactories: InterchainAccountFactories = { |
||||
router: new InterchainAccountRouter__factory(), |
||||
proxyAdmin: new ProxyAdmin__factory(), |
||||
}; |
||||
|
||||
export type InterchainAccountContracts = |
||||
ProxiedRouterContracts<InterchainAccountRouter>; |
||||
|
||||
export type InterchainQueryFactories = |
||||
ProxiedRouterFactories<InterchainQueryRouter>; |
||||
|
||||
export const interchainQueryFactories: InterchainQueryFactories = { |
||||
router: new InterchainQueryRouter__factory(), |
||||
proxyAdmin: new ProxyAdmin__factory(), |
||||
}; |
||||
|
||||
export type InterchainQueryContracts = |
||||
ProxiedRouterContracts<InterchainQueryRouter>; |
||||
|
||||
export abstract class MiddlewareRouterDeployer< |
||||
MiddlewareRouterConfig extends RouterConfig, |
||||
MiddlewareRouterContracts extends ProxiedRouterContracts, |
||||
MiddlewareFactories extends ProxiedRouterFactories, |
||||
> extends HyperlaneRouterDeployer< |
||||
MiddlewareRouterConfig, |
||||
MiddlewareRouterContracts, |
||||
MiddlewareFactories |
||||
> { |
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<MiddlewareRouterConfig>, |
||||
factories: MiddlewareFactories, |
||||
protected create2salt = 'middlewarerouter', |
||||
) { |
||||
super(multiProvider, configMap, factories); |
||||
} |
||||
|
||||
async initializeArgs( |
||||
chain: ChainName, |
||||
config: MiddlewareRouterConfig, |
||||
): Promise<[string, string, string, string]> { |
||||
// configure owner as signer for additional initialization steps
|
||||
// ownership is transferred to config.owner in HyperlaneRouterDeployer.deploy
|
||||
const owner = await this.multiProvider.getSignerAddress(chain); |
||||
return [ |
||||
config.mailbox, |
||||
config.interchainGasPaymaster, |
||||
config.interchainSecurityModule ?? ethers.constants.AddressZero, |
||||
owner, |
||||
]; |
||||
} |
||||
|
||||
async deployContracts( |
||||
chain: ChainName, |
||||
config: MiddlewareRouterConfig, |
||||
): Promise<MiddlewareRouterContracts> { |
||||
const proxyAdmin = await this.deployContract( |
||||
chain, |
||||
'proxyAdmin', |
||||
[] as any, // generic type inference fails here
|
||||
); |
||||
|
||||
const initArgs = await this.initializeArgs(chain, config); |
||||
const proxiedRouter = await this.deployProxiedContract( |
||||
chain, |
||||
'router', |
||||
[] as any, // generic type inference fails here
|
||||
initArgs as any, // generic type inference fails here
|
||||
proxyAdmin.address, |
||||
{ |
||||
create2Salt: this.create2salt, |
||||
}, |
||||
); |
||||
return { |
||||
proxyAdmin, |
||||
proxiedRouter, |
||||
router: proxiedRouter.contract, // for backwards compatibility
|
||||
} as any; // generic type inference fails here
|
||||
} |
||||
} |
||||
|
||||
type InterchainAccountConfig = RouterConfig; |
||||
|
||||
export class InterchainAccountDeployer extends MiddlewareRouterDeployer< |
||||
InterchainAccountConfig, |
||||
InterchainAccountContracts, |
||||
InterchainAccountFactories |
||||
> { |
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<InterchainAccountConfig>, |
||||
create2Salt = 'accountsrouter', |
||||
) { |
||||
super(multiProvider, configMap, interchainAccountFactories, create2Salt); |
||||
} |
||||
|
||||
// The OwnableMulticall implementation has an immutable owner address that
|
||||
// must be set to the InterchainAccountRouter proxy address. To achieve this, we
|
||||
// 1. deploy the proxy first with a dummy implementation
|
||||
// 2. deploy the real InterchainAccountRouter and OwnableMulticall implementation with proxy address
|
||||
// 3. upgrade the proxy to the real implementation and initialize
|
||||
async deployContracts( |
||||
chain: ChainName, |
||||
config: InterchainAccountConfig, |
||||
): Promise<InterchainAccountContracts> { |
||||
const proxyAdmin = await this.deployContract(chain, 'proxyAdmin', []); |
||||
|
||||
// manually recover from cache because cannot use HyperlaneDeployer.deployProxiedContract
|
||||
const cached = this.deployedContracts[chain]?.proxiedRouter; |
||||
if (cached && cached.addresses.proxy && cached.addresses.implementation) { |
||||
this.logger('Recovered full InterchainAccountRouter'); |
||||
return { |
||||
proxyAdmin, |
||||
proxiedRouter: cached, |
||||
router: cached.contract, |
||||
}; |
||||
} |
||||
|
||||
const deployer = await this.multiProvider.getSignerAddress(chain); |
||||
|
||||
// 1. deploy the proxy first with a dummy implementation (proxy admin contract)
|
||||
const proxy = await this.deployContractFromFactory( |
||||
chain, |
||||
new TransparentUpgradeableProxy__factory(), |
||||
'TransparentUpgradeableProxy', |
||||
[proxyAdmin.address, deployer, '0x'], |
||||
{ create2Salt: this.create2salt }, |
||||
); |
||||
|
||||
// 2. deploy the real InterchainAccountRouter and OwnableMulticall implementation with proxy address
|
||||
const domainId = this.multiProvider.getDomainId(chain); |
||||
const implementation = await this.deployContract(chain, 'router', [ |
||||
domainId, |
||||
proxy.address, |
||||
]); |
||||
|
||||
// 3. upgrade the proxy to the real implementation and initialize
|
||||
// adapted from HyperlaneDeployer.deployProxy.useCreate2
|
||||
const initArgs = await this.initializeArgs(chain, config); |
||||
const initData = this.factories.router.interface.encodeFunctionData( |
||||
'initialize', |
||||
initArgs, |
||||
); |
||||
await super.upgradeAndInitialize( |
||||
chain, |
||||
proxy, |
||||
implementation.address, |
||||
initData, |
||||
); |
||||
await super.changeAdmin(chain, proxy, proxyAdmin.address); |
||||
|
||||
const proxiedRouter = new ProxiedContract( |
||||
implementation.attach(proxy.address), |
||||
{ |
||||
kind: ProxyKind.Transparent, |
||||
implementation: implementation.address, |
||||
proxy: proxy.address, |
||||
}, |
||||
); |
||||
|
||||
return { |
||||
proxyAdmin, |
||||
proxiedRouter, |
||||
router: proxiedRouter.contract, |
||||
}; |
||||
} |
||||
} |
||||
|
||||
type InterchainQueryConfig = RouterConfig; |
||||
|
||||
export class InterchainQueryDeployer extends MiddlewareRouterDeployer< |
||||
InterchainQueryConfig, |
||||
InterchainQueryContracts, |
||||
InterchainQueryFactories |
||||
> { |
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<InterchainQueryConfig>, |
||||
create2salt = 'queryrouter2', |
||||
) { |
||||
super(multiProvider, configMap, interchainQueryFactories, create2salt); |
||||
} |
||||
} |
@ -0,0 +1,58 @@ |
||||
import { InterchainQueryRouter } from '@hyperlane-xyz/core'; |
||||
|
||||
import { |
||||
HyperlaneEnvironment, |
||||
hyperlaneEnvironments, |
||||
} from '../../consts/environments'; |
||||
import { HyperlaneAddresses } from '../../contracts'; |
||||
import { MultiProvider } from '../../providers/MultiProvider'; |
||||
import { RouterApp } from '../../router/RouterApps'; |
||||
import { ChainMap, ChainName } from '../../types'; |
||||
|
||||
import { |
||||
InterchainQueryContracts, |
||||
interchainQueryFactories, |
||||
} from './contracts'; |
||||
|
||||
export type InterchainQueryContractsMap = ChainMap<InterchainQueryContracts>; |
||||
|
||||
export class InterchainQuery extends RouterApp<InterchainQueryContracts> { |
||||
constructor( |
||||
contractsMap: InterchainQueryContractsMap, |
||||
multiProvider: MultiProvider, |
||||
) { |
||||
super(contractsMap, multiProvider); |
||||
} |
||||
|
||||
router(contracts: InterchainQueryContracts): InterchainQueryRouter { |
||||
return contracts.interchainQueryRouter.contract; |
||||
} |
||||
|
||||
static fromAddresses( |
||||
addresses: ChainMap<HyperlaneAddresses>, |
||||
multiProvider: MultiProvider, |
||||
): InterchainQuery { |
||||
const { contracts, intersectionProvider } = |
||||
this.buildContracts<InterchainQueryContracts>( |
||||
addresses, |
||||
interchainQueryFactories, |
||||
multiProvider, |
||||
); |
||||
return new InterchainQuery(contracts, intersectionProvider); |
||||
} |
||||
|
||||
static fromEnvironment<Env extends HyperlaneEnvironment>( |
||||
env: Env, |
||||
multiProvider: MultiProvider, |
||||
): InterchainQuery { |
||||
const envAddresses = hyperlaneEnvironments[env]; |
||||
if (!envAddresses) { |
||||
throw new Error(`No addresses found for ${env}`); |
||||
} |
||||
return InterchainQuery.fromAddresses(envAddresses, multiProvider); |
||||
} |
||||
|
||||
getContracts(chain: ChainName): InterchainQueryContracts { |
||||
return super.getContracts(chain); |
||||
} |
||||
} |
@ -0,0 +1,11 @@ |
||||
import { MiddlewareRouterChecker } from '../MiddlewareRouterChecker'; |
||||
|
||||
import { InterchainQuery } from './InterchainQuery'; |
||||
import { InterchainQueryConfig } from './InterchainQueryDeployer'; |
||||
import { InterchainQueryContracts } from './contracts'; |
||||
|
||||
export class InterchainQueryChecker extends MiddlewareRouterChecker< |
||||
InterchainQuery, |
||||
InterchainQueryConfig, |
||||
InterchainQueryContracts |
||||
> {} |
@ -0,0 +1,31 @@ |
||||
import { InterchainQueryRouter__factory } from '@hyperlane-xyz/core'; |
||||
|
||||
import { MultiProvider } from '../../providers/MultiProvider'; |
||||
import { RouterConfig } from '../../router/types'; |
||||
import { ChainMap } from '../../types'; |
||||
import { MiddlewareRouterDeployer } from '../MiddlewareRouterDeployer'; |
||||
|
||||
import { |
||||
InterchainQueryContracts, |
||||
InterchainQueryFactories, |
||||
interchainQueryFactories, |
||||
} from './contracts'; |
||||
|
||||
export type InterchainQueryConfig = RouterConfig; |
||||
|
||||
export class InterchainQueryDeployer extends MiddlewareRouterDeployer< |
||||
InterchainQueryConfig, |
||||
InterchainQueryContracts, |
||||
InterchainQueryFactories, |
||||
InterchainQueryRouter__factory |
||||
> { |
||||
readonly routerContractName = 'interchainQueryRouter'; |
||||
|
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<InterchainQueryConfig>, |
||||
create2salt = 'queryrouter2', |
||||
) { |
||||
super(multiProvider, configMap, interchainQueryFactories, create2salt); |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
import { |
||||
InterchainQueryRouter, |
||||
InterchainQueryRouter__factory, |
||||
ProxyAdmin, |
||||
ProxyAdmin__factory, |
||||
} from '@hyperlane-xyz/core'; |
||||
|
||||
import { ProxiedContract } from '../../proxy'; |
||||
|
||||
export type InterchainQueryFactories = { |
||||
interchainQueryRouter: InterchainQueryRouter__factory; |
||||
proxyAdmin: ProxyAdmin__factory; |
||||
}; |
||||
|
||||
export const interchainQueryFactories = { |
||||
interchainQueryRouter: new InterchainQueryRouter__factory(), |
||||
proxyAdmin: new ProxyAdmin__factory(), |
||||
}; |
||||
|
||||
export type InterchainQueryContracts = { |
||||
interchainQueryRouter: ProxiedContract<InterchainQueryRouter>; |
||||
proxyAdmin: ProxyAdmin; |
||||
}; |
@ -1,39 +0,0 @@ |
||||
import { BigNumber } from 'ethers'; |
||||
|
||||
import { GasRouter } from '@hyperlane-xyz/core'; |
||||
import { types } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { HyperlaneApp } from '../HyperlaneApp'; |
||||
import { ChainMap, ChainName } from '../types'; |
||||
import { objMap, promiseObjAll } from '../utils/objects'; |
||||
|
||||
import { RouterContracts } from './types'; |
||||
|
||||
export class RouterApp< |
||||
Contracts extends RouterContracts, |
||||
> extends HyperlaneApp<Contracts> { |
||||
getSecurityModules = (): Promise<ChainMap<types.Address>> => |
||||
promiseObjAll( |
||||
objMap(this.contractsMap, (_, contracts) => |
||||
contracts.router.interchainSecurityModule(), |
||||
), |
||||
); |
||||
|
||||
getOwners = (): Promise<ChainMap<types.Address>> => |
||||
promiseObjAll( |
||||
objMap(this.contractsMap, (_, contracts) => contracts.router.owner()), |
||||
); |
||||
} |
||||
|
||||
export class GasRouterApp< |
||||
Contracts extends RouterContracts<GasRouter>, |
||||
> extends RouterApp<Contracts> { |
||||
async quoteGasPayment( |
||||
origin: ChainName, |
||||
destination: ChainName, |
||||
): Promise<BigNumber> { |
||||
return this.getContracts(origin).router.quoteGasPayment( |
||||
this.multiProvider.getDomainId(destination), |
||||
); |
||||
} |
||||
} |
@ -1 +1 @@ |
||||
Subproject commit df21762a269d3d813fa28799a1a0216d5457e008 |
||||
Subproject commit 306b107e662c4c87e963693bfe29cf2985f3090f |
Loading…
Reference in new issue