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": { |
"test1": { |
||||||
"storageGasOracle": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", |
"storageGasOracle": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1", |
||||||
"validatorAnnounce": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", |
"validatorAnnounce": "0x0165878A594ca255338adfa4d48449f69242Eb8F", |
||||||
"proxyAdmin": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", |
"proxyAdmin": "0x59b670e9fA9D0A427751Af201D676719a970857b", |
||||||
"mailbox": { |
"mailbox": { |
||||||
"kind": "Transparent", |
"kind": "Transparent", |
||||||
"proxy": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", |
"proxy": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", |
||||||
"implementation": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" |
"implementation": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" |
||||||
}, |
}, |
||||||
"interchainGasPaymaster": { |
"interchainGasPaymaster": { |
||||||
"kind": "Transparent", |
"kind": "Transparent", |
||||||
"proxy": "0x0165878A594ca255338adfa4d48449f69242Eb8F", |
"proxy": "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f", |
||||||
"implementation": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" |
"implementation": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" |
||||||
}, |
}, |
||||||
"defaultIsmInterchainGasPaymaster": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", |
"defaultIsmInterchainGasPaymaster": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", |
||||||
"multisigIsm": "0x5FbDB2315678afecb367f032d93F642f64180aa3" |
"multisigIsm": "0x5FbDB2315678afecb367f032d93F642f64180aa3" |
||||||
}, |
}, |
||||||
"test2": { |
"test2": { |
||||||
"storageGasOracle": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed", |
"storageGasOracle": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E", |
||||||
"validatorAnnounce": "0x09635F643e140090A9A8Dcd712eD6285858ceBef", |
"validatorAnnounce": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", |
||||||
"proxyAdmin": "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE", |
"proxyAdmin": "0x67d269191c92Caf3cD7723F116c85e6E9bf55933", |
||||||
"mailbox": { |
"mailbox": { |
||||||
"kind": "Transparent", |
"kind": "Transparent", |
||||||
"proxy": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", |
"proxy": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", |
||||||
"implementation": "0x4A679253410272dd5232B3Ff7cF5dbB88f295319" |
"implementation": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" |
||||||
}, |
}, |
||||||
"interchainGasPaymaster": { |
"interchainGasPaymaster": { |
||||||
"kind": "Transparent", |
"kind": "Transparent", |
||||||
"proxy": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", |
"proxy": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB", |
||||||
"implementation": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c" |
"implementation": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" |
||||||
}, |
}, |
||||||
"defaultIsmInterchainGasPaymaster": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1", |
"defaultIsmInterchainGasPaymaster": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", |
||||||
"multisigIsm": "0x9A676e781A523b5d0C0e43731313A708CB607508" |
"multisigIsm": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" |
||||||
}, |
}, |
||||||
"test3": { |
"test3": { |
||||||
"storageGasOracle": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB", |
"storageGasOracle": "0x95401dc811bb5740090279Ba06cfA8fcF6113778", |
||||||
"validatorAnnounce": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528", |
"validatorAnnounce": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", |
||||||
"proxyAdmin": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690", |
"proxyAdmin": "0xf5059a5D33d5853360D16C683c16e67980206f36", |
||||||
"mailbox": { |
"mailbox": { |
||||||
"kind": "Transparent", |
"kind": "Transparent", |
||||||
"proxy": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49", |
"proxy": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c", |
||||||
"implementation": "0x998abeb3E57409262aE5b751f60747921B33613E" |
"implementation": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed" |
||||||
}, |
}, |
||||||
"interchainGasPaymaster": { |
"interchainGasPaymaster": { |
||||||
"kind": "Transparent", |
"kind": "Transparent", |
||||||
"proxy": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", |
"proxy": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49", |
||||||
"implementation": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" |
"implementation": "0x998abeb3E57409262aE5b751f60747921B33613E" |
||||||
}, |
}, |
||||||
"defaultIsmInterchainGasPaymaster": "0x851356ae760d987E095750cCeb3bC6014560891C", |
"defaultIsmInterchainGasPaymaster": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", |
||||||
"multisigIsm": "0xc5a5C42992dECbae36851359345FE25997F5C42d" |
"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