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
Asa Oines 2 years ago committed by GitHub
parent 08f3b9b32b
commit 17b400806e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/node.yml
  2. 2
      typescript/helloworld
  3. 110
      typescript/infra/config/environments/testnet3/middleware/queries/verification.json
  4. 18
      typescript/infra/scripts/check-deploy.ts
  5. 25
      typescript/infra/src/middleware/account/govern.ts
  6. 25
      typescript/infra/src/middleware/query/govern.ts
  7. 3
      typescript/infra/src/utils/utils.ts
  8. 15
      typescript/sdk/src/HyperlaneApp.ts
  9. 8
      typescript/sdk/src/consts/environments/mainnet.json
  10. 52
      typescript/sdk/src/consts/environments/test.json
  11. 18
      typescript/sdk/src/core/HyperlaneCoreChecker.ts
  12. 71
      typescript/sdk/src/deploy/HyperlaneAppChecker.ts
  13. 1
      typescript/sdk/src/deploy/types.ts
  14. 18
      typescript/sdk/src/gas/HyperlaneIgpChecker.ts
  15. 27
      typescript/sdk/src/index.ts
  16. 20
      typescript/sdk/src/middleware/MiddlewareRouterChecker.ts
  17. 96
      typescript/sdk/src/middleware/MiddlewareRouterDeployer.ts
  18. 59
      typescript/sdk/src/middleware/account/InterchainAccount.ts
  19. 11
      typescript/sdk/src/middleware/account/InterchainAccountChecker.ts
  20. 113
      typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts
  21. 43
      typescript/sdk/src/middleware/account/accounts.hardhat-test.ts
  22. 23
      typescript/sdk/src/middleware/account/contracts.ts
  23. 207
      typescript/sdk/src/middleware/deploy.ts
  24. 45
      typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts
  25. 16
      typescript/sdk/src/middleware/liquidity-layer/contracts.ts
  26. 3
      typescript/sdk/src/middleware/liquidity-layer/liquidity-layer.hardhat-test.ts
  27. 58
      typescript/sdk/src/middleware/query/InterchainQuery.ts
  28. 11
      typescript/sdk/src/middleware/query/InterchainQueryChecker.ts
  29. 31
      typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts
  30. 23
      typescript/sdk/src/middleware/query/contracts.ts
  31. 35
      typescript/sdk/src/middleware/query/queries.hardhat-test.ts
  32. 15
      typescript/sdk/src/proxy.ts
  33. 13
      typescript/sdk/src/router/GasRouterDeployer.ts
  34. 24
      typescript/sdk/src/router/HyperlaneRouterChecker.ts
  35. 38
      typescript/sdk/src/router/HyperlaneRouterDeployer.ts
  36. 33
      typescript/sdk/src/router/RouterApps.ts
  37. 39
      typescript/sdk/src/router/app.ts
  38. 29
      typescript/sdk/src/router/types.ts
  39. 53
      typescript/sdk/src/test/envSubsetDeployer/app.ts
  40. 12
      typescript/sdk/src/test/envSubsetDeployer/check-single-chain.ts
  41. 13
      typescript/sdk/src/test/envSubsetDeployer/deploy.hardhat-test.ts
  42. 2
      typescript/token

@ -115,7 +115,7 @@ jobs:
strategy:
matrix:
environment: [testnet3, mainnet2]
module: [core, igp]
module: [core, igp, ica, iqs]
steps:
- uses: actions/checkout@v3

@ -1 +1 @@
Subproject commit 3b443c79bcb7671ef3d49ba7d6765c9adb139789
Subproject commit 591edb07f44e67d95220e6c89d669f11cde68501

@ -2,65 +2,127 @@
"alfajores": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"fuji": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"mumbai": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"bsctestnet": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"goerli": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"moonbasealpha": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"optimismgoerli": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"arbitrumgoerli": [
{
"name": "InterchainQueryRouter",
"address": "0xF782C6C4A02f2c71BB8a1Db0166FAB40ea956818",
"isProxy": false,
"constructorArguments": "0x"
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
],
"sepolia": [
{
"name": "InterchainQueryRouter",
"address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59",
"constructorArguments": "0x",
"isProxy": false
},
{
"name": "TransparentUpgradeableProxy",
"address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559",
"constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"isProxy": true
}
]
}

@ -3,23 +3,31 @@ import {
HyperlaneCoreChecker,
HyperlaneIgp,
HyperlaneIgpChecker,
InterchainAccount,
InterchainAccountChecker,
InterchainQuery,
InterchainQueryChecker,
} from '@hyperlane-xyz/sdk';
import { deployEnvToSdkEnv } from '../src/config/environment';
import { HyperlaneCoreGovernor } from '../src/core/govern';
import { HyperlaneIgpGovernor } from '../src/gas/govern';
import { HyperlaneAppGovernor } from '../src/govern/HyperlaneAppGovernor';
import { InterchainAccountGovernor } from '../src/middleware/account/govern';
import { InterchainQueryGovernor } from '../src/middleware/query/govern';
import { impersonateAccount, useLocalProvider } from '../src/utils/fork';
import {
Modules,
getArgsWithModuleAndFork,
getEnvironmentConfig,
getRouterConfig,
} from './utils';
async function check() {
const { fork, govern, module, environment } = await getArgsWithModuleAndFork()
.boolean('govern')
.default('govern', false)
.alias('g', 'govern').argv;
const config = await getEnvironmentConfig();
const multiProvider = await config.getMultiProvider();
@ -44,6 +52,16 @@ async function check() {
const igp = HyperlaneIgp.fromEnvironment(env, multiProvider);
const checker = new HyperlaneIgpChecker(multiProvider, igp, config.igp);
governor = new HyperlaneIgpGovernor(checker, config.owners);
} else if (module === Modules.INTERCHAIN_ACCOUNTS) {
const config = await getRouterConfig(environment, multiProvider);
const ica = InterchainAccount.fromEnvironment(env, multiProvider);
const checker = new InterchainAccountChecker(multiProvider, ica, config);
governor = new InterchainAccountGovernor(checker, config.owners);
} else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) {
const config = await getRouterConfig(environment, multiProvider);
const iqs = InterchainQuery.fromEnvironment(env, multiProvider);
const checker = new InterchainQueryChecker(multiProvider, iqs, config);
governor = new InterchainQueryGovernor(checker, config.owners);
} else {
console.log(`Skipping ${module}, checker or governor unimplemented`);
return;

@ -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');
}
}

@ -179,9 +179,6 @@ export function writeJsonAtPath(filepath: string, obj: any) {
}
export function writeJSON(directory: string, filename: string, obj: any) {
if (!fs.existsSync(directory)) {
fs.mkdirSync(directory, { recursive: true });
}
writeJsonAtPath(path.join(directory, filename), obj);
}

@ -1,5 +1,6 @@
import {
HyperlaneAddresses,
HyperlaneContract,
HyperlaneContracts,
HyperlaneFactories,
buildContracts,
@ -7,9 +8,10 @@ import {
serializeContracts,
} from './contracts';
import { MultiProvider } from './providers/MultiProvider';
import { isProxiedContract } from './proxy';
import { ChainMap, ChainName } from './types';
import { MultiGeneric } from './utils/MultiGeneric';
import { objMap, pick } from './utils/objects';
import { objFilter, objMap, pick } from './utils/objects';
export class HyperlaneApp<
Contracts extends HyperlaneContracts,
@ -45,6 +47,17 @@ export class HyperlaneApp<
return this.get(chain);
}
getFlattenedFilteredContracts<K extends HyperlaneContract>(
chain: ChainName,
filter: (k: ChainName, v: HyperlaneContract) => v is K,
): { [key: string]: K } {
const filtered = objFilter(this.getContracts(chain), filter);
const flattened = objMap(filtered, (name, contract) =>
isProxiedContract(contract) ? contract.contract : contract,
);
return flattened;
}
getAddresses(chain: ChainName): HyperlaneAddresses {
return serializeContracts(this.get(chain));
}

@ -17,7 +17,6 @@
"multisigIsm": "0x9bDE63104EE030d9De419EEd6bA7D14b86D6fE3f",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"ethereum": {
@ -38,7 +37,6 @@
"multisigIsm": "0xec48E52D960E54a179f70907bF28b105813877ee",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"avalanche": {
@ -59,7 +57,6 @@
"multisigIsm": "0xeE80ab5B563cB3825133f29502bA34eD3707cb8C",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"polygon": {
@ -80,7 +77,6 @@
"multisigIsm": "0x61A80297e77FC5395bd6Ff60EEacf7CD4f18d4a4",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"bsc": {
@ -101,7 +97,6 @@
"multisigIsm": "0x3a579C0bd04FC4C98A8D70EEABD9094e7be4B26D",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"arbitrum": {
@ -122,7 +117,6 @@
"multisigIsm": "0x32B92bd3e5045B67FDD8dbb7A58D25980836d04C",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"optimism": {
@ -143,7 +137,6 @@
"multisigIsm": "0xAab1D11E2063Bae5EB01fa946cA8d2FDe3db05D5",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"moonbeam": {
@ -164,7 +157,6 @@
"multisigIsm": "0xf3b1F415740A26568C45b1c771A737E31C198F09",
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35",
"interchainAccountRouter": "0xE0Be420779cAd6E2bEA1E4F7C02F996D9ED1fCB5",
"interchainQueryRouter": "0x234b19282985882d6d6fd54dEBa272271f4eb784"
},
"gnosis": {

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

@ -40,13 +40,7 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
async checkDomainOwnership(chain: ChainName): Promise<void> {
const config = this.configMap[chain];
if (config.owner) {
const contracts = this.app.getContracts(chain);
const ownables = [
contracts.proxyAdmin,
contracts.mailbox.contract,
contracts.multisigIsm,
];
return this.checkOwnership(chain, config.owner, ownables);
return this.checkOwnership(chain, config.owner);
}
}
@ -118,16 +112,6 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
);
}
async checkProxiedContracts(chain: ChainName): Promise<void> {
const contracts = this.app.getContracts(chain);
await this.checkProxiedContract(
chain,
'Mailbox',
contracts.mailbox.addresses,
contracts.proxyAdmin.address,
);
}
async checkValidatorAnnounce(chain: ChainName): Promise<void> {
const expectedValidators = new Set<string>();
const remotes = Object.keys(this.configMap).filter((c) => c !== chain);

@ -6,11 +6,11 @@ import { utils } from '@hyperlane-xyz/utils';
import { HyperlaneApp } from '../HyperlaneApp';
import { MultiProvider } from '../providers/MultiProvider';
import { TransparentProxyAddresses } from '../proxy';
import { ProxiedContract, isProxiedContract } from '../proxy';
import { ChainMap, ChainName } from '../types';
import { objMap } from '../utils/objects';
import { objMap, promiseObjAll } from '../utils/objects';
import { proxyAdmin, proxyImplementation, proxyViolation } from './proxy';
import { proxyAdmin } from './proxy';
import {
BytecodeMismatchViolation,
CheckerViolation,
@ -62,34 +62,36 @@ export abstract class HyperlaneAppChecker<
this.violations.push(violation);
}
async checkProxiedContract(
chain: ChainName,
name: string,
proxiedAddress: TransparentProxyAddresses,
proxyAdminAddress?: types.Address,
): Promise<void> {
const provider = this.multiProvider.getProvider(chain);
const implementation = await proxyImplementation(
provider,
proxiedAddress.proxy,
);
if (implementation !== proxiedAddress.implementation) {
this.addViolation(
proxyViolation(chain, name, proxiedAddress, implementation),
async checkProxiedContracts(chain: ChainName): Promise<void> {
const expectedAdmin = this.app.getContracts(chain).proxyAdmin.address;
if (!expectedAdmin) {
throw new Error(
`Checking proxied contracts for ${chain} with no admin provided`,
);
}
if (proxyAdminAddress) {
const admin = await proxyAdmin(provider, proxiedAddress.proxy);
if (admin !== proxyAdminAddress) {
const provider = this.multiProvider.getProvider(chain);
const isProxied = (
_: string,
contract: any,
): contract is ProxiedContract<any, any> => {
return isProxiedContract(contract);
};
const proxied = this.app.getFlattenedFilteredContracts(chain, isProxied);
await promiseObjAll(
objMap(proxied, async (name, contract) => {
// Check the ProxiedContract's admin matches expectation
const actualAdmin = await proxyAdmin(provider, contract.address);
if (!utils.eqAddress(actualAdmin, expectedAdmin)) {
this.addViolation({
type: ViolationType.ProxyAdmin,
chain,
name,
expected: proxyAdminAddress,
actual: admin,
expected: expectedAdmin,
actual: actualAdmin,
} as ProxyAdminViolation);
}
}
}),
);
}
private removeBytecodeMetadata(bytecode: string): string {
@ -122,17 +124,24 @@ export abstract class HyperlaneAppChecker<
}
}
async checkOwnership(
chain: ChainName,
owner: types.Address,
ownables: Ownable[],
): Promise<void> {
await Promise.all(
ownables.map(async (contract) => {
// TODO: Require owner in config if ownables is non-empty
async checkOwnership(chain: ChainName, owner: types.Address): Promise<void> {
const isOwnable = (_: string, contract: any): contract is Ownable => {
return (
contract !== null &&
typeof contract === 'object' &&
contract.owner &&
contract.transferOwnership
);
};
const ownables = this.app.getFlattenedFilteredContracts(chain, isOwnable);
await promiseObjAll(
objMap(ownables, async (name, contract) => {
const actual = await contract.owner();
if (actual.toLowerCase() != owner.toLowerCase()) {
if (!utils.eqAddress(actual, owner)) {
const violation: OwnerViolation = {
chain,
name,
type: ViolationType.Owner,
actual,
expected: owner,

@ -22,6 +22,7 @@ export enum ViolationType {
export interface OwnerViolation extends CheckerViolation {
type: ViolationType.Owner;
contract: Ownable;
name: string;
}
export interface ProxyAdminViolation extends CheckerViolation {

@ -31,13 +31,7 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker<
async checkDomainOwnership(chain: ChainName): Promise<void> {
const config = this.configMap[chain];
if (config.owner) {
const contracts = this.app.getContracts(chain);
const ownables = [
contracts.proxyAdmin,
contracts.interchainGasPaymaster.contract,
contracts.defaultIsmInterchainGasPaymaster,
];
return this.checkOwnership(chain, config.owner, ownables);
return this.checkOwnership(chain, config.owner);
}
}
@ -78,16 +72,6 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker<
);
}
async checkProxiedContracts(chain: ChainName): Promise<void> {
const contracts = this.app.getContracts(chain);
await this.checkProxiedContract(
chain,
'InterchainGasPaymaster',
contracts.interchainGasPaymaster.addresses,
contracts.proxyAdmin.address,
);
}
async checkOverheadInterchainGasPaymaster(local: ChainName): Promise<void> {
const coreContracts = this.app.getContracts(local);
const defaultIsmIgp = coreContracts.defaultIsmInterchainGasPaymaster;

@ -72,9 +72,9 @@ export {
} from './core/types';
export { HyperlaneAppChecker } from './deploy/HyperlaneAppChecker';
export {
HyperlaneDeployer,
DeployOptions,
DeployerOptions,
DeployOptions,
HyperlaneDeployer,
} from './deploy/HyperlaneDeployer';
export { ProxyViolation } from './deploy/proxy';
export {
@ -110,12 +110,13 @@ export {
OverheadIgpConfig,
} from './gas/types';
export { HyperlaneApp } from './HyperlaneApp';
export { interchainAccountFactories } from './middleware/account/contracts';
export { InterchainAccount } from './middleware/account/InterchainAccount';
export { InterchainAccountChecker } from './middleware/account/InterchainAccountChecker';
export {
InterchainAccountConfig,
InterchainAccountDeployer,
interchainAccountFactories,
InterchainQueryDeployer,
interchainQueryFactories,
} from './middleware/deploy';
} from './middleware/account/InterchainAccountDeployer';
export {
LiquidityLayerContracts,
liquidityLayerFactories,
@ -128,6 +129,13 @@ export {
LiquidityLayerDeployer,
PortalAdapterConfig,
} from './middleware/liquidity-layer/LiquidityLayerRouterDeployer';
export { interchainQueryFactories } from './middleware/query/contracts';
export { InterchainQuery } from './middleware/query/InterchainQuery';
export { InterchainQueryChecker } from './middleware/query/InterchainQueryChecker';
export {
InterchainQueryConfig,
InterchainQueryDeployer,
} from './middleware/query/InterchainQueryDeployer';
export { MultiProvider, providerBuilder } from './providers/MultiProvider';
export { RetryJsonRpcProvider, RetryProvider } from './providers/RetryProvider';
export {
@ -140,12 +148,7 @@ export { GasRouterDeployer } from './router/GasRouterDeployer';
export { HyperlaneRouterChecker } from './router/HyperlaneRouterChecker';
export { HyperlaneRouterDeployer } from './router/HyperlaneRouterDeployer';
export { GasRouterApp, Router, RouterApp } from './router/RouterApps';
export {
GasRouterConfig,
RouterConfig,
RouterContracts,
RouterFactories,
} from './router/types';
export { GasRouterConfig, RouterConfig } from './router/types';
export {
createRouterConfigMap,
deployTestIgpsAndGetRouterConfig,

@ -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
};
}
}

@ -7,19 +7,18 @@ import {
TestRecipient__factory,
} from '@hyperlane-xyz/core';
import { Chains } from '../consts/chains';
import { TestCoreApp } from '../core/TestCoreApp';
import { TestCoreDeployer } from '../core/TestCoreDeployer';
import { MultiProvider } from '../providers/MultiProvider';
import { RouterConfig } from '../router/types';
import { deployTestIgpsAndGetRouterConfig } from '../test/testUtils';
import { ChainMap } from '../types';
import { objMap, promiseObjAll } from '../utils/objects';
import { Chains } from '../../consts/chains';
import { TestCoreApp } from '../../core/TestCoreApp';
import { TestCoreDeployer } from '../../core/TestCoreDeployer';
import { MultiProvider } from '../../providers/MultiProvider';
import { RouterConfig } from '../../router/types';
import { deployTestIgpsAndGetRouterConfig } from '../../test/testUtils';
import { ChainMap } from '../../types';
import {
InterchainAccountContracts,
InterchainAccountDeployer,
} from './deploy';
import { InterchainAccount } from './InterchainAccount';
import { InterchainAccountChecker } from './InterchainAccountChecker';
import { InterchainAccountDeployer } from './InterchainAccountDeployer';
import { InterchainAccountContracts } from './contracts';
describe('InterchainAccounts', async () => {
const localChain = Chains.test1;
@ -54,21 +53,15 @@ describe('InterchainAccounts', async () => {
beforeEach(async () => {
const deployer = new InterchainAccountDeployer(multiProvider, config);
contracts = await deployer.deploy();
local = contracts[localChain].router;
remote = contracts[remoteChain].router;
local = contracts[localChain].interchainAccountRouter.contract;
remote = contracts[remoteChain].interchainAccountRouter.contract;
});
it('deploys and sets configured ISMs', async () => {
const deployedIsms = await promiseObjAll(
objMap(contracts, (_, c) => c.router.interchainSecurityModule()),
);
expect(deployedIsms).to.eql(
objMap(
config,
(_, c) => c.interchainSecurityModule ?? ethers.constants.AddressZero,
),
);
it('checks', async () => {
const app = new InterchainAccount(contracts, multiProvider);
const checker = new InterchainAccountChecker(multiProvider, app, config);
await checker.check();
expect(checker.violations.length).to.eql(0);
});
it('forwards calls from interchain account', async () => {

@ -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);
}
}

@ -2,6 +2,7 @@ import {
CircleBridgeAdapter,
CircleBridgeAdapter__factory,
LiquidityLayerRouter,
LiquidityLayerRouter__factory,
PortalAdapter,
PortalAdapter__factory,
} from '@hyperlane-xyz/core';
@ -10,8 +11,8 @@ import { utils } from '@hyperlane-xyz/utils';
import { MultiProvider } from '../../providers/MultiProvider';
import { RouterConfig } from '../../router/types';
import { ChainMap, ChainName } from '../../types';
import { objFilter, objMap } from '../../utils/objects';
import { MiddlewareRouterDeployer } from '../deploy';
import { objMap } from '../../utils/objects';
import { MiddlewareRouterDeployer } from '../MiddlewareRouterDeployer';
import {
LiquidityLayerContracts,
@ -54,8 +55,11 @@ export type LiquidityLayerConfig = RouterConfig & BridgeAdapterConfig;
export class LiquidityLayerDeployer extends MiddlewareRouterDeployer<
LiquidityLayerConfig,
LiquidityLayerContracts,
LiquidityLayerFactories
LiquidityLayerFactories,
LiquidityLayerRouter__factory
> {
readonly routerContractName = 'liquidityLayerRouter';
constructor(
multiProvider: MultiProvider,
configMap: ChainMap<LiquidityLayerConfig>,
@ -71,23 +75,23 @@ export class LiquidityLayerDeployer extends MiddlewareRouterDeployer<
await super.enrollRemoteRouters(contractsMap);
this.logger(`Enroll CircleBridgeAdapters with each other`);
// Hack to allow use of super.enrollRemoteRouters
await super.enrollRemoteRouters(
objFilter(
objMap(contractsMap, (_chain, contracts) => ({
router: contracts.circleBridgeAdapter,
})),
(chain, _): _ is { router: CircleBridgeAdapter } => !!_.router,
),
objMap(contractsMap, (_, contracts) => ({
liquidityLayerRouter: {
contract: contracts.circleBridgeAdapter,
},
})) as unknown as ChainMap<LiquidityLayerContracts>,
);
this.logger(`Enroll PortalAdapters with each other`);
// Hack to allow use of super.enrollRemoteRouters
await super.enrollRemoteRouters(
objFilter(
objMap(contractsMap, (_chain, contracts) => ({
router: contracts.portalAdapter,
})),
(chain, _): _ is { router: PortalAdapter } => !!_.router,
),
objMap(contractsMap, (_, contracts) => ({
liquidityLayerRouter: {
contract: contracts.portalAdapter,
},
})) as unknown as ChainMap<LiquidityLayerContracts>,
);
}
@ -97,7 +101,10 @@ export class LiquidityLayerDeployer extends MiddlewareRouterDeployer<
chain: ChainName,
config: LiquidityLayerConfig,
): Promise<LiquidityLayerContracts> {
const routerContracts = await super.deployContracts(chain, config);
const routerContracts = (await super.deployContracts(
chain,
config,
)) as LiquidityLayerContracts;
const bridgeAdapters: Partial<LiquidityLayerContracts> = {};
@ -106,7 +113,7 @@ export class LiquidityLayerDeployer extends MiddlewareRouterDeployer<
chain,
config.circle,
config.owner,
routerContracts.router,
routerContracts.liquidityLayerRouter.contract,
);
}
if (config.portal) {
@ -114,14 +121,14 @@ export class LiquidityLayerDeployer extends MiddlewareRouterDeployer<
chain,
config.portal,
config.owner,
routerContracts.router,
routerContracts.liquidityLayerRouter.contract,
);
}
return {
...routerContracts,
...bridgeAdapters,
} as any;
} as LiquidityLayerContracts;
}
async deployPortalAdapter(

@ -8,28 +8,24 @@ import {
ProxyAdmin__factory,
} from '@hyperlane-xyz/core';
import {
ProxiedRouterContracts,
ProxiedRouterFactories,
} from '../../router/types';
import { ProxiedContract } from '../../proxy';
import { ProxiedContracts, ProxiedFactories } from '../../router/types';
export type LiquidityLayerFactories =
ProxiedRouterFactories<LiquidityLayerRouter> & {
export type LiquidityLayerFactories = ProxiedFactories & {
liquidityLayerRouter: LiquidityLayerRouter__factory;
circleBridgeAdapter: CircleBridgeAdapter__factory;
portalAdapter: PortalAdapter__factory;
};
export const liquidityLayerFactories: LiquidityLayerFactories = {
router: new LiquidityLayerRouter__factory(),
circleBridgeAdapter: new CircleBridgeAdapter__factory(),
portalAdapter: new PortalAdapter__factory(),
// TODO: where to put these?
proxyAdmin: new ProxyAdmin__factory(),
liquidityLayerRouter: new LiquidityLayerRouter__factory(),
};
export type LiquidityLayerContracts =
ProxiedRouterContracts<LiquidityLayerRouter> & {
export type LiquidityLayerContracts = ProxiedContracts & {
liquidityLayerRouter: ProxiedContract<LiquidityLayerRouter>;
circleBridgeAdapter?: CircleBridgeAdapter;
portalAdapter?: PortalAdapter;
};

@ -121,7 +121,8 @@ describe('LiquidityLayerRouter', async () => {
liquidityLayerApp = new LiquidityLayerApp(contracts, multiProvider, config);
local = liquidityLayerApp.getContracts(localChain).router;
local =
liquidityLayerApp.getContracts(localChain).liquidityLayerRouter.contract;
});
it('can transfer tokens via Circle', async () => {

@ -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;
};

@ -9,16 +9,19 @@ import {
} from '@hyperlane-xyz/core';
import { utils } from '@hyperlane-xyz/utils';
import { chainMetadata } from '../consts/chainMetadata';
import { Chains } from '../consts/chains';
import { TestCoreApp } from '../core/TestCoreApp';
import { TestCoreDeployer } from '../core/TestCoreDeployer';
import { MultiProvider } from '../providers/MultiProvider';
import { RouterConfig } from '../router/types';
import { deployTestIgpsAndGetRouterConfig } from '../test/testUtils';
import { ChainMap } from '../types';
import { chainMetadata } from '../../consts/chainMetadata';
import { Chains } from '../../consts/chains';
import { TestCoreApp } from '../../core/TestCoreApp';
import { TestCoreDeployer } from '../../core/TestCoreDeployer';
import { MultiProvider } from '../../providers/MultiProvider';
import { RouterConfig } from '../../router/types';
import { deployTestIgpsAndGetRouterConfig } from '../../test/testUtils';
import { ChainMap } from '../../types';
import { InterchainQueryDeployer } from './deploy';
import { InterchainQuery } from './InterchainQuery';
import { InterchainQueryChecker } from './InterchainQueryChecker';
import { InterchainQueryDeployer } from './InterchainQueryDeployer';
import { InterchainQueryContracts } from './contracts';
describe('InterchainQueryRouter', async () => {
const localChain = Chains.test1;
@ -26,6 +29,7 @@ describe('InterchainQueryRouter', async () => {
const localDomain = chainMetadata[localChain].chainId;
const remoteDomain = chainMetadata[remoteChain].chainId;
let contracts: ChainMap<InterchainQueryContracts>;
let signer: SignerWithAddress;
let local: InterchainQueryRouter;
let remote: InterchainQueryRouter;
@ -52,14 +56,21 @@ describe('InterchainQueryRouter', async () => {
beforeEach(async () => {
const InterchainQuery = new InterchainQueryDeployer(multiProvider, config);
const contracts = await InterchainQuery.deploy();
contracts = await InterchainQuery.deploy();
local = contracts[localChain].router;
remote = contracts[remoteChain].router;
local = contracts[localChain].interchainQueryRouter.contract;
remote = contracts[remoteChain].interchainQueryRouter.contract;
testQuery = await new TestQuery__factory(signer).deploy(local.address);
});
it('checks', async () => {
const app = new InterchainQuery(contracts, multiProvider);
const checker = new InterchainQueryChecker(multiProvider, app, config);
await checker.check();
expect(checker.violations.length).to.eql(0);
});
it('completes query round trip and invokes callback', async () => {
const secret = 123;
const sender = testQuery.address;

@ -59,3 +59,18 @@ export class ProxiedContract<
);
}
}
export function isProxiedContract(
contract: unknown,
): contract is ProxiedContract<any, any> {
// The presence of `implementation` is intentionally not checked
// to allow deploying new implementations by deleting the implementation
// from the artifacts
return (
contract !== null &&
typeof contract === 'object' &&
'addresses' in contract &&
'contract' in contract &&
isProxyAddresses((contract as any).addresses)
);
}

@ -1,23 +1,22 @@
import { debug } from 'debug';
import { GasRouter } from '@hyperlane-xyz/core';
import { HyperlaneFactories } from '../contracts';
import { DeployerOptions } from '../deploy/HyperlaneDeployer';
import { MultiProvider } from '../providers/MultiProvider';
import { ChainMap } from '../types';
import { HyperlaneRouterDeployer } from './HyperlaneRouterDeployer';
import { GasRouterConfig, RouterContracts, RouterFactories } from './types';
import { GasRouterContracts } from './RouterApps';
import { GasRouterConfig } from './types';
export abstract class GasRouterDeployer<
Config extends GasRouterConfig,
Contracts extends RouterContracts<GasRouter>,
Factories extends RouterFactories<GasRouter>,
> extends HyperlaneRouterDeployer<Config, Contracts, Factories> {
Contracts extends GasRouterContracts,
> extends HyperlaneRouterDeployer<Config, Contracts, HyperlaneFactories> {
constructor(
multiProvider: MultiProvider,
configMap: ChainMap<Config>,
factories: Factories,
factories: HyperlaneFactories,
options?: DeployerOptions,
) {
super(multiProvider, configMap, factories, {

@ -1,24 +1,22 @@
import { ethers } from 'ethers';
import { Ownable } from '@hyperlane-xyz/core';
import { utils } from '@hyperlane-xyz/utils';
import { HyperlaneApp } from '../HyperlaneApp';
import { HyperlaneContracts } from '../contracts';
import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker';
import { RouterContracts } from '../router/types';
import { ChainName } from '../types';
import { RouterApp } from './RouterApps';
import { RouterConfig } from './types';
export class HyperlaneRouterChecker<
App extends HyperlaneApp<Contracts>,
App extends RouterApp<Contracts>,
Config extends RouterConfig,
Contracts extends RouterContracts,
Contracts extends HyperlaneContracts,
> extends HyperlaneAppChecker<App, Config> {
checkOwnership(chain: ChainName): Promise<void> {
const owner = this.configMap[chain].owner;
const ownables = this.ownables(chain);
return super.checkOwnership(chain, owner, ownables);
return super.checkOwnership(chain, owner);
}
async checkChain(chain: ChainName): Promise<void> {
@ -28,7 +26,7 @@ export class HyperlaneRouterChecker<
}
async checkHyperlaneConnectionClient(chain: ChainName): Promise<void> {
const router = this.app.getContracts(chain).router;
const router = this.app.router(this.app.getContracts(chain));
const mailbox = await router.mailbox();
const igp = await router.interchainGasPaymaster();
const ism = await router.interchainSecurityModule();
@ -48,19 +46,17 @@ export class HyperlaneRouterChecker<
}
async checkEnrolledRouters(chain: ChainName): Promise<void> {
const router = this.app.getContracts(chain).router;
const router = this.app.router(this.app.getContracts(chain));
await Promise.all(
this.app.remoteChains(chain).map(async (remoteChain) => {
const remoteRouter = this.app.getContracts(remoteChain).router;
const remoteRouter = this.app.router(
this.app.getContracts(remoteChain),
);
const remoteDomainId = this.multiProvider.getDomainId(remoteChain);
const address = await router.routers(remoteDomainId);
utils.assert(address === utils.addressToBytes32(remoteRouter.address));
}),
);
}
ownables(chain: ChainName): Ownable[] {
return [this.app.getContracts(chain).router];
}
}

@ -1,24 +1,22 @@
import { debug } from 'debug';
import { Router } from '@hyperlane-xyz/core';
import { utils } from '@hyperlane-xyz/utils';
import { HyperlaneContracts, HyperlaneFactories } from '../contracts';
import {
DeployerOptions,
HyperlaneDeployer,
} from '../deploy/HyperlaneDeployer';
import { MultiProvider } from '../providers/MultiProvider';
import {
RouterConfig,
RouterContracts,
RouterFactories,
} from '../router/types';
import { RouterConfig } from '../router/types';
import { ChainMap } from '../types';
import { objMap, promiseObjAll } from '../utils/objects';
export abstract class HyperlaneRouterDeployer<
Config extends RouterConfig,
Contracts extends RouterContracts,
Factories extends RouterFactories,
Contracts extends HyperlaneContracts,
Factories extends HyperlaneFactories,
> extends HyperlaneDeployer<Config, Contracts, Factories> {
constructor(
multiProvider: MultiProvider,
@ -32,6 +30,8 @@ export abstract class HyperlaneRouterDeployer<
});
}
abstract router(contracts: Contracts): Router;
async initConnectionClients(
contractsMap: ChainMap<Contracts>,
): Promise<void> {
@ -39,24 +39,20 @@ export abstract class HyperlaneRouterDeployer<
objMap(contractsMap, async (local, contracts) =>
super.initConnectionClient(
local,
contracts.router,
this.router(contracts),
this.configMap[local],
),
),
);
}
async enrollRemoteRouters(
contractsMap: ChainMap<RouterContracts>,
): Promise<void> {
async enrollRemoteRouters(contractsMap: ChainMap<Contracts>): Promise<void> {
this.logger(
`Enrolling deployed routers with each other (if not already)...`,
);
// Make all routers aware of each other.
const deployedChains = Object.keys(contractsMap);
for (const [chain, contracts] of Object.entries<RouterContracts>(
contractsMap,
)) {
for (const [chain, contracts] of Object.entries<Contracts>(contractsMap)) {
// only enroll chains which are deployed
const deployedRemoteChains = this.multiProvider
.getRemoteChains(chain)
@ -65,9 +61,9 @@ export abstract class HyperlaneRouterDeployer<
const enrollEntries = await Promise.all(
deployedRemoteChains.map(async (remote) => {
const remoteDomain = this.multiProvider.getDomainId(remote);
const current = await contracts.router.routers(remoteDomain);
const current = await this.router(contracts).routers(remoteDomain);
const expected = utils.addressToBytes32(
contractsMap[remote].router.address,
this.router(contractsMap[remote]).address,
);
return current !== expected ? [remoteDomain, expected] : undefined;
}),
@ -83,14 +79,14 @@ export abstract class HyperlaneRouterDeployer<
return;
}
await super.runIfOwner(chain, contracts.router, async () => {
await super.runIfOwner(chain, this.router(contracts), async () => {
const chains = domains.map((id) => this.multiProvider.getChainName(id));
this.logger(
`Enrolling remote routers (${chains.join(', ')}) on ${chain}`,
);
await this.multiProvider.handleTx(
chain,
contracts.router.enrollRemoteRouters(
this.router(contracts).enrollRemoteRouters(
domains,
addresses,
this.multiProvider.getTransactionOverrides(chain),
@ -105,13 +101,13 @@ export abstract class HyperlaneRouterDeployer<
await promiseObjAll(
objMap(contractsMap, async (chain, contracts) => {
const owner = this.configMap[chain].owner;
const currentOwner = await contracts.router.owner();
const currentOwner = await this.router(contracts).owner();
if (owner != currentOwner) {
this.logger(`Transfer ownership of ${chain}'s router to ${owner}`);
await super.runIfOwner(chain, contracts.router, async () => {
await super.runIfOwner(chain, this.router(contracts), async () => {
await this.multiProvider.handleTx(
chain,
contracts.router.transferOwnership(
this.router(contracts).transferOwnership(
owner,
this.multiProvider.getTransactionOverrides(chain),
),

@ -1,35 +1,48 @@
import type { BigNumber } from 'ethers';
import { GasRouter } from '@hyperlane-xyz/core';
import { GasRouter, GasRouter__factory, Router } from '@hyperlane-xyz/core';
import type { types } from '@hyperlane-xyz/utils';
import { HyperlaneApp } from '../HyperlaneApp';
import { HyperlaneContracts } from '../contracts';
import { ChainMap, ChainName } from '../types';
import { objMap, promiseObjAll } from '../utils/objects';
import { RouterContracts } from './types';
export { Router } from '@hyperlane-xyz/core';
export class RouterApp<
Contracts extends RouterContracts,
export abstract class RouterApp<
Contracts extends HyperlaneContracts,
> extends HyperlaneApp<Contracts> {
abstract router(contracts: Contracts): Router;
getSecurityModules = (): Promise<ChainMap<types.Address>> =>
promiseObjAll(
objMap(this.contractsMap, (_, contracts) =>
contracts.router.interchainSecurityModule(),
this.router(contracts).interchainSecurityModule(),
),
);
getOwners = (): Promise<ChainMap<types.Address>> =>
promiseObjAll(
objMap(this.contractsMap, (_, contracts) => contracts.router.owner()),
objMap(this.contractsMap, (_, contracts) =>
this.router(contracts).owner(),
),
);
}
export class GasRouterApp<
Contracts extends RouterContracts<GasRouter>,
> extends RouterApp<Contracts> {
export type GasRouterContracts = {
router: GasRouter;
};
export type GasRouterFactories = {
router: GasRouter__factory;
};
export class GasRouterApp extends RouterApp<GasRouterContracts> {
router(contracts: GasRouterContracts): GasRouter {
return contracts.router;
}
async quoteGasPayment(
origin: ChainName,
destination: ChainName,

@ -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,10 +1,7 @@
import { ethers } from 'ethers';
import { ProxyAdmin, ProxyAdmin__factory, Router } from '@hyperlane-xyz/core';
import { ProxyAdmin, ProxyAdmin__factory } from '@hyperlane-xyz/core';
import type { types } from '@hyperlane-xyz/utils';
import { HyperlaneContracts, HyperlaneFactories } from '../contracts';
import { ProxiedContract, TransparentProxyAddresses } from '../proxy';
export type OwnableConfig = {
owner: types.Address;
@ -18,30 +15,12 @@ type GasConfig = {
export type GasRouterConfig = RouterConfig & GasConfig;
export type RouterContracts<RouterContract extends Router = Router> =
HyperlaneContracts & {
router: RouterContract;
export type ProxiedFactories = HyperlaneFactories & {
proxyAdmin: ProxyAdmin__factory;
};
export type ProxiedRouterContracts<RouterContract extends Router = Router> =
RouterContracts<RouterContract> & {
export type ProxiedContracts = HyperlaneContracts & {
proxyAdmin: ProxyAdmin;
proxiedRouter: ProxiedContract<RouterContract, TransparentProxyAddresses>;
};
type RouterFactory<RouterContract extends Router = Router> =
ethers.ContractFactory & {
deploy: (...args: any[]) => Promise<RouterContract>;
};
export type RouterFactories<RouterContract extends Router = Router> =
HyperlaneFactories & {
router: RouterFactory<RouterContract>;
};
export type ProxiedRouterFactories<RouterContract extends Router = Router> =
RouterFactories<RouterContract> & {
proxyAdmin: ProxyAdmin__factory;
};
export type ConnectionClientConfig = {

@ -1,6 +1,5 @@
import { TestRouter__factory } from '@hyperlane-xyz/core';
import { TestRouter, TestRouter__factory } from '@hyperlane-xyz/core';
import { HyperlaneApp } from '../../HyperlaneApp';
import { chainMetadata } from '../../consts/chainMetadata';
import { Chains } from '../../consts/chains';
import { HyperlaneCore } from '../../core/HyperlaneCore';
@ -8,43 +7,56 @@ import { HyperlaneDeployer } from '../../deploy/HyperlaneDeployer';
import { MultiProvider } from '../../providers/MultiProvider';
import { HyperlaneRouterChecker } from '../../router/HyperlaneRouterChecker';
import { HyperlaneRouterDeployer } from '../../router/HyperlaneRouterDeployer';
import {
RouterConfig,
RouterContracts,
RouterFactories,
} from '../../router/types';
import { RouterApp } from '../../router/RouterApps';
import { RouterConfig } from '../../router/types';
import { ChainMap, ChainName } from '../../types';
import { objMap, pick, promiseObjAll } from '../../utils/objects';
export const alfajoresChainConfig = pick(chainMetadata, [Chains.alfajores]);
export class EnvSubsetApp extends HyperlaneApp<RouterContracts> {}
export type TestRouterContracts = {
router: TestRouter;
};
export type TestRouterFactories = {
router: TestRouter__factory;
};
export const testRouterFactories: TestRouterFactories = {
router: new TestRouter__factory(),
};
export class EnvSubsetApp extends RouterApp<TestRouterContracts> {
router(contracts: TestRouterContracts) {
return contracts.router;
}
}
export class EnvSubsetChecker extends HyperlaneRouterChecker<
EnvSubsetApp,
RouterConfig,
RouterContracts
TestRouterContracts
> {}
export const envSubsetFactories: RouterFactories = {
router: new TestRouter__factory(),
};
export class EnvSubsetDeployer extends HyperlaneRouterDeployer<
RouterConfig,
RouterContracts,
RouterFactories
TestRouterContracts,
TestRouterFactories
> {
constructor(
multiProvider: MultiProvider,
configMap: ChainMap<RouterConfig>,
protected core: HyperlaneCore,
) {
super(multiProvider, configMap, envSubsetFactories, {});
super(multiProvider, configMap, testRouterFactories, {});
}
router(contracts: TestRouterContracts): TestRouter {
return contracts.router;
}
// Consider moving this up to HyperlaneRouterDeployer
async initRouter(contractsMap: ChainMap<RouterContracts>): Promise<void> {
async initRouter(contractsMap: ChainMap<TestRouterContracts>): Promise<void> {
this.logger(`Calling initialize on routers...`);
await promiseObjAll(
objMap(contractsMap, async (chain, contracts) => {
@ -53,17 +65,16 @@ export class EnvSubsetDeployer extends HyperlaneRouterDeployer<
const overrides = this.multiProvider.getTransactionOverrides(chain);
await this.multiProvider.handleTx(
chain,
// @ts-ignore TestRouter does implement this, though Router type does not
contracts.router.initialize(mailbox, igp, overrides),
this.router(contracts).initialize(mailbox, igp, overrides),
);
}),
);
}
async deploy(): Promise<ChainMap<RouterContracts>> {
async deploy(): Promise<ChainMap<TestRouterContracts>> {
const contractsMap = (await HyperlaneDeployer.prototype.deploy.apply(
this,
)) as ChainMap<RouterContracts>;
)) as ChainMap<TestRouterContracts>;
await this.initRouter(contractsMap);
await this.enrollRemoteRouters(contractsMap);
return contractsMap;

@ -2,11 +2,15 @@ import { buildContracts } from '../../contracts';
import { HyperlaneCore } from '../../core/HyperlaneCore';
import { HyperlaneIgp } from '../../gas/HyperlaneIgp';
import { MultiProvider } from '../../providers/MultiProvider';
import { RouterContracts } from '../../router/types';
import { ChainMap } from '../../types';
import { createRouterConfigMap } from '../testUtils';
import { EnvSubsetApp, EnvSubsetChecker, envSubsetFactories } from './app';
import {
EnvSubsetApp,
EnvSubsetChecker,
TestRouterContracts,
testRouterFactories,
} from './app';
// Copied from output of deploy-single-chain.ts script
const deploymentAddresses = {
@ -23,8 +27,8 @@ async function check() {
const contractsMap = buildContracts(
deploymentAddresses,
envSubsetFactories,
) as ChainMap<RouterContracts>;
testRouterFactories,
) as ChainMap<TestRouterContracts>;
const app = new EnvSubsetApp(contractsMap, multiProvider);
const core = HyperlaneCore.fromEnvironment('testnet', multiProvider);
const igp = HyperlaneIgp.fromEnvironment('testnet', multiProvider);

@ -5,18 +5,23 @@ import { TestChains } from '../../consts/chains';
import { TestCoreApp } from '../../core/TestCoreApp';
import { TestCoreDeployer } from '../../core/TestCoreDeployer';
import { MultiProvider } from '../../providers/MultiProvider';
import { RouterConfig, RouterContracts } from '../../router/types';
import { RouterConfig } from '../../router/types';
import { ChainMap, ChainName } from '../../types';
import { deployTestIgpsAndGetRouterConfig, testCoreConfig } from '../testUtils';
import { EnvSubsetApp, EnvSubsetChecker, EnvSubsetDeployer } from './app';
import {
EnvSubsetApp,
EnvSubsetChecker,
EnvSubsetDeployer,
TestRouterContracts,
} from './app';
// Tests deploying the basic EnvSubsetApp to a local hardhat-based test env
describe('deploy app for full test env', async () => {
let multiProvider: MultiProvider;
let config: ChainMap<RouterConfig>;
let deployer: EnvSubsetDeployer;
let contracts: ChainMap<RouterContracts>;
let contracts: ChainMap<TestRouterContracts>;
let app: EnvSubsetApp;
before(async () => {
@ -46,7 +51,7 @@ describe('deploy app to test env subset', async () => {
let multiProvider: MultiProvider;
let config: ChainMap<RouterConfig>;
let deployer: EnvSubsetDeployer;
let contracts: ChainMap<RouterContracts>;
let contracts: ChainMap<TestRouterContracts>;
let app: EnvSubsetApp;
before(async () => {

@ -1 +1 @@
Subproject commit df21762a269d3d813fa28799a1a0216d5457e008
Subproject commit 306b107e662c4c87e963693bfe29cf2985f3090f
Loading…
Cancel
Save