From 7badd9eec2e2080621de6f5308c3f7f084eb3bac Mon Sep 17 00:00:00 2001 From: Anton Kovalchuk Date: Wed, 6 Mar 2024 17:23:24 +0200 Subject: [PATCH] add rebasable token e2e tests --- .env.example | 3 + test/optimism/bridging-rebasable.e2e.test.ts | 152 ++++++++++++++++++ ...=> bridging-rebasable.integration.test.ts} | 0 utils/optimism/LidoBridgeAdapter.ts | 43 ++++- utils/optimism/testing.ts | 2 +- utils/testing/env.ts | 9 ++ 6 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 test/optimism/bridging-rebasable.e2e.test.ts rename test/optimism/{bridging-rebase.integration.test.ts => bridging-rebasable.integration.test.ts} (100%) diff --git a/.env.example b/.env.example index 767859ae..fbf40032 100644 --- a/.env.example +++ b/.env.example @@ -77,6 +77,9 @@ TESTING_ARB_L2_GATEWAY_ROUTER=0x57f54f87C44d816f60b92864e23b8c0897D4d81D TESTING_OPT_NETWORK= TESTING_OPT_L1_TOKEN=0xaF8a2F0aE374b03376155BF745A3421Dac711C12 TESTING_OPT_L2_TOKEN=0xAED5F9aaF167923D34174b8E636aaF040A11f6F7 +TESTING_OPT_L1_REBASABLE_TOKEN=0xB82381A3fBD3FaFA77B3a7bE693342618240067b +TESTING_OPT_L2_REBASABLE_TOKEN=0x6696Cb7bb602FC744254Ad9E07EfC474FBF78857 +TESTING_OPT_L2_TOKEN_RATE_ORACLE=0x8ea513d1e5Be31fb5FC2f2971897594720de9E70 TESTING_OPT_L1_ERC20_TOKEN_BRIDGE=0x243b661276670bD17399C488E7287ea4D416115b TESTING_OPT_L2_ERC20_TOKEN_BRIDGE=0x447CD1794d209Ac4E6B4097B34658bc00C4d0a51 diff --git a/test/optimism/bridging-rebasable.e2e.test.ts b/test/optimism/bridging-rebasable.e2e.test.ts new file mode 100644 index 00000000..868f0a68 --- /dev/null +++ b/test/optimism/bridging-rebasable.e2e.test.ts @@ -0,0 +1,152 @@ +import { + CrossChainMessenger, + MessageStatus, + } from "@eth-optimism/sdk"; + import { assert } from "chai"; + import { TransactionResponse } from "@ethersproject/providers"; + + import env from "../../utils/env"; + import { wei } from "../../utils/wei"; + import network from "../../utils/network"; + import optimism from "../../utils/optimism"; + import { ERC20Mintable } from "../../typechain"; + import { scenario } from "../../utils/testing"; + import { sleep } from "../../utils/testing/e2e"; + import { LidoBridgeAdapter } from "../../utils/optimism/LidoBridgeAdapter"; + + let depositTokensTxResponse: TransactionResponse; + let withdrawTokensTxResponse: TransactionResponse; + + scenario("Optimism :: Bridging via deposit/withdraw E2E test", ctxFactory) + .step( + "Validate tester has required amount of L1 token", + async ({ l1TokenRebasable, l1Tester, depositAmount }) => { + const balanceBefore = await l1TokenRebasable.balanceOf(l1Tester.address); + if (balanceBefore.lt(depositAmount)) { + try { + await (l1TokenRebasable as ERC20Mintable).mint( + l1Tester.address, + depositAmount + ); + } catch {} + const balanceAfter = await l1TokenRebasable.balanceOf(l1Tester.address); + assert.isTrue( + balanceAfter.gte(depositAmount), + "Tester has not enough L1 token" + ); + } + } + ) + + .step("Set allowance for L1LidoTokensBridge to deposit", async (ctx) => { + const allowanceTxResponse = await ctx.crossChainMessenger.approveERC20( + ctx.l1TokenRebasable.address, + ctx.l2TokenRebasable.address, + ctx.depositAmount + ); + + await allowanceTxResponse.wait(); + + assert.equalBN( + await ctx.l1TokenRebasable.allowance( + ctx.l1Tester.address, + ctx.l1LidoTokensBridge.address + ), + ctx.depositAmount + ); + }) + + .step("Bridge tokens to L2 via depositERC20()", async (ctx) => { + depositTokensTxResponse = await ctx.crossChainMessenger.depositERC20( + ctx.l1TokenRebasable.address, + ctx.l2TokenRebasable.address, + ctx.depositAmount + ); + await depositTokensTxResponse.wait(); + }) + + .step("Waiting for status to change to RELAYED", async (ctx) => { + await ctx.crossChainMessenger.waitForMessageStatus( + depositTokensTxResponse.hash, + MessageStatus.RELAYED + ); + }) + + .step("Withdraw tokens from L2 via withdrawERC20()", async (ctx) => { + withdrawTokensTxResponse = await ctx.crossChainMessenger.withdrawERC20( + ctx.l1TokenRebasable.address, + ctx.l2TokenRebasable.address, + ctx.withdrawalAmount + ); + await withdrawTokensTxResponse.wait(); + }) + + .step("Waiting for status to change to READY_TO_PROVE", async (ctx) => { + await ctx.crossChainMessenger.waitForMessageStatus( + withdrawTokensTxResponse.hash, + MessageStatus.READY_TO_PROVE + ); + }) + + .step("Proving the L2 -> L1 message", async (ctx) => { + const tx = await ctx.crossChainMessenger.proveMessage( + withdrawTokensTxResponse.hash + ); + await tx.wait(); + }) + + .step("Waiting for status to change to IN_CHALLENGE_PERIOD", async (ctx) => { + await ctx.crossChainMessenger.waitForMessageStatus( + withdrawTokensTxResponse.hash, + MessageStatus.IN_CHALLENGE_PERIOD + ); + }) + + .step("Waiting for status to change to READY_FOR_RELAY", async (ctx) => { + await ctx.crossChainMessenger.waitForMessageStatus( + withdrawTokensTxResponse.hash, + MessageStatus.READY_FOR_RELAY + ); + }) + + .step("Finalizing L2 -> L1 message", async (ctx) => { + const finalizationPeriod = await ctx.crossChainMessenger.contracts.l1.L2OutputOracle.FINALIZATION_PERIOD_SECONDS(); + await sleep(finalizationPeriod * 1000); + await ctx.crossChainMessenger.finalizeMessage(withdrawTokensTxResponse); + }) + + .step("Waiting for status to change to RELAYED", async (ctx) => { + await ctx.crossChainMessenger.waitForMessageStatus( + withdrawTokensTxResponse, + MessageStatus.RELAYED + ); + }) + + .run(); + + async function ctxFactory() { + const networkName = env.network("TESTING_OPT_NETWORK", "sepolia"); + const testingSetup = await optimism.testing(networkName).getE2ETestSetup(); + + return { + depositAmount: wei`0.0025 ether`, + withdrawalAmount: wei`0.0025 ether`, + l1Tester: testingSetup.l1Tester, + l1TokenRebasable: testingSetup.l1TokenRebasable, + l2TokenRebasable: testingSetup.l2TokenRebasable, + l1LidoTokensBridge: testingSetup.l1LidoTokensBridge, + crossChainMessenger: new CrossChainMessenger({ + l2ChainId: network.chainId("opt", networkName), + l1ChainId: network.chainId("eth", networkName), + l1SignerOrProvider: testingSetup.l1Tester, + l2SignerOrProvider: testingSetup.l2Tester, + bridges: { + LidoBridge: { + Adapter: LidoBridgeAdapter, + l1Bridge: testingSetup.l1LidoTokensBridge.address, + l2Bridge: testingSetup.l2ERC20TokenBridge.address, + }, + }, + }), + }; + } diff --git a/test/optimism/bridging-rebase.integration.test.ts b/test/optimism/bridging-rebasable.integration.test.ts similarity index 100% rename from test/optimism/bridging-rebase.integration.test.ts rename to test/optimism/bridging-rebasable.integration.test.ts diff --git a/utils/optimism/LidoBridgeAdapter.ts b/utils/optimism/LidoBridgeAdapter.ts index 14e70967..ac561d4b 100644 --- a/utils/optimism/LidoBridgeAdapter.ts +++ b/utils/optimism/LidoBridgeAdapter.ts @@ -31,13 +31,48 @@ export class LidoBridgeAdapter extends StandardBridgeAdapter { stateMutability: 'view', type: 'function', }, + { + inputs: [], + name: 'L1_TOKEN_REBASABLE', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'L2_TOKEN_REBASABLE', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, ], this.messenger.l1Provider); - const allowedL1Token = await l1Bridge.L1_TOKEN_NON_REBASABLE(); - if (!(0, hexStringEquals)(allowedL1Token, (0, toAddress)(l1Token))) { + + const allowedL1RebasableToken = await l1Bridge.L1_TOKEN_REBASABLE(); + const allowedL1NonRebasableToken = await l1Bridge.L1_TOKEN_NON_REBASABLE(); + + if ((!(0, hexStringEquals)(allowedL1RebasableToken, (0, toAddress)(l1Token))) && + (!(0, hexStringEquals)(allowedL1NonRebasableToken, (0, toAddress)(l1Token)))) + { return false; } - const allowedL2Token = await l1Bridge.L2_TOKEN_NON_REBASABLE(); - if (!(0, hexStringEquals)(allowedL2Token, (0, toAddress)(l2Token))) { + + const allowedL2RebasableToken = await l1Bridge.L2_TOKEN_REBASABLE(); + const allowedL2NonRebasableToken = await l1Bridge.L2_TOKEN_NON_REBASABLE(); + + if ((!(0, hexStringEquals)(allowedL2RebasableToken, (0, toAddress)(l2Token))) && + (!(0, hexStringEquals)(allowedL2NonRebasableToken, (0, toAddress)(l2Token)))) { return false; } return true; diff --git a/utils/optimism/testing.ts b/utils/optimism/testing.ts index 9517877c..5f36907c 100644 --- a/utils/optimism/testing.ts +++ b/utils/optimism/testing.ts @@ -160,7 +160,7 @@ async function loadDeployedBridges( l1SignerOrProvider ), l1TokenRebasable: IERC20__factory.connect( - testingUtils.env.OPT_L1_TOKEN(), + testingUtils.env.OPT_L1_REBASABLE_TOKEN(), l1SignerOrProvider ), diff --git a/utils/testing/env.ts b/utils/testing/env.ts index faf8acd6..1a00941e 100644 --- a/utils/testing/env.ts +++ b/utils/testing/env.ts @@ -33,6 +33,15 @@ export default { OPT_L2_TOKEN() { return env.address("TESTING_OPT_L2_TOKEN"); }, + OPT_L2_TOKEN_RATE_ORACLE() { + return env.address("TESTING_OPT_L2_TOKEN_RATE_ORACLE"); + }, + OPT_L1_REBASABLE_TOKEN() { + return env.address("TESTING_OPT_L1_REBASABLE_TOKEN"); + }, + OPT_L2_REBASABLE_TOKEN() { + return env.address("TESTING_OPT_L2_REBASABLE_TOKEN"); + }, OPT_L1_ERC20_TOKEN_BRIDGE() { return env.address("TESTING_OPT_L1_ERC20_TOKEN_BRIDGE"); },