Skip to content

Commit

Permalink
feat: detangle chainId / domainId EVM assumptions (#4798)
Browse files Browse the repository at this point in the history
### Description

Detangle assumption that chainId == domainId for EVM chains.
- required to support new domain IDs for sept26 chain deploy batch
hyperlane-xyz/hyperlane-registry#352

Domain IDs and Chain Names are still unique, but chainId is no longer
guaranteed to be a unique identifier.
- required to support shadow lumia (current deployment) + prod lumia
(new deployment with different domain id)

Touches:
- evm core/ism/hook/warp modules
- multiprovider + chain metadata manager
- safe service
- renaming `ChainNameOrId` to `ChainNameOrDomain` (where bulk of the
diff comes from)

### Drive-by changes

- ~~add name of a `chain` to `Annotated` types, since chain ID on a
populated transaction is a looser tie to a chain than `domainId` or
`chain`~~
- ~~simplify multiprovider.sendtransaction, since `chain` is provided
with the annotated transaction~~

- ensure domainId is not optional in the chain metadata schema
- requires registry fix
hyperlane-xyz/hyperlane-registry#357
- update ethers v5 json rpc submitter to require a `chain` as well

### Related issues

- mostly redoes work from
#4599
- will have to be rechecked after zksync changes are also in

### Backward compatibility

no

### Testing

ci, manual

---------

Signed-off-by: pbio <[email protected]>
  • Loading branch information
paulbalaji authored Nov 6, 2024
1 parent 30d92c3 commit e3b97c4
Show file tree
Hide file tree
Showing 47 changed files with 358 additions and 282 deletions.
8 changes: 8 additions & 0 deletions .changeset/thirty-adults-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@hyperlane-xyz/utils': major
'@hyperlane-xyz/cli': major
'@hyperlane-xyz/sdk': major
'@hyperlane-xyz/widgets': major
---

Detangle assumption that chainId == domainId for EVM chains. Domain IDs and Chain Names are still unique, but chainId is no longer guaranteed to be a unique identifier. Domain ID is no longer an optional field and is now required for all chain metadata.
2 changes: 1 addition & 1 deletion .registryrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14836804e543a64cd3747cd6bbea6e245b6353b1
1ea2849e2fc1e750bac67e35827c9d682c7fd4bf
134 changes: 67 additions & 67 deletions rust/main/config/mainnet_config.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
submitter:
chain: anvil2
type: impersonatedAccount
userAddress: '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb'
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
anvil2:
submitter:
chain: anvil2
type: jsonRpc
anvil3:
submitter:
chain: anvil3
type: jsonRpc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
submitter:
chain: anvil2
type: jsonRpc
28 changes: 3 additions & 25 deletions typescript/cli/src/config/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { stringify as yamlStringify } from 'yaml';
import {
AnnotatedEV5Transaction,
SubmissionStrategy,
getChainIdFromTxs,
} from '@hyperlane-xyz/sdk';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { assert, errorToString } from '@hyperlane-xyz/utils';

import { WriteCommandContext } from '../context/types.js';
Expand Down Expand Up @@ -34,9 +34,9 @@ export async function runSubmit({
'Submission strategy required to submit transactions.\nPlease create a submission strategy. See examples in cli/examples/submit/strategy/*.',
);
const transactions = getTransactions(transactionsFilepath);
const chain = getChainFromTxs(multiProvider, transactions);
const chainId = getChainIdFromTxs(transactions);

const protocol = chainMetadata[chain].protocol;
const protocol = chainMetadata[chainId].protocol;
const submitterBuilder = await getSubmitterBuilder<typeof protocol>({
submissionStrategy,
multiProvider,
Expand All @@ -60,28 +60,6 @@ export async function runSubmit({
}
}

/**
* Retrieves the chain name from transactions[0].
*
* @param multiProvider - The MultiProvider instance to use for chain name lookup.
* @param transactions - The list of populated transactions.
* @returns The name of the chain that the transactions are submitted on.
* @throws If the transactions are not all on the same chain or chain is not found
*/
function getChainFromTxs(
multiProvider: MultiProvider,
transactions: AnnotatedEV5Transaction[],
) {
const firstTransaction = transactions[0];
assert(firstTransaction.chainId, 'Invalid transaction: chainId is required');
const sameChainIds = transactions.every(
(t: AnnotatedEV5Transaction) => t.chainId === firstTransaction.chainId,
);
assert(sameChainIds, 'Transactions must be submitted on the same chains');

return multiProvider.getChainName(firstTransaction.chainId);
}

function getTransactions(
transactionsFilepath: string,
): AnnotatedEV5Transaction[] {
Expand Down
13 changes: 11 additions & 2 deletions typescript/cli/src/deploy/warp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -885,12 +885,20 @@ async function submitWarpApplyTransactions(
params: WarpApplyParams,
chainTransactions: Record<string, AnnotatedEV5Transaction[]>,
): Promise<void> {
const { multiProvider } = params.context;
// Create mapping of chain ID to chain name for all chains in warpDeployConfig
const chains = Object.keys(params.warpDeployConfig);
const chainIdToName = Object.fromEntries(
chains.map((chain) => [
params.context.multiProvider.getChainId(chain),
chain,
]),
);

await promiseObjAll(
objMap(chainTransactions, async (chainId, transactions) => {
await retryAsync(
async () => {
const chain = multiProvider.getChainName(chainId);
const chain = chainIdToName[chainId];
const submitter: TxSubmitterBuilder<ProtocolType> =
await getWarpApplySubmitter({
chain,
Expand Down Expand Up @@ -935,6 +943,7 @@ async function getWarpApplySubmitter({
? readChainSubmissionStrategy(strategyUrl)[chain]
: {
submitter: {
chain,
type: TxSubmitterType.JSON_RPC,
},
};
Expand Down
4 changes: 3 additions & 1 deletion typescript/cli/src/submit/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ async function getSubmitter<TProtocol extends ProtocolType>(
): Promise<TxSubmitterInterface<TProtocol>> {
switch (submitterMetadata.type) {
case TxSubmitterType.JSON_RPC:
return new EV5JsonRpcTxSubmitter(multiProvider);
return new EV5JsonRpcTxSubmitter(multiProvider, {
...submitterMetadata,
});
case TxSubmitterType.IMPERSONATED_ACCOUNT:
return new EV5ImpersonatedAccountTxSubmitter(multiProvider, {
...submitterMetadata,
Expand Down
8 changes: 6 additions & 2 deletions typescript/cli/src/tests/commands/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,18 @@ export async function deployOrUseExistingCore(

return addresses;
}
export async function getChainId(chainName: string, key: string) {

export async function getDomainId(
chainName: string,
key: string,
): Promise<string> {
const { registry } = await getContext({
registryUri: REGISTRY_PATH,
registryOverrideUri: '',
key,
});
const chainMetadata = await registry.getChainMetadata(chainName);
return String(chainMetadata?.chainId);
return String(chainMetadata?.domainId);
}

export async function deployToken(privateKey: string, chain: string) {
Expand Down
18 changes: 9 additions & 9 deletions typescript/cli/src/tests/warp-apply.e2e-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
REGISTRY_PATH,
deployOrUseExistingCore,
extendWarpConfig,
getChainId,
getDomainId,
updateOwner,
} from './commands/helpers.js';
import {
Expand Down Expand Up @@ -128,7 +128,7 @@ describe('WarpApply e2e tests', async function () {
warpConfigPath,
);

const chain2Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY);
const chain2Id = await getDomainId(CHAIN_NAME_3, ANVIL_KEY);
const remoteRouterKeys1 = Object.keys(
updatedWarpDeployConfig1[CHAIN_NAME_2].remoteRouters!,
);
Expand All @@ -141,7 +141,7 @@ describe('WarpApply e2e tests', async function () {
warpConfigPath,
);

const chain1Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY);
const chain1Id = await getDomainId(CHAIN_NAME_2, ANVIL_KEY);
const remoteRouterKeys2 = Object.keys(
updatedWarpDeployConfig2[CHAIN_NAME_3].remoteRouters!,
);
Expand Down Expand Up @@ -182,7 +182,7 @@ describe('WarpApply e2e tests', async function () {
warpConfigPath,
);

const chain2Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY);
const chain2Id = await getDomainId(CHAIN_NAME_3, ANVIL_KEY);
const remoteRouterKeys1 = Object.keys(
updatedWarpDeployConfig1[CHAIN_NAME_2].remoteRouters!,
);
Expand All @@ -195,7 +195,7 @@ describe('WarpApply e2e tests', async function () {
warpConfigPath,
);

const chain1Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY);
const chain1Id = await getDomainId(CHAIN_NAME_2, ANVIL_KEY);
const remoteRouterKeys2 = Object.keys(
updatedWarpDeployConfig2[CHAIN_NAME_3].remoteRouters!,
);
Expand Down Expand Up @@ -247,8 +247,8 @@ describe('WarpApply e2e tests', async function () {
expect(updatedWarpDeployConfig_3.anvil3.owner).to.equal(randomOwner);

// Check that both chains enrolled
const chain2Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY);
const chain3Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY);
const chain2Id = await getDomainId(CHAIN_NAME_2, ANVIL_KEY);
const chain3Id = await getDomainId(CHAIN_NAME_3, ANVIL_KEY);

const remoteRouterKeys2 = Object.keys(
updatedWarpDeployConfig_2[CHAIN_NAME_2].remoteRouters!,
Expand Down Expand Up @@ -295,8 +295,8 @@ describe('WarpApply e2e tests', async function () {
warpConfigPath,
);

const chain2Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY);
const chain3Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY);
const chain2Id = await getDomainId(CHAIN_NAME_2, ANVIL_KEY);
const chain3Id = await getDomainId(CHAIN_NAME_3, ANVIL_KEY);

// Destination gas should be set in the existing chain (chain2) to include the extended chain (chain3)
const destinationGas_2 =
Expand Down
7 changes: 3 additions & 4 deletions typescript/infra/scripts/funding/fund-keys-from-deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { BigNumber, ethers } from 'ethers';
import { Gauge, Registry } from 'prom-client';
import { format } from 'util';

import { eclipsemainnet } from '@hyperlane-xyz/registry';
import {
ChainMap,
ChainName,
Expand Down Expand Up @@ -818,8 +817,8 @@ class ContextFunder {
) {
const l1Chain = L2ToL1[l2Chain];
const crossChainMessenger = new CrossChainMessenger({
l1ChainId: this.multiProvider.getDomainId(l1Chain),
l2ChainId: this.multiProvider.getDomainId(l2Chain),
l1ChainId: this.multiProvider.getEvmChainId(l1Chain),
l2ChainId: this.multiProvider.getEvmChainId(l2Chain),
l1SignerOrProvider: this.multiProvider.getSignerOrProvider(l1Chain),
l2SignerOrProvider: this.multiProvider.getSignerOrProvider(l2Chain),
});
Expand All @@ -832,7 +831,7 @@ class ContextFunder {
private async bridgeToArbitrum(l2Chain: ChainName, amount: BigNumber) {
const l1Chain = L2ToL1[l2Chain];
const l2Network = await getL2Network(
this.multiProvider.getDomainId(l2Chain),
this.multiProvider.getEvmChainId(l2Chain),
);
const ethBridger = new EthBridger(l2Network);
return ethBridger.deposit({
Expand Down
5 changes: 3 additions & 2 deletions typescript/infra/scripts/generate-scraper-add-domain-sql.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ cd "$(dirname "$0")/../../../"
# Generate the SQL command for inserting domains into the scraper database
echo "insert into domain (id, time_created, time_updated, name, native_token, chain_id, is_test_net, is_deprecated) values"
for name in "$@"; do
chain_id=$(yq e ".$name.chainId" ../hyperlane-registry/chains/metadata.yaml)
domain_id=$(yq e ".$name.domainId" ../hyperlane-registry/chains/metadata.yaml)
if [ -z "$domain_id" ]; then
echo "Error: domain_id for $name not found" >&2
Expand All @@ -22,8 +23,8 @@ for name in "$@"; do
fi
is_testnet=$(yq e ".$name.isTestnet" ../hyperlane-registry/chains/metadata.yaml)
if [ "$is_testnet" = "true" ]; then
echo "($domain_id, current_timestamp, current_timestamp, '$name', '$native_token_symbol', $domain_id, true, false)"
echo "($domain_id, current_timestamp, current_timestamp, '$name', '$native_token_symbol', $chain_id, true, false)"
else
echo "($domain_id, current_timestamp, current_timestamp, '$name', '$native_token_symbol', $domain_id, false, false)"
echo "($domain_id, current_timestamp, current_timestamp, '$name', '$native_token_symbol', $chain_id, false, false)"
fi
done | paste -sd, -
16 changes: 9 additions & 7 deletions typescript/infra/scripts/helloworld/kathy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,15 @@ async function main(): Promise<boolean> {
messageReceiptSeconds.labels({ origin, remote }).inc(0);
}

chains.map(async (chain) => {
return updateWalletBalanceMetricFor(
app,
chain,
coreConfig.owners[chain].owner,
);
});
await Promise.all(
chains.map(async (chain) => {
return updateWalletBalanceMetricFor(
app,
chain,
coreConfig.owners[chain].owner,
);
}),
);

// Incremented each time an entire cycle has occurred
let currentCycle = 0;
Expand Down
4 changes: 2 additions & 2 deletions typescript/infra/src/utils/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export async function deleteSafeTx(
safeTxHash: string,
): Promise<void> {
const signer = multiProvider.getSigner(chain);
const domainId = multiProvider.getDomainId(chain);
const chainId = multiProvider.getEvmChainId(chain);
const txServiceUrl =
multiProvider.getChainMetadata(chain).gnosisSafeTransactionServiceUrl;

Expand Down Expand Up @@ -179,7 +179,7 @@ export async function deleteSafeTx(
domain: {
name: 'Safe Transaction Service',
version: '1.0',
chainId: domainId,
chainId: chainId,
verifyingContract: safeAddress,
},
primaryType: 'DeleteRequest',
Expand Down
3 changes: 2 additions & 1 deletion typescript/sdk/src/contracts/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Contract } from 'ethers';
import { Ownable, Ownable__factory } from '@hyperlane-xyz/core';
import {
Address,
EvmChainId,
ProtocolType,
ValueOf,
eqAddress,
Expand Down Expand Up @@ -262,7 +263,7 @@ export function appFromAddressesMapHelper<F extends HyperlaneFactories>(
}

export function transferOwnershipTransactions(
chainId: number,
chainId: EvmChainId,
contract: Address,
actual: OwnableConfig,
expected: OwnableConfig,
Expand Down
2 changes: 1 addition & 1 deletion typescript/sdk/src/core/AbstractHyperlaneModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export abstract class HyperlaneModule<
public abstract read(): Promise<TConfig>;
public abstract update(
config: TConfig,
): Promise<Annotated<ProtocolTypedTransaction<TProtocol>['transaction'][]>>;
): Promise<Annotated<ProtocolTypedTransaction<TProtocol>['transaction']>[]>;

// /*
// Types and static methods can be challenging. Ensure each implementation includes a static create function.
Expand Down
2 changes: 1 addition & 1 deletion typescript/sdk/src/core/EvmCoreModule.hardhat-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('EvmCoreModule', async () => {

// Check that it's actually a mailbox by calling one of it's methods
expect(await mailboxContract.localDomain()).to.equal(
multiProvider.getChainId(CHAIN),
multiProvider.getDomainId(CHAIN),
);
});

Expand Down
17 changes: 9 additions & 8 deletions typescript/sdk/src/core/EvmCoreModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Mailbox, Mailbox__factory } from '@hyperlane-xyz/core';
import {
Address,
Domain,
EvmChainId,
ProtocolType,
eqAddress,
rootLogger,
Expand Down Expand Up @@ -31,7 +32,7 @@ import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js';
import { IsmConfig } from '../ism/types.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { AnnotatedEV5Transaction } from '../providers/ProviderType.js';
import { ChainNameOrId } from '../types.js';
import { ChainName, ChainNameOrId } from '../types.js';

import {
HyperlaneModule,
Expand All @@ -50,19 +51,19 @@ export class EvmCoreModule extends HyperlaneModule<
> {
protected logger = rootLogger.child({ module: 'EvmCoreModule' });
protected coreReader: EvmCoreReader;
public readonly chainName: string;
public readonly chainName: ChainName;

// We use domainId here because MultiProvider.getDomainId() will always
// return a number, and EVM the domainId and chainId are the same.
public readonly chainId: EvmChainId;
public readonly domainId: Domain;

constructor(
protected readonly multiProvider: MultiProvider,
args: HyperlaneModuleParams<CoreConfig, DeployedCoreAddresses>,
) {
super(args);
this.coreReader = new EvmCoreReader(multiProvider, this.args.chain);
this.chainName = this.multiProvider.getChainName(this.args.chain);
this.coreReader = new EvmCoreReader(multiProvider, args.chain);
this.chainName = multiProvider.getChainName(args.chain);
this.chainId = multiProvider.getEvmChainId(args.chain);
this.domainId = multiProvider.getDomainId(args.chain);
}

Expand Down Expand Up @@ -133,7 +134,7 @@ export class EvmCoreModule extends HyperlaneModule<
);
updateTransactions.push({
annotation: `Setting default ISM for Mailbox ${mailbox} to ${deployedIsm}`,
chainId: this.domainId,
chainId: this.chainId,
to: contractToUpdate.address,
data: contractToUpdate.interface.encodeFunctionData('setDefaultIsm', [
deployedIsm,
Expand Down Expand Up @@ -203,7 +204,7 @@ export class EvmCoreModule extends HyperlaneModule<
expectedConfig: CoreConfig,
): AnnotatedEV5Transaction[] {
return transferOwnershipTransactions(
this.domainId,
this.chainId,
this.args.addresses.mailbox,
actualConfig,
expectedConfig,
Expand Down
Loading

0 comments on commit e3b97c4

Please sign in to comment.