From 85a1bf0fbabcca76d4d7952edf9de21115f1269a Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Thu, 4 May 2023 13:24:12 -0400 Subject: [PATCH] Deploy LL + CircleBridgeAdapter to mainnet (#2166) ### Description Deploy LL + CircleBridgeAdapter to mainnet with the relevant changes and artifacts --- .../config/environments/mainnet2/index.ts | 5 ++ .../environments/mainnet2/liquidityLayer.ts | 49 +++++++++++++++ .../middleware/liquidity-layer/addresses.json | 12 ++++ .../liquidity-layer/verification.json | 42 +++++++++++++ .../liquidity-layer/LiquidityLayerApp.ts | 63 ++++++++++++------- 5 files changed, 149 insertions(+), 22 deletions(-) create mode 100644 typescript/infra/config/environments/mainnet2/liquidityLayer.ts create mode 100644 typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/addresses.json create mode 100644 typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/verification.json diff --git a/typescript/infra/config/environments/mainnet2/index.ts b/typescript/infra/config/environments/mainnet2/index.ts index 5fd50a5ad..f32fa53e7 100644 --- a/typescript/infra/config/environments/mainnet2/index.ts +++ b/typescript/infra/config/environments/mainnet2/index.ts @@ -13,6 +13,7 @@ import { storageGasOracleConfig } from './gas-oracle'; import { helloWorld } from './helloworld'; import { igp } from './igp'; import { infrastructure } from './infrastructure'; +import { bridgeAdapterConfigs, relayerConfig } from './liquidityLayer'; import { owners } from './owners'; export const environment: EnvironmentConfig = { @@ -39,4 +40,8 @@ export const environment: EnvironmentConfig = { helloWorld, keyFunderConfig, storageGasOracleConfig, + liquidityLayerConfig: { + bridgeAdapters: bridgeAdapterConfigs, + relayer: relayerConfig, + }, }; diff --git a/typescript/infra/config/environments/mainnet2/liquidityLayer.ts b/typescript/infra/config/environments/mainnet2/liquidityLayer.ts new file mode 100644 index 000000000..a8779cf90 --- /dev/null +++ b/typescript/infra/config/environments/mainnet2/liquidityLayer.ts @@ -0,0 +1,49 @@ +import { + AgentConnectionType, + BridgeAdapterConfig, + BridgeAdapterType, + ChainMap, + Chains, + chainMetadata, +} from '@hyperlane-xyz/sdk'; + +import { LiquidityLayerRelayerConfig } from '../../../src/config/middleware'; + +import { environment } from './chains'; + +const circleDomainMapping = [ + { hyperlaneDomain: chainMetadata[Chains.ethereum].chainId, circleDomain: 0 }, + { hyperlaneDomain: chainMetadata[Chains.avalanche].chainId, circleDomain: 1 }, +]; + +export const bridgeAdapterConfigs: ChainMap = { + [Chains.ethereum]: { + circle: { + type: BridgeAdapterType.Circle, + tokenMessengerAddress: '0xBd3fa81B58Ba92a82136038B25aDec7066af3155', + messageTransmitterAddress: '0x0a992d191DEeC32aFe36203Ad87D7d289a738F81', + usdcAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + circleDomainMapping, + }, + }, + [Chains.avalanche]: { + circle: { + type: BridgeAdapterType.Circle, + tokenMessengerAddress: '0x6B25532e1060CE10cc3B0A99e5683b91BFDe6982', + messageTransmitterAddress: '0x8186359af5f57fbb40c6b14a588d2a59c0c29880', + usdcAddress: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', + circleDomainMapping, + }, + }, +}; + +export const relayerConfig: LiquidityLayerRelayerConfig = { + docker: { + repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', + tag: '59410cd-20230420-091923', + }, + namespace: environment, + prometheusPushGateway: + 'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091', + connectionType: AgentConnectionType.Http, +}; diff --git a/typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/addresses.json b/typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/addresses.json new file mode 100644 index 000000000..b562fe599 --- /dev/null +++ b/typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/addresses.json @@ -0,0 +1,12 @@ +{ + "ethereum": { + "proxyAdmin": "0x75EE15Ee1B4A75Fa3e2fDF5DF3253c25599cc659", + "liquidityLayerRouter": "0x9954A0d5C9ac7e4a3687f9B08c0FF272f9d0dc71", + "circleBridgeAdapter": "0xf7Cb9e767247144D89bcf36614D56C33FD4Db562" + }, + "avalanche": { + "proxyAdmin": "0xd7CF8c05fd81b8cA7CfF8E6C49B08a9D63265c9B", + "liquidityLayerRouter": "0xEff8C988b9F9f606059c436F5C1Cc431571C8B03", + "circleBridgeAdapter": "0x0BFf79f395A73817df1d3c80D78bb3C57Fbbc2Ed" + } +} diff --git a/typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/verification.json b/typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/verification.json new file mode 100644 index 000000000..67184b099 --- /dev/null +++ b/typescript/infra/config/environments/mainnet2/middleware/liquidity-layer/verification.json @@ -0,0 +1,42 @@ +{ + "ethereum": [ + { + "name": "LiquidityLayerRouter", + "address": "0x9954A0d5C9ac7e4a3687f9B08c0FF272f9d0dc71", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x75FE1c9cf9CD1f49bD655F4a173FE5CA7C22D8E1", + "constructorArguments": "0000000000000000000000009954a0d5c9ac7e4a3687f9b08c0ff272f9d0dc7100000000000000000000000075ee15ee1b4a75fa3e2fdf5df3253c25599cc65900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "CircleBridgeAdapter", + "address": "0xf7Cb9e767247144D89bcf36614D56C33FD4Db562", + "constructorArguments": "", + "isProxy": false + } + ], + "avalanche": [ + { + "name": "LiquidityLayerRouter", + "address": "0xDc68A5829F7Edfe2954EEe1bff23C3C994197596", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xEff8C988b9F9f606059c436F5C1Cc431571C8B03", + "constructorArguments": "000000000000000000000000dc68a5829f7edfe2954eee1bff23c3c994197596000000000000000000000000d7cf8c05fd81b8ca7cff8e6c49b08a9d63265c9b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "CircleBridgeAdapter", + "address": "0x0BFf79f395A73817df1d3c80D78bb3C57Fbbc2Ed", + "constructorArguments": "", + "isProxy": false + } + ] +} diff --git a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts index 7fe31414c..cac562a25 100644 --- a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts +++ b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerApp.ts @@ -4,12 +4,12 @@ import { CircleBridgeAdapter__factory, ICircleMessageTransmitter__factory, ITokenMessenger__factory, + Mailbox__factory, PortalAdapter__factory, } from '@hyperlane-xyz/core'; import { utils } from '@hyperlane-xyz/utils'; import { HyperlaneApp } from '../../HyperlaneApp'; -import { Chains } from '../../consts/chains'; import { HyperlaneContracts } from '../../contracts'; import { MultiProvider } from '../../providers/MultiProvider'; import { ChainMap, ChainName } from '../../types'; @@ -20,8 +20,10 @@ import { liquidityLayerFactories } from './contracts'; const PORTAL_VAA_SERVICE_TESTNET_BASE_URL = 'https://wormhole-v2-testnet-api.certus.one/v1/signed_vaa/'; -const CIRCLE_ATTESTATIONS_BASE_URL = +const CIRCLE_ATTESTATIONS_TESTNET_BASE_URL = 'https://iris-api-sandbox.circle.com/attestations/'; +const CIRCLE_ATTESTATIONS_MAINNET_BASE_URL = + 'https://iris-api.circle.com/attestations/'; const PORTAL_VAA_SERVICE_SUCCESS_CODE = 5; @@ -29,6 +31,7 @@ const TokenMessengerInterface = ITokenMessenger__factory.createInterface(); const CircleBridgeAdapterInterface = CircleBridgeAdapter__factory.createInterface(); const PortalAdapterInterface = PortalAdapter__factory.createInterface(); +const MailboxInterface = Mailbox__factory.createInterface(); const BridgedTokenTopic = CircleBridgeAdapterInterface.getEventTopic( CircleBridgeAdapterInterface.getEvent('BridgedToken'), @@ -69,6 +72,7 @@ export class LiquidityLayerApp extends HyperlaneApp< } async fetchCircleMessageTransactions(chain: ChainName): Promise { + console.log(`Fetch circle messages for ${chain}`); const url = new URL(this.multiProvider.getExplorerApiUrl(chain)); url.searchParams.set('module', 'logs'); url.searchParams.set('action', 'getLogs'); @@ -80,7 +84,7 @@ export class LiquidityLayerApp extends HyperlaneApp< const req = await fetchWithTimeout(url); const response = await req.json(); - return response.result.map((_: any) => _.transactionHash).flat(); + return response.result.map((tx: any) => tx.transactionHash).flat(); } async fetchPortalBridgeTransactions(chain: ChainName): Promise { @@ -99,7 +103,7 @@ export class LiquidityLayerApp extends HyperlaneApp< throw Error(`Expected result in response: ${response}`); } - return response.result.map((_: any) => _.transactionHash).flat(); + return response.result.map((tx: any) => tx.transactionHash).flat(); } async parsePortalMessages( @@ -109,9 +113,9 @@ export class LiquidityLayerApp extends HyperlaneApp< const provider = this.multiProvider.getProvider(chain); const receipt = await provider.getTransactionReceipt(txHash); const matchingLogs = receipt.logs - .map((_) => { + .map((log) => { try { - return [PortalAdapterInterface.parseLog(_)]; + return [PortalAdapterInterface.parseLog(log)]; } catch { return []; } @@ -119,7 +123,7 @@ export class LiquidityLayerApp extends HyperlaneApp< .flat(); if (matchingLogs.length == 0) return []; - const event = matchingLogs.find((_) => _!.name === 'BridgedToken')!; + const event = matchingLogs.find((log) => log!.name === 'BridgedToken')!; const portalSequence = event.args.portalSequence.toNumber(); const nonce = event.args.nonce.toNumber(); const destination = this.multiProvider.getChainName(event.args.destination); @@ -131,30 +135,41 @@ export class LiquidityLayerApp extends HyperlaneApp< chain: ChainName, txHash: string, ): Promise { + console.debug(`Parse Circle messages for chain ${chain} ${txHash}`); const provider = this.multiProvider.getProvider(chain); const receipt = await provider.getTransactionReceipt(txHash); const matchingLogs = receipt.logs - .map((_) => { + .map((log) => { try { - return [TokenMessengerInterface.parseLog(_)]; + return [TokenMessengerInterface.parseLog(log)]; } catch { try { - return [CircleBridgeAdapterInterface.parseLog(_)]; + return [CircleBridgeAdapterInterface.parseLog(log)]; } catch { - return []; + try { + return [MailboxInterface.parseLog(log)]; + } catch { + return []; + } } } }) .flat(); if (matchingLogs.length == 0) return []; - const message = matchingLogs.find((_) => _!.name === 'MessageSent')!.args - .message; - const nonce = matchingLogs.find((_) => _!.name === 'BridgedToken')!.args + const message = matchingLogs.find((log) => log!.name === 'MessageSent')! + .args.message; + const nonce = matchingLogs.find((log) => log!.name === 'BridgedToken')!.args .nonce; - const remoteChain = chain === Chains.fuji ? Chains.goerli : Chains.fuji; + + const destinationDomain = matchingLogs.find( + (log) => log!.name === 'Dispatch', + )!.args.destination; + + const remoteChain = this.multiProvider.getChainName(destinationDomain); const domain = this.config[chain].circle!.circleDomainMapping.find( - (_) => _.hyperlaneDomain === this.multiProvider.getDomainId(chain), + (mapping) => + mapping.hyperlaneDomain === this.multiProvider.getDomainId(chain), )!.circleDomain; return [ { @@ -196,8 +211,9 @@ export class LiquidityLayerApp extends HyperlaneApp< const wormholeOriginDomain = this.config[ message.destination ].portal!.wormholeDomainMapping.find( - (_) => - _.hyperlaneDomain === this.multiProvider.getDomainId(message.origin), + (mapping) => + mapping.hyperlaneDomain === + this.multiProvider.getDomainId(message.origin), )?.wormholeDomain; const emitter = utils.strip0x( utils.addressToBytes32( @@ -207,7 +223,7 @@ export class LiquidityLayerApp extends HyperlaneApp< const vaa = await fetchWithTimeout( `${PORTAL_VAA_SERVICE_TESTNET_BASE_URL}${wormholeOriginDomain}/${emitter}/${message.portalSequence}`, - ).then((_) => _.json()); + ).then((response) => response.json()); if (vaa.code && vaa.code === PORTAL_VAA_SERVICE_SUCCESS_CODE) { console.log(`VAA not yet found for nonce ${message.nonce}`); @@ -253,10 +269,13 @@ export class LiquidityLayerApp extends HyperlaneApp< return; } + console.log(`Attempt Circle message delivery`, JSON.stringify(message)); + const messageHash = ethers.utils.keccak256(message.message); - const attestationsB = await fetchWithTimeout( - `${CIRCLE_ATTESTATIONS_BASE_URL}${messageHash}`, - ); + const baseurl = this.multiProvider.getChainMetadata(message.chain).isTestnet + ? CIRCLE_ATTESTATIONS_TESTNET_BASE_URL + : CIRCLE_ATTESTATIONS_MAINNET_BASE_URL; + const attestationsB = await fetchWithTimeout(`${baseurl}${messageHash}`); const attestations = await attestationsB.json(); if (attestations.status !== 'complete') {