diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2a4ea99c65..d47caa6772 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -239,7 +239,12 @@ jobs: strategy: matrix: environment: [testnet4, mainnet3] - module: [ism, core, helloworld] + module: [core, igp, helloworld] + include: + - environment: testnet4 + chain: sepolia + - environment: mainnet3 + chain: arbitrum steps: - uses: actions/checkout@v3 @@ -255,8 +260,8 @@ jobs: !./rust key: ${{ github.sha }} - - name: Test ${{ matrix.environment }} ${{ matrix.module }} deployment (check, deploy, govern, check again) - run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} + - name: Fork test ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} deployment + run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} coverage: runs-on: ubuntu-latest diff --git a/typescript/infra/config/environments/mainnet3/core.ts b/typescript/infra/config/environments/mainnet3/core.ts index c228b2d94e..6992f46bc3 100644 --- a/typescript/infra/config/environments/mainnet3/core.ts +++ b/typescript/infra/config/environments/mainnet3/core.ts @@ -21,7 +21,7 @@ import { objMap } from '@hyperlane-xyz/utils'; import { supportedChainNames } from './chains'; import { igp } from './igp'; -import { owners, safes } from './owners'; +import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { const originMultisigs: ChainMap = Object.fromEntries( @@ -50,12 +50,12 @@ export const core: ChainMap = objMap(owners, (local, owner) => { threshold: 1, }), ), - owner, + ...owner, }; const pausableIsm: PausableIsmConfig = { type: IsmType.PAUSABLE, - owner, + ...owner, }; const defaultIsm: AggregationIsmConfig = { @@ -75,7 +75,7 @@ export const core: ChainMap = objMap(owners, (local, owner) => { const pausableHook: PausableHookConfig = { type: HookType.PAUSABLE, - owner, + ...owner, }; const defaultHook: AggregationHookConfig = { @@ -87,20 +87,14 @@ export const core: ChainMap = objMap(owners, (local, owner) => { type: HookType.PROTOCOL_FEE, maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token protocolFee: BigNumber.from(0).toString(), // 0 wei - beneficiary: owner, - owner, + beneficiary: owner.owner, + ...owner, }; return { - owner, defaultIsm, defaultHook, requiredHook, - ownerOverrides: { - proxyAdmin: - local === 'arbitrum' - ? `0xAC98b0cD1B64EA4fe133C6D2EDaf842cE5cF4b01` - : safes[local] ?? owner, - }, + ...owner, }; }); diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index aee860b461..19a894d136 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -28,7 +28,7 @@ function getGasOracles(local: MainnetChains) { } export const igp: ChainMap = objMap(owners, (chain, owner) => ({ - owner, + ...owner, oracleKey: DEPLOYER_ADDRESS, beneficiary: KEY_FUNDER_ADDRESS, gasOracleType: getGasOracles(chain), diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index e78ad2003a..6bf7df49c1 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -1,7 +1,11 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; +import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; import { Address, objMap } from '@hyperlane-xyz/utils'; -export const safes: ChainMap
= { +export const timelocks: ChainMap
= { + arbitrum: '0xAC98b0cD1B64EA4fe133C6D2EDaf842cE5cF4b01', +}; + +export const safes: ChainMap
= { celo: '0x1DE69322B55AC7E0999F8e7738a1428C8b130E4d', ethereum: '0x12C5AB61Fe17dF9c65739DBa73dF294708f78d23', avalanche: '0xDF9B28B76877f1b1B4B8a11526Eb7D8D7C49f4f3', @@ -13,14 +17,16 @@ export const safes: ChainMap
= { gnosis: '0x36b0AA0e7d04e7b825D7E409FEa3c9A3d57E4C22', // solana: 'EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3', // TODO: create gnosis safes here - base: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - scroll: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - polygonzkevm: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - mantapacific: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', + base: undefined, + scroll: undefined, + polygonzkevm: undefined, + mantapacific: undefined, }; -// export const owners = safes; - -// temporarily keep ownership on deployer key const deployer = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; -export const owners = objMap(safes, (_, __) => deployer); +export const owners: ChainMap = objMap(safes, (local, __) => ({ + owner: deployer, // TODO: change this to the safe + ownerOverrides: { + proxyAdmin: timelocks[local] ?? safes[local] ?? deployer, + }, +})); diff --git a/typescript/infra/config/environments/test/core.ts b/typescript/infra/config/environments/test/core.ts index 397059c52c..5bfefd1456 100644 --- a/typescript/infra/config/environments/test/core.ts +++ b/typescript/infra/config/environments/test/core.ts @@ -22,7 +22,7 @@ import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { const defaultIsm: RoutingIsmConfig = { type: IsmType.ROUTING, - owner, + ...owner, domains: Object.fromEntries( Object.entries(chainToValidator) .filter(([chain, _]) => chain !== local) @@ -46,7 +46,7 @@ export const core: ChainMap = objMap(owners, (local, owner) => { const defaultHook: FallbackRoutingHookConfig = { type: HookType.FALLBACK_ROUTING, - owner, + ...owner, fallback: merkleHook, domains: Object.fromEntries( Object.entries(chainToValidator) @@ -59,14 +59,14 @@ export const core: ChainMap = objMap(owners, (local, owner) => { type: HookType.PROTOCOL_FEE, maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token protocolFee: BigNumber.from(1).toString(), // 1 wei - beneficiary: owner, - owner, + beneficiary: owner.owner, + ...owner, }; return { - owner, defaultIsm, defaultHook, requiredHook, + ...owner, }; }); diff --git a/typescript/infra/config/environments/test/igp.ts b/typescript/infra/config/environments/test/igp.ts index 6590c933ab..cb7ffa4bf6 100644 --- a/typescript/infra/config/environments/test/igp.ts +++ b/typescript/infra/config/environments/test/igp.ts @@ -19,7 +19,7 @@ function getGasOracles(local: TestChains) { ); } -export const igp: ChainMap = objMap(owners, (chain, owner) => { +export const igp: ChainMap = objMap(owners, (chain, ownerConfig) => { const overhead = Object.fromEntries( exclude(chain, chainNames).map((remote) => [ remote, @@ -30,10 +30,10 @@ export const igp: ChainMap = objMap(owners, (chain, owner) => { ]), ); return { - owner, - oracleKey: owner, - beneficiary: owner, + oracleKey: ownerConfig.owner, + beneficiary: ownerConfig.owner, gasOracleType: getGasOracles(chain), overhead, + ...ownerConfig, }; }); diff --git a/typescript/infra/config/environments/test/owners.ts b/typescript/infra/config/environments/test/owners.ts index 3134145ed0..4156bd0ecb 100644 --- a/typescript/infra/config/environments/test/owners.ts +++ b/typescript/infra/config/environments/test/owners.ts @@ -1,10 +1,9 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; import { chainNames } from './chains'; // Owner is hardhat account 0 const OWNER_ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; -export const owners: ChainMap
= Object.fromEntries( - chainNames.map((chain) => [chain, OWNER_ADDRESS]), +export const owners: ChainMap = Object.fromEntries( + chainNames.map((chain) => [chain, { owner: OWNER_ADDRESS }]), ); diff --git a/typescript/infra/config/environments/testnet4/core.ts b/typescript/infra/config/environments/testnet4/core.ts index 5d23ef799a..7973020cbb 100644 --- a/typescript/infra/config/environments/testnet4/core.ts +++ b/typescript/infra/config/environments/testnet4/core.ts @@ -24,87 +24,90 @@ import { supportedChainNames } from './chains'; import { igp } from './igp'; import { owners } from './owners'; -export const core: ChainMap = objMap(owners, (local, owner) => { - const originMultisigs: ChainMap = Object.fromEntries( - supportedChainNames - .filter((chain) => chain !== local) - .map((origin) => [origin, defaultMultisigConfigs[origin]]), - ); +export const core: ChainMap = objMap( + owners, + (local, ownerConfig) => { + const originMultisigs: ChainMap = Object.fromEntries( + supportedChainNames + .filter((chain) => chain !== local) + .map((origin) => [origin, defaultMultisigConfigs[origin]]), + ); - const merkleRoot = (multisig: MultisigConfig): MultisigIsmConfig => ({ - type: IsmType.MERKLE_ROOT_MULTISIG, - ...multisig, - }); + const merkleRoot = (multisig: MultisigConfig): MultisigIsmConfig => ({ + type: IsmType.MERKLE_ROOT_MULTISIG, + ...multisig, + }); - const messageIdIsm = (multisig: MultisigConfig): MultisigIsmConfig => ({ - type: IsmType.MESSAGE_ID_MULTISIG, - ...multisig, - }); + const messageIdIsm = (multisig: MultisigConfig): MultisigIsmConfig => ({ + type: IsmType.MESSAGE_ID_MULTISIG, + ...multisig, + }); - const routingIsm: RoutingIsmConfig = { - type: IsmType.ROUTING, - domains: objMap( - originMultisigs, - (_, multisig): AggregationIsmConfig => ({ - type: IsmType.AGGREGATION, - modules: [messageIdIsm(multisig), merkleRoot(multisig)], - threshold: 1, - }), - ), - owner, - }; + const routingIsm: RoutingIsmConfig = { + type: IsmType.ROUTING, + domains: objMap( + originMultisigs, + (_, multisig): AggregationIsmConfig => ({ + type: IsmType.AGGREGATION, + modules: [messageIdIsm(multisig), merkleRoot(multisig)], + threshold: 1, + }), + ), + ...ownerConfig, + }; - const pausableIsm: PausableIsmConfig = { - type: IsmType.PAUSABLE, - owner, - }; + const pausableIsm: PausableIsmConfig = { + type: IsmType.PAUSABLE, + ...ownerConfig, + }; - const defaultIsm: AggregationIsmConfig = { - type: IsmType.AGGREGATION, - modules: [routingIsm, pausableIsm], - threshold: 2, - }; + const defaultIsm: AggregationIsmConfig = { + type: IsmType.AGGREGATION, + modules: [routingIsm, pausableIsm], + threshold: 2, + }; - const merkleHook: MerkleTreeHookConfig = { - type: HookType.MERKLE_TREE, - }; + const merkleHook: MerkleTreeHookConfig = { + type: HookType.MERKLE_TREE, + }; - const igpHook: IgpHookConfig = { - type: HookType.INTERCHAIN_GAS_PAYMASTER, - ...igp[local], - }; + const igpHook: IgpHookConfig = { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + ...igp[local], + }; - const pausableHook: PausableHookConfig = { - type: HookType.PAUSABLE, - owner, - }; + const pausableHook: PausableHookConfig = { + type: HookType.PAUSABLE, + ...ownerConfig, + }; - const aggregationHooks = objMap( - originMultisigs, - (_origin, _): AggregationHookConfig => ({ - type: HookType.AGGREGATION, - hooks: [pausableHook, merkleHook, igpHook], - }), - ); + const aggregationHooks = objMap( + originMultisigs, + (_origin, _): AggregationHookConfig => ({ + type: HookType.AGGREGATION, + hooks: [pausableHook, merkleHook, igpHook], + }), + ); - const defaultHook: DomainRoutingHookConfig = { - type: HookType.ROUTING, - owner, - domains: aggregationHooks, - }; + const defaultHook: DomainRoutingHookConfig = { + type: HookType.ROUTING, + domains: aggregationHooks, + ...ownerConfig, + }; - const requiredHook: ProtocolFeeHookConfig = { - type: HookType.PROTOCOL_FEE, - maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token - protocolFee: BigNumber.from(1).toString(), // 1 wei of native token - beneficiary: owner, - owner, - }; + const requiredHook: ProtocolFeeHookConfig = { + type: HookType.PROTOCOL_FEE, + maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token + protocolFee: BigNumber.from(1).toString(), // 1 wei of native token + beneficiary: ownerConfig.owner, + ...ownerConfig, + }; - return { - owner, - defaultIsm, - defaultHook, - requiredHook, - }; -}); + return { + defaultIsm, + defaultHook, + requiredHook, + ...ownerConfig, + }; + }, +); diff --git a/typescript/infra/config/environments/testnet4/igp.ts b/typescript/infra/config/environments/testnet4/igp.ts index c098d0af61..0a4cd987a6 100644 --- a/typescript/infra/config/environments/testnet4/igp.ts +++ b/typescript/infra/config/environments/testnet4/igp.ts @@ -19,11 +19,11 @@ function getGasOracles(local: TestnetChains) { ); } -export const igp: ChainMap = objMap(owners, (chain, owner) => { +export const igp: ChainMap = objMap(owners, (chain, ownerConfig) => { return { - owner, - oracleKey: owner, - beneficiary: owner, + ...ownerConfig, + oracleKey: ownerConfig.owner, + beneficiary: ownerConfig.owner, gasOracleType: getGasOracles(chain), overhead: Object.fromEntries( exclude(chain, supportedChainNames).map((remote) => [ diff --git a/typescript/infra/config/environments/testnet4/owners.ts b/typescript/infra/config/environments/testnet4/owners.ts index 2522ea3892..b9c1e2e3c3 100644 --- a/typescript/infra/config/environments/testnet4/owners.ts +++ b/typescript/infra/config/environments/testnet4/owners.ts @@ -1,14 +1,16 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; import { ethereumChainNames } from './chains'; const ETHEREUM_DEPLOYER_ADDRESS = '0xfaD1C94469700833717Fa8a3017278BC1cA8031C'; // const SEALEVEL_DEPLOYER_ADDRESS = '6DjHX6Ezjpq3zZMZ8KsqyoFYo1zPSDoiZmLLkxD4xKXS'; -export const owners: ChainMap
= { +export const owners: ChainMap = { ...Object.fromEntries( - ethereumChainNames.map((chain) => [chain, ETHEREUM_DEPLOYER_ADDRESS]), + ethereumChainNames.map((chain) => [ + chain, + { owner: ETHEREUM_DEPLOYER_ADDRESS }, + ]), ), // [chainMetadata.solanadevnet.name]: SEALEVEL_DEPLOYER_ADDRESS, }; diff --git a/typescript/infra/config/routingIsm.ts b/typescript/infra/config/routingIsm.ts index 41271f5207..7f25c6b9f3 100644 --- a/typescript/infra/config/routingIsm.ts +++ b/typescript/infra/config/routingIsm.ts @@ -12,6 +12,7 @@ import { import { DeployEnvironment } from '../src/config'; import { Contexts } from './contexts'; +import { environments } from './environments'; import { ethereumChainNames as mainnet3Chains } from './environments/mainnet3/chains'; import { owners as mainnet3Owners } from './environments/mainnet3/owners'; import { owners as testOwners } from './environments/test/owners'; @@ -55,7 +56,7 @@ export const routingIsm = ( return { type: IsmType.ROUTING, domains: aggregationIsms, - owner: owners[environment][local], + owner: environments[environment].core[local].owner, }; }; diff --git a/typescript/infra/fork-all.sh b/typescript/infra/fork-all.sh new file mode 100755 index 0000000000..6f39861b45 --- /dev/null +++ b/typescript/infra/fork-all.sh @@ -0,0 +1,18 @@ +ENVIRONMENT=$1 +MODULE=$2 + +if [ -z "$ENVIRONMENT" ] || [ -z "$MODULE" ]; then + echo "Usage: fork-all.sh " + exit 1 +fi + +CHAINS=`yarn ts-node ./scripts/print-chain-metadatas.ts -e $ENVIRONMENT | \ + jq -r 'to_entries | map(select(.value.protocol=="ethereum")) | map(.key) ' | \ + tr -d '\"[],'` + +# echo all subsequent commands +set -x + +for CHAIN in $CHAINS; do + ./fork.sh $ENVIRONMENT $MODULE $CHAIN +done diff --git a/typescript/infra/fork.sh b/typescript/infra/fork.sh index cb909d043b..13ed5a9a9e 100755 --- a/typescript/infra/fork.sh +++ b/typescript/infra/fork.sh @@ -1,19 +1,9 @@ ENVIRONMENT=$1 MODULE=$2 +CHAIN=$3 -if [ -z "$ENVIRONMENT" ]; then - echo "Usage: fork.sh " - exit 1 -fi - -if [ "$ENVIRONMENT" == "testnet4" ]; then - FORK_CHAIN="goerli" - RPC_URL="https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" -elif [ "$ENVIRONMENT" == "mainnet3" ]; then - FORK_CHAIN="arbitrum" - RPC_URL="https://arb1.arbitrum.io/rpc" -else - echo "Unknown environment $ENVIRONMENT" +if [ -z "$ENVIRONMENT" ] || [ -z "$MODULE" ] || [ -z "$CHAIN" ]; then + echo "Usage: fork.sh " exit 1 fi @@ -23,24 +13,36 @@ trap 'jobs -p | xargs -r kill' EXIT # exit 1 on any subsequent failures set -e -anvil --fork-url $RPC_URL --silent > /dev/null & +RPC_URL=`yarn ts-node ./scripts/print-chain-metadatas.ts -e $ENVIRONMENT | jq -r ".$CHAIN.rpcUrls[0].http"` + +anvil --fork-url $RPC_URL --fork-retry-backoff 3 --compute-units-per-second 200 --gas-price 1 --silent & ANVIL_PID=$! while ! cast bn &> /dev/null; do sleep 1 done -echo "=== Run $MODULE checker against forked $ENVIRONMENT ===" -yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE +# echo all subsequent commands +set -x -echo "=== Run $MODULE deployer against forked $ENVIRONMENT ===" -yarn ts-node ./scripts/deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE +yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $CHAIN -m $MODULE + +# get balance +DEPLOYER="0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" +BEFORE=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) + +yarn ts-node ./scripts/deploy.ts -e $ENVIRONMENT -f $CHAIN -m $MODULE + +AFTER=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +DEPLOY_DELTA="$((BEFORE-AFTER))" # build SDK to get the latest addresses yarn --cwd ../sdk build -echo "=== Run $MODULE govern against forked $ENVIRONMENT ===" -yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN --govern -m $MODULE +BEFORE=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $CHAIN --govern -m $MODULE + +AFTER=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +GOVERN_DELTA="$((BEFORE-AFTER))" -echo "=== Run $MODULE checker against forked $ENVIRONMENT after governance ===" -yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE +yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $CHAIN -m $MODULE diff --git a/typescript/infra/scripts/check-deploy.ts b/typescript/infra/scripts/check-deploy.ts index c09889cf25..75df19b2d8 100644 --- a/typescript/infra/scripts/check-deploy.ts +++ b/typescript/infra/scripts/check-deploy.ts @@ -69,11 +69,11 @@ async function check() { config.core, ismFactory, ); - governor = new HyperlaneCoreGovernor(checker, config.owners); + governor = new HyperlaneCoreGovernor(checker); } else if (module === Modules.INTERCHAIN_GAS_PAYMASTER) { const igp = HyperlaneIgp.fromEnvironment(env, multiProvider); const checker = new HyperlaneIgpChecker(multiProvider, igp, config.igp); - governor = new HyperlaneIgpGovernor(checker, config.owners); + governor = new HyperlaneIgpGovernor(checker); } else if (module === Modules.INTERCHAIN_ACCOUNTS) { const ica = InterchainAccount.fromEnvironment(env, multiProvider); const checker = new InterchainAccountChecker( @@ -81,7 +81,7 @@ async function check() { ica, routerConfig, ); - governor = new ProxiedRouterGovernor(checker, config.owners); + governor = new ProxiedRouterGovernor(checker); } else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) { const iqs = InterchainQuery.fromEnvironment(env, multiProvider); const checker = new InterchainQueryChecker( @@ -89,7 +89,7 @@ async function check() { iqs, routerConfig, ); - governor = new ProxiedRouterGovernor(checker, config.owners); + governor = new ProxiedRouterGovernor(checker); } else if (module === Modules.HELLO_WORLD) { const app = await getHelloWorldApp( config, @@ -104,7 +104,7 @@ async function check() { routerConfig, ismFactory, ); - governor = new ProxiedRouterGovernor(checker, config.owners); + governor = new ProxiedRouterGovernor(checker); } else { console.log(`Skipping ${module}, checker or governor unimplemented`); return; diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index d9d58194fc..bc2e9c0039 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -4,7 +4,6 @@ import { prompt } from 'prompts'; import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld'; import { ChainMap, - Chains, HypERC20Deployer, HyperlaneCore, HyperlaneCoreDeployer, @@ -50,19 +49,13 @@ async function main() { let multiProvider = await envConfig.getMultiProvider(); - // TODO: make this more generic - const deployerAddress = - environment === 'testnet4' - ? '0xfaD1C94469700833717Fa8a3017278BC1cA8031C' - : '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; - if (fork) { multiProvider = multiProvider.extendChainMetadata({ [fork]: { blocks: { confirmations: 0 } }, }); await useLocalProvider(multiProvider, fork); - const signer = await impersonateAccount(deployerAddress); + const signer = await impersonateAccount(envConfig.owners[fork].owner); multiProvider.setSharedSigner(signer); } @@ -79,7 +72,6 @@ async function main() { ); deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory); } else if (module === Modules.WARP) { - const owner = deployerAddress; const neutronRouter = '6b04c49fcfd98bc4ea9c05cd5790462a39537c00028333474aebe6ddf20b73a3'; const ismFactory = HyperlaneIsmFactory.fromAddressesMap( @@ -97,8 +89,7 @@ async function main() { deployEnvToSdkEnv[environment], multiProvider, ); - const routerConfig = core.getRouterConfig(owner); - const targetChains = [Chains.arbitrum]; + const routerConfig = core.getRouterConfig(envConfig.owners); config = { arbitrum: { ...routerConfig['arbitrum'], @@ -150,7 +141,7 @@ async function main() { deployer = new TestQuerySenderDeployer(multiProvider); } else if (module === Modules.HELLO_WORLD) { const core = HyperlaneCore.fromEnvironment(env, multiProvider); - config = core.getRouterConfig(deployerAddress); + config = core.getRouterConfig(envConfig.owners); deployer = new HelloWorldDeployer(multiProvider); } else { console.log(`Skipping ${module}, deployer unimplemented`); @@ -176,7 +167,7 @@ async function main() { addresses, verification, read: environment !== 'test', - write: true, + write: !fork, }; // Don't write agent config in fork tests const agentConfig = @@ -188,9 +179,8 @@ async function main() { } : undefined; - // prompt for confirmation - if ((environment === 'mainnet3' || environment === 'testnet4') && !fork) { - console.log(JSON.stringify(config, null, 2)); + // prompt for confirmation in production environments + if (environment !== 'test' && !fork) { const { value: confirmed } = await prompt({ type: 'confirm', name: 'value', diff --git a/typescript/infra/scripts/helloworld/kathy.ts b/typescript/infra/scripts/helloworld/kathy.ts index 2599b895f6..2715e4a4b3 100644 --- a/typescript/infra/scripts/helloworld/kathy.ts +++ b/typescript/infra/scripts/helloworld/kathy.ts @@ -246,7 +246,7 @@ async function main(): Promise { } chains.map((chain) => - updateWalletBalanceMetricFor(app, chain, coreConfig.owners[chain]), + updateWalletBalanceMetricFor(app, chain, coreConfig.owners[chain].owner), ); // Incremented each time an entire cycle has occurred @@ -365,14 +365,16 @@ async function main(): Promise { messagesSendCount.labels({ ...labels, status: 'failure' }).inc(); errorOccurred = true; } - updateWalletBalanceMetricFor(app, origin, coreConfig.owners[origin]).catch( - (e) => { - warn('Failed to update wallet balance for chain', { - chain: origin, - err: format(e), - }); - }, - ); + updateWalletBalanceMetricFor( + app, + origin, + coreConfig.owners[origin].owner, + ).catch((e) => { + warn('Failed to update wallet balance for chain', { + chain: origin, + err: format(e), + }); + }); // Break if we should stop sending messages if (await nextMessage()) { diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index 96f75e5666..d4f301bbd8 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -7,9 +7,9 @@ import { HyperlaneEnvironment, IgpConfig, MultiProvider, + OwnableConfig, RpcConsensusType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; import { environments } from '../../config/environments'; @@ -38,7 +38,7 @@ export type EnvironmentConfig = { agents: Partial>; core: ChainMap; igp: ChainMap; - owners: ChainMap
; + owners: ChainMap; infra: InfrastructureConfig; getMultiProvider: ( context?: Contexts, diff --git a/typescript/infra/src/deployment/deploy.ts b/typescript/infra/src/deployment/deploy.ts index df7de5390d..f003b9e52b 100644 --- a/typescript/infra/src/deployment/deploy.ts +++ b/typescript/infra/src/deployment/deploy.ts @@ -90,7 +90,6 @@ export async function postDeploy( const deployedAddresses = serializeContractsMap(deployer.deployedContracts); const cachedAddresses = deployer.cachedAddresses; const addresses = objMerge(deployedAddresses, cachedAddresses); - console.log(addresses); // cache addresses of deployed contracts writeMergedJSONAtPath(cache.addresses, addresses); diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index d7680d821a..8ad934facb 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -5,6 +5,7 @@ import { ChainName, HyperlaneApp, HyperlaneAppChecker, + OwnableConfig, OwnerViolation, } from '@hyperlane-xyz/sdk'; import { Address, CallData, objMap } from '@hyperlane-xyz/utils'; @@ -31,19 +32,14 @@ export type AnnotatedCallData = CallData & { export abstract class HyperlaneAppGovernor< App extends HyperlaneApp, - Config, + Config extends OwnableConfig, > { readonly checker: HyperlaneAppChecker; - private owners: ChainMap
; private calls: ChainMap; private canPropose: ChainMap>; - constructor( - checker: HyperlaneAppChecker, - owners: ChainMap
, - ) { + constructor(checker: HyperlaneAppChecker) { this.checker = checker; - this.owners = owners; this.calls = objMap(this.checker.app.contractsMap, () => []); this.canPropose = objMap(this.checker.app.contractsMap, () => new Map()); } @@ -120,7 +116,11 @@ export abstract class HyperlaneAppGovernor< ); await sendCallsForType( SubmissionType.SAFE, - new SafeMultiSend(this.checker.multiProvider, chain, this.owners[chain]), + new SafeMultiSend( + this.checker.multiProvider, + chain, + this.checker.configMap[chain].owner, + ), ); await sendCallsForType(SubmissionType.MANUAL, new ManualMultiSend(chain)); } @@ -162,7 +162,7 @@ export abstract class HyperlaneAppGovernor< } // 2. Check if the call will succeed via Gnosis Safe. - const safeAddress = this.owners[chain]; + const safeAddress = this.checker.configMap[chain].owner; if (!safeAddress) throw new Error(`Owner address not found for ${chain}`); // 2a. Confirm that the signer is a Safe owner or delegate. // This should implicitly check whether or not the owner is a gnosis diff --git a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts index d41d3effd8..56ec26544d 100644 --- a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts @@ -1,5 +1,4 @@ import { - ChainMap, CoreConfig, CoreViolationType, HyperlaneCore, @@ -9,7 +8,6 @@ import { OwnerViolation, ViolationType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from '../govern/HyperlaneAppGovernor'; @@ -17,11 +15,8 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor< HyperlaneCore, CoreConfig > { - constructor( - readonly checker: HyperlaneCoreChecker, - owners: ChainMap
, - ) { - super(checker, owners); + constructor(readonly checker: HyperlaneCoreChecker) { + super(checker); } protected async handleMailboxViolation(violation: MailboxViolation) { diff --git a/typescript/infra/src/govern/HyperlaneIgpGovernor.ts b/typescript/infra/src/govern/HyperlaneIgpGovernor.ts index 46a9ec0ff8..4f1f94ce44 100644 --- a/typescript/infra/src/govern/HyperlaneIgpGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneIgpGovernor.ts @@ -2,18 +2,16 @@ import { BigNumber, ethers } from 'ethers'; import { InterchainGasPaymaster } from '@hyperlane-xyz/core'; import { - ChainMap, ChainName, HyperlaneIgp, - HyperlaneIgpChecker, IgpBeneficiaryViolation, IgpConfig, IgpGasOraclesViolation, IgpOverheadViolation, IgpViolation, IgpViolationType, + OwnerViolation, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from '../govern/HyperlaneAppGovernor'; @@ -21,10 +19,6 @@ export class HyperlaneIgpGovernor extends HyperlaneAppGovernor< HyperlaneIgp, IgpConfig > { - constructor(checker: HyperlaneIgpChecker, owners: ChainMap
) { - super(checker, owners); - } - protected async mapViolationsToCalls() { for (const violation of this.checker.violations) { switch (violation.type) { @@ -32,6 +26,10 @@ export class HyperlaneIgpGovernor extends HyperlaneAppGovernor< this.handleIgpViolation(violation as IgpViolation); break; } + case 'Owner': { + super.handleOwnerViolation(violation as OwnerViolation); + break; + } default: throw new Error(`Unsupported violation type ${violation.type}`); } diff --git a/typescript/infra/src/govern/ProxiedRouterGovernor.ts b/typescript/infra/src/govern/ProxiedRouterGovernor.ts index b2e5eccb99..9d421c62fe 100644 --- a/typescript/infra/src/govern/ProxiedRouterGovernor.ts +++ b/typescript/infra/src/govern/ProxiedRouterGovernor.ts @@ -1,8 +1,6 @@ import { - ChainMap, ConnectionClientViolation, ConnectionClientViolationType, - HyperlaneAppChecker, OwnerViolation, RouterApp, RouterConfig, @@ -10,7 +8,6 @@ import { RouterViolationType, ViolationType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from './HyperlaneAppGovernor'; @@ -18,13 +15,6 @@ export class ProxiedRouterGovernor< App extends RouterApp, Config extends RouterConfig, > extends HyperlaneAppGovernor { - constructor( - checker: HyperlaneAppChecker, - owners: ChainMap
, - ) { - super(checker, owners); - } - protected async mapViolationsToCalls() { for (const violation of this.checker.violations) { switch (violation.type) { diff --git a/typescript/infra/src/utils/fork.ts b/typescript/infra/src/utils/fork.ts index 46de55d010..3d50f8eac3 100644 --- a/typescript/infra/src/utils/fork.ts +++ b/typescript/infra/src/utils/fork.ts @@ -1,5 +1,4 @@ import { JsonRpcProvider, JsonRpcSigner } from '@ethersproject/providers'; -import { ethers } from 'ethers'; import { ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; @@ -19,10 +18,6 @@ export const impersonateAccount = async ( ): Promise => { const provider = new JsonRpcProvider('http://127.0.0.1:8545'); await provider.send('hardhat_impersonateAccount', [account]); - await provider.send('hardhat_setBalance', [ - account, - ethers.utils.parseEther('42').toHexString(), - ]); return provider.getSigner(account); }; diff --git a/typescript/sdk/src/consts/chainMetadata.ts b/typescript/sdk/src/consts/chainMetadata.ts index 5df2771b33..4b699e75a6 100644 --- a/typescript/sdk/src/consts/chainMetadata.ts +++ b/typescript/sdk/src/consts/chainMetadata.ts @@ -320,7 +320,7 @@ export const ethereum: ChainMetadata = { nativeToken: etherToken, protocol: ProtocolType.Ethereum, rpcUrls: [ - { http: 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161' }, + { http: 'https://ethereum.publicnode.com' }, { http: 'https://cloudflare-eth.com' }, ], }; @@ -692,12 +692,7 @@ export const polygon: ChainMetadata = { protocol: ProtocolType.Ethereum, rpcUrls: [ { - http: 'https://rpc-mainnet.matic.quiknode.pro', - pagination: { - // Needs to be low to avoid RPC timeouts - maxBlockRange: 10000, - minBlockNumber: 19657100, - }, + http: 'https://polygon-bor.publicnode.com', }, { http: 'https://polygon-rpc.com' }, ], @@ -726,7 +721,11 @@ export const polygonzkevm: ChainMetadata = { name: Chains.polygonzkevm, nativeToken: etherToken, protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://rpc.ankr.com/polygon_zkevm' }], + rpcUrls: [ + { http: 'https://zkevm-rpc.com' }, + { http: 'https://polygonzkevm-mainnet.g.alchemy.com/v2/demo' }, + { http: 'https://rpc.ankr.com/polygon_zkevm' }, + ], }; export const polygonzkevmtestnet: ChainMetadata = { diff --git a/typescript/sdk/src/core/HyperlaneCore.ts b/typescript/sdk/src/core/HyperlaneCore.ts index 78d922428d..2d7f928dcc 100644 --- a/typescript/sdk/src/core/HyperlaneCore.ts +++ b/typescript/sdk/src/core/HyperlaneCore.ts @@ -18,6 +18,7 @@ import { } from '../consts/environments'; import { appFromAddressesMapHelper } from '../contracts/contracts'; import { HyperlaneAddressesMap } from '../contracts/types'; +import { OwnableConfig } from '../deploy/types'; import { MultiProvider } from '../providers/MultiProvider'; import { RouterConfig } from '../router/types'; import { ChainMap, ChainName } from '../types'; @@ -50,14 +51,12 @@ export class HyperlaneCore extends HyperlaneApp { } getRouterConfig = ( - owners: Address | ChainMap
, + owners: Address | ChainMap, ): ChainMap => - objMap(this.contractsMap, (chain, contracts) => { - return { - mailbox: contracts.mailbox.address, - owner: typeof owners === 'string' ? owners : owners[chain], - }; - }); + objMap(this.contractsMap, (chain, contracts) => ({ + mailbox: contracts.mailbox.address, + owner: typeof owners === 'string' ? owners : owners[chain].owner, + })); quoteGasPayment = ( origin: ChainName, diff --git a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts index d534149a98..27ce391c89 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts @@ -33,6 +33,7 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< const config = this.configMap[chain]; const ownableOverrides: Record = { + ...config.ownerOverrides, storageGasOracle: config.oracleKey, }; await super.checkOwnership(chain, config.owner, ownableOverrides);