Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Add smart contract e2e tests #85

Merged
merged 16 commits into from
Sep 24, 2024
6 changes: 6 additions & 0 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ jobs:
pip3 install yq
yq --version

- name: Install polycli
run: |
tmp_dir=$(mktemp -d) && curl -L https://github.com/0xPolygon/polygon-cli/releases/download/v0.1.48/polycli_v0.1.48_linux_amd64.tar.gz | tar -xz -C "$tmp_dir" && mv "$tmp_dir"/* /usr/local/bin/polycli && rm -rf "$tmp_dir"
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved
sudo chmod +x /usr/local/bin/polycli
/usr/local/bin/polycli version

- name: Install foundry
uses: foundry-rs/foundry-toolchain@v1

Expand Down
143 changes: 128 additions & 15 deletions test/basic-e2e.bats
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ setup() {
}

@test "Send EOA transaction" {
local sender_addr=$(cast wallet address --private-key "$private_key")
local initial_nonce=$(cast nonce "$sender_addr" --rpc-url "$rpc_url") || return 1
local sender_addr=$(cast wallet address --private-key "$sender_private_key")
local initial_nonce=$(cast nonce "$sender_addr" --rpc-url "$l2_rpc_url") || return 1
local value="10ether"

# case 1: Transaction successful sender has sufficient balance
Expand All @@ -20,40 +20,153 @@ setup() {
# case 2: Transaction rejected as sender attempts to transfer more than it has in its wallet.
# Transaction will fail pre-validation check on the node and will be dropped subsequently from the pool
# without recording it on the chain and hence nonce will not change
local sender_balance=$(cast balance "$sender_addr" --ether --rpc-url "$rpc_url") || return 1
local sender_balance=$(cast balance "$sender_addr" --ether --rpc-url "$l2_rpc_url") || return 1
local excessive_value=$(echo "$sender_balance + 1" | bc)"ether"
run sendTx "$private_key" "$receiver" "$excessive_value"
run sendTx "$l2_rpc_url" "$sender_private_key" "$receiver" "$excessive_value"
assert_failure

# Check whether the sender's nonce was updated correctly
local final_nonce=$(cast nonce "$sender_addr" --rpc-url "$rpc_url") || return 1
local final_nonce=$(cast nonce "$sender_addr" --rpc-url "$l2_rpc_url") || return 1
assert_equal "$final_nonce" "$(echo "$initial_nonce + 1" | bc)"
}

@test "Deploy ERC20Mock contract" {
@test "Test ERC20Mock contract" {
local contract_artifact="./contracts/erc20mock/ERC20Mock.json"
wallet_A_output=$(cast wallet new)
address_A=$(echo "$wallet_A_output" | grep "Address" | awk '{print $2}')
address_A_private_key=$(echo "$wallet_A_output" | grep "Private key" | awk '{print $3}')
address_B=$(cast wallet new | grep "Address" | awk '{print $2}')

# Deploy ERC20Mock
run deployContract "$l2_rpc_url" "$sender_private_key" "$contract_artifact"
assert_success
contract_addr=$(echo "$output" | tail -n 1)

# Mint ERC20 tokens
local mintFnSig="mint(address,uint256)"
rachit77 marked this conversation as resolved.
Show resolved Hide resolved
local amount="5"

run sendTx "$l2_rpc_url" "$sender_private_key" "$contract_addr" "$mint_fn_sig" "$receiver" "$amount"
run sendTx "$l2_rpc_url" "$sender_private_key" "$contract_addr" "$mintFnSig" "$address_A" "$amount"
assert_success
assert_output --regexp "Transaction successful \(transaction hash: 0x[a-fA-F0-9]{64}\)"

# Assert that balance is correct
run queryContract "$l2_rpc_url" "$contract_addr" "$balance_of_fn_sig" "$receiver"
## Case 2: Insufficient gas scenario => Transactions fails
# nonce would not increase since transaction fails at the node's pre-validation check
# Get bytecode from the contract artifact
local bytecode=$(jq -r .bytecode "$contract_artifact")
if [[ -z "$bytecode" || "$bytecode" == "null" ]]; then
echo "Error: Failed to read bytecode from $contract_artifact"
return 1
fi

# Estimate gas, gas price and gas cost
local gas_units=$(cast estimate --rpc-url "$l2_rpc_url" --create "$bytecode")
rachit77 marked this conversation as resolved.
Show resolved Hide resolved
gas_units=$(echo "scale=0; $gas_units / 2" | bc)
local gas_price=$(cast gas-price --rpc-url "$l2_rpc_url")
local value=$(echo "$gas_units * $gas_price" | bc)
local value_ether=$(cast to-unit "$value" ether)"ether"

# Transfer insufficient funds
rachit77 marked this conversation as resolved.
Show resolved Hide resolved
cast_output=$(cast send --rpc-url "$l2_rpc_url" --private-key "$sender_private_key" "$address_A" --value "$value_ether" --legacy 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: Failed to send transaction. Output:"
echo "$cast_output"
return 1
fi
Comment on lines +78 to +83
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you simply use the send_tx function? Seems like we could spare a couple of lines of code. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the send_tx function, there are a few redundant balance checks for the sender and receiver. These checks have already been verified in other test cases, so I decided it would be more efficient to skip them for subsequent transactions to avoid unnecessary repetition.

WDYT?

Copy link
Collaborator

@Stefan-Ethernal Stefan-Ethernal Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like too much there are the balance checks in the send_tx, it should be extracted as a separate function IMO, and invoked only when needed (basically keeping these helper functions focused), but that's probably a separate topic...

For now, I'm ok with this, but eventually, I would like never to call the cast send explicitly but rely on the helper function (because it has validations and error checks).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging this for now, will refactor the part you suggested when we increase the scope e2e tests.


# Fetch initial nonce for address_A
local address_A_initial_nonce=$(cast nonce "$address_A" --rpc-url "$l2_rpc_url") || return 1
# Attempt to deploy contract with insufficient gas
run deployContract "$l2_rpc_url" "$address_A_private_key" "$contract_artifact"
assert_failure

## Case 3: Transaction should fail as address_A tries to transfer more tokens than it has
# nonce would not increase
# Transfer funds for gas fees to address_A
value_ether="4ether"
cast_output=$(cast send --rpc-url "$l2_rpc_url" --private-key "$sender_private_key" "$address_A" --value "$value_ether" --legacy 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: Failed to send transaction. Output:"
echo "$cast_output"
return 1
fi
Comment on lines +95 to +100
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you simply use the send_tx function? Seems like we could spare a couple of lines of code. WDYT?


# Fetch balance of address_A to simulate excessive transfer
local balanceOfFnSig="balanceOf(address) (uint256)"
run queryContract "$l2_rpc_url" "$contract_addr" "$balanceOfFnSig" "$address_A"
assert_success
local address_A_Balance=$(echo "$output" | tail -n 1)
address_A_Balance=$(echo "$address_A_Balance" | xargs)

# Set excessive amount for transfer
local excessive_amount=$(echo "$address_A_Balance + 1" | bc)

# Attempt transfer of excessive amount from address_A to address_B
local tranferFnSig="transfer(address,uint256)"
run sendTx "$l2_rpc_url" "$address_A_private_key" "$contract_addr" "$tranferFnSig" "$address_B" "$excessive_amount"
assert_failure

# Verify balance of address_A after failed transaction
run queryContract "$l2_rpc_url" "$contract_addr" "$balanceOfFnSig" "$address_A"
assert_success
address_A_BalanceAfterFailedTx=$(echo "$output" | tail -n 1)
address_A_BalanceAfterFailedTx=$(echo "$address_A_BalanceAfterFailedTx" | xargs)

# Ensure balance is unchanged
assert_equal "$address_A_BalanceAfterFailedTx" "$address_A_Balance"

# Verify balance of address_B is still zero
run queryContract "$l2_rpc_url" "$contract_addr" "$balanceOfFnSig" "$address_B"
assert_success
receiverBalance=$(echo "$output" | tail -n 1)
local address_B_Balance=$(echo "$output" | tail -n 1)
address_B_Balance=$(echo "$address_B_Balance" | xargs)

# Convert balance and amount to a standard format for comparison (e.g., remove any leading/trailing whitespace)
receiverBalance=$(echo "$receiverBalance" | xargs)
amount=$(echo "$amount" | xargs)
assert_equal "$address_B_Balance" "0"

# Check if the balance is equal to the amount
assert_equal "$receiverBalance" "$amount"
# Nonce should not increase
local address_A_final_nonce=$(cast nonce "$address_A" --rpc-url "$l2_rpc_url") || return 1
assert_equal "$address_A_final_nonce" "$address_A_initial_nonce"
}


@test "Deploy and test UniswapV3 contract" {
run polycli loadtest uniswapv3 --legacy -v 600 --rpc-url $l2_rpc_url --private-key $sender_private_key

assert_success

# Remove ANSI escape codes from the output
output=$(echo "$output" | sed -r "s/\x1B\[[0-9;]*[mGKH]//g")

# Check if the WETH9 contract were deployed
rachit77 marked this conversation as resolved.
Show resolved Hide resolved
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=WETH9"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=UniswapV3Factory"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=UniswapInterfaceMulticall"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=ProxyAdmin"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=TickLens"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=NFTDescriptor"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=NonfungibleTokenPositionDescriptor"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=TransparentUpgradeableProxy"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=NonfungiblePositionManager"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=V3Migrator"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=UniswapV3Staker"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=QuoterV2"
assert_output --regexp "Contract deployed address=0x[a-fA-F0-9]{40} name=SwapRouter02"

# Check if ERC20 tokens were minted
assert_output --regexp "Minted tokens amount=[0-9]+ recipient=0x[a-fA-F0-9]{40} token=SwapperA"
assert_output --regexp "Minted tokens amount=[0-9]+ recipient=0x[a-fA-F0-9]{40} token=SwapperB"

# Check if liquidity pool was created and initialized
assert_output --regexp "Pool created and initialized fees=[0-9]+"

# Check if liquidity was provided to the pool
assert_output --regexp "Liquidity provided to the pool liquidity=[0-9]+"

# Check if transaction got executed successfully
assert_output --regexp "Starting main load test loop currentNonce=[0-9]+"
assert_output --regexp "Finished main load test loop lastNonce=[0-9]+ startNonce=[0-9]+"
assert_output --regexp "Got final block number currentNonce=[0-9]+ final block number=[0-9]+"
assert_output --regexp "Num errors numErrors=0"
assert_output --regexp "Finished"
}

25 changes: 9 additions & 16 deletions test/helpers/common.bash
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,18 @@ function sendTx() {
return 1
}

# Get initial ether balances of sender and receiver
local sender_initial_balance receiver_initial_balance
sender_initial_balance=$(cast balance "$sender" --ether --rpc-url "$rpc_url") || return 1
receiver_initial_balance=$(cast balance "$receiver_addr" --ether --rpc-url "$rpc_url") || return 1

# Check if the value_or_function_sig is a numeric value (Ether to be transferred)
if [[ "$value_or_function_sig" =~ ^[0-9]+(\.[0-9]+)?(ether)?$ ]]; then
# Case: Ether transfer (EOA transaction)
send_eoa_transaction "$private_key" "$receiver_addr" "$value_or_function_sig" "$sender" "$sender_initial_balance" "$receiver_initial_balance"
# Get initial ether balances of sender and receiver
local sender_initial_balance receiver_initial_balance
sender_initial_balance=$(cast balance "$sender_addr" --ether --rpc-url "$rpc_url") || return 1
receiver_initial_balance=$(cast balance "$receiver_addr" --ether --rpc-url "$rpc_url") || return 1

send_eoa_transaction "$private_key" "$receiver_addr" "$value_or_function_sig" "$sender_addr" "$sender_initial_balance" "$receiver_initial_balance"
else
# Case: Smart contract interaction (contract interaction with function signature and parameters)
send_smart_contract_transaction "$private_key" "$receiver_addr" "$value_or_function_sig" "$sender" "${params[@]}"
send_smart_contract_transaction "$private_key" "$receiver_addr" "$value_or_function_sig" "${params[@]}"
fi
}

Expand Down Expand Up @@ -151,21 +151,14 @@ function send_smart_contract_transaction() {
local private_key="$1"
local receiver_addr="$2"
local function_sig="$3"
local sender="$4"
shift 4
shift 3
local params=("$@")

# Verify if the function signature starts with "function"
if [[ ! "$function_sig" =~ ^function\ .+\(.+\)$ ]]; then
echo "Error: Invalid function signature format '$function_sig'."
return 1
fi
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved

echo "Sending smart contract transaction to $receiver_addr with function signature: '$function_sig' and params: ${params[*]}" >&3

# Send the smart contract interaction using cast
local cast_output tx_hash
cast_output=$(cast send --rpc-url "$rpc_url" --private-key "$private_key" "$receiver_addr" "$function_sig" "${params[@]}" --legacy 2>&1)
cast_output=$(cast send "$receiver_addr" --rpc-url "$rpc_url" --private-key "$private_key" --legacy "$function_sig" "${params[@]}" 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: Failed to send transaction. Output:"
echo "$cast_output"
Expand Down
Loading