Skip to content

Commit

Permalink
Custom gas e2e (#1318)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Sinclair <[email protected]>
  • Loading branch information
BrodyHughes and DanielSinclair authored Feb 20, 2024
1 parent 8cf0ab1 commit 41c7e6f
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 59 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ jobs:
- uses: geekyeggo/delete-artifact@v2
with:
name: node_modules.tar.gz
- name: Anvil Cleanup
run: yarn anvil:kill
- uses: geekyeggo/delete-artifact@v2
with:
name: screenshots
74 changes: 73 additions & 1 deletion e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as fs from 'node:fs';

import { Contract } from '@ethersproject/contracts';
import { getDefaultProvider } from '@ethersproject/providers';
import { ethers } from 'ethers';
import {
Builder,
By,
Expand Down Expand Up @@ -736,13 +737,84 @@ export async function getOnchainBalance(addy: string, contract: string) {
}
}

async function fetchLatestTransactionHash(
provider: ethers.providers.JsonRpcProvider,
): Promise<string | null> {
const latestBlock = await provider.getBlockWithTransactions('latest');
if (latestBlock.transactions.length === 0) return null;
return latestBlock.transactions[latestBlock.transactions.length - 1].hash;
}

async function validateTransactionGasSettings(
transactionHash: string,
provider: ethers.providers.JsonRpcProvider,
expectedMaxPriorityFeePerGasInGwei: number,
expectedBaseFeeInGwei: number,
): Promise<void> {
if (!transactionHash) throw new Error('No transaction hash provided.');
const transaction = await provider.getTransaction(transactionHash);
if (!transaction) throw new Error('Transaction not found.');

const expectedTotalMaxFeePerGasInGwei =
expectedMaxPriorityFeePerGasInGwei + expectedBaseFeeInGwei;
const expectedMaxPriorityFeePerGasWei = ethers.utils.parseUnits(
expectedMaxPriorityFeePerGasInGwei.toString(),
'gwei',
);
const expectedTotalMaxFeePerGasWei = ethers.utils.parseUnits(
expectedTotalMaxFeePerGasInGwei.toString(),
'gwei',
);

const actualMaxPriorityFeePerGas = transaction.maxPriorityFeePerGas;
const actualMaxFeePerGas = transaction.maxFeePerGas;

if (
!actualMaxPriorityFeePerGas ||
!actualMaxFeePerGas ||
!actualMaxPriorityFeePerGas.eq(expectedMaxPriorityFeePerGasWei) ||
!actualMaxFeePerGas.eq(expectedTotalMaxFeePerGasWei)
) {
throw new Error(
`Gas settings mismatch. Expected maxPriorityFeePerGas: ${expectedMaxPriorityFeePerGasInGwei} gwei, expected total maxFeePerGas (Max Base Fee + Miner Tip): ${expectedTotalMaxFeePerGasInGwei} gwei, but got maxPriorityFeePerGas: ${
actualMaxPriorityFeePerGas
? ethers.utils.formatUnits(actualMaxPriorityFeePerGas, 'gwei')
: 'none'
} gwei, maxFeePerGas: ${
actualMaxFeePerGas
? ethers.utils.formatUnits(actualMaxFeePerGas, 'gwei')
: 'none'
} gwei.`,
);
}
}

export async function verifyCustomGasSettings(
maxBaseFee: number,
minerTip: number,
): Promise<void> {
const provider = new ethers.providers.JsonRpcProvider(
'http://127.0.0.1:8545',
);
const transactionHash = await fetchLatestTransactionHash(provider);
if (transactionHash) {
await validateTransactionGasSettings(
transactionHash,
provider,
maxBaseFee,
minerTip,
);
}
}

export async function transactionStatus() {
const provider = getDefaultProvider('http://127.0.0.1:8545');
const blockData = await provider.getBlock('latest');
const txnReceipt = await provider.getTransactionReceipt(
blockData.transactions[0],
blockData.transactions[blockData.transactions.length - 1],
);
const txnStatus = txnReceipt.status === 1 ? 'success' : 'failure';

return txnStatus;
}

Expand Down
10 changes: 10 additions & 0 deletions e2e/serial/optimismTransactions/1_sendFlow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
checkWalletName,
delay,
delayTime,
doNotFindElementByTestId,
executePerformShortcut,
findElementByTestId,
findElementByText,
Expand Down Expand Up @@ -121,6 +122,15 @@ describe('Complete Hardhat Optimism send flow', () => {
expect(Number(valueNum)).toBe(0);
});

it('should not be able to open up either of the gas menu options', async () => {
await doNotFindElementByTestId({ id: 'gas-menu', driver });
await doNotFindElementByTestId({ id: 'custom-gas-menu', driver });
await executePerformShortcut({ driver, key: 'C' });
await doNotFindElementByTestId({ id: 'custom-gas-sheet', driver });
await executePerformShortcut({ driver, key: 'G' });
await doNotFindElementByTestId({ id: 'transaction-speed-title', driver });
});

it('should be able to initiate Optimisim ETH transaction', async () => {
await driver.actions().sendKeys('1').perform();
const value = await findElementByTestId({ id: 'send-input-mask', driver });
Expand Down
94 changes: 93 additions & 1 deletion e2e/serial/send/1_sendFlow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from 'vitest';

import {
delayTime,
executePerformShortcut,
findElementById,
findElementByIdAndClick,
findElementByTestId,
Expand All @@ -21,6 +22,7 @@ import {
takeScreenshotOnFailure,
transactionStatus,
typeOnTextInput,
verifyCustomGasSettings,
waitAndClick,
} from '../../helpers';
import { TEST_VARIABLES } from '../../walletVariables';
Expand Down Expand Up @@ -206,6 +208,95 @@ it('should be able to click max and switch on send flow', async () => {
await inputMask.sendKeys('0.01');
});

it('should be able to open gas dropdown via shortcut', async () => {
await delayTime('long');
await executePerformShortcut({ driver, key: 'g' });
const txnSpeed1 = await findElementByText(driver, 'Transaction Speed');
expect(txnSpeed1).toBeTruthy();
await executePerformShortcut({ driver, key: 'ESCAPE' });
});

it('should be able to switch gas prices via dropdown on send flow', async () => {
await findElementByTestIdAndClick({ id: 'gas-menu', driver });
const txnSpeed2 = await findElementByText(driver, 'Transaction Speed');
expect(txnSpeed2).toBeTruthy();
await findElementByTextAndClick(driver, 'Urgent');
await delayTime('medium');
const urgent = await findElementByText(driver, 'Urgent');
expect(urgent).toBeTruthy();
});

it('should be able to open custom gas sheet via shortcut', async () => {
await delayTime('long');
await executePerformShortcut({ driver, key: 'c' });
const gasSettings1 = await findElementByText(driver, 'Gas Settings');
expect(gasSettings1).toBeTruthy();
await executePerformShortcut({ driver, key: 'ESCAPE' });
});

it('should be able to open up the custom gas menu on the send flow', async () => {
await findElementByTestIdAndClick({ id: 'custom-gas-menu', driver });
const gasSettings = await findElementByText(driver, 'Gas Settings');
expect(gasSettings).toBeTruthy();
});

it('should be able to open up the explainers on the custom gas menu', async () => {
// explainer 1
await findElementByTestIdAndClick({
id: 'current-base-fee-explainer',
driver,
});
await delayTime('short');
const current = await findElementByText(driver, 'The base fee is');
expect(current).toBeTruthy();
await findElementByTestIdAndClick({ id: 'explainer-action-button', driver });

// explainer 2
await findElementByTestIdAndClick({ id: 'max-base-fee-explainer', driver });
await delayTime('short');
const max = await findElementByText(driver, 'This is the maximum');
expect(max).toBeTruthy();
await findElementByTestIdAndClick({ id: 'explainer-action-button', driver });

// explainer 3
await findElementByTestIdAndClick({
id: 'max-priority-fee-explainer',
driver,
});
await delayTime('short');
const miner = await findElementByText(driver, 'The miner tip goes');
expect(miner).toBeTruthy();
await findElementByTestIdAndClick({ id: 'explainer-action-button', driver });
});

it('should be able to customize gas', async () => {
await delayTime('short');
await executePerformShortcut({ driver, key: 'TAB' });
await executePerformShortcut({ driver, key: 'BACK_SPACE', timesToPress: 5 });
await driver.actions().sendKeys('400').perform();
await delayTime('short');
await executePerformShortcut({ driver, key: 'TAB' });
await executePerformShortcut({ driver, key: 'BACK_SPACE', timesToPress: 5 });
await driver.actions().sendKeys('400').perform();
const baseFeeGweiInputMask = await querySelector(
driver,
"[data-testid='max-base-fee-input'] [data-testid='gwei-input-mask']",
);

expect(await baseFeeGweiInputMask.getAttribute('value')).toContain('400');

const minerTipGweiInputMask = await querySelector(
driver,
"[data-testid='miner-tip-input'] [data-testid='gwei-input-mask']",
);

expect(await minerTipGweiInputMask.getAttribute('value')).toContain('400');
await findElementByTestIdAndClick({ id: 'set-gas-button', driver });

const gasMenu = await findElementByTestId({ id: 'gas-menu', driver });
expect(await gasMenu.getText()).toContain('Custom');
});

it('should be able to go to review on send flow', async () => {
await findElementByTestIdAndClick({ id: 'send-review-button', driver });
});
Expand Down Expand Up @@ -236,7 +327,8 @@ it('should be able to interact with destination menu on review on send flow', as
it('should be able to send transaction on review on send flow', async () => {
await findElementByTestIdAndClick({ id: 'review-confirm-button', driver });
const sendTransaction = await transactionStatus();
expect(await sendTransaction).toBe('success');
expect(sendTransaction).toBe('success');
await verifyCustomGasSettings(400, 400);
});

it('should be able to rename a wallet from the wallet switcher', async () => {
Expand Down
32 changes: 15 additions & 17 deletions e2e/serial/swap/2_swapFlow2.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import 'chromedriver';
import 'geckodriver';
import { Contract } from '@ethersproject/contracts';
import { StaticJsonRpcProvider } from '@ethersproject/providers';
import { WebDriver } from 'selenium-webdriver';
import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from 'vitest';
import { erc20ABI } from 'wagmi';

import { ChainId } from '~/core/types/chains';

Expand All @@ -17,13 +14,15 @@ import {
findElementByTestIdAndClick,
findElementByText,
getExtensionIdByName,
getOnchainBalance,
getRootUrl,
getTextFromText,
goToPopup,
goToWelcome,
initDriverWithOptions,
querySelector,
takeScreenshotOnFailure,
transactionStatus,
typeOnTextInput,
waitAndClick,
waitUntilElementByTestIdIsPresent,
Expand Down Expand Up @@ -143,17 +142,11 @@ it('should be able to go to review a unlock and swap', async () => {
});

it('should be able to execute unlock and swap', async () => {
const provider = new StaticJsonRpcProvider('http://127.0.0.1:8545');
await provider.ready;
await delayTime('short');
const tokenContract = new Contract(
SWAP_VARIABLES.USDC_MAINNET_ADDRESS,
erc20ABI,
provider,
);
const usdcBalanceBeforeSwap = await tokenContract.balanceOf(
const balanceBefore = await getOnchainBalance(
WALLET_TO_USE_ADDRESS,
SWAP_VARIABLES.USDC_MAINNET_ADDRESS,
);
console.log(`Balance before swap: ${balanceBefore}`);

await findElementByTestIdAndClick({
id: 'swap-settings-navbar-button',
Expand Down Expand Up @@ -184,20 +177,25 @@ it('should be able to execute unlock and swap', async () => {
});
await delayTime('long');
await findElementByTestIdAndClick({ id: 'swap-review-execute', driver });
await delayTime('very-long');
await delayTime('very-long');
const usdcBalanceAfterSwap = await tokenContract.balanceOf(
const txnStatus = await transactionStatus();
console.log(txnStatus);
expect(txnStatus).toBe('success');
const balanceAfter = await getOnchainBalance(
WALLET_TO_USE_ADDRESS,
SWAP_VARIABLES.USDC_MAINNET_ADDRESS,
);
console.log('bal after: ', balanceAfter);
const balanceDifference = subtract(
usdcBalanceBeforeSwap.toString(),
usdcBalanceAfterSwap.toString(),
balanceBefore.toString(),
balanceAfter.toString(),
);
const usdcBalanceDifference = convertRawAmountToDecimalFormat(
balanceDifference.toString(),
6,
);

console.log('usdc DIFF: ', usdcBalanceDifference);

expect(Number(usdcBalanceDifference)).toBe(50);
});

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"// Build and zip": "",
"bundle": "yarn build && yarn zip",
"// Runs tests": "",
"anvil": "ETH_MAINNET_RPC=$(grep ETH_MAINNET_RPC .env | cut -d '=' -f2) && anvil --fork-url $ETH_MAINNET_RPC",
"anvil": "ETH_MAINNET_RPC=$(grep ETH_MAINNET_RPC .env | cut -d '=' -f2) && anvil --fork-url $ETH_MAINNET_RPC --fork-block-number 19000000",
"anvil:optimism": "OPTIMISM_MAINNET_RPC=$(grep OPTIMISM_MAINNET_RPC .env | cut -d '=' -f2) && anvil --fork-url $OPTIMISM_MAINNET_RPC",
"anvil:kill": "lsof -i :8545|tail -n +2|awk '{print $2}'|xargs -r kill -s SIGINT",
"test": "./scripts/unit-tests.sh",
Expand Down
41 changes: 26 additions & 15 deletions scripts/e2e-serial-tests.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
#!/bin/bash
ANVIL_PORT=8545

# Launch anvil in the bg
yarn anvil:kill
yarn anvil --chain-id 1337 &
echo "Launching Anvil..."
# Function to check if Anvil is running
is_anvil_running() {
if nc -z localhost $ANVIL_PORT; then
echo "Anvil is already running."
return 0
else
echo "Anvil is not running."
return 1
fi
}

# Give it some time to boot
interval=5
until nc -z localhost $ANVIL_PORT; do
sleep $interval
interval=$((interval * 2))
done
echo "Anvil Launched..."
# Launch Anvil if it's not already running
if ! is_anvil_running; then
yarn anvil:kill
yarn anvil --chain-id 1337 &
echo "Launching Anvil..."

# Give it some time to boot
interval=5
until nc -z localhost $ANVIL_PORT; do
sleep $interval
interval=$((interval * 2))
done
echo "Anvil Launched..."
else
echo "Using existing Anvil instance."
fi

# Run the tests and store the result
echo "Running Tests..."
Expand All @@ -21,9 +36,5 @@ yarn vitest e2e/serial/$1 --config ./e2e/serial/vitest.config.ts --reporter=verb
# Store exit code
TEST_RESULT=$?

# kill anvil
echo "Cleaning Up..."
yarn anvil:kill

# return the result of the tests
exit "$TEST_RESULT"
Loading

0 comments on commit 41c7e6f

Please sign in to comment.