From e502a9b6dda203c0ca6908018a38569a31a2613e Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 26 Jul 2024 16:58:18 +0200 Subject: [PATCH] ci: run e2e tests with L3 (#1789) --- .github/workflows/build-test.yml | 10 ++- README.md | 8 +- package.json | 3 +- packages/arb-token-bridge-ui/.e2e.env.sample | 4 + .../src/hooks/useNetworks.ts | 4 +- .../src/types/ChainQueryParam.ts | 9 +- .../src/util/bridgeUiConfig.ts | 8 ++ .../arb-token-bridge-ui/src/util/networks.ts | 45 +++++++++- .../src/util/wagmi/getWagmiChain.ts | 6 +- .../src/util/wagmi/setup.ts | 2 + .../src/util/wagmi/wagmiAdditionalNetworks.ts | 23 ++++- .../arb-token-bridge-ui/synpress.config.ts | 88 ++++++++++++------- .../tests/e2e/specs/approveToken.cy.ts | 8 +- .../tests/e2e/specs/depositERC20.cy.ts | 29 +++--- .../tests/e2e/specs/depositETH.cy.ts | 19 ++-- .../tests/e2e/specs/login.cy.ts | 10 ++- .../tests/e2e/specs/redeemRetryable.cy.ts | 19 ++-- .../tests/e2e/specs/switchNetworks.cy.ts | 10 ++- .../tests/e2e/specs/withdrawERC20.cy.ts | 28 +++--- .../tests/e2e/specs/withdrawETH.cy.ts | 17 ++-- .../tests/support/commands.ts | 12 ++- .../tests/support/common.ts | 36 +++++--- 22 files changed, 290 insertions(+), 108 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index f5cf60c041..7c4ff2e510 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -160,7 +160,7 @@ jobs: # based on https://github.com/Synthetixio/synpress/blob/dev/.github/workflows/e2e_cypress-action.yml test-e2e: - name: "Test E2E" + name: "Test E2E${{ matrix.orbit-test == '1' && ' with L3' || '' }} - ${{ matrix.tests.name }}" runs-on: ubuntu-latest needs: [build, check-files, check-is-hotfix, load-e2e-files] if: needs.check-files.outputs.run_tests == 'true' && needs.check-is-hotfix.outputs.is_hotfix == 'false' @@ -169,6 +169,7 @@ jobs: matrix: tests: ${{ fromJson(needs.load-e2e-files.outputs.matrix) }} + orbit-test: ['0', '1'] steps: - name: Free Disk Space (Ubuntu) @@ -216,14 +217,14 @@ jobs: uses: OffchainLabs/actions/run-nitro-test-node@a20a76172ce524832ac897bef2fa10a62ed81c29 with: nitro-testnode-ref: aab133aceadec2e622f15fa438f6327e3165392d - l3-node: false - no-l3-token-bridge: true + l3-node: ${{ matrix.orbit-test == '1' }} + no-l3-token-bridge: ${{ matrix.orbit-test == '0' }} - name: Run e2e tests via cypress-io/github-action uses: cypress-io/github-action@8d3918616d8ac34caa2b49afc8b408b6a872a6f5 # pin@v6.7.1 with: start: yarn start - command: yarn test:e2e --browser chromium + command: ${{ matrix.orbit-test == '1' && 'yarn test:e2e:orbit --browser chromium' || 'yarn test:e2e --browser chromium' }} wait-on: http://127.0.0.1:3000 wait-on-timeout: 120 spec: ./packages/arb-token-bridge-ui/tests/e2e/specs/* @@ -237,6 +238,7 @@ jobs: NEXT_PUBLIC_INFURA_KEY: ${{ secrets.NEXT_PUBLIC_INFURA_KEY }} NEXT_PUBLIC_LOCAL_ETHEREUM_RPC_URL: http://127.0.0.1:8545 NEXT_PUBLIC_LOCAL_ARBITRUM_RPC_URL: http://127.0.0.1:8547 + NEXT_PUBLIC_LOCAL_L3_RPC_URL: http://127.0.0.1:3347 - name: Archive e2e artifacts uses: actions/upload-artifact@v4 diff --git a/README.md b/README.md index ac740b394a..73f9acd030 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ It is important for any code change to pass both unit and end-to-end tests. This 1. First, make sure you have a Nitro test node running. Follow the instructions [here](https://docs.arbitrum.io/node-running/how-tos/local-dev-node). - Use the following command to run your test nodes locally for our tests. + Use the following command to run your test nodes locally for our tests. You may omit `--l3node --l3-token-bridge` if you don't intend on testing Orbit chains. ```bash ./test-node.bash --init --no-simple --tokenbridge --l3node --l3-token-bridge @@ -158,6 +158,12 @@ It is important for any code change to pass both unit and end-to-end tests. This $ yarn test:e2e:cctp ``` +6. For Orbit tests, run + + ```bash + $ yarn test:e2e:orbit + ``` + Read more about the test setup [here](/packages/arb-token-bridge-ui/tests/e2e/README.md).
diff --git a/package.json b/package.json index a8703a5038..b826dc8d2c 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "lint": "yarn workspace arb-token-bridge-ui lint", "lint:fix": "yarn workspace arb-token-bridge-ui lint:fix", "test:e2e": "yarn workspace arb-token-bridge-ui env-cmd --silent --file .e2e.env yarn synpress run --configFile synpress.config.ts", - "test:e2e:cctp": "E2E_CCTP=true yarn test:e2e" + "test:e2e:cctp": "E2E_CCTP=true yarn test:e2e", + "test:e2e:orbit": "E2E_ORBIT=true yarn test:e2e" }, "resolutions": { "**/@walletconnect/ethereum-provider": "2.13.1", diff --git a/packages/arb-token-bridge-ui/.e2e.env.sample b/packages/arb-token-bridge-ui/.e2e.env.sample index 5997993b2e..573ce3d690 100644 --- a/packages/arb-token-bridge-ui/.e2e.env.sample +++ b/packages/arb-token-bridge-ui/.e2e.env.sample @@ -1,6 +1,10 @@ NEXT_PUBLIC_INFURA_KEY= NETWORK_NAME=localhost +NEXT_PUBLIC_LOCAL_ETHEREUM_RPC_URL=http://127.0.0.1:8545 +NEXT_PUBLIC_LOCAL_ARBITRUM_RPC_URL=http://127.0.0.1:8547 +NEXT_PUBLIC_LOCAL_L3_RPC_URL=http://127.0.0.1:3347 + CYPRESS_RECORD_VIDEO=false # We don't use PRIVATE_KEY because Synpress is looking for that to set up MetaMask diff --git a/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts b/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts index e6f0747224..64a394d3fb 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts @@ -11,7 +11,8 @@ import { arbitrumNova, arbitrumSepolia, localL1Network as local, - localL2Network as arbitrumLocal + localL2Network as arbitrumLocal, + localL3Network as l3Local } from '../util/wagmi/wagmiAdditionalNetworks' import { getDestinationChainIds } from '../util/networks' @@ -38,6 +39,7 @@ export function isSupportedChainId( arbitrumNova.id, arbitrumSepolia.id, arbitrumLocal.id, + l3Local.id, local.id, ...getOrbitChains().map(chain => chain.chainId), ...customChainIds diff --git a/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts b/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts index bd64417782..6a9dde793a 100644 --- a/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts +++ b/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts @@ -17,7 +17,8 @@ const chainQueryParams = [ 'arbitrum-nova', 'arbitrum-sepolia', 'custom-localhost', - 'arbitrum-localhost' + 'arbitrum-localhost', + 'l3-localhost' ] as const export type ChainKeyQueryParam = (typeof chainQueryParams)[number] @@ -61,6 +62,9 @@ export function getChainQueryParamForChain(chainId: ChainId): ChainQueryParam { case ChainId.ArbitrumLocal: return 'arbitrum-localhost' + case ChainId.L3Local: + return 'l3-localhost' + default: const customChain = getCustomChainFromLocalStorageById(chainId) @@ -105,6 +109,9 @@ export function getChainForChainKeyQueryParam( case 'arbitrum-localhost': return customChains.localL2Network + case 'l3-localhost': + return customChains.localL3Network + default: const orbitChain = getOrbitChains().find( chain => diff --git a/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts b/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts index 96569209ba..d68e0a1719 100644 --- a/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts +++ b/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts @@ -86,6 +86,14 @@ export function getBridgeUiConfigForChain(chainId: number): BridgeUiConfig { name: 'Arbitrum Local' } } + case ChainId.L3Local: + return { + color: '#12AAFF', + network: { + name: 'L3 Local', + logo: '/images/OrbitLogo.svg' + } + } case ChainId.ArbitrumNova: return { color: '#E57310', diff --git a/packages/arb-token-bridge-ui/src/util/networks.ts b/packages/arb-token-bridge-ui/src/util/networks.ts index 6734f73b31..a92c90cfe8 100644 --- a/packages/arb-token-bridge-ui/src/util/networks.ts +++ b/packages/arb-token-bridge-ui/src/util/networks.ts @@ -23,7 +23,9 @@ export enum ChainId { ArbitrumNova = 42170, // L2 Testnets ArbitrumSepolia = 421614, - ArbitrumLocal = 412346 + ArbitrumLocal = 412346, + // L3 Testnets + L3Local = 333333 } type L1Network = { @@ -300,6 +302,38 @@ export const defaultL2Network: ArbitrumNetwork = { } } +export const defaultL3Network: ArbitrumNetwork = { + chainId: 333333, + parentChainId: ChainId.ArbitrumLocal, + confirmPeriodBlocks: 20, + ethBridge: { + bridge: '0xA584795e24628D9c067A6480b033C9E96281fcA3', + inbox: '0xDcA690902d3154886Ec259308258D10EA5450996', + outbox: '0xda243bD61B011024FC923164db75Dde198AC6175', + rollup: '0xf9B0F86aCc3e42B7DF373c9a8adb2803BF0a7662', + sequencerInbox: '0x16c54EE2015CD824415c2077F4103f444E00A8cb' + }, + isCustom: true, + name: 'L3 Local', + retryableLifetimeSeconds: 604800, + tokenBridge: { + parentCustomGateway: '0xA191D519260A06b32f8D04c84b9F457B8Caa0514', + parentErc20Gateway: '0x6B0805Fc6e275ef66a0901D0CE68805631E271e5', + parentGatewayRouter: '0xfE03DBdf7A126994dBd749631D7fbaB58C618c58', + parentMultiCall: '0x20a3627Dcc53756E38aE3F92717DE9B23617b422', + parentProxyAdmin: '0x1A61102c26ad3f64bA715B444C93388491fd8E68', + parentWeth: '0xA1abD387192e3bb4e84D3109181F9f005aBaF5CA', + parentWethGateway: '0x77603b0ea6a797C74Fa9ef11b5BdE04A4E03D550', + childCustomGateway: '0xD4816AeF8f85A3C1E01Cd071a81daD4fa941625f', + childErc20Gateway: '0xaa7d51aFFEeB32d99b1CB2fd6d81D7adA4a896e8', + childGatewayRouter: '0x8B6BC759226f8Fe687c8aD8Cc0DbF85E095e9297', + childMultiCall: '0x052B15c8Ff0544287AE689C4F2FC53A3905d7Db3', + childProxyAdmin: '0x36C56eC2CF3a3f53db9F01d0A5Ae84b36fb0A1e2', + childWeth: '0x582a8dBc77f665dF2c49Ce0a138978e9267dd968', + childWethGateway: '0xA6AB233B3c7bfd0399834897b5073974A3D467e2' + } +} + export const localL1NetworkRpcUrl = loadEnvironmentVariableWithFallback({ env: process.env.NEXT_PUBLIC_LOCAL_ETHEREUM_RPC_URL, fallback: 'http://127.0.0.1:8545' @@ -308,13 +342,19 @@ export const localL2NetworkRpcUrl = loadEnvironmentVariableWithFallback({ env: process.env.NEXT_PUBLIC_LOCAL_ARBITRUM_RPC_URL, fallback: 'http://127.0.0.1:8547' }) +export const localL3NetworkRpcUrl = loadEnvironmentVariableWithFallback({ + env: process.env.NEXT_PUBLIC_LOCAL_L3_RPC_URL, + fallback: 'http://127.0.0.1:3347' +}) export function registerLocalNetwork() { try { rpcURLs[defaultL1Network.chainId] = localL1NetworkRpcUrl rpcURLs[defaultL2Network.chainId] = localL2NetworkRpcUrl + rpcURLs[defaultL3Network.chainId] = localL3NetworkRpcUrl registerCustomArbitrumNetwork(defaultL2Network) + registerCustomArbitrumNetwork(defaultL3Network) } catch (error: any) { console.error(`Failed to register local network: ${error.message}`) } @@ -323,7 +363,8 @@ export function registerLocalNetwork() { export function isNetwork(chainId: ChainId) { const customChains = getCustomChainsFromLocalStorage() const isMainnetOrbitChain = chainId in orbitMainnets - const isTestnetOrbitChain = chainId in orbitTestnets + const isL3Local = chainId === ChainId.L3Local + const isTestnetOrbitChain = chainId in orbitTestnets || isL3Local const isEthereumMainnet = chainId === ChainId.Ethereum diff --git a/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts b/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts index a0832597c7..bd6774c656 100644 --- a/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts +++ b/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts @@ -8,7 +8,8 @@ import { arbitrumNova, arbitrumSepolia, localL1Network, - localL2Network + localL2Network, + localL3Network } from './wagmiAdditionalNetworks' import { ChainId } from '../networks' import { getCustomChainFromLocalStorageById } from '../networks' @@ -53,6 +54,9 @@ export function getWagmiChain(chainId: number): Chain { case ChainId.ArbitrumLocal: return localL2Network + case ChainId.L3Local: + return localL3Network + default: throw new Error(`[getWagmiChain] Unexpected chain id: ${chainId}`) } diff --git a/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts b/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts index c8c062bed7..0023574b53 100644 --- a/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts +++ b/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts @@ -10,6 +10,7 @@ import { arbitrumSepolia, localL1Network as local, localL2Network as arbitrumLocal, + localL3Network as l3Local, holesky } from './wagmiAdditionalNetworks' import { isTestingEnvironment } from '../CommonUtils' @@ -40,6 +41,7 @@ const chainList = isTestingEnvironment // add local environments during testing local, arbitrumLocal, + l3Local, // user-added custom chains ...customChains ] diff --git a/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts b/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts index c6f9bc8811..4ab0f3b6a9 100644 --- a/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts +++ b/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts @@ -142,6 +142,27 @@ export const localL2Network: Chain = { } }, blockExplorers: { - default: { name: 'Blockscout', url: '' } + default: { name: 'Blockscout', url: 'https://etherscan.io' } + } +} + +/** + * For e2e testing + */ +export const localL3Network: Chain = { + id: ChainId.L3Local, + name: 'L3 Local', + network: 'l3-localhost', + nativeCurrency: ether, + rpcUrls: { + default: { + http: [rpcURLs[ChainId.L3Local]!] + }, + public: { + http: [rpcURLs[ChainId.L3Local]!] + } + }, + blockExplorers: { + default: { name: 'Blockscout', url: 'https://etherscan.io' } } } diff --git a/packages/arb-token-bridge-ui/synpress.config.ts b/packages/arb-token-bridge-ui/synpress.config.ts index 77c40f7413..5c0699332a 100644 --- a/packages/arb-token-bridge-ui/synpress.config.ts +++ b/packages/arb-token-bridge-ui/synpress.config.ts @@ -9,15 +9,13 @@ import { getL2ERC20Address } from './src/util/TokenUtils' import specFiles from './tests/e2e/specfiles.json' import cctpFiles from './tests/e2e/cctp.json' -import { - NetworkName, - NetworkType, - l1WethGateway, - wethTokenAddressL1, - wethTokenAddressL2 -} from './tests/support/common' +import { NetworkName, NetworkType } from './tests/support/common' -import { registerLocalNetwork } from './src/util/networks' +import { + defaultL2Network, + defaultL3Network, + registerLocalNetwork +} from './src/util/networks' let tests: string[] if (process.env.TEST_FILE) { @@ -28,8 +26,21 @@ if (process.env.TEST_FILE) { tests = specFiles.map(file => file.file) } +const isOrbitTest = process.env.E2E_ORBIT == 'true' const shouldRecordVideo = process.env.CYPRESS_RECORD_VIDEO === 'true' +const l1WethGateway = isOrbitTest + ? defaultL3Network.tokenBridge.parentWethGateway + : defaultL2Network.tokenBridge.parentWethGateway + +const l1WethAddress = isOrbitTest + ? defaultL3Network.tokenBridge.parentWeth + : defaultL2Network.tokenBridge.parentWeth + +const l2WethAddress = isOrbitTest + ? defaultL3Network.tokenBridge.childWeth + : defaultL2Network.tokenBridge.childWeth + export default defineConfig({ userAgent: 'synpress', retries: shouldRecordVideo ? 0 : 2, @@ -54,12 +65,15 @@ export default defineConfig({ require('cypress-terminal-report/src/installLogsPrinter')(on) registerLocalNetwork() - if (!ethRpcUrl) { + if (!ethRpcUrl && !isOrbitTest) { throw new Error('NEXT_PUBLIC_LOCAL_ETHEREUM_RPC_URL variable missing.') } if (!arbRpcUrl) { throw new Error('NEXT_PUBLIC_LOCAL_ARBITRUM_RPC_URL variable missing.') } + if (!l3RpcUrl && isOrbitTest) { + throw new Error('NEXT_PUBLIC_LOCAL_L3_RPC_URL variable missing.') + } if (!sepoliaRpcUrl) { throw new Error( 'process.env.NEXT_PUBLIC_SEPOLIA_RPC_URL variable missing.' @@ -82,7 +96,7 @@ export default defineConfig({ // Send minted ERC-20 to the test userWallet await l1ERC20Token - .connect(localWallet.connect(ethProvider)) + .connect(localWallet.connect(parentProvider)) .transfer(userWalletAddress, BigNumber.from(50000000)) // Fund the userWallet. We do this to run tests on a small amount of ETH. @@ -98,8 +112,8 @@ export default defineConfig({ await approveWeth() // Set Cypress variables - config.env.ETH_RPC_URL = ethRpcUrl - config.env.ARB_RPC_URL = arbRpcUrl + config.env.ETH_RPC_URL = isOrbitTest ? arbRpcUrl : ethRpcUrl + config.env.ARB_RPC_URL = isOrbitTest ? l3RpcUrl : arbRpcUrl config.env.ETH_SEPOLIA_RPC_URL = sepoliaRpcUrl config.env.ARB_SEPOLIA_RPC_URL = arbSepoliaRpcUrl config.env.ADDRESS = userWalletAddress @@ -107,15 +121,18 @@ export default defineConfig({ config.env.INFURA_KEY = process.env.NEXT_PUBLIC_INFURA_KEY config.env.ERC20_TOKEN_ADDRESS_L1 = l1ERC20Token.address config.env.LOCAL_WALLET_PRIVATE_KEY = localWallet.privateKey + config.env.ORBIT_TEST = isOrbitTest ? '1' : '0' config.env.CUSTOM_DESTINATION_ADDRESS = await getCustomDestinationAddress() config.env.ERC20_TOKEN_ADDRESS_L2 = await getL2ERC20Address({ erc20L1Address: l1ERC20Token.address, - l1Provider: ethProvider, - l2Provider: arbProvider + l1Provider: parentProvider, + l2Provider: childProvider }) + config.env.L1_WETH_ADDRESS = l1WethAddress + config.env.L2_WETH_ADDRESS = l2WethAddress config.env.REDEEM_RETRYABLE_TEST_TX = await generateTestTxForRedeemRetryable() @@ -155,12 +172,17 @@ const ethRpcUrl = (() => { })() const arbRpcUrl = process.env.NEXT_PUBLIC_LOCAL_ARBITRUM_RPC_URL +const l3RpcUrl = process.env.NEXT_PUBLIC_LOCAL_L3_RPC_URL const sepoliaRpcUrl = process.env.NEXT_PUBLIC_SEPOLIA_RPC_URL ?? SEPOLIA_INFURA_RPC_URL const arbSepoliaRpcUrl = 'https://sepolia-rollup.arbitrum.io/rpc' -const ethProvider = new StaticJsonRpcProvider(ethRpcUrl) -const arbProvider = new StaticJsonRpcProvider(arbRpcUrl) +const parentProvider = new StaticJsonRpcProvider( + isOrbitTest ? arbRpcUrl : ethRpcUrl +) +const childProvider = new StaticJsonRpcProvider( + isOrbitTest ? l3RpcUrl : arbRpcUrl +) if (!process.env.PRIVATE_KEY_CUSTOM) { throw new Error('PRIVATE_KEY_CUSTOM variable missing.') @@ -175,7 +197,7 @@ const userWallet = new Wallet(process.env.PRIVATE_KEY_USER) async function deployERC20ToL1() { console.log('Deploying ERC20 to L1...') const contract = new TestERC20__factory().connect( - localWallet.connect(ethProvider) + localWallet.connect(parentProvider) ) const token = await contract.deploy() await token.deployed() @@ -185,12 +207,12 @@ async function deployERC20ToL1() { async function deployERC20ToL2(erc20L1Address: string) { console.log('Deploying ERC20 to L2...') - const bridger = await Erc20Bridger.fromProvider(arbProvider) + const bridger = await Erc20Bridger.fromProvider(childProvider) const deploy = await bridger.deposit({ amount: BigNumber.from(0), erc20ParentAddress: erc20L1Address, - parentSigner: localWallet.connect(ethProvider), - childProvider: arbProvider + parentSigner: localWallet.connect(parentProvider), + childProvider }) await deploy.wait() } @@ -198,7 +220,8 @@ async function deployERC20ToL2(erc20L1Address: string) { async function fundUserWalletEth(networkType: NetworkType) { console.log(`Funding ETH to user wallet: ${networkType}...`) const address = await userWallet.getAddress() - const provider = networkType === 'parentChain' ? ethProvider : arbProvider + const provider = + networkType === 'parentChain' ? parentProvider : childProvider const balance = await provider.getBalance(address) // Fund only if the balance is less than 2 eth if (balance.lt(utils.parseEther('2'))) { @@ -220,9 +243,9 @@ function getWethContract( async function wrapEth(networkType: NetworkType) { console.log(`Wrapping ETH: ${networkType}...`) const amount = networkType === 'parentChain' ? '0.2' : '0.1' - const address = - networkType === 'parentChain' ? wethTokenAddressL1 : wethTokenAddressL2 - const provider = networkType === 'parentChain' ? ethProvider : arbProvider + const address = networkType === 'parentChain' ? l1WethAddress : l2WethAddress + const provider = + networkType === 'parentChain' ? parentProvider : childProvider const tx = await getWethContract(provider, address).deposit({ value: utils.parseEther(amount) }) @@ -231,8 +254,7 @@ async function wrapEth(networkType: NetworkType) { async function approveWeth() { console.log('Approving WETH...') - const tx = await getWethContract(ethProvider, wethTokenAddressL1).approve( - // L1 WETH gateway + const tx = await getWethContract(parentProvider, l1WethAddress).approve( l1WethGateway, constants.MaxInt256 ) @@ -248,18 +270,16 @@ async function generateTestTxForRedeemRetryable() { console.log('Adding a test transaction for redeeming retryable...') const walletAddress = await userWallet.getAddress() - const l1Provider = ethProvider - const l2Provider = arbProvider const erc20Token = { symbol: 'WETH', decimals: 18, - address: wethTokenAddressL1 + address: l1WethAddress } const amount = utils.parseUnits('0.001', erc20Token.decimals) - const erc20Bridger = await Erc20Bridger.fromProvider(l2Provider) + const erc20Bridger = await Erc20Bridger.fromProvider(childProvider) const depositRequest = await erc20Bridger.getDepositRequest({ - parentProvider: l1Provider, - childProvider: l2Provider, + parentProvider, + childProvider, from: walletAddress, erc20ParentAddress: erc20Token.address, amount, @@ -269,8 +289,8 @@ async function generateTestTxForRedeemRetryable() { }) const tx = await erc20Bridger.deposit({ ...depositRequest, - parentSigner: userWallet.connect(ethProvider), - childProvider: arbProvider, + parentSigner: userWallet.connect(parentProvider), + childProvider, retryableGasOverrides: { gasLimit: { base: BigNumber.from(0) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts index c49198544c..12c4923ac3 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts @@ -2,7 +2,9 @@ import { importTokenThroughUI, ERC20TokenName, ERC20TokenSymbol, - zeroToLessThanOneETH + zeroToLessThanOneETH, + getL1NetworkName, + getL2NetworkName } from '../../support/common' const ERC20TokenAddressL1 = Cypress.env('ERC20_TOKEN_ADDRESS_L1') @@ -26,8 +28,8 @@ describe('Approve token and deposit afterwards', () => { .click() .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Ethereum Local', zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL1NetworkName(), zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) }) cy.waitUntil(() => cy.findMoveFundsButton().should('not.be.disabled'), { errorMsg: 'move funds button is disabled (expected to be enabled)', diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index 15ba0288c4..95038f0130 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -7,7 +7,8 @@ import { getInitialERC20Balance, getL1NetworkConfig, zeroToLessThanOneETH, - wethTokenAddressL1 + getL1NetworkName, + getL2NetworkName } from '../../support/common' import { shortenAddress } from '../../../src/util/CommonUtils' @@ -18,6 +19,10 @@ describe('Deposit ERC20 Token', () => { // we have to make sure we preserve a healthy LocalStorage state // because it is cleared between each `it` cypress test + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + const depositTime = isOrbitTest ? 'Less than a minute' : '10 minutes' + const l1WethAddress = Cypress.env('L1_WETH_ADDRESS') + // Happy Path context('User has some ERC20 and is on L1', () => { let l1ERC20bal: string @@ -25,7 +30,7 @@ describe('Deposit ERC20 Token', () => { // log in to metamask before deposit beforeEach(() => { getInitialERC20Balance({ - tokenAddress: wethTokenAddressL1, + tokenAddress: l1WethAddress, multiCallerAddress: getL1NetworkConfig().multiCall, address: Cypress.env('ADDRESS'), rpcURL: Cypress.env('ETH_RPC_URL') @@ -34,8 +39,8 @@ describe('Deposit ERC20 Token', () => { it('should show L1 and L2 chains, and ETH correctly', () => { cy.login({ networkType: 'parentChain' }) - cy.findSourceChainButton('Ethereum Local') - cy.findDestinationChainButton('Arbitrum Local') + cy.findSourceChainButton(getL1NetworkName()) + cy.findDestinationChainButton(getL2NetworkName()) cy.findSelectTokenButton('ETH') }) @@ -46,7 +51,7 @@ describe('Deposit ERC20 Token', () => { context('should add a new token', () => { cy.searchAndSelectToken({ tokenName: 'WETH', - tokenAddress: wethTokenAddressL1 + tokenAddress: l1WethAddress }) }) @@ -62,8 +67,8 @@ describe('Deposit ERC20 Token', () => { // .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Ethereum Local', zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL1NetworkName(), zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) }) }) @@ -72,7 +77,7 @@ describe('Deposit ERC20 Token', () => { .click() .then(() => { cy.confirmMetamaskTransaction().then(() => { - cy.findByText('10 minutes').should('be.visible') + cy.findByText(depositTime).should('be.visible') cy.findByText( `${formatAmount(ERC20AmountToSend, { symbol: 'WETH' @@ -90,7 +95,7 @@ describe('Deposit ERC20 Token', () => { context('should add a new token', () => { cy.searchAndSelectToken({ tokenName: 'WETH', - tokenAddress: wethTokenAddressL1 + tokenAddress: l1WethAddress }) }) @@ -99,8 +104,8 @@ describe('Deposit ERC20 Token', () => { // .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Ethereum Local', zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL1NetworkName(), zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) }) }) @@ -113,7 +118,7 @@ describe('Deposit ERC20 Token', () => { .click() .then(() => { cy.confirmMetamaskTransaction().then(() => { - cy.findByText('10 minutes').should('be.visible') + cy.findByText(depositTime).should('be.visible') cy.findByText( `${formatAmount(ERC20AmountToSend, { symbol: 'WETH' diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts index 701d2991a7..efa2822a7e 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts @@ -2,17 +2,24 @@ * When user wants to bridge ETH from L1 to L2 */ -import { zeroToLessThanOneETH } from '../../support/common' +import { + getL1NetworkName, + getL2NetworkName, + zeroToLessThanOneETH +} from '../../support/common' import { formatAmount } from '../../../src/util/NumberUtils' describe('Deposit ETH', () => { const ETHAmountToDeposit = 0.0001 + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + const depositTime = isOrbitTest ? 'Less than a minute' : '10 minutes' + // Happy Path it('should show L1 and L2 chains correctly', () => { cy.login({ networkType: 'parentChain' }) - cy.findSourceChainButton('Ethereum Local') - cy.findDestinationChainButton('Arbitrum Local') + cy.findSourceChainButton(getL1NetworkName()) + cy.findDestinationChainButton(getL2NetworkName()) }) it('should show gas estimations and bridge successfully', () => { @@ -21,12 +28,12 @@ describe('Deposit ETH', () => { // .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Ethereum Local', zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL1NetworkName(), zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) }) cy.findMoveFundsButton().click() cy.confirmMetamaskTransaction().then(() => { - cy.findByText('10 minutes').should('be.visible') + cy.findByText(depositTime).should('be.visible') cy.findByText( `${formatAmount(ETHAmountToDeposit, { symbol: 'ETH' diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts index 59f43fc4ef..25c8552974 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts @@ -3,7 +3,11 @@ */ import { formatAmount } from '../../../src/util/NumberUtils' -import { getInitialETHBalance } from './../../support/common' +import { + getInitialETHBalance, + getL1NetworkName, + getL2NetworkName +} from './../../support/common' describe('Login Account', () => { let l1ETHbal @@ -39,7 +43,7 @@ describe('Login Account', () => { .should('be.visible') .siblings() .contains('Balance: ') - cy.findSourceChainButton('Ethereum Local') - cy.findDestinationChainButton('Arbitrum Local') + cy.findSourceChainButton(getL1NetworkName()) + cy.findDestinationChainButton(getL2NetworkName()) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 3577bcb82b..d6986867e1 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -3,38 +3,41 @@ import { Transaction } from '../../../src/hooks/useTransactions' import { AssetType } from '../../../src/hooks/arbTokenBridge.types' import { getInitialERC20Balance, - getL2NetworkConfig, - wethTokenAddressL2 + getL2NetworkConfig } from '../../support/common' const wethAmountToDeposit = 0.001 function mockErc20RedeemDepositTransaction(): Transaction { + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + return { txID: Cypress.env('REDEEM_RETRYABLE_TEST_TX'), value: wethAmountToDeposit.toString(), type: 'deposit-l1', direction: 'deposit', source: 'local_storage_cache', - parentChainId: 1337, - childChainId: 412346, + parentChainId: isOrbitTest ? 412346 : 1337, + childChainId: isOrbitTest ? 333333 : 412346, status: 'pending', assetName: 'WETH', assetType: AssetType.ERC20, sender: Cypress.env('ADDRESS'), destination: Cypress.env('ADDRESS'), - l1NetworkID: '1337', - l2NetworkID: '412346', + l1NetworkID: isOrbitTest ? '412346' : '1337', + l2NetworkID: isOrbitTest ? '333333' : '412346', timestampCreated: Math.floor(Date.now() / 1000).toString() } } describe('Redeem ERC20 Deposit', () => { + const l2WethAddress = Cypress.env('L2_WETH_ADDRESS') + context('User has some ERC20 and is on L1', () => { let l2ERC20bal: string beforeEach(() => { getInitialERC20Balance({ - tokenAddress: wethTokenAddressL2, + tokenAddress: l2WethAddress, multiCallerAddress: getL2NetworkConfig().multiCall, address: Cypress.env('ADDRESS'), rpcURL: Cypress.env('ARB_RPC_URL') @@ -52,7 +55,7 @@ describe('Redeem ERC20 Deposit', () => { context('should add a new token', () => { cy.searchAndSelectToken({ tokenName: 'WETH', - tokenAddress: wethTokenAddressL2 + tokenAddress: l2WethAddress }) // check the balance on the destination chain before redeeming diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/switchNetworks.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/switchNetworks.cy.ts index 1d793c2eb0..8522e44ddf 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/switchNetworks.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/switchNetworks.cy.ts @@ -1,9 +1,11 @@ +import { getL1NetworkName, getL2NetworkName } from '../../support/common' + describe('Switch Networks', () => { context('User is on test network L1', () => { it('should show L1 and L2 chains correctly', () => { cy.login({ networkType: 'parentChain' }) - cy.findSourceChainButton('Ethereum Local') - cy.findDestinationChainButton('Arbitrum Local') + cy.findSourceChainButton(getL1NetworkName()) + cy.findDestinationChainButton(getL2NetworkName()) }) context( @@ -11,13 +13,13 @@ describe('Switch Networks', () => { () => { it('should switch "from: Ethereum" to "from: Arbitrum" successfully', () => { cy.login({ networkType: 'parentChain' }) - cy.findSourceChainButton('Ethereum Local') + cy.findSourceChainButton(getL1NetworkName()) cy.findByRole('button', { name: /Switch Networks/i }) .should('be.visible') .click() - cy.findSourceChainButton('Arbitrum Local') + cy.findSourceChainButton(getL2NetworkName()) }) } ) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index acbd653cc7..27769fe234 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -7,7 +7,8 @@ import { formatAmount } from '../../../src/util/NumberUtils' import { getInitialERC20Balance, getL2NetworkConfig, - wethTokenAddressL2, + getL1NetworkName, + getL2NetworkName, zeroToLessThanOneETH } from '../../support/common' @@ -19,11 +20,12 @@ describe('Withdraw ERC20 Token', () => { // Happy Path context('User is on L2 and imports ERC-20', () => { let l2ERC20bal: string + const l2WethAddress = Cypress.env('L2_WETH_ADDRESS') // log in to metamask before withdrawal beforeEach(() => { getInitialERC20Balance({ - tokenAddress: wethTokenAddressL2, + tokenAddress: l2WethAddress, multiCallerAddress: getL2NetworkConfig().multiCall, address: Cypress.env('ADDRESS'), rpcURL: Cypress.env('ARB_RPC_URL') @@ -37,8 +39,8 @@ describe('Withdraw ERC20 Token', () => { it('should show form fields correctly', () => { cy.login({ networkType: 'childChain' }) - cy.findSourceChainButton('Arbitrum Local') - cy.findDestinationChainButton('Ethereum Local') + cy.findSourceChainButton(getL2NetworkName()) + cy.findDestinationChainButton(getL1NetworkName()) cy.findMoveFundsButton().should('be.disabled') cy.findSelectTokenButton('ETH') }) @@ -50,7 +52,7 @@ describe('Withdraw ERC20 Token', () => { context('should add ERC-20 correctly', () => { cy.searchAndSelectToken({ tokenName: 'WETH', - tokenAddress: wethTokenAddressL2 + tokenAddress: l2WethAddress }) }) @@ -59,9 +61,12 @@ describe('Withdraw ERC20 Token', () => { // .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) cy.findGasFeeForChain( - /You'll have to pay Ethereum Local gas fee upon claiming./i + new RegExp( + `You'll have to pay ${getL1NetworkName()} gas fee upon claiming.`, + 'i' + ) ) }) }) @@ -116,7 +121,7 @@ describe('Withdraw ERC20 Token', () => { context('should add a new token', () => { cy.searchAndSelectToken({ tokenName: 'WETH', - tokenAddress: wethTokenAddressL2 + tokenAddress: l2WethAddress }) }) @@ -125,9 +130,12 @@ describe('Withdraw ERC20 Token', () => { // .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) cy.findGasFeeForChain( - /You'll have to pay Ethereum Local gas fee upon claiming./i + new RegExp( + `You'll have to pay ${getL1NetworkName()} gas fee upon claiming.`, + 'i' + ) ) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts index 271efc043c..b3953bb489 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts @@ -2,7 +2,11 @@ * When user wants to bridge ETH from L2 to L1 */ -import { zeroToLessThanOneETH } from '../../support/common' +import { + getL1NetworkName, + getL2NetworkName, + zeroToLessThanOneETH +} from '../../support/common' import { formatAmount } from '../../../src/util/NumberUtils' describe('Withdraw ETH', () => { @@ -12,8 +16,8 @@ describe('Withdraw ETH', () => { context('user has some ETH and is on L2', () => { it('should show form fields correctly', () => { cy.login({ networkType: 'childChain' }) - cy.findSourceChainButton('Arbitrum Local') - cy.findDestinationChainButton('Ethereum Local') + cy.findSourceChainButton(getL2NetworkName()) + cy.findDestinationChainButton(getL1NetworkName()) cy.findMoveFundsButton().should('be.disabled') }) @@ -24,9 +28,12 @@ describe('Withdraw ETH', () => { // .then(() => { cy.findGasFeeSummary(zeroToLessThanOneETH) - cy.findGasFeeForChain('Arbitrum Local', zeroToLessThanOneETH) + cy.findGasFeeForChain(getL2NetworkName(), zeroToLessThanOneETH) cy.findGasFeeForChain( - /You'll have to pay Ethereum Local gas fee upon claiming./i + new RegExp( + `You'll have to pay ${getL1NetworkName()} gas fee upon claiming.`, + 'i' + ) ) }) }) diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index b5f8186c5d..417ea9be04 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -54,7 +54,17 @@ export function login({ function _startWebApp() { const sourceChain = networkNameWithDefault === 'mainnet' ? 'ethereum' : networkNameWithDefault - startWebApp(url, { ...query, sourceChain }) + + // when testing Orbit chains we want to set destination chain to L3 + const destinationChain = + networkType === 'parentChain' && network.chainId === '412346' + ? 'l3-localhost' + : '' + startWebApp(url, { + ...query, + sourceChain, + destinationChain + }) } shouldChangeNetwork(networkNameWithDefault).then(changeNetwork => { diff --git a/packages/arb-token-bridge-ui/tests/support/common.ts b/packages/arb-token-bridge-ui/tests/support/common.ts index a9af8439ba..7e38dedf75 100644 --- a/packages/arb-token-bridge-ui/tests/support/common.ts +++ b/packages/arb-token-bridge-ui/tests/support/common.ts @@ -6,12 +6,13 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers' import { BigNumber } from 'ethers' import { MultiCaller } from '@arbitrum/sdk' import { MULTICALL_TESTNET_ADDRESS } from '../../src/constants' -import { defaultL2Network } from '../../src/util/networks' +import { defaultL2Network, defaultL3Network } from '../../src/util/networks' export type NetworkType = 'parentChain' | 'childChain' export type NetworkName = | 'custom-localhost' | 'arbitrum-localhost' + | 'l3-localhost' | 'arbitrum-sepolia' | 'mainnet' | 'sepolia' @@ -25,25 +26,43 @@ type NetworkConfig = { multiCall: string } +export const getL1NetworkName = () => { + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + return isOrbitTest ? 'Arbitrum Local' : 'Ethereum Local' +} + +export const getL2NetworkName = () => { + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + return isOrbitTest ? 'L3 Local' : 'Arbitrum Local' +} + export const getL1NetworkConfig = (): NetworkConfig => { + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + return { - networkName: 'custom-localhost', + networkName: isOrbitTest ? 'arbitrum-localhost' : 'custom-localhost', rpcUrl: Cypress.env('ETH_RPC_URL'), - chainId: '1337', + chainId: isOrbitTest ? '412346' : '1337', symbol: 'ETH', isTestnet: true, - multiCall: defaultL2Network.tokenBridge.parentMultiCall + multiCall: isOrbitTest + ? defaultL2Network.tokenBridge.childMultiCall + : defaultL2Network.tokenBridge.parentMultiCall } } export const getL2NetworkConfig = (): NetworkConfig => { + const isOrbitTest = Cypress.env('ORBIT_TEST') == '1' + return { - networkName: 'arbitrum-localhost', + networkName: isOrbitTest ? 'l3-localhost' : 'arbitrum-localhost', rpcUrl: Cypress.env('ARB_RPC_URL'), - chainId: '412346', + chainId: isOrbitTest ? '333333' : '412346', symbol: 'ETH', isTestnet: true, - multiCall: defaultL2Network.tokenBridge.childMultiCall + multiCall: isOrbitTest + ? defaultL3Network.tokenBridge.childMultiCall + : defaultL2Network.tokenBridge.childMultiCall } } @@ -69,9 +88,6 @@ export const getL2TestnetNetworkConfig = (): NetworkConfig => { } } -export const l1WethGateway = defaultL2Network.tokenBridge.parentWethGateway -export const wethTokenAddressL1 = defaultL2Network.tokenBridge.parentWeth -export const wethTokenAddressL2 = defaultL2Network.tokenBridge.childWeth export const ERC20TokenName = 'IntArbTestToken' export const ERC20TokenSymbol = 'IARB' export const invalidTokenAddress = '0x0000000000000000000000000000000000000000'