feat: enroll new chains + validators on default ISMs (#4694)

### Description

feat: enroll new chains + validators on default ISMs

- migrate new chains to ICAs
- migrate some existing SAFE owned chains to ICAs
- ensure defaultHook on inevm is made hot again
- add J to safe signers

### Drive-by changes

- IGP gas/token price updates
- add tx overrides when deploying ICA accounts
- swap out A for J on safe signers
- add support for updating owners when doing a safe multisend
- add support for checking and surfacing defaulthook/requiredhook owner
violations
- improve logging + skip bytecode mismatches for now
- parallelise call inference in base app governor
- parallelise deployment of ICAs in get-owner-ica.ts
- reuse existing configured gasPrices for sealevel chains

### Related issues

na

### Backward compatibility

yes

### Testing

will be testing as part of check-deploy
pull/4720/head
Paul Balaji 1 month ago committed by GitHub
parent eeae55bf95
commit 02a5b92ba7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/few-goats-add.md
  2. 7
      typescript/infra/config/environments/mainnet3/chains.ts
  3. 28
      typescript/infra/config/environments/mainnet3/gasPrices.json
  4. 40
      typescript/infra/config/environments/mainnet3/owners.ts
  5. 2
      typescript/infra/config/environments/mainnet3/safe/safeSigners.json
  6. 138
      typescript/infra/config/environments/mainnet3/tokenPrices.json
  7. 57
      typescript/infra/scripts/get-owner-ica.ts
  8. 24
      typescript/infra/scripts/print-gas-prices.ts
  9. 55
      typescript/infra/src/govern/HyperlaneAppGovernor.ts
  10. 6
      typescript/infra/src/govern/HyperlaneCoreGovernor.ts
  11. 6
      typescript/infra/src/govern/HyperlaneIgpGovernor.ts
  12. 54
      typescript/infra/src/utils/safe.ts
  13. 80
      typescript/sdk/src/consts/multisigIsm.ts
  14. 49
      typescript/sdk/src/core/HyperlaneCoreChecker.ts
  15. 7
      typescript/sdk/src/deploy/HyperlaneAppChecker.ts
  16. 3
      typescript/sdk/src/middleware/account/InterchainAccount.ts

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': minor
---
Enroll new validators. Add tx overrides when deploying ICA accounts. Core checker now surfaces owner violations for defaultHook and requiredHook. App checker temporarily ignores bytecode mismatch violations.

@ -59,12 +59,17 @@ export const chainMetadataOverrides: ChainMap<Partial<ChainMetadata>> = {
// gasLimit: 6800000, // set when deploying contracts
},
},
// set when deploying contracts
// Deploy-only overrides, set when deploying contracts
// chiliz: {
// transactionOverrides: {
// maxFeePerGas: 100000 * 10 ** 9, // 100,000 gwei
// },
// },
// zircuit: {
// blocks: {
// confirmations: 3,
// },
// },
};
export const getRegistry = async (useSecrets = true): Promise<IRegistry> =>

@ -24,7 +24,7 @@
"decimals": 9
},
"base": {
"amount": "0.008669818",
"amount": "0.015396226",
"decimals": 9
},
"bitlayer": {
@ -32,7 +32,7 @@
"decimals": 9
},
"blast": {
"amount": "0.004707204",
"amount": "0.005712307",
"decimals": 9
},
"bob": {
@ -72,15 +72,15 @@
"decimals": 9
},
"eclipsemainnet": {
"amount": "0.001",
"decimals": 9
"amount": "0.0000001",
"decimals": 1
},
"endurance": {
"amount": "3.0756015",
"decimals": 9
},
"ethereum": {
"amount": "21.610477208",
"amount": "14.852716956",
"decimals": 9
},
"everclear": {
@ -88,11 +88,11 @@
"decimals": 9
},
"flare": {
"amount": "29.55878872",
"amount": "49.455765643",
"decimals": 9
},
"flow": {
"amount": "0.0000001",
"amount": "0.1",
"decimals": 9
},
"fraxtal": {
@ -104,11 +104,11 @@
"decimals": 9
},
"gnosis": {
"amount": "2.000000007",
"amount": "1.500000007",
"decimals": 9
},
"immutablezkevm": {
"amount": "10.00000005",
"amount": "10.1",
"decimals": 9
},
"inevm": {
@ -124,7 +124,7 @@
"decimals": 9
},
"linea": {
"amount": "0.240000007",
"amount": "0.243",
"decimals": 9
},
"lisk": {
@ -156,7 +156,7 @@
"decimals": 9
},
"metis": {
"amount": "1.247735823",
"amount": "1.278943587",
"decimals": 9
},
"mint": {
@ -236,12 +236,12 @@
"decimals": 9
},
"shibarium": {
"amount": "28.138673121",
"amount": "39.319461243",
"decimals": 9
},
"solanamainnet": {
"amount": "0.001",
"decimals": 9
"amount": "0.5",
"decimals": 1
},
"superposition": {
"amount": "0.01",

@ -68,13 +68,14 @@ export const icas: Partial<
inevm: '0xFDF9EDcb2243D51f5f317b9CEcA8edD2bEEE036e',
// Jul 26, 2024 batch
// -------------------------------------
// ----------------------------------------------------------
xlayer: '0x1571c482fe9E76bbf50829912b1c746792966369',
cheesechain: '0xEe2C5320BE9bC7A1492187cfb289953b53E3ff1b',
worldchain: '0x1996DbFcFB433737fE404F58D2c32A7f5f334210',
// zircuit: '0x0d67c56E818a02ABa58cd2394b95EF26db999aA3', // already has a safe
// Aug 5, 2024 batch
// ----------------------------------------------------------
cyber: '0x984Fe5a45Ac4aaeC4E4655b50f776aB79c9Be19F',
degenchain: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A',
kroma: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A',
@ -88,9 +89,10 @@ export const icas: Partial<
sanko: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752',
tangle: '0xCC2aeb692197C7894E561d31ADFE8F79746f7d9F',
xai: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A',
// taiko: '0x483D218D2FEe7FC7204ba15F00C7901acbF9697D', // already has a safe
// taiko: '0x483D218D2FEe7FC7204ba15F00C7901acbF9697D', // renzo chain
// Aug 26, 2024 batch
// ----------------------------------------------------------
astar: '0x6b241544eBa7d89B51b72DF85a0342dAa37371Ca',
astarzkevm: '0x526c6DAee1175A1A2337E703B63593acb327Dde4',
bitlayer: '0xe6239316cA60814229E801fF0B9DD71C9CA29008',
@ -101,9 +103,41 @@ export const icas: Partial<
shibarium: '0x6348FAe3a8374dbAAaE84EEe5458AE4063Fe2be7',
// Sep 9, 2024 batch
// ----------------------------
// ----------------------------------------------------------
everclear: '0x63B2075372D6d0565F51210D0A296D3c8a773aB6',
oortmainnet: '0x7021D11F9fAe455AB2f45D72dbc2C64d116Cb657',
// Sep 19, 2024 SAFE --> ICA v1 Migration
// ----------------------------------------------------------
celo: '0x3fA264c58E1365f1d5963B831b864EcdD2ddD19b',
avalanche: '0x8c8695cD9905e22d84E466804ABE55408A87e595',
polygon: '0xBDD25dd5203fedE33FD631e30fEF9b9eF2598ECE',
moonbeam: '0x480e5b5De6a29F07fe8295C60A1845d36b7BfdE6',
gnosis: '0xD42125a4889A7A36F32d7D12bFa0ae52B0AD106b',
scroll: '0x2a3fe2513F4A7813683d480724AB0a3683EfF8AC',
polygonzkevm: '0x66037De438a59C966214B78c1d377c4e93a5C7D1',
ancient8: '0xA9FD5BeB556AB1859D7625B381110a257f56F98C',
redstone: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752',
mantle: '0x08C880b88335CA3e85Ebb4E461245a7e899863c9',
bob: '0xc99e58b9A4E330e2E4d09e2c94CD3c553904F588',
zetachain: '0xc876B8e63c3ff5b636d9492715BE375644CaD345',
zoramainnet: '0x84977Eb15E0ff5824a6129c789F70e88352C230b',
fusemainnet: '0xbBdb1682B2922C282b56DD716C29db5EFbdb5632',
endurance: '0x470E04D8a3b7938b385093B93CeBd8Db7A1E557C',
// sei: '0xabad187003EdeDd6C720Fc633f929EA632996567', // renzo chain
// Oct 16, 2024 batch
// ----------------------------------------------------------
immutablezkevm: '0x8483e1480B62cB9f0aCecEbF42469b9f4013577a',
rari: '0x1124D54E989570A798769E534eAFbE1444f40AF6',
rootstock: '0x69350aeA98c5195F2c3cC6E6A065d0d8B12F659A',
alephzeroevm: '0x004a4C2e4Cd4F5Bd564fe0A6Ab2Da56258aE576f',
chiliz: '0xb52D281aD2BA9761c16f400d755837493e2baDB7',
lumia: '0x418E10Ac9e0b84022d0636228d05bc74172e0e41',
superposition: '0x34b57ff8fBA8da0cFdA795CC0F874FfaB14B1DE9',
flow: '0xf48377f8A3ddA7AAD7C2460C81d939434c829b45',
metall2: '0x2f1b1B0Fb7652E621316460f6c3b019F61d8dC9a',
polynomial: '0xC20eFa1e5A378af9233e9b24515eb3408d43f900',
} as const;
export const DEPLOYER = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba';

@ -3,7 +3,7 @@
"0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba",
"0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA",
"0x3b7f8f68A4FD0420FeA2F42a1eFc53422f205599",
"0x88436919fAa2310d32A36D20d13E0a441D24fAc3",
"0x478be6076f31E9666123B9721D0B6631baD944AF",
"0x003DDD9eEAb62013b7332Ab4CC6B10077a8ca961",
"0xd00d6A31485C93c597D1d8231eeeE0ed17B9844B",
"0x483fd7284A696343FEc0819DDF2cf7E06E8A06E5",

@ -1,73 +1,73 @@
{
"ancient8": "2437.96",
"alephzeroevm": "0.36741",
"arbitrum": "2437.96",
"astar": "0.059165",
"astarzkevm": "2437.96",
"avalanche": "26.77",
"base": "2437.96",
"bitlayer": "62244",
"blast": "2437.96",
"bob": "2437.96",
"bsc": "572.12",
"celo": "0.764821",
"cheesechain": "0.00448064",
"chiliz": "0.069844",
"coredao": "0.912209",
"cyber": "2437.96",
"degenchain": "0.00934571",
"dogechain": "0.110085",
"eclipsemainnet": "2437.96",
"endurance": "2.11",
"ethereum": "2437.96",
"everclear": "2437.96",
"flare": "0.01456139",
"flow": "0.533589",
"fraxtal": "2434.37",
"fusemainnet": "0.02952521",
"gnosis": "1.009",
"immutablezkevm": "1.48",
"inevm": "20.24",
"injective": "20.24",
"kroma": "2437.96",
"linea": "2437.96",
"lisk": "2437.96",
"lukso": "1.51",
"lumia": "0.954153",
"mantapacific": "2437.96",
"mantle": "0.59813",
"merlin": "62293",
"metall2": "2437.96",
"metis": "34.53",
"mint": "2437.96",
"mode": "2437.96",
"molten": "0.632429",
"moonbeam": "0.163919",
"neutron": "0.390086",
"oortmainnet": "0.11645",
"optimism": "2437.96",
"osmosis": "0.521323",
"polygon": "0.371959",
"polygonzkevm": "2437.96",
"polynomial": "2437.96",
"proofofplay": "2437.96",
"rari": "2437.96",
"ancient8": "2629.74",
"alephzeroevm": "0.381786",
"arbitrum": "2629.74",
"astar": "0.061114",
"astarzkevm": "2629.74",
"avalanche": "27.96",
"base": "2629.74",
"bitlayer": "67813",
"blast": "2629.74",
"bob": "2629.74",
"bsc": "597.89",
"celo": "0.817141",
"cheesechain": "0.00556724",
"chiliz": "0.079288",
"coredao": "0.98348",
"cyber": "2629.74",
"degenchain": "0.00882961",
"dogechain": "0.126177",
"eclipsemainnet": "2629.74",
"endurance": "2.16",
"ethereum": "2629.74",
"everclear": "2629.74",
"flare": "0.01493582",
"flow": "0.558323",
"fraxtal": "2629.35",
"fusemainnet": "0.02901498",
"gnosis": "0.997404",
"immutablezkevm": "1.54",
"inevm": "21.05",
"injective": "21.05",
"kroma": "2629.74",
"linea": "2629.74",
"lisk": "2629.74",
"lukso": "1.47",
"lumia": "0.969511",
"mantapacific": "2629.74",
"mantle": "0.636484",
"merlin": "67781",
"metall2": "2629.74",
"metis": "45.78",
"mint": "2629.74",
"mode": "2629.74",
"molten": "0.436605",
"moonbeam": "0.169406",
"neutron": "0.408859",
"oortmainnet": "0.114304",
"optimism": "2629.74",
"osmosis": "0.558566",
"polygon": "0.371646",
"polygonzkevm": "2629.74",
"polynomial": "2629.74",
"proofofplay": "2629.74",
"rari": "2629.74",
"real": "1",
"redstone": "2437.96",
"rootstock": "61812",
"sanko": "41.59",
"scroll": "2437.96",
"sei": "0.444401",
"shibarium": "0.404651",
"solanamainnet": "144.84",
"superposition": "2437.96",
"taiko": "2437.96",
"redstone": "2629.74",
"rootstock": "67219",
"sanko": "70.7",
"scroll": "2629.74",
"sei": "0.447635",
"shibarium": "0.410927",
"solanamainnet": "155.35",
"superposition": "2629.74",
"taiko": "2629.74",
"tangle": "1",
"viction": "0.359062",
"worldchain": "2437.96",
"xai": "0.215315",
"xlayer": "42.29",
"zetachain": "0.581304",
"zircuit": "2437.96",
"zoramainnet": "2437.96"
"viction": "0.369839",
"worldchain": "2629.74",
"xai": "0.216438",
"xlayer": "41.56",
"zetachain": "0.617959",
"zircuit": "2629.74",
"zoramainnet": "2629.74"
}

@ -1,11 +1,13 @@
import { AccountConfig, InterchainAccount } from '@hyperlane-xyz/sdk';
import { Address, assert, eqAddress } from '@hyperlane-xyz/utils';
import { Address, eqAddress } from '@hyperlane-xyz/utils';
import { getArgs as getEnvArgs, withChainsRequired } from './agent-utils.js';
import { isEthereumProtocolChain } from '../src/utils/utils.js';
import { getArgs as getEnvArgs, withChains } from './agent-utils.js';
import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js';
function getArgs() {
return withChainsRequired(getEnvArgs())
return withChains(getEnvArgs())
.option('ownerChain', {
type: 'string',
description: 'Origin chain where the governing owner lives',
@ -51,20 +53,47 @@ async function main() {
owner: originOwner,
};
const getOwnerIcaChains = (
chains?.length ? chains : config.supportedChainNames
).filter(isEthereumProtocolChain);
const results: Record<string, { ICA: Address; Deployed?: string }> = {};
for (const chain of chains) {
const account = await ica.getAccount(chain, ownerConfig);
results[chain] = { ICA: account };
const settledResults = await Promise.allSettled(
getOwnerIcaChains.map(async (chain) => {
try {
const account = await ica.getAccount(chain, ownerConfig);
const result: { ICA: Address; Deployed?: string } = { ICA: account };
if (deploy) {
const deployedAccount = await ica.deployAccount(chain, ownerConfig);
result.Deployed = eqAddress(account, deployedAccount) ? '✅' : '❌';
if (result.Deployed === '❌') {
console.warn(
`Mismatch between account and deployed account for ${chain}`,
);
}
}
if (deploy) {
const deployedAccount = await ica.deployAccount(chain, ownerConfig);
assert(
eqAddress(account, deployedAccount),
'Fatal mismatch between account and deployed account',
);
results[chain].Deployed = '✅';
return { chain, result };
} catch (error) {
console.error(`Error processing chain ${chain}:`, error);
return { chain, error };
}
}),
);
settledResults.forEach((settledResult) => {
if (settledResult.status === 'fulfilled') {
const { chain, result, error } = settledResult.value;
if (error || !result) {
console.error(`Failed to process ${chain}:`, error);
} else {
results[chain] = result;
}
} else {
console.error(`Promise rejected:`, settledResult.reason);
}
}
});
console.table(results);
}

@ -7,8 +7,10 @@ import { ProtocolType } from '@hyperlane-xyz/utils';
// Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')`
// to avoid circular dependencies.
import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js';
import mainnet3GasPrices from '../config/environments/mainnet3/gasPrices.json' assert { type: 'json' };
import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js';
import { getRegistry as getTestnet4Registry } from '../config/environments/testnet4/chains.js';
import testnet4GasPrices from '../config/environments/testnet4/gasPrices.json' assert { type: 'json' };
import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/supportedChainNames.js';
import {
GasPriceConfig,
@ -19,15 +21,17 @@ import { getArgs } from './agent-utils.js';
async function main() {
const { environment } = await getArgs().argv;
const { registry, supportedChainNames } =
const { registry, supportedChainNames, gasPrices } =
environment === 'mainnet3'
? {
registry: await getMainnet3Registry(),
supportedChainNames: mainnet3SupportedChainNames,
gasPrices: mainnet3GasPrices,
}
: {
registry: await getTestnet4Registry(),
supportedChainNames: testnet4SupportedChainNames,
gasPrices: testnet4GasPrices,
};
const chainMetadata = await registry.getMetadata();
@ -37,7 +41,11 @@ async function main() {
await Promise.all(
supportedChainNames.map(async (chain) => [
chain,
await getGasPrice(mpp, chain),
await getGasPrice(
mpp,
chain,
gasPrices[chain as keyof typeof gasPrices],
),
]),
),
);
@ -48,6 +56,7 @@ async function main() {
async function getGasPrice(
mpp: MultiProtocolProvider,
chain: string,
currentGasPrice?: GasPriceConfig,
): Promise<GasPriceConfig> {
const protocolType = mpp.getProtocol(chain);
switch (protocolType) {
@ -68,11 +77,14 @@ async function getGasPrice(
};
}
case ProtocolType.Sealevel:
// Return the gas price from the config if it exists, otherwise return some default
// TODO get a reasonable value
return {
amount: '0.001',
decimals: 9,
};
return (
currentGasPrice ?? {
amount: 'PLEASE SET A GAS PRICE FOR SEALEVEL',
decimals: 1,
}
);
default:
throw new Error(`Unsupported protocol type: ${protocolType}`);
}

@ -26,6 +26,8 @@ import {
retryAsync,
} from '@hyperlane-xyz/utils';
import { getSafeAndService, updateSafeOwner } from '../utils/safe.js';
import {
ManualMultiSend,
MultiSend,
@ -159,7 +161,22 @@ export abstract class HyperlaneAppGovernor<
submissionType: SubmissionType,
multiSend: MultiSend,
) => {
const callsForSubmissionType = filterCalls(submissionType) || [];
const callsForSubmissionType = [];
const filteredCalls = filterCalls(submissionType);
// If calls are being submitted via a safe, we need to check for any safe owner changes first
if (submissionType === SubmissionType.SAFE) {
const { safeSdk } = await getSafeAndService(
chain,
this.checker.multiProvider,
(multiSend as SafeMultiSend).safeAddress,
);
const updateOwnerCalls = await updateSafeOwner(safeSdk);
callsForSubmissionType.push(...updateOwnerCalls, ...filteredCalls);
} else {
callsForSubmissionType.push(...filteredCalls);
}
if (callsForSubmissionType.length > 0) {
this.printSeparator();
const confirmed = await summarizeCalls(
@ -257,7 +274,6 @@ export abstract class HyperlaneAppGovernor<
protected async inferCallSubmissionTypes() {
const newCalls: ChainMap<AnnotatedCallData[]> = {};
const pushNewCall = (inferredCall: InferredCall) => {
newCalls[inferredCall.chain] = newCalls[inferredCall.chain] || [];
newCalls[inferredCall.chain].push({
@ -267,20 +283,29 @@ export abstract class HyperlaneAppGovernor<
});
};
for (const chain of Object.keys(this.calls)) {
try {
for (const call of this.calls[chain]) {
const inferredCall = await this.inferCallSubmissionType(chain, call);
pushNewCall(inferredCall);
const results: ChainMap<InferredCall[]> = {};
await Promise.all(
Object.keys(this.calls).map(async (chain) => {
try {
results[chain] = await Promise.all(
this.calls[chain].map((call) =>
this.inferCallSubmissionType(chain, call),
),
);
} catch (error) {
console.error(
chalk.red(
`Error inferring call submission types for chain ${chain}: ${error}`,
),
);
results[chain] = [];
}
} catch (error) {
console.error(
chalk.red(
`Error inferring call submission types for chain ${chain}: ${error}`,
),
);
}
}
}),
);
Object.entries(results).forEach(([_, inferredCalls]) => {
inferredCalls.forEach(pushNewCall);
});
this.calls = newCalls;
}

@ -84,7 +84,11 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor<
return this.handleProxyAdminViolation(violation as ProxyAdminViolation);
}
default:
throw new Error(`Unsupported violation type ${violation.type}`);
throw new Error(
`Unsupported violation type ${violation.type}: ${JSON.stringify(
violation,
)}`,
);
}
}
}

@ -29,7 +29,11 @@ export class HyperlaneIgpGovernor extends HyperlaneAppGovernor<
return super.handleOwnerViolation(violation as OwnerViolation);
}
default:
throw new Error(`Unsupported violation type ${violation.type}`);
throw new Error(
`Unsupported violation type ${violation.type}: ${JSON.stringify(
violation,
)}`,
);
}
}

@ -6,7 +6,7 @@ import {
SafeTransaction,
} from '@safe-global/safe-core-sdk-types';
import chalk from 'chalk';
import { ethers } from 'ethers';
import { BigNumber, ethers } from 'ethers';
import {
ChainNameOrId,
@ -14,7 +14,10 @@ import {
getSafe,
getSafeService,
} from '@hyperlane-xyz/sdk';
import { Address, CallData } from '@hyperlane-xyz/utils';
import { Address, CallData, eqAddress } from '@hyperlane-xyz/utils';
import safeSigners from '../../config/environments/mainnet3/safe/safeSigners.json' assert { type: 'json' };
import { AnnotatedCallData } from '../govern/HyperlaneAppGovernor.js';
export async function getSafeAndService(
chain: ChainNameOrId,
@ -222,3 +225,50 @@ export async function deleteSafeTx(
);
}
}
export async function updateSafeOwner(
safeSdk: Safe.default,
): Promise<AnnotatedCallData[]> {
const threshold = await safeSdk.getThreshold();
const owners = await safeSdk.getOwners();
const newOwners = safeSigners.signers;
const ownersToRemove = owners.filter(
(owner) => !newOwners.some((newOwner) => eqAddress(owner, newOwner)),
);
const ownersToAdd = newOwners.filter(
(newOwner) => !owners.some((owner) => eqAddress(newOwner, owner)),
);
console.log(chalk.magentaBright('Owners to remove:', ownersToRemove));
console.log(chalk.magentaBright('Owners to add:', ownersToAdd));
const transactions: AnnotatedCallData[] = [];
for (const ownerToRemove of ownersToRemove) {
const { data: removeTxData } = await safeSdk.createRemoveOwnerTx({
ownerAddress: ownerToRemove,
threshold,
});
transactions.push({
to: removeTxData.to,
data: removeTxData.data,
value: BigNumber.from(removeTxData.value),
description: `Remove safe owner ${ownerToRemove}`,
});
}
for (const ownerToAdd of ownersToAdd) {
const { data: addTxData } = await safeSdk.createAddOwnerTx({
ownerAddress: ownerToAdd,
threshold,
});
transactions.push({
to: addTxData.to,
data: addTxData.data,
value: BigNumber.from(addTxData.value),
description: `Add safe owner ${ownerToAdd}`,
});
}
return transactions;
}

@ -4,8 +4,12 @@ import { ChainMap } from '../types.js';
// TODO: consider migrating these to the registry too
export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
alephzeroevm: {
threshold: 1,
validators: ['0xcae8fab142adc4e434bb7409e40dd932cc3851aa'],
threshold: 2,
validators: [
'0xcae8fab142adc4e434bb7409e40dd932cc3851aa',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
alfajores: {
@ -175,8 +179,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
chiliz: {
threshold: 1,
validators: ['0x82d024f453b1a3f3f6606226f06b038da27596f3'],
threshold: 2,
validators: [
'0x82d024f453b1a3f3f6606226f06b038da27596f3',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
citreatestnet: {
@ -286,8 +294,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
flow: {
threshold: 1,
validators: ['0x3aee1090318e9c54d1d23194dcd0f2bee00ddc97'],
threshold: 2,
validators: [
'0x3aee1090318e9c54d1d23194dcd0f2bee00ddc97',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
formtestnet: {
@ -343,8 +355,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
immutablezkevm: {
threshold: 1,
validators: ['0xa787c2952a4d22f776ee6e87e828e6f75de24330'],
threshold: 2,
validators: [
'0xa787c2952a4d22f776ee6e87e828e6f75de24330',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
inevm: {
@ -402,8 +418,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
lumia: {
threshold: 1,
validators: ['0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f'],
threshold: 2,
validators: [
'0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
mantapacific: {
@ -438,8 +458,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
metall2: {
threshold: 1,
validators: ['0x1b000e1e1f0a032ed382c6d69a2d58f6fe773c09'],
threshold: 2,
validators: [
'0x1b000e1e1f0a032ed382c6d69a2d58f6fe773c09',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
metis: {
@ -567,8 +591,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
polynomial: {
threshold: 1,
validators: ['0xa63ad0891e921ad5947d57e05831fabb9816eca7'],
threshold: 2,
validators: [
'0xa63ad0891e921ad5947d57e05831fabb9816eca7',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
proofofplay: {
@ -581,8 +609,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
rari: {
threshold: 1,
validators: ['0x989d6862e09de21337078efbd86843a3eb1133e3'],
threshold: 2,
validators: [
'0x989d6862e09de21337078efbd86843a3eb1133e3',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
real: {
@ -604,8 +636,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
rootstock: {
threshold: 1,
validators: ['0xcb8e3a72cf427feff27416d0e2ec375a052eaaee'],
threshold: 2,
validators: [
'0xcb8e3a72cf427feff27416d0e2ec375a052eaaee',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
sanko: {
@ -721,8 +757,12 @@ export const defaultMultisigConfigs: ChainMap<MultisigConfig> = {
},
superposition: {
threshold: 1,
validators: ['0x5978d0e6afa9270ddb87cff43a8fa7a763a5dfc4'],
threshold: 2,
validators: [
'0x5978d0e6afa9270ddb87cff43a8fa7a763a5dfc4',
'0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly
'0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis
],
},
superpositiontestnet: {

@ -1,10 +1,12 @@
import { ethers, utils as ethersUtils } from 'ethers';
import { Ownable__factory } from '@hyperlane-xyz/core';
import { assert, eqAddress, rootLogger } from '@hyperlane-xyz/utils';
import { BytecodeHash } from '../consts/bytecode.js';
import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js';
import { proxyImplementation } from '../deploy/proxy.js';
import { OwnerViolation, ViolationType } from '../deploy/types.js';
import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js';
import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js';
import { collectValidators, moduleMatchesConfig } from '../ism/utils.js';
@ -66,6 +68,31 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
return this.checkOwnership(chain, config.owner, config.ownerOverrides);
}
async checkHook(
chain: ChainName,
hookName: string,
hookAddress: string,
expectedHookOwner: string,
): Promise<void> {
const hook = Ownable__factory.connect(
hookAddress,
this.multiProvider.getProvider(chain),
);
const hookOwner = await hook.owner();
if (!eqAddress(hookOwner, expectedHookOwner)) {
const violation: OwnerViolation = {
type: ViolationType.Owner,
chain,
name: hookName,
actual: hookOwner,
expected: expectedHookOwner,
contract: hook,
};
this.addViolation(violation);
}
}
async checkMailbox(chain: ChainName): Promise<void> {
const contracts = this.app.getContracts(chain);
const mailbox = contracts.mailbox;
@ -77,9 +104,27 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
)} for ${chain}`,
);
const actualIsmAddress = await mailbox.defaultIsm();
const config = this.configMap[chain];
const expectedHookOwner = this.getOwner(
config.owner,
'fallbackRoutingHook',
config.ownerOverrides,
);
await this.checkHook(
chain,
'defaultHook',
await mailbox.defaultHook(),
expectedHookOwner,
);
await this.checkHook(
chain,
'requiredHook',
await mailbox.requiredHook(),
expectedHookOwner,
);
const actualIsmAddress = await mailbox.defaultIsm();
const matches = await moduleMatchesConfig(
chain,
actualIsmAddress,

@ -12,6 +12,7 @@ import {
eqAddress,
objMap,
promiseObjAll,
rootLogger,
} from '@hyperlane-xyz/utils';
import { HyperlaneApp } from '../app/HyperlaneApp.js';
@ -82,6 +83,10 @@ export abstract class HyperlaneAppChecker<
}
addViolation(violation: CheckerViolation): void {
if (violation.type === ViolationType.BytecodeMismatch) {
rootLogger.warn({ violation }, `Found bytecode mismatch. Ignoring...`);
return;
}
this.violations.push(violation);
}
@ -208,7 +213,7 @@ export abstract class HyperlaneAppChecker<
return bytecode.substring(0, bytecode.length - 90);
}
private getOwner(
protected getOwner(
owner: Address,
contractName: string,
ownableOverrides?: Record<string, Address>,

@ -120,6 +120,8 @@ export class InterchainAccount extends RouterApp<InterchainAccountFactories> {
.getProvider(destinationChain)
.getCode(destinationAccount)) === '0x'
) {
const txOverrides =
this.multiProvider.getTransactionOverrides(destinationChain);
await this.multiProvider.handleTx(
destinationChain,
destinationRouter[
@ -129,6 +131,7 @@ export class InterchainAccount extends RouterApp<InterchainAccountFactories> {
config.owner,
originRouterAddress,
destinationIsmAddress,
txOverrides,
),
);
this.logger.debug(`Interchain account deployed at ${destinationAccount}`);

Loading…
Cancel
Save