From 557cd4d5675ec00bca52b89deea6838a58d11681 Mon Sep 17 00:00:00 2001 From: Christophe Date: Mon, 26 Aug 2024 09:44:06 +0000 Subject: [PATCH 01/14] test: Add CCTP synpress config --- package.json | 2 +- packages/arb-token-bridge-ui/.e2e.env.sample | 2 + .../synpress.cctp.config.ts | 163 ++++++++++++++++++ .../arb-token-bridge-ui/synpress.config.ts | 89 ++-------- .../tests/e2e/cypress.d.ts | 10 +- .../tests/e2e/getCommonSynpressConfig.ts | 21 +++ .../tests/support/commands.ts | 101 ++--------- .../tests/support/common.ts | 134 ++++++++++---- .../tests/support/index.ts | 7 +- tsconfig.eslint.json | 3 +- 10 files changed, 324 insertions(+), 208 deletions(-) create mode 100644 packages/arb-token-bridge-ui/synpress.cctp.config.ts create mode 100644 packages/arb-token-bridge-ui/tests/e2e/getCommonSynpressConfig.ts diff --git a/package.json b/package.json index 74e39a32d6..c60ae31523 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "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": "yarn test:e2e --configFile synpress.cctp.config.ts", "test:e2e:orbit": "E2E_ORBIT=true yarn test:e2e" }, "resolutions": { diff --git a/packages/arb-token-bridge-ui/.e2e.env.sample b/packages/arb-token-bridge-ui/.e2e.env.sample index 573ce3d690..28ca742afc 100644 --- a/packages/arb-token-bridge-ui/.e2e.env.sample +++ b/packages/arb-token-bridge-ui/.e2e.env.sample @@ -12,6 +12,8 @@ CYPRESS_RECORD_VIDEO=false # Below key is only used to fund the newly created wallet PRIVATE_KEY_CUSTOM=b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659 +PRIVATE_KEY_CCTP= + # We set up MetaMask ourselves SKIP_METAMASK_SETUP=true diff --git a/packages/arb-token-bridge-ui/synpress.cctp.config.ts b/packages/arb-token-bridge-ui/synpress.cctp.config.ts new file mode 100644 index 0000000000..e08b6405b9 --- /dev/null +++ b/packages/arb-token-bridge-ui/synpress.cctp.config.ts @@ -0,0 +1,163 @@ +import { Wallet, utils } from 'ethers' +import { defineConfig } from 'cypress' +import { StaticJsonRpcProvider } from '@ethersproject/providers' +import synpressPlugins from '@synthetixio/synpress/plugins' +import logsPrinter from 'cypress-terminal-report/src/installLogsPrinter' +import { getCommonSynpressConfig } from './tests/e2e/getCommonSynpressConfig' +import { + setupCypressTasks, + fundEth, + fundUsdc, + getCustomDestinationAddress +} from './tests/support/common' +import specFiles from './tests/e2e/cctp.json' + +const shouldRecordVideo = process.env.CYPRESS_RECORD_VIDEO === 'true' + +const tests = process.env.TEST_FILE + ? [process.env.TEST_FILE] + : specFiles.map(file => file.file) + +const INFURA_KEY = process.env.NEXT_PUBLIC_INFURA_KEY +if (typeof INFURA_KEY === 'undefined') { + throw new Error('Infura API key not provided') +} + +const SEPOLIA_INFURA_RPC_URL = `https://sepolia.infura.io/v3/${INFURA_KEY}` +const ARB_SEPOLIA_INFURA_RPC_URL = `https://arbitrum-sepolia.infura.io/v3/${INFURA_KEY}` + +const sepoliaProvider = new StaticJsonRpcProvider(SEPOLIA_INFURA_RPC_URL) +const arbSepoliaProvider = new StaticJsonRpcProvider(ARB_SEPOLIA_INFURA_RPC_URL) + +if (!process.env.PRIVATE_KEY_CCTP) { + throw new Error('PRIVATE_KEY_CCTP variable missing.') +} + +// Wallet funded on Sepolia and ArbSepolia with ETH and USDC +const localWallet = new Wallet(process.env.PRIVATE_KEY_CCTP) +// Generate a new wallet every time +const userWallet = Wallet.createRandom() + +if (!process.env.PRIVATE_KEY_CCTP) { + throw new Error('PRIVATE_KEY_CCTP variable missing.') +} + +if (!process.env.PRIVATE_KEY_USER) { + throw new Error('PRIVATE_KEY_USER variable missing.') +} + +async function fundWallets() { + const userWalletAddress = await userWallet.getAddress() + console.log(`Funding wallet ${userWalletAddress}`) + + /** + * We need 0.0002 USDC per test (0.0001 for same address and 0.0001 for custom address) + * And in the worst case, we run each tests 3 time + */ + const usdcAmount = utils.parseUnits('0.0006', 6) + const ethAmountSepolia = utils.parseEther('0.01') + const ethAmountArbSepolia = utils.parseEther('0.002') + const ethPromises: (() => Promise)[] = [] + const usdcPromises: (() => Promise)[] = [] + + if (tests.length === 2) { + ethPromises.push(() => + fundEth({ + address: userWalletAddress, + provider: sepoliaProvider, + sourceWallet: localWallet, + amount: ethAmountSepolia + }) + ) + ethPromises.push(() => + fundEth({ + address: userWalletAddress, + provider: arbSepoliaProvider, + sourceWallet: localWallet, + amount: ethAmountArbSepolia + }) + ) + usdcPromises.push( + () => + fundUsdc({ + address: userWalletAddress, + provider: sepoliaProvider, + networkType: 'parentChain', + sourceWallet: localWallet, + amount: usdcAmount + }), + () => + fundUsdc({ + address: userWalletAddress, + provider: arbSepoliaProvider, + networkType: 'childChain', + sourceWallet: localWallet, + amount: usdcAmount + }) + ) + } else if (tests[0].includes('deposit')) { + ethPromises.push(() => + fundEth({ + address: userWalletAddress, + provider: sepoliaProvider, + sourceWallet: localWallet, + amount: ethAmountSepolia + }) + ) + usdcPromises.push(() => + fundUsdc({ + address: userWalletAddress, + provider: sepoliaProvider, + networkType: 'parentChain', + sourceWallet: localWallet, + amount: usdcAmount + }) + ) + } else { + ethPromises.push(() => + fundEth({ + address: userWalletAddress, + provider: arbSepoliaProvider, + sourceWallet: localWallet, + amount: ethAmountArbSepolia + }) + ) + usdcPromises.push(() => + fundUsdc({ + address: userWalletAddress, + provider: arbSepoliaProvider, + networkType: 'childChain', + sourceWallet: localWallet, + amount: usdcAmount + }) + ) + } + + await Promise.all(ethPromises.map(fn => fn())) + await Promise.all(usdcPromises.map(fn => fn())) +} + +export default defineConfig({ + ...getCommonSynpressConfig(shouldRecordVideo), + e2e: { + async setupNodeEvents(on, config) { + logsPrinter(on) + + await fundWallets() + + config.env.PRIVATE_KEY = userWallet.privateKey + config.env.PRIVATE_KEY_CCTP = process.env.PRIVATE_KEY_CCTP + config.env.SEPOLIA_INFURA_RPC_URL = SEPOLIA_INFURA_RPC_URL + config.env.ARB_SEPOLIA_INFURA_RPC_URL = ARB_SEPOLIA_INFURA_RPC_URL + config.env.CUSTOM_DESTINATION_ADDRESS = + await getCustomDestinationAddress() + + setupCypressTasks(on, { requiresNetworkSetup: false }) + synpressPlugins(on, config) + return config + }, + baseUrl: 'http://localhost:3000', + specPattern: tests, + supportFile: 'tests/support/index.ts' + } +}) diff --git a/packages/arb-token-bridge-ui/synpress.config.ts b/packages/arb-token-bridge-ui/synpress.config.ts index f0a528f108..f81a48cfc1 100644 --- a/packages/arb-token-bridge-ui/synpress.config.ts +++ b/packages/arb-token-bridge-ui/synpress.config.ts @@ -12,16 +12,17 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers' import synpressPlugins from '@synthetixio/synpress/plugins' import { TestWETH9__factory } from '@arbitrum/sdk/dist/lib/abi/factories/TestWETH9__factory' import { Erc20Bridger } from '@arbitrum/sdk' +import logsPrinter from 'cypress-terminal-report/src/installLogsPrinter' import { getL2ERC20Address } from './src/util/TokenUtils' import specFiles from './tests/e2e/specfiles.json' -import cctpFiles from './tests/e2e/cctp.json' import { contractAbi, contractByteCode } from './testErc20Token' import { - NetworkName, checkForAssertions, generateActivityOnChains, NetworkType, fundEth, + setupCypressTasks, + getCustomDestinationAddress, ERC20TokenSymbol, ERC20TokenDecimals, ERC20TokenName @@ -32,15 +33,11 @@ import { defaultL3Network, registerLocalNetwork } from './src/util/networks' +import { getCommonSynpressConfig } from './tests/e2e/getCommonSynpressConfig' -let tests: string[] -if (process.env.TEST_FILE) { - tests = [process.env.TEST_FILE] -} else if (process.env.E2E_CCTP) { - tests = cctpFiles.map(file => file.file) -} else { - tests = specFiles.map(file => file.file) -} +const tests = process.env.TEST_FILE + ? [process.env.TEST_FILE] + : specFiles.map(file => file.file) const isOrbitTest = process.env.E2E_ORBIT == 'true' const shouldRecordVideo = process.env.CYPRESS_RECORD_VIDEO === 'true' @@ -58,27 +55,10 @@ const l2WethAddress = isOrbitTest : defaultL2Network.tokenBridge!.childWeth export default defineConfig({ - userAgent: 'synpress', - retries: shouldRecordVideo ? 0 : 2, - screenshotsFolder: 'cypress/screenshots', - videosFolder: 'cypress/videos', - video: shouldRecordVideo, - screenshotOnRunFailure: true, - chromeWebSecurity: true, - modifyObstructiveCode: false, - scrollBehavior: false, - viewportWidth: 1366, - viewportHeight: 850, - env: { - coverage: false - }, - defaultCommandTimeout: 30000, - pageLoadTimeout: 30000, - requestTimeout: 30000, + ...getCommonSynpressConfig(shouldRecordVideo), e2e: { - // @ts-ignore async setupNodeEvents(on, config) { - require('cypress-terminal-report/src/installLogsPrinter')(on) + logsPrinter(on) registerLocalNetwork() if (!ethRpcUrl && !isOrbitTest) { @@ -101,18 +81,16 @@ export default defineConfig({ // Fund the userWallet. We do this to run tests on a small amount of ETH. await Promise.all([ fundEth({ - networkType: 'parentChain', address: userWalletAddress, - parentProvider, - childProvider, - sourceWallet: localWallet + provider: parentProvider, + sourceWallet: localWallet, + amount: utils.parseEther('2') }), fundEth({ - networkType: 'childChain', address: userWalletAddress, - parentProvider, - childProvider, - sourceWallet: localWallet + provider: childProvider, + sourceWallet: localWallet, + amount: utils.parseEther('2') }) ]) @@ -163,7 +141,7 @@ export default defineConfig({ await generateTestTxForRedeemRetryable() synpressPlugins(on, config) - setupCypressTasks(on) + setupCypressTasks(on, { requiresNetworkSetup: true }) return config }, baseUrl: 'http://localhost:3000', @@ -327,11 +305,6 @@ async function fundErc20ToChildChain(l1ERC20Token: Contract) { await depositRec.waitForChildTransactionReceipt(childProvider) } -async function getCustomDestinationAddress() { - console.log('Getting custom destination address...') - return (await Wallet.createRandom().getAddress()).toLowerCase() -} - async function generateTestTxForRedeemRetryable() { console.log('Adding a test transaction for redeeming retryable...') @@ -366,33 +339,3 @@ async function generateTestTxForRedeemRetryable() { const receipt = await tx.wait() return receipt.transactionHash } - -function setupCypressTasks(on: Cypress.PluginEvents) { - let currentNetworkName: NetworkName | null = null - let networkSetupComplete = false - let walletConnectedToDapp = false - - on('task', { - setCurrentNetworkName: (networkName: NetworkName) => { - currentNetworkName = networkName - return null - }, - getCurrentNetworkName: () => { - return currentNetworkName - }, - setNetworkSetupComplete: () => { - networkSetupComplete = true - return null - }, - getNetworkSetupComplete: () => { - return networkSetupComplete - }, - setWalletConnectedToDapp: () => { - walletConnectedToDapp = true - return null - }, - getWalletConnectedToDapp: () => { - return walletConnectedToDapp - } - }) -} diff --git a/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts b/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts index 8669de0cad..2b5ef93310 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts @@ -5,9 +5,6 @@ import { login, logout, openTransactionsPanel, - resetCctpAllowance, - fundUserUsdcTestnet, - fundUserWalletEth, searchAndSelectToken, fillCustomDestinationAddress, typeAmount, @@ -22,7 +19,8 @@ import { findTransactionDetailsCustomDestinationAddress, findTransactionInTransactionHistory, findClaimButton, - selectTransactionsPanelTab + selectTransactionsPanelTab, + confirmSpending } from '../support/commands' import { NetworkType, NetworkName } from '../support/common' @@ -44,9 +42,6 @@ declare global { logout(): typeof logout selectTransactionsPanelTab: typeof selectTransactionsPanelTab openTransactionsPanel: typeof openTransactionsPanel - resetCctpAllowance: typeof resetCctpAllowance - fundUserUsdcTestnet: typeof fundUserUsdcTestnet - fundUserWalletEth: typeof fundUserWalletEth typeRecursively(text: string): Chainable> searchAndSelectToken({ tokenName, @@ -68,6 +63,7 @@ declare global { findTransactionDetailsCustomDestinationAddress: typeof findTransactionDetailsCustomDestinationAddress findTransactionInTransactionHistory: typeof findTransactionInTransactionHistory findClaimButton: typeof findClaimButton + confirmSpending: typeof confirmSpending } } } diff --git a/packages/arb-token-bridge-ui/tests/e2e/getCommonSynpressConfig.ts b/packages/arb-token-bridge-ui/tests/e2e/getCommonSynpressConfig.ts new file mode 100644 index 0000000000..b74879e34a --- /dev/null +++ b/packages/arb-token-bridge-ui/tests/e2e/getCommonSynpressConfig.ts @@ -0,0 +1,21 @@ +export function getCommonSynpressConfig(shouldRecordVideo: boolean) { + return { + userAgent: 'synpress', + retries: shouldRecordVideo ? 0 : 2, + screenshotsFolder: 'cypress/screenshots', + videosFolder: 'cypress/videos', + video: shouldRecordVideo, + screenshotOnRunFailure: true, + chromeWebSecurity: true, + modifyObstructiveCode: false, + scrollBehavior: false, + viewportWidth: 1366, + viewportHeight: 850, + env: { + coverage: false + }, + defaultCommandTimeout: 30000, + pageLoadTimeout: 30000, + requestTimeout: 30000 + } as const satisfies Cypress.ConfigOptions +} diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index 4d4a7d0666..f46abb3f1b 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -14,15 +14,10 @@ import { NetworkName, startWebApp, getL1NetworkConfig, - getL2NetworkConfig, - getInitialERC20Balance + getL2NetworkConfig } from './common' -import { Wallet, utils } from 'ethers' -import { CommonAddress } from '../../src/util/CommonAddressUtils' -import { StaticJsonRpcProvider } from '@ethersproject/providers' -import { ERC20__factory } from '@arbitrum/sdk/dist/lib/abi/factories/ERC20__factory' -import { MULTICALL_TESTNET_ADDRESS } from '../../src/constants' import { shortenAddress } from '../../src/util/CommonUtils' +import { MatcherOptions } from '@testing-library/cypress' function shouldChangeNetwork(networkName: NetworkName) { // synpress throws if trying to connect to a network we are already connected to @@ -57,7 +52,7 @@ export function login({ // when testing Orbit chains we want to set destination chain to L3 const destinationChain = - networkType === 'parentChain' && network.chainId === '412346' + networkType === 'parentChain' && network.chainId === 412346 ? 'l3-localhost' : '' startWebApp(url, { @@ -159,77 +154,6 @@ export const openTransactionsPanel = (tab: 'pending' | 'settled') => { ) } -const l1RpcUrl = Cypress.env('ETH_SEPOLIA_RPC_URL') -const l2RpcUrl = Cypress.env('ARB_SEPOLIA_RPC_URL') -const l1Provider = new StaticJsonRpcProvider(l1RpcUrl) -const l2Provider = new StaticJsonRpcProvider(l2RpcUrl) -const userWallet = new Wallet(Cypress.env('PRIVATE_KEY')) -const localWallet = new Wallet(Cypress.env('LOCAL_WALLET_PRIVATE_KEY')) - -export async function resetCctpAllowance(networkType: NetworkType) { - const provider = networkType === 'parentChain' ? l1Provider : l2Provider - const { USDC, tokenMessengerContractAddress } = - networkType === 'parentChain' - ? CommonAddress.Sepolia - : CommonAddress.ArbitrumSepolia - - const contract = ERC20__factory.connect(USDC, userWallet.connect(provider)) - const allowance = await contract.allowance( - userWallet.address, - tokenMessengerContractAddress - ) - if (allowance.gt(0)) { - await contract.decreaseAllowance(tokenMessengerContractAddress, allowance) - } -} - -export async function fundUserUsdcTestnet(networkType: NetworkType) { - console.log(`Funding USDC to user wallet (testnet): ${networkType}...`) - const usdcContractAddress = - networkType === 'parentChain' - ? CommonAddress.Sepolia.USDC - : CommonAddress.ArbitrumSepolia.USDC - - const usdcBalance = await getInitialERC20Balance({ - address: userWallet.address, - rpcURL: networkType === 'parentChain' ? l1RpcUrl : l2RpcUrl, - tokenAddress: usdcContractAddress, - multiCallerAddress: MULTICALL_TESTNET_ADDRESS - }) - - // Fund only if the balance is less than 0.0001 USDC - if (usdcBalance && usdcBalance.lt(utils.parseUnits('0.0001', 6))) { - console.log(`Adding USDC to user wallet (testnet): ${networkType}...`) - const l1Provider = new StaticJsonRpcProvider(l1RpcUrl) - const l2Provider = new StaticJsonRpcProvider(l2RpcUrl) - const provider = networkType === 'parentChain' ? l1Provider : l2Provider - const contract = new ERC20__factory().connect(localWallet.connect(provider)) - const token = contract.attach(usdcContractAddress) - await token.deployed() - const tx = await token.transfer( - userWallet.address, - utils.parseUnits('1', 6) - ) - await tx.wait() - } -} - -export async function fundUserWalletEth(networkType: NetworkType) { - console.log(`Funding ETH to user wallet (testnet): ${networkType}...`) - const address = await userWallet.getAddress() - const provider = networkType === 'parentChain' ? l1Provider : l2Provider - const balance = await provider.getBalance(address) - // Fund only if the balance is less than 0.005 eth - const amountToTransfer = '0.005' - if (balance.lt(utils.parseEther(amountToTransfer))) { - const tx = await localWallet.connect(provider).sendTransaction({ - to: address, - value: utils.parseEther(amountToTransfer) - }) - await tx.wait() - } -} - export const searchAndSelectToken = ({ tokenName, tokenAddress @@ -266,7 +190,7 @@ export const fillCustomDestinationAddress = () => { .should('be.visible') .click() - cy.findByPlaceholderText(Cypress.env('ADDRESS')) + cy.findByLabelText('Custom Destination Address Input') .should('be.visible') .typeRecursively(Cypress.env('CUSTOM_DESTINATION_ADDRESS')) } @@ -398,15 +322,23 @@ export function findClaimButton( return cy.findByLabelText(`Claim ${amountToClaim}`) } +/** + * Currently, Synpress confirmMetamaskPermissionToSpend is clicking only once + * We need to call it twice to confirm it + */ +export function confirmSpending( + params: Parameters[0] +) { + cy.confirmMetamaskPermissionToSpend(params) + cy.confirmMetamaskPermissionToSpend(params) +} + Cypress.Commands.addAll({ connectToApp, login, logout, openTransactionsPanel, selectTransactionsPanelTab, - resetCctpAllowance, - fundUserUsdcTestnet, - fundUserWalletEth, searchAndSelectToken, fillCustomDestinationAddress, typeAmount, @@ -420,5 +352,6 @@ Cypress.Commands.addAll({ closeTransactionDetails, findTransactionInTransactionHistory, findClaimButton, - findTransactionDetailsCustomDestinationAddress + findTransactionDetailsCustomDestinationAddress, + confirmSpending }) diff --git a/packages/arb-token-bridge-ui/tests/support/common.ts b/packages/arb-token-bridge-ui/tests/support/common.ts index 9228c6e1dc..e716a612ce 100644 --- a/packages/arb-token-bridge-ui/tests/support/common.ts +++ b/packages/arb-token-bridge-ui/tests/support/common.ts @@ -8,6 +8,8 @@ import { MultiCaller } from '@arbitrum/sdk' import { MULTICALL_TESTNET_ADDRESS } from '../../src/constants' import { defaultL2Network, defaultL3Network } from '../../src/util/networks' import { getChainIdFromProvider } from '../../src/token-bridge-sdk/utils' +import { CommonAddress } from '../../../arb-token-bridge-ui/src/util/CommonAddressUtils' +import { ERC20__factory } from '@arbitrum/sdk/dist/lib/abi/factories/ERC20__factory' export type NetworkType = 'parentChain' | 'childChain' export type NetworkName = @@ -21,7 +23,7 @@ export type NetworkName = type NetworkConfig = { networkName: NetworkName rpcUrl: string - chainId: string + chainId: number symbol: string isTestnet: boolean multiCall: string @@ -43,7 +45,7 @@ export const getL1NetworkConfig = (): NetworkConfig => { return { networkName: isOrbitTest ? 'arbitrum-localhost' : 'custom-localhost', rpcUrl: Cypress.env('ETH_RPC_URL'), - chainId: isOrbitTest ? '412346' : '1337', + chainId: isOrbitTest ? 412346 : 1337, symbol: 'ETH', isTestnet: true, multiCall: isOrbitTest @@ -58,7 +60,7 @@ export const getL2NetworkConfig = (): NetworkConfig => { return { networkName: isOrbitTest ? 'l3-localhost' : 'arbitrum-localhost', rpcUrl: Cypress.env('ARB_RPC_URL'), - chainId: isOrbitTest ? '333333' : '412346', + chainId: isOrbitTest ? 333333 : 412346, symbol: 'ETH', isTestnet: true, multiCall: isOrbitTest @@ -71,7 +73,7 @@ export const getL1TestnetNetworkConfig = (): NetworkConfig => { return { networkName: 'sepolia', rpcUrl: Cypress.env('ETH_SEPOLIA_RPC_URL'), - chainId: '11155111', + chainId: 11155111, symbol: 'ETH', isTestnet: true, multiCall: MULTICALL_TESTNET_ADDRESS @@ -82,7 +84,7 @@ export const getL2TestnetNetworkConfig = (): NetworkConfig => { return { networkName: 'arbitrum-sepolia', rpcUrl: Cypress.env('ARB_SEPOLIA_RPC_URL'), - chainId: '421614', + chainId: 421614, symbol: 'ETH', isTestnet: true, multiCall: MULTICALL_TESTNET_ADDRESS @@ -182,33 +184,6 @@ export const wait = (ms = 0): Promise => { return new Promise(res => setTimeout(res, ms)) } -export async function fundEth({ - address, // wallet address where funding is required - networkType, // parentChain or childChain - parentProvider, - childProvider, - sourceWallet // source wallet that will fund the `address` -}: { - address: string - parentProvider: Provider - childProvider: Provider - sourceWallet: Wallet - networkType: NetworkType -}) { - console.log(`Funding ETH to user wallet: ${networkType}...`) - 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'))) { - const tx = await sourceWallet.connect(provider).sendTransaction({ - to: address, - value: utils.parseEther('2') - }) - await tx.wait() - } -} - export async function generateActivityOnChains({ parentProvider, childProvider, @@ -237,9 +212,7 @@ export async function generateActivityOnChains({ const minerParent = Wallet.createRandom().connect(parentProvider) await fundEth({ address: await minerParent.getAddress(), - networkType: 'parentChain', - parentProvider, - childProvider, + provider: parentProvider, sourceWallet: wallet }) @@ -247,9 +220,7 @@ export async function generateActivityOnChains({ const minerChild = Wallet.createRandom().connect(childProvider) await fundEth({ address: await minerChild.getAddress(), - networkType: 'childChain', - parentProvider, - childProvider, + provider: childProvider, sourceWallet: wallet }) @@ -294,3 +265,90 @@ export async function checkForAssertions({ ) } } + +export async function fundUsdc({ + address, // wallet address where funding is required + provider, + amount, + networkType, + sourceWallet +}: { + address: string + provider: Provider + amount: BigNumber + sourceWallet: Wallet + networkType: NetworkType +}) { + console.log('Funding USDC to user wallet...') + const usdcContractAddress = + networkType === 'parentChain' + ? CommonAddress.Sepolia.USDC + : CommonAddress.ArbitrumSepolia.USDC + + const contract = new ERC20__factory().connect(sourceWallet.connect(provider)) + const token = contract.attach(usdcContractAddress) + await token.deployed() + const tx = await token.transfer(address, amount) + await tx.wait() +} + +export async function fundEth({ + address, // wallet address where funding is required + provider, + sourceWallet, // source wallet that will fund the `address`, + amount = utils.parseEther('2') +}: { + address: string + provider: Provider + sourceWallet: Wallet + amount?: BigNumber +}) { + console.log(`Funding ETH to user wallet ${address}...`) + const balance = await provider.getBalance(address) + // Fund only if the balance is less than 2 eth + if (balance.lt(amount)) { + const tx = await sourceWallet.connect(provider).sendTransaction({ + to: address, + value: amount + }) + await tx.wait() + } +} + +export async function getCustomDestinationAddress() { + console.log('Getting custom destination address...') + return (await Wallet.createRandom().getAddress()).toLowerCase() +} + +export function setupCypressTasks( + on: Cypress.PluginEvents, + { requiresNetworkSetup }: { requiresNetworkSetup: boolean } +) { + let currentNetworkName: NetworkName | null = null + let networkSetupComplete = !requiresNetworkSetup + let walletConnectedToDapp = false + + on('task', { + setCurrentNetworkName: (networkName: NetworkName) => { + currentNetworkName = networkName + return null + }, + getCurrentNetworkName: () => { + return currentNetworkName + }, + setNetworkSetupComplete: () => { + networkSetupComplete = true + return null + }, + getNetworkSetupComplete: () => { + return networkSetupComplete + }, + setWalletConnectedToDapp: () => { + walletConnectedToDapp = true + return null + }, + getWalletConnectedToDapp: () => { + return walletConnectedToDapp + } + }) +} diff --git a/packages/arb-token-bridge-ui/tests/support/index.ts b/packages/arb-token-bridge-ui/tests/support/index.ts index c446f45741..e9fc25e037 100644 --- a/packages/arb-token-bridge-ui/tests/support/index.ts +++ b/packages/arb-token-bridge-ui/tests/support/index.ts @@ -1,4 +1,3 @@ -import './commands' import '@synthetixio/synpress/support' import logCollector from 'cypress-terminal-report/src/installLogsCollector' @@ -7,10 +6,11 @@ import { getL2NetworkConfig, getL2TestnetNetworkConfig } from './common' +import './commands' Cypress.Keyboard.defaults({ // tests are flaky in CI with low keystroke delay - keystrokeDelay: 150 + keystrokeDelay: 250 }) logCollector({ @@ -20,14 +20,13 @@ logCollector({ 'cons:debug', 'cons:error', 'cons:info', - 'cons:log', 'cons:warn' ] }) before(() => { // connect to sepolia to avoid connecting to localhost twice and failing - cy.setupMetamask(Cypress.env('PRIVATE_KEY'), 'sepolia') + cy.setupMetamask(Cypress.env('PRIVATE_KEY'), 'mainnet') .task('getNetworkSetupComplete') .then(complete => { if (!complete) { diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 456b61d6a1..d0a591b7ca 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -10,6 +10,7 @@ // adding eslintrc here fixes eslint from throwing an error // https://stackoverflow.com/questions/63118405/how-to-fix-eslintrc-the-file-does-not-match-your-project-config ".eslintrc.js", - "./packages/arb-token-bridge-ui/tests/**/*.ts" + "./packages/arb-token-bridge-ui/tests/**/*.ts", + "./packages/arb-token-bridge-ui/synpress*.ts" ] } From 4334b658a136358eef4f973f3af1b7352056ffe1 Mon Sep 17 00:00:00 2001 From: Christophe Date: Mon, 26 Aug 2024 09:45:41 +0000 Subject: [PATCH 02/14] Fix approve token test --- packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dedd71466d..fcd048774e 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 @@ -40,7 +40,7 @@ describe('Approve token for deposit', () => { cy.findByRole('button', { name: /Pay approval fee of/ }).click() - cy.confirmMetamaskPermissionToSpend('1') + cy.confirmSpending({ spendLimit: '1' }) }) }) }) From ffb579bcf2fe38ecbf073cc2314ba37e63b0f350 Mon Sep 17 00:00:00 2001 From: Christophe Date: Mon, 26 Aug 2024 09:46:03 +0000 Subject: [PATCH 03/14] Minimize diff --- packages/arb-token-bridge-ui/tests/support/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/tests/support/index.ts b/packages/arb-token-bridge-ui/tests/support/index.ts index e9fc25e037..fbb4e83260 100644 --- a/packages/arb-token-bridge-ui/tests/support/index.ts +++ b/packages/arb-token-bridge-ui/tests/support/index.ts @@ -26,7 +26,7 @@ logCollector({ before(() => { // connect to sepolia to avoid connecting to localhost twice and failing - cy.setupMetamask(Cypress.env('PRIVATE_KEY'), 'mainnet') + cy.setupMetamask(Cypress.env('PRIVATE_KEY'), 'sepolia') .task('getNetworkSetupComplete') .then(complete => { if (!complete) { From 4b51541b76a2b2d2bce021cb94246d7375ad4a0f Mon Sep 17 00:00:00 2001 From: Christophe Date: Mon, 26 Aug 2024 09:55:39 +0000 Subject: [PATCH 04/14] Simplify funding --- .../synpress.cctp.config.ts | 114 +++++++----------- 1 file changed, 43 insertions(+), 71 deletions(-) diff --git a/packages/arb-token-bridge-ui/synpress.cctp.config.ts b/packages/arb-token-bridge-ui/synpress.cctp.config.ts index e08b6405b9..018b19c268 100644 --- a/packages/arb-token-bridge-ui/synpress.cctp.config.ts +++ b/packages/arb-token-bridge-ui/synpress.cctp.config.ts @@ -11,6 +11,7 @@ import { getCustomDestinationAddress } from './tests/support/common' import specFiles from './tests/e2e/cctp.json' +import { sepolia } from 'wagmi' const shouldRecordVideo = process.env.CYPRESS_RECORD_VIDEO === 'true' @@ -50,6 +51,40 @@ async function fundWallets() { const userWalletAddress = await userWallet.getAddress() console.log(`Funding wallet ${userWalletAddress}`) + const fundEthHelper = (network: 'sepolia' | 'arbSepolia') => { + return () => + fundEth({ + address: userWalletAddress, + sourceWallet: localWallet, + ...(network === 'sepolia' + ? { + provider: sepoliaProvider, + amount: ethAmountSepolia + } + : { + provider: arbSepoliaProvider, + amount: ethAmountArbSepolia + }) + }) + } + const fundUsdcHelper = (network: 'sepolia' | 'arbSepolia') => { + return () => + fundUsdc({ + address: userWalletAddress, + sourceWallet: localWallet, + amount: usdcAmount, + ...(network === 'sepolia' + ? { + provider: sepoliaProvider, + networkType: 'parentChain' + } + : { + provider: arbSepoliaProvider, + networkType: 'childChain' + }) + }) + } + /** * We need 0.0002 USDC per test (0.0001 for same address and 0.0001 for custom address) * And in the worst case, we run each tests 3 time @@ -60,77 +95,14 @@ async function fundWallets() { const ethPromises: (() => Promise)[] = [] const usdcPromises: (() => Promise)[] = [] - if (tests.length === 2) { - ethPromises.push(() => - fundEth({ - address: userWalletAddress, - provider: sepoliaProvider, - sourceWallet: localWallet, - amount: ethAmountSepolia - }) - ) - ethPromises.push(() => - fundEth({ - address: userWalletAddress, - provider: arbSepoliaProvider, - sourceWallet: localWallet, - amount: ethAmountArbSepolia - }) - ) - usdcPromises.push( - () => - fundUsdc({ - address: userWalletAddress, - provider: sepoliaProvider, - networkType: 'parentChain', - sourceWallet: localWallet, - amount: usdcAmount - }), - () => - fundUsdc({ - address: userWalletAddress, - provider: arbSepoliaProvider, - networkType: 'childChain', - sourceWallet: localWallet, - amount: usdcAmount - }) - ) - } else if (tests[0].includes('deposit')) { - ethPromises.push(() => - fundEth({ - address: userWalletAddress, - provider: sepoliaProvider, - sourceWallet: localWallet, - amount: ethAmountSepolia - }) - ) - usdcPromises.push(() => - fundUsdc({ - address: userWalletAddress, - provider: sepoliaProvider, - networkType: 'parentChain', - sourceWallet: localWallet, - amount: usdcAmount - }) - ) - } else { - ethPromises.push(() => - fundEth({ - address: userWalletAddress, - provider: arbSepoliaProvider, - sourceWallet: localWallet, - amount: ethAmountArbSepolia - }) - ) - usdcPromises.push(() => - fundUsdc({ - address: userWalletAddress, - provider: arbSepoliaProvider, - networkType: 'childChain', - sourceWallet: localWallet, - amount: usdcAmount - }) - ) + if (tests.some(testFile => testFile.includes('deposit'))) { + ethPromises.push(fundEthHelper('sepolia')) + usdcPromises.push(fundUsdcHelper('sepolia')) + } + + if (tests.some(testFile => testFile.includes('withdraw'))) { + ethPromises.push(fundEthHelper('arbSepolia')) + usdcPromises.push(fundUsdcHelper('arbSepolia')) } await Promise.all(ethPromises.map(fn => fn())) From cb4af3b1fb703da574ff8bb557d52a51de64124c Mon Sep 17 00:00:00 2001 From: Christophe Date: Mon, 26 Aug 2024 10:03:29 +0000 Subject: [PATCH 05/14] Add changes in AdvancedSettigns --- .../src/components/TransferPanel/AdvancedSettings.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx index ca44aef3ff..a6fb791c63 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx @@ -263,6 +263,7 @@ export const AdvancedSettings = () => { onChange={e => setDestinationAddress(e.target.value?.toLowerCase().trim()) } + aria-label="Custom Destination Address Input" /> {isEOA && (