Skip to content

Commit

Permalink
feat: new mainnet ICA deployment & tooling (#4264)
Browse files Browse the repository at this point in the history
### Description

- Corresponding registry PR:
hyperlane-xyz/hyperlane-registry#130
- Decided to deploy new ICA routers instead of going through the big
hassle of using the existing one
- Added a new script to make it easier to get / deploy an ICA

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->

### Testing

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->
  • Loading branch information
tkporter authored Aug 6, 2024
1 parent ff3c989 commit 69fba68
Show file tree
Hide file tree
Showing 8 changed files with 907 additions and 273 deletions.

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions typescript/infra/scripts/check-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
HyperlaneIsmFactory,
InterchainAccount,
InterchainAccountChecker,
InterchainAccountConfig,
InterchainQuery,
InterchainQueryChecker,
} from '@hyperlane-xyz/sdk';
import { objFilter } from '@hyperlane-xyz/utils';

import { Contexts } from '../config/contexts.js';
import { DEPLOYER } from '../config/environments/mainnet3/owners.js';
Expand Down Expand Up @@ -74,7 +76,15 @@ async function check() {
multiProvider,
);
const routerConfig = core.getRouterConfig(envConfig.owners);
const ica = InterchainAccount.fromAddressesMap(chainAddresses, multiProvider);
const icaChainAddresses = objFilter(
chainAddresses,
(chain, addresses): addresses is Record<string, string> =>
!!chainAddresses[chain]?.interchainAccountRouter,
);
const ica = InterchainAccount.fromAddressesMap(
icaChainAddresses,
multiProvider,
);

let governor: HyperlaneAppGovernor<any, any>;
if (module === Modules.CORE) {
Expand All @@ -93,7 +103,10 @@ async function check() {
const checker = new InterchainAccountChecker(
multiProvider,
ica,
routerConfig,
objFilter(
routerConfig,
(chain, _): _ is InterchainAccountConfig => !!icaChainAddresses[chain],
),
);
governor = new ProxiedRouterGovernor(checker);
} else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) {
Expand Down
6 changes: 5 additions & 1 deletion typescript/infra/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ async function main() {
} else if (module === Modules.INTERCHAIN_ACCOUNTS) {
const { core } = await getHyperlaneCore(environment, multiProvider);
config = core.getRouterConfig(envConfig.owners);
deployer = new InterchainAccountDeployer(multiProvider, contractVerifier);
deployer = new InterchainAccountDeployer(
multiProvider,
contractVerifier,
concurrentDeploy,
);
const addresses = getAddresses(environment, Modules.INTERCHAIN_ACCOUNTS);
InterchainAccount.fromAddressesMap(addresses, multiProvider);
} else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) {
Expand Down
82 changes: 82 additions & 0 deletions typescript/infra/scripts/get-owner-ica.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ethers } from 'ethers';

import { AccountConfig, InterchainAccount } from '@hyperlane-xyz/sdk';
import { assert, eqAddress, rootLogger } from '@hyperlane-xyz/utils';

import { getSecretRpcEndpoints } from '../src/agents/index.js';

import { getArgs as getEnvArgs, withChainRequired } from './agent-utils.js';
import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js';

function getArgs() {
return withChainRequired(getEnvArgs())
.option('ownerChain', {
type: 'string',
description: 'Origin chain where the governing owner lives',
demandOption: true,
})
.option('owner', {
type: 'string',
description:
"Address of the owner on the ownerChain. Defaults to the environment's configured owner for the ownerChain.",
demandOption: false,
})
.option('deploy', {
type: 'boolean',
description: 'Deploys the ICA if it does not exist',
default: false,
})
.alias('chain', 'destinationChain').argv;
}

async function main() {
const {
environment,
ownerChain,
chain,
deploy,
owner: ownerOverride,
} = await getArgs();
const config = getEnvironmentConfig(environment);
const multiProvider = await config.getMultiProvider();

const originOwner = ownerOverride ?? config.owners[ownerChain]?.owner;
if (!originOwner) {
throw new Error(`No owner found for ${ownerChain}`);
}

rootLogger.info(`Governance owner on ${ownerChain}: ${originOwner}`);

const { chainAddresses } = await getHyperlaneCore(environment, multiProvider);
const ica = InterchainAccount.fromAddressesMap(chainAddresses, multiProvider);

const ownerConfig: AccountConfig = {
origin: ownerChain,
owner: originOwner,
};

const account = await ica.getAccount(chain, ownerConfig);

rootLogger.info(`ICA on ${chain}: ${account}`);

if (deploy) {
// Ensuring the account was deployed
const deployedAccount = await ica.deployAccount(chain, ownerConfig);
// This shouldn't ever happen, but let's be safe
assert(
eqAddress(account, deployedAccount),
'Fatal mismatch between account and deployed account',
);

rootLogger.info(
`ICA deployed or recovered on ${chain}: ${deployedAccount}`,
);
}
}

main()
.then()
.catch((err) => {
console.error('Error:', err);
process.exit(1);
});
2 changes: 2 additions & 0 deletions typescript/sdk/src/core/HyperlaneCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export class HyperlaneCore extends HyperlaneApp<CoreFactories> {
(chain, contracts): RouterConfig => ({
mailbox: contracts.mailbox.address,
owner: typeof owners === 'string' ? owners : owners[chain].owner,
ownerOverrides:
typeof owners === 'string' ? undefined : owners[chain].ownerOverrides,
}),
);
// filter for EVM chains
Expand Down
38 changes: 38 additions & 0 deletions typescript/sdk/src/middleware/account/InterchainAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,42 @@ export class InterchainAccount extends RouterApp<InterchainAccountFactories> {
return new InterchainAccount(helper.contractsMap, helper.multiProvider);
}

async getAccount(
destinationChain: ChainName,
config: AccountConfig,
routerOverride?: Address,
ismOverride?: Address,
): Promise<Address> {
return this.getOrDeployAccount(
false,
destinationChain,
config,
routerOverride,
ismOverride,
);
}

async deployAccount(
destinationChain: ChainName,
config: AccountConfig,
routerOverride?: Address,
ismOverride?: Address,
): Promise<Address> {
return this.getOrDeployAccount(
true,
destinationChain,
config,
routerOverride,
ismOverride,
);
}

protected async getOrDeployAccount(
deployIfNotExists: boolean,
destinationChain: ChainName,
config: AccountConfig,
routerOverride?: Address,
ismOverride?: Address,
): Promise<Address> {
const originDomain = this.multiProvider.tryGetDomainId(config.origin);
if (!originDomain) {
Expand All @@ -71,6 +102,13 @@ export class InterchainAccount extends RouterApp<InterchainAccountFactories> {
const destinationAccount = await destinationRouter[
'getLocalInterchainAccount(uint32,address,address,address)'
](originDomain, config.owner, originRouterAddress, destinationIsmAddress);

// If not deploying anything, return the account address.
if (!deployIfNotExists) {
return destinationAccount;
}

// If the account does not exist, deploy it.
if (
(await this.multiProvider
.getProvider(destinationChain)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ export class InterchainAccountDeployer extends ProxiedRouterDeployer<
constructor(
multiProvider: MultiProvider,
contractVerifier?: ContractVerifier,
concurrentDeploy?: boolean,
) {
super(multiProvider, interchainAccountFactories, {
contractVerifier,
concurrentDeploy,
});
}
routerContractName(): string {
Expand Down
2 changes: 1 addition & 1 deletion typescript/sdk/src/router/ProxiedRouterChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export abstract class ProxiedRouterChecker<
> extends HyperlaneRouterChecker<Factories, App, Config> {
async checkOwnership(chain: ChainName): Promise<void> {
const config = this.configMap[chain];
let ownableOverrides = {};
let ownableOverrides = config.ownerOverrides;
if (config.timelock) {
ownableOverrides = {
proxyAdmin: this.app.getAddresses(chain).timelockController,
Expand Down

0 comments on commit 69fba68

Please sign in to comment.