diff --git a/e2e/scripts/check-e2e-wallet-balance.ts b/e2e/scripts/check-e2e-wallet-balance.ts index 6b3d39e6df0..2d448961a3c 100644 --- a/e2e/scripts/check-e2e-wallet-balance.ts +++ b/e2e/scripts/check-e2e-wallet-balance.ts @@ -1,7 +1,11 @@ -import { E2E_TEST_WALLET } from './consts' +import { E2E_TEST_WALLET, E2E_TEST_WALLET_SECURE_SEND } from './consts' import { checkBalance, getBalance } from './utils' ;(async () => { console.log(E2E_TEST_WALLET) console.table(await getBalance(E2E_TEST_WALLET)) await checkBalance(E2E_TEST_WALLET) + + console.log(E2E_TEST_WALLET_SECURE_SEND) + console.table(await getBalance(E2E_TEST_WALLET_SECURE_SEND)) + await checkBalance(E2E_TEST_WALLET_SECURE_SEND) })() diff --git a/e2e/scripts/consts.ts b/e2e/scripts/consts.ts index 93982baf142..7fb65ff556f 100644 --- a/e2e/scripts/consts.ts +++ b/e2e/scripts/consts.ts @@ -1,2 +1,4 @@ export const E2E_TEST_WALLET = '0x6131a6d616a4be3737b38988847270a64bc10caa' +export const E2E_TEST_WALLET_SECURE_SEND = '0x86b8f44386cb2d457db79c3dab8cf42f9d8a3fc0' export const E2E_TEST_FAUCET = '0xe5F5363e31351C38ac82DBAdeaD91Fd5a7B08846' +export const REFILL_TOKENS = ['CELO', 'cUSD', 'cEUR'] diff --git a/e2e/scripts/fund-e2e-accounts.ts b/e2e/scripts/fund-e2e-accounts.ts index 2fd13fa492d..40827d60606 100644 --- a/e2e/scripts/fund-e2e-accounts.ts +++ b/e2e/scripts/fund-e2e-accounts.ts @@ -1,7 +1,12 @@ import { newKitFromWeb3, StableToken } from '@celo/contractkit' import dotenv from 'dotenv' import Web3 from 'web3' -import { E2E_TEST_FAUCET, E2E_TEST_WALLET } from './consts' +import { + E2E_TEST_FAUCET, + E2E_TEST_WALLET, + E2E_TEST_WALLET_SECURE_SEND, + REFILL_TOKENS, +} from './consts' import { checkBalance, getBalance } from './utils' dotenv.config({ path: `${__dirname}/../.env` }) @@ -11,10 +16,16 @@ const kit = newKitFromWeb3(web3) const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! ;(async () => { - // Get E2E Test Wallet Balance & Valora Faucet Balance - const receivingBalance = await getBalance(E2E_TEST_WALLET) - const sendingBalance = (await getBalance(E2E_TEST_FAUCET)) ?? {} - console.table(await getBalance(E2E_TEST_FAUCET)) + const walletsToBeFunded = [E2E_TEST_WALLET, E2E_TEST_WALLET_SECURE_SEND] + const walletBalances = await Promise.all(walletsToBeFunded.map(getBalance)) + for (let i = 0; i < walletsToBeFunded.length; i++) { + console.log(`Initial balance for ${walletsToBeFunded[i]}:`) + console.table(walletBalances[i]) + } + + const faucetTokenBalances = (await getBalance(E2E_TEST_FAUCET)) ?? {} + console.log('Initial faucet balance:') + console.table(faucetTokenBalances) // Connect Valora E2E Test Faucet - Private Key Stored in GitHub Secrets kit.connection.addAccount( @@ -30,24 +41,27 @@ const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! const ceurExchange = await kit.contracts.getExchange(StableToken.cEUR) // Balance Faucet - let targetTokenRegex = /[cusd|celo|ceur]/gi - let balances = await getBalance(E2E_TEST_FAUCET) - let sum = 0 - const numOfTokens = 3 // Only cusd, celo and ceur - for (let balance in balances) { - console.log(`${balance}: ${balances[balance]}`) - // Sum only the target balances - if (targetTokenRegex.test(balance)) sum += balances[balance] - } + let totalTokenHoldings = 0 // the absolute number of faucet tokens the faucet is holding + Object.entries(faucetTokenBalances).forEach(([tokenSymbol, tokenBalance]) => { + if (REFILL_TOKENS.includes(tokenSymbol)) { + totalTokenHoldings += tokenBalance + } + }) + const targetFaucetTokenBalance = totalTokenHoldings / REFILL_TOKENS.length - for (let balance in balances) { - const target = sum / numOfTokens - if (balances[balance] >= sum / numOfTokens) { - const toSell = balances[balance] - target - console.log(toSell) - const amountToExchange = kit.web3.utils.toWei(`${toSell}`, 'ether') - console.log(`${balance} balance higher than ${sum / numOfTokens}`) - switch (balance) { + // Ensure that the faucet has enough balance for each refill tokens + for (const [tokenSymbol, tokenBalance] of Object.entries(faucetTokenBalances)) { + if (!REFILL_TOKENS.includes(tokenSymbol)) { + continue + } + + if (tokenBalance >= targetFaucetTokenBalance) { + const sellAmount = tokenBalance - targetFaucetTokenBalance + const amountToExchange = kit.web3.utils.toWei(`${sellAmount}`, 'ether') + console.log( + `${tokenSymbol} balance is ${tokenBalance}, which is higher than target ${targetFaucetTokenBalance}. Selling ${sellAmount}.` + ) + switch (tokenSymbol) { case 'CELO': try { const celoApproveTx = await celoToken @@ -98,10 +112,12 @@ const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! } } } else { - const toBuy = target - balances[balance] - const amountToExchange = kit.web3.utils.toWei(`${toBuy}`, 'ether') - console.log(`${balance} balance lower than ${sum / numOfTokens}`) - switch (balance) { + const buyAmount = targetFaucetTokenBalance - tokenBalance + const amountToExchange = kit.web3.utils.toWei(`${buyAmount}`, 'ether') + console.log( + `${tokenSymbol} balance is ${tokenBalance}, which is lower than target ${targetFaucetTokenBalance}. Buying ${buyAmount}.` + ) + switch (tokenSymbol) { case 'CELO': try { const celoApproveTx = await celoToken @@ -155,42 +171,51 @@ const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! } // Set Amount To Send - let amountToSend = web3.utils.toWei('100', 'ether') + const amountToSend = '100' + const amountToSendWei = web3.utils.toWei(amountToSend, 'ether') - // Loop through E2E Test Wallet Balance Object - for (const coin in receivingBalance) { - let tx: any - // Add funds if balance is less than 100 add 100 - if (receivingBalance[coin] < 200 && sendingBalance[coin] > 100) { - switch (coin) { - case 'CELO': - tx = await celoToken - .transfer(E2E_TEST_WALLET, amountToSend) - .send({ from: E2E_TEST_FAUCET }) - break - case 'cUSD': - tx = await cusdToken - .transfer(E2E_TEST_WALLET, amountToSend) - .send({ from: E2E_TEST_FAUCET }) - break - case 'cEUR': - tx = await ceurToken - .transfer(E2E_TEST_WALLET, amountToSend) - .send({ from: E2E_TEST_FAUCET }) - break - } - // Wait for the transactions to be processed - let receipt = await tx.waitReceipt() + for (let i = 0; i < walletsToBeFunded.length; i++) { + const walletAddress = walletsToBeFunded[i] + const walletBalance = walletBalances[i] + for (const tokenSymbol of REFILL_TOKENS) { + if (walletBalance && walletBalance[tokenSymbol] < 200) { + console.log(`Sending ${amountToSend} ${tokenSymbol} to ${walletAddress}`) + let tx: any + switch (tokenSymbol) { + case 'CELO': + tx = await celoToken + .transfer(walletAddress, amountToSendWei) + .send({ from: E2E_TEST_FAUCET }) + break + case 'cUSD': + tx = await cusdToken + .transfer(walletAddress, amountToSendWei) + .send({ from: E2E_TEST_FAUCET }) + break + case 'cEUR': + tx = await ceurToken + .transfer(walletAddress, amountToSendWei) + .send({ from: E2E_TEST_FAUCET }) + break + } + const receipt = await tx.waitReceipt() - // Print Receipt - console.log(' Transaction receipt: %o', receipt) + console.log( + `Received tx hash ${receipt.transactionHash} for ${tokenSymbol} transfer to ${walletAddress}` + ) + } } } + console.log('Finished funding wallets.') // Log Balances console.log('E2E Test Account:', E2E_TEST_WALLET) console.table(await getBalance(E2E_TEST_WALLET)) + console.log('E2E Test Account Secure Send:', E2E_TEST_WALLET_SECURE_SEND) + console.table(await getBalance(E2E_TEST_WALLET_SECURE_SEND)) console.log('Valora Test Facuet:', E2E_TEST_FAUCET) console.table(await getBalance(E2E_TEST_FAUCET)) + await checkBalance(E2E_TEST_WALLET) + await checkBalance(E2E_TEST_WALLET_SECURE_SEND) })() diff --git a/e2e/scripts/utils.ts b/e2e/scripts/utils.ts index cc94f0eb6a0..fff5bff4d3a 100644 --- a/e2e/scripts/utils.ts +++ b/e2e/scripts/utils.ts @@ -1,13 +1,19 @@ +import { REFILL_TOKENS } from './consts' + export const Web3 = require('web3') export const ContractKit = require('@celo/contractkit') export const dotenv = require('dotenv') export const web3 = new Web3('https://alfajores-forno.celo-testnet.org') export const kit = ContractKit.newKitFromWeb3(web3) -export async function checkBalance(address: string, minBalance = 10) { - let balanceObject = await getBalance(address) - for (const balance in balanceObject) { - if (balanceObject[balance as keyof typeof balanceObject] < minBalance) { +export async function checkBalance( + address: string, + minBalance = 10, + tokenSymbols: string[] = REFILL_TOKENS +) { + const balance = (await getBalance(address)) ?? {} + for (const [tokenSymbol, tokenBalance] of Object.entries(balance)) { + if (tokenSymbols.includes(tokenSymbol) && tokenBalance < minBalance) { throw new Error( `${balance} balance of ${address} is below ${minBalance}. Please refill from the faucet https://celo.org/developers/faucet or run ./fund-e2e-accounts.ts if a Valora Dev.` ) diff --git a/e2e/src/Send.spec.js b/e2e/src/Send.spec.js index 2ae52359b24..ebf8975403d 100644 --- a/e2e/src/Send.spec.js +++ b/e2e/src/Send.spec.js @@ -1,13 +1,7 @@ import SecureSend from './usecases/SecureSend' import Send from './usecases/Send' -import { quickOnboarding } from './utils/utils' describe('Given', () => { - beforeAll(async () => { - await quickOnboarding() - }) - describe('Send', Send) - // TODO: unskip this test if we enable CPV in CI - describe.skip('SecureSend cUSD', SecureSend) + describe('SecureSend with CPV', SecureSend) }) diff --git a/e2e/src/usecases/SecureSend.js b/e2e/src/usecases/SecureSend.js index c250bc08e3e..e788e54b409 100644 --- a/e2e/src/usecases/SecureSend.js +++ b/e2e/src/usecases/SecureSend.js @@ -1,86 +1,92 @@ -import { reloadReactNative } from '../utils/retries' +import { + SAMPLE_BACKUP_KEY_VERIFIED, + VERIFIED_PHONE_NUMBER, + SAMPLE_WALLET_ADDRESS_VERIFIED_2, +} from '../utils/consts' +import { launchApp } from '../utils/retries' import { addComment, enterPinUiIfNecessary, inputNumberKeypad, scrollIntoView, - sleep, + quickOnboarding, } from '../utils/utils' const faker = require('@faker-js/faker') -const PHONE_NUMBER = '+12057368924' -const LAST_ACCOUNT_CHARACTERS = 'FD08' -const AMOUNT_TO_SEND = '0.1' +const AMOUNT_TO_SEND = '0.01' export default SecureSend = () => { - beforeEach(async () => { - await reloadReactNative() - }) - - it('Send cUSD to phone number with multiple mappings', async () => { - let randomContent = faker.lorem.words() - await waitFor(element(by.id('HomeAction-Send'))) - .toBeVisible() - .withTimeout(30000) - await element(by.id('HomeAction-Send')).tap() - - // Look for an address and tap on it. - await element(by.id('SearchInput')).tap() - await element(by.id('SearchInput')).replaceText(PHONE_NUMBER) - await element(by.id('RecipientItem')).tap() + describe.each([{ web3Library: 'contract-kit' }, { web3Library: 'viem' }])( + 'Secure send flow with phone number lookup (with $web3Library)', + ({ web3Library }) => { + beforeAll(async () => { + // uninstall the app to remove secure send mapping + await device.uninstallApp() + await device.installApp() + await launchApp({ + newInstance: true, + permissions: { notifications: 'YES', contacts: 'YES' }, + launchArgs: { + statsigGateOverrides: `use_new_send_flow=false,use_viem_for_send=${ + web3Library === 'viem' + }`, + }, + }) + await quickOnboarding(SAMPLE_BACKUP_KEY_VERIFIED) + }) - // Select the currency - await waitFor(element(by.id('cUSDBalance'))) - .toBeVisible() - .withTimeout(30 * 1000) - await element(by.id('cUSDBalance')).tap() + it('Send cUSD to phone number with multiple mappings', async () => { + let randomContent = faker.lorem.words() + await waitFor(element(by.id('HomeAction-Send'))) + .toBeVisible() + .withTimeout(30000) + await element(by.id('HomeAction-Send')).tap() + await waitFor(element(by.id('SendSearchInput'))).toBeVisible() - // Enter the amount and review - await inputNumberKeypad(AMOUNT_TO_SEND) - await element(by.id('Review')).tap() + await element(by.id('SearchInput')).tap() + await element(by.id('SearchInput')).replaceText(VERIFIED_PHONE_NUMBER) + await element(by.id('RecipientItem')).tap() - // hack: we shouldn't need this but the test fails without - await sleep(3000) + // Select the currency + await waitFor(element(by.id('cUSDTouchable'))).toBeVisible() + await element(by.id('cUSDTouchable')).tap() - // Click Edit if confirm account isn't served - try { - await element(by.id('accountEditButton')).tap() - } catch {} + // Enter the amount and review + await inputNumberKeypad(AMOUNT_TO_SEND) + await element(by.id('Review')).tap() - // Use the last digits of the account to confirm the sender. - await waitFor(element(by.id('confirmAccountButton'))) - .toBeVisible() - .withTimeout(30000) - await element(by.id('confirmAccountButton')).tap() - for (let index = 0; index < 4; index++) { - const character = LAST_ACCOUNT_CHARACTERS[index] - await element(by.id(`SingleDigitInput/digit${index}`)).replaceText(character) - } - - // Scroll to see submit button - await scrollIntoView('Submit', 'KeyboardAwareScrollView', 50) - await element(by.id('ConfirmAccountButton')).tap() + // Use the last digits of the account to confirm the sender. + await waitFor(element(by.id('confirmAccountButton'))).toBeVisible() + await element(by.id('confirmAccountButton')).tap() + // TODO: test case for AddressValidationType.PARTIAL but relies on mapping phone number to another address with unique last 4 digits + // for (let index = 0; index < 4; index++) { + // const character = LAST_ACCOUNT_CHARACTERS[index] + // await element(by.id(`SingleDigitInput/digit${index}`)).replaceText(character) + // } + await element(by.id('ValidateRecipientAccount/TextInput')).replaceText( + SAMPLE_WALLET_ADDRESS_VERIFIED_2 + ) - // Write a comment. - await addComment(randomContent) + // Scroll to see submit button + await scrollIntoView('Submit', 'KeyboardAwareScrollView', 50) + await element(by.id('ConfirmAccountButton')).tap() - // Wait for the confirm button to be clickable. If it takes too long this test - // will be flaky :( - await sleep(3000) + // Write a comment. + await addComment(randomContent) - // Confirm and input PIN if necessary. - await element(by.id('ConfirmButton')).tap() - await enterPinUiIfNecessary() + // Confirm and input PIN if necessary. + await element(by.id('ConfirmButton')).tap() + await enterPinUiIfNecessary() - // Return to home screen. - await waitFor(element(by.id('HomeAction-Send'))) - .toBeVisible() - .withTimeout(30 * 1000) + // Return to home screen. + await waitFor(element(by.id('HomeAction-Send'))) + .toBeVisible() + .withTimeout(30 * 1000) - // TODO: See why these are taking so long in e2e tests to appear - // Look for the latest transaction and assert - // await waitFor(element(by.text(`${randomContent}`))) - // .toBeVisible() - // .withTimeout(60000) - }) + await waitFor(element(by.text(`${randomContent}`))) + .toBeVisible() + .withTimeout(60000) + }) + } + ) } diff --git a/e2e/src/usecases/Send.js b/e2e/src/usecases/Send.js index f2731884762..0bb978c7072 100644 --- a/e2e/src/usecases/Send.js +++ b/e2e/src/usecases/Send.js @@ -1,10 +1,19 @@ import { DEFAULT_RECIPIENT_ADDRESS } from '../utils/consts' import { launchApp, reloadReactNative } from '../utils/retries' -import { enterPinUiIfNecessary, inputNumberKeypad, addComment } from '../utils/utils' +import { + enterPinUiIfNecessary, + inputNumberKeypad, + addComment, + quickOnboarding, +} from '../utils/utils' const faker = require('@faker-js/faker') import jestExpect from 'expect' export default Send = () => { + beforeAll(async () => { + await quickOnboarding() + }) + let randomComment = faker.lorem.words() describe.each([{ web3Library: 'contract-kit' }, { web3Library: 'viem' }])( 'When multi-token send flow (with $web3Library)', diff --git a/e2e/src/utils/consts.js b/e2e/src/utils/consts.js index 1ee7f6e0a26..ae33024e1b9 100644 --- a/e2e/src/utils/consts.js +++ b/e2e/src/utils/consts.js @@ -7,6 +7,10 @@ export const SAMPLE_WALLET_ADDRESS = '0x6131a6d616a4be3737b38988847270a64bc10caa export const SAMPLE_BACKUP_KEY_VERIFIED = 'embody siege middle glory soda solar nasty report swap now never any' export const SAMPLE_WALLET_ADDRESS_VERIFIED = '0x86b8f44386cb2d457db79c3dab8cf42f9d8a3fc0' // corresponds to the backup key above +export const SAMPLE_BACKUP_KEY_VERIFIED_2 = + 'bench album relax truth pond orchard diet unaware cloud tackle twin tongue' +export const SAMPLE_WALLET_ADDRESS_VERIFIED_2 = '0x5fe1407f47b1310ff232a8d368b36099eff61604' // corresponds to the backup key above +export const VERIFIED_PHONE_NUMBER = '+15203140983' // all verified addresses are using this number export const SAMPLE_BACKUP_KEY_12_WORDS = 'cheese size muscle either false spend price lunar undo share kite whisper' export const SAMPLE_WALLET_ADDRESS_12_WORDS = '0x19cf37810fd5933bd63e02388e37203841c703de' // corresponds to the backup key above diff --git a/e2e/src/utils/utils.js b/e2e/src/utils/utils.js index fefa17f0510..338ad6bfb7c 100644 --- a/e2e/src/utils/utils.js +++ b/e2e/src/utils/utils.js @@ -201,10 +201,17 @@ export async function quickOnboarding(mnemonic = SAMPLE_BACKUP_KEY) { await element(by.id('ConfirmUseAccountDialog/PrimaryAction')).tap() } catch {} - // Verify Education - await waitForElementId('PhoneVerificationSkipHeader') - // Skip - await element(by.id('PhoneVerificationSkipHeader')).tap() + // this onboarding step is bypassed for already verified wallets + try { + // Verify Education + await waitForElementId('PhoneVerificationSkipHeader') + // Skip + await element(by.id('PhoneVerificationSkipHeader')).tap() + } catch { + console.log( + 'Error trying to skip phone verification step during onboarding, likely due to wallet already being verified' + ) + } // Assert on Wallet Home Screen await dismissCashInBottomSheet() diff --git a/scripts/fund_e2e_account.sh b/scripts/fund_e2e_account.sh deleted file mode 100755 index eaca1f13484..00000000000 --- a/scripts/fund_e2e_account.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env sh -set -euo pipefail - -CURRENT_CONFIG=$(celocli config:get | awk '{print $2 }') -celocli config:set -n https://alfajores-forno.celo-testnet.org - -TEST_ACCOUNT=0x6131a6d616a4be3737b38988847270a64bc10caa - -echo "Verifying balancess of test account: $TEST_ACCOUNT" - -celocli account:balance $TEST_ACCOUNT >> balance.tmp -WEI_DECIMALS=$((10**18)) -printf -v USD_BALANCE "%.f" "$(grep usd balance.tmp | awk -v wei_decimals="$WEI_DECIMALS" '{print $2 / wei_decimals}')" -printf -v CELO_BALANCE "%.f" "$(grep gold balance.tmp | awk -v wei_decimals="$WEI_DECIMALS" '{print $2 / wei_decimals}')" - -echo "cUSD balance: $USD_BALANCE" -echo "CELO balance: $CELO_BALANCE" - -if [ "$USD_BALANCE" -lt "200" ]; then - echo "Adding USD to test account" - celotooljs account faucet --celo-env alfajores --account $TEST_ACCOUNT --dollar 1000 --yesreally -fi -if [ "$CELO_BALANCE" -lt "200" ]; then - echo "Adding CELO to test account" - celotooljs account faucet --celo-env alfajores --account $TEST_ACCOUNT --gold 1000 --yesreally -fi - -echo "Restoring celocli to use $CURRENT_CONFIG" -celocli config:set -n $CURRENT_CONFIG - -rm balance.tmp