From 9356f35d1894f7b306299e133c7fea03560831f6 Mon Sep 17 00:00:00 2001 From: bennett Date: Wed, 25 Sep 2024 14:15:47 -0500 Subject: [PATCH 1/2] improve: swap Optimism and Base OpStackAdapters with BaseChainAdapters Signed-off-by: bennett --- src/clients/bridges/AdapterManager.ts | 20 +++++++++++++------- test/AdapterManager.SendTokensCrossChain.ts | 16 ++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/clients/bridges/AdapterManager.ts b/src/clients/bridges/AdapterManager.ts index ec8a9d732..d23ec0bf8 100644 --- a/src/clients/bridges/AdapterManager.ts +++ b/src/clients/bridges/AdapterManager.ts @@ -9,7 +9,7 @@ import { import { InventoryConfig, OutstandingTransfers } from "../../interfaces"; import { BigNumber, isDefined, winston, Signer, getL2TokenAddresses, TransactionResponse, assert } from "../../utils"; import { SpokePoolClient, HubPoolClient } from "../"; -import { ArbitrumAdapter, PolygonAdapter, ZKSyncAdapter, LineaAdapter, OpStackAdapter, ScrollAdapter } from "./"; +import { ArbitrumAdapter, PolygonAdapter, ZKSyncAdapter, LineaAdapter, ScrollAdapter } from "./"; import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants"; import { BaseChainAdapter } from "../../adapter"; @@ -60,12 +60,15 @@ export class AdapterManager { ); }; if (this.spokePoolClients[OPTIMISM] !== undefined) { - this.adapters[OPTIMISM] = new OpStackAdapter( + this.adapters[OPTIMISM] = new BaseChainAdapter( + spokePoolClients, OPTIMISM, + hubChainId, + filterMonitoredAddresses(OPTIMISM), logger, SUPPORTED_TOKENS[OPTIMISM], - spokePoolClients, - filterMonitoredAddresses(OPTIMISM) + constructBridges(OPTIMISM), + DEFAULT_GAS_MULTIPLIER[OPTIMISM] ?? 1 ); } if (this.spokePoolClients[POLYGON] !== undefined) { @@ -78,12 +81,15 @@ export class AdapterManager { this.adapters[ZK_SYNC] = new ZKSyncAdapter(logger, spokePoolClients, filterMonitoredAddresses(ZK_SYNC)); } if (this.spokePoolClients[BASE] !== undefined) { - this.adapters[BASE] = new OpStackAdapter( + this.adapters[BASE] = new BaseChainAdapter( + spokePoolClients, BASE, + hubChainId, + filterMonitoredAddresses(BASE), logger, SUPPORTED_TOKENS[BASE], - spokePoolClients, - filterMonitoredAddresses(BASE) + constructBridges(BASE), + DEFAULT_GAS_MULTIPLIER[BASE] ?? 1 ); } if (this.spokePoolClients[LINEA] !== undefined) { diff --git a/test/AdapterManager.SendTokensCrossChain.ts b/test/AdapterManager.SendTokensCrossChain.ts index 235d8053f..f6bd5e77f 100644 --- a/test/AdapterManager.SendTokensCrossChain.ts +++ b/test/AdapterManager.SendTokensCrossChain.ts @@ -126,7 +126,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { mainnetTokens.bal, // l1 token getL2TokenAddresses(mainnetTokens.bal)[chainId], // l2 token amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.bal].l2Gas, // l2Gas "0x" // data ); @@ -142,7 +142,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { mainnetTokens.usdc, // l1 token TOKEN_SYMBOLS_MAP["USDC.e"].addresses[chainId], // l2 token amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.usdc].canonicalBridge.l2Gas, // l2Gas "0x" // data ); @@ -175,7 +175,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { mainnetTokens.dai, // l1 token getL2TokenAddresses(mainnetTokens.dai)[chainId], // l2 token amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.dai].l2Gas, // l2Gas "0x" // data ); @@ -184,7 +184,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { expect(l1AtomicDepositor.bridgeWethToOvm).to.have.been.calledWith( relayer.address, // to amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.weth].l2Gas, // l2Gas chainId // chainId ); }); @@ -354,7 +354,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { mainnetTokens.usdc, // l1 token TOKEN_SYMBOLS_MAP.USDbC.addresses[chainId], // l2 token amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.usdc].canonicalBridge.l2Gas, // l2Gas "0x" // data ); @@ -363,7 +363,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { mainnetTokens.bal, // l1 token getL2TokenAddresses(mainnetTokens.bal)[chainId], // l2 token amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.bal].l2Gas, // l2Gas "0x" // data ); @@ -373,7 +373,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { mainnetTokens.dai, // l1 token getL2TokenAddresses(mainnetTokens.dai)[chainId], // l2 token amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.dai].l2Gas, // l2Gas "0x" // data ); @@ -382,7 +382,7 @@ describe("AdapterManager: Send tokens cross-chain", async function () { expect(l1AtomicDepositor.bridgeWethToOvm).to.have.been.calledWith( relayer.address, // to amountToSend, // amount - addAttrib(adapterManager.adapters[chainId]).l2Gas, // l2Gas + addAttrib(adapterManager.adapters[chainId]).bridges[mainnetTokens.weth].l2Gas, // l2Gas chainId // chainId ); }); From 8dabd8491fc47a0edee9d1defb177ca590fca685 Mon Sep 17 00:00:00 2001 From: bennett Date: Mon, 30 Sep 2024 10:08:21 -0500 Subject: [PATCH 2/2] update snx ABI Signed-off-by: bennett --- src/adapter/bridges/SnxOptimismBridge.ts | 36 +++++++++++++++++++++--- src/common/abi/SnxOptimismBridgeL1.json | 2 +- test/generic-adapters/OpStack.ts | 20 ++++++------- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/adapter/bridges/SnxOptimismBridge.ts b/src/adapter/bridges/SnxOptimismBridge.ts index e7cb310f5..791f563a4 100644 --- a/src/adapter/bridges/SnxOptimismBridge.ts +++ b/src/adapter/bridges/SnxOptimismBridge.ts @@ -1,4 +1,12 @@ -import { Contract, BigNumber, paginatedEventQuery, EventSearchConfig, Signer, Provider } from "../../utils"; +import { + Contract, + BigNumber, + paginatedEventQuery, + EventSearchConfig, + Signer, + Provider, + isContractDeployedToAddress, +} from "../../utils"; import { CONTRACT_ADDRESSES } from "../../common"; import { BaseBridgeAdapter, BridgeTransactionDetails, BridgeEvents } from "./BaseBridgeAdapter"; import { processEvent } from "../utils"; @@ -43,11 +51,19 @@ export class SnxOptimismBridge extends BaseBridgeAdapter { toAddress: string, eventConfig: EventSearchConfig ): Promise { - // @dev For the SnxBridge, only the `toAddress` is indexed on the L2 event so we treat the `fromAddress` as the - // toAddress when fetching the L1 event. + const hubPoolAddress = this.getHubPool().address; + // @dev Since the SnxOptimism bridge has no _from field when querying for finalizations, we cannot use + // the hub pool to determine cross chain transfers (since we do not assume knowledge of the spoke pool address). + if (fromAddress === hubPoolAddress) { + return Promise.resolve({}); + } + // If `toAddress` is a contract on L2, then assume the contract is the spoke pool, and further assume that the sender + // is the hub pool. + const isSpokePool = await this.isL2ChainContract(toAddress); + fromAddress = isSpokePool ? hubPoolAddress : fromAddress; const events = await paginatedEventQuery( this.getL1Bridge(), - this.getL1Bridge().filters.DepositInitiated(undefined, toAddress), + this.getL1Bridge().filters.DepositInitiated(fromAddress), eventConfig ); return { @@ -70,4 +86,16 @@ export class SnxOptimismBridge extends BaseBridgeAdapter { [this.resolveL2TokenAddress(l1Token)]: events.map((event) => processEvent(event, "_amount", "_to", "_from")), }; } + + private getHubPool(): Contract { + const hubPoolContractData = CONTRACT_ADDRESSES[this.hubChainId]?.hubPool; + if (!hubPoolContractData) { + throw new Error(`hubPoolContractData not found for chain ${this.hubChainId}`); + } + return new Contract(hubPoolContractData.address, hubPoolContractData.abi, this.l1Signer); + } + + private isL2ChainContract(address: string): Promise { + return isContractDeployedToAddress(address, this.getL2Bridge().provider); + } } diff --git a/src/common/abi/SnxOptimismBridgeL1.json b/src/common/abi/SnxOptimismBridgeL1.json index dc278956a..4f6d07ae9 100644 --- a/src/common/abi/SnxOptimismBridgeL1.json +++ b/src/common/abi/SnxOptimismBridgeL1.json @@ -3,7 +3,7 @@ "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "_from", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "_to", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "_to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "_amount", "type": "uint256" } ], "name": "DepositInitiated", diff --git a/test/generic-adapters/OpStack.ts b/test/generic-adapters/OpStack.ts index 18537ec74..cefa2ddf7 100644 --- a/test/generic-adapters/OpStack.ts +++ b/test/generic-adapters/OpStack.ts @@ -183,17 +183,15 @@ describe("Cross Chain Adapter: OP Stack", async function () { describe("Custom bridge: SNX", () => { it("return only relevant L1 bridge init events", async () => { const snxBridge = adapter.bridges[l1SnxAddress]; - await snxBridgeContract.emitDepositInitiated(monitoredEoa, notMonitoredEoa, 1); - await snxBridgeContract.emitDepositInitiated(notMonitoredEoa, monitoredEoa, 1); + await snxBridgeContract.emitDepositInitiated(monitoredEoa, monitoredEoa, 1); + await snxBridgeContract.emitDepositInitiated(notMonitoredEoa, notMonitoredEoa, 1); const events = ( await snxBridge.queryL1BridgeInitiationEvents(l1SnxAddress, monitoredEoa, monitoredEoa, searchConfig) )[l2SnxAddress]; expect(events.length).to.equal(1); - // For the SnxBridge, only the `toAddress` is indexed on the L2 event so we treat the `fromAddress` as the - // toAddress when fetching the L1 event. expect(events[0].to).to.equal(monitoredEoa); - expect(events[0].from).to.equal(notMonitoredEoa); + expect(events[0].from).to.equal(monitoredEoa); }); it("return only relevant L2 bridge finalization events", async () => { @@ -321,8 +319,8 @@ describe("Cross Chain Adapter: OP Stack", async function () { // WETH transfers: 1x outstanding await wethBridgeContract.emitDepositInitiated(atomicDepositorAddress, monitoredEoa, outstandingAmount); // SNX transfers: 1x outstanding, 1x finalized - await snxBridgeContract.emitDepositInitiated(notMonitoredEoa, monitoredEoa, outstandingAmount); - await snxBridgeContract.emitDepositInitiated(notMonitoredEoa, monitoredEoa, finalizedAmount); + await snxBridgeContract.emitDepositInitiated(monitoredEoa, monitoredEoa, outstandingAmount); + await snxBridgeContract.emitDepositInitiated(monitoredEoa, monitoredEoa, finalizedAmount); await snxBridgeContract.emitDepositFinalized(monitoredEoa, finalizedAmount); // DAI transfers: 1x outstanding, 1x finalized await daiBridgeContract.emitDepositInitiated( @@ -371,16 +369,16 @@ describe("Cross Chain Adapter: OP Stack", async function () { // Get deposit tx hashes of outstanding transfers const outstandingWethEvent = ( - await wethBridge.queryL1BridgeInitiationEvents(l1WethAddress, monitoredEoa, undefined, searchConfig) + await wethBridge.queryL1BridgeInitiationEvents(l1WethAddress, monitoredEoa, monitoredEoa, searchConfig) )[l2WethAddress].find((event) => event.amount.toNumber() === outstandingAmount); const outstandingSnxEvent = ( - await snxBridge.queryL1BridgeInitiationEvents(l1SnxAddress, monitoredEoa, undefined, searchConfig) + await snxBridge.queryL1BridgeInitiationEvents(l1SnxAddress, monitoredEoa, monitoredEoa, searchConfig) )[l2SnxAddress].find((event) => event.amount.toNumber() === outstandingAmount); const outstandingDaiEvent = ( - await daiBridge.queryL1BridgeInitiationEvents(l1DaiAddress, monitoredEoa, undefined, searchConfig) + await daiBridge.queryL1BridgeInitiationEvents(l1DaiAddress, monitoredEoa, monitoredEoa, searchConfig) )[l2DaiAddress].find((event) => event.amount.toNumber() === outstandingAmount); const outstandingErc20Event = ( - await erc20Bridge.queryL1BridgeInitiationEvents(l1Erc20Address, monitoredEoa, undefined, searchConfig) + await erc20Bridge.queryL1BridgeInitiationEvents(l1Erc20Address, monitoredEoa, monitoredEoa, searchConfig) )[l2Erc20Address].find((event) => event.amount.toNumber() === outstandingAmount); const outstandingOfMonitored = (