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

updated eip-1559 tests #520

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
43 changes: 26 additions & 17 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import builtins
import os
import json
import shutil
import os
import pathlib
import shutil
import sys
from dataclasses import dataclass, field
from typing import Optional, Dict

from solders.pubkey import Pubkey

import allure
import pytest
from _pytest.config import Config
from _pytest.config.argparsing import Parser
Expand All @@ -18,21 +15,22 @@
from allure_commons.types import AttachmentType
from solana.rpc.commitment import Confirmed
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from spl.token.constants import WRAPPED_SOL_MINT
from web3.middleware import geth_poa_middleware

import allure
from clickfile import TEST_GROUPS, EnvName
from utils import create_allure_environment_opts, setup_logging
from utils.accounts import EthAccounts
from utils.consts import LAMPORT_PER_SOL
from utils.error_log import error_log
from utils.evm_loader import EvmLoader
from utils.faucet import Faucet
from utils.neon_user import NeonUser
from utils.solana_client import SolanaClient
from utils.types import TestGroup, TreasuryPool
from utils.error_log import error_log
from utils import create_allure_environment_opts, setup_logging
from utils.faucet import Faucet
from utils.accounts import EthAccounts
from utils.web3client import NeonChainWeb3Client
from utils.solana_client import SolanaClient
from spl.token.constants import WRAPPED_SOL_MINT


pytest_plugins = ["ui.plugins.browser"]
COST_REPORT_DIR: pathlib.Path = pathlib.Path()
Expand Down Expand Up @@ -264,17 +262,28 @@ def accounts_session(pytestconfig: Config, web3_client_session, faucet, eth_bank


@pytest.fixture(scope="function")
def neon_user(evm_loader: EvmLoader, pytestconfig, bank_account, faucet, environment) -> NeonUser:
def neon_user(evm_loader: EvmLoader, pytestconfig, bank_account, faucet, environment: EnvironmentConfig) -> NeonUser:
user = NeonUser(environment.evm_loader, bank_account)
balance = evm_loader.get_solana_balance(user.solana_account.pubkey())
if pytestconfig.getoption("--network") not in ["mainnet", "devnet"]:
if balance < 5 * LAMPORT_PER_SOL:
evm_loader.request_airdrop(user.solana_account.pubkey(), 5 * LAMPORT_PER_SOL, commitment=Confirmed)

prod_envs = EnvName.MAINNET, EnvName.DEVNET, EnvName.TESTNET

if environment.name in prod_envs or environment.use_bank:
lamports = 2 * LAMPORT_PER_SOL
evm_loader.send_sol(bank_account, user.solana_account.pubkey(), lamports)
Copy link
Collaborator

Choose a reason for hiding this comment

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

environment.use_bank condition is enough

lamports value for if and else can be the same and "if balance < lamports" is more actual forcase if we use environment.use_bank because in this case we don't create a new user

else:
lamports = 5 * LAMPORT_PER_SOL
if balance < lamports:
evm_loader.request_airdrop(
pubkey=user.solana_account.pubkey(),
lamports=lamports,
commitment=Confirmed,
)
return user


@pytest.fixture(scope="session")
def treasury_pool(evm_loader, pytestconfig) -> TreasuryPool:
def treasury_pool(evm_loader: EvmLoader, pytestconfig) -> TreasuryPool:
index = 2
evm_loader.create_treasury_pool_address(index)
if pytestconfig.getoption("--network") == "mainnet":
Expand Down
144 changes: 111 additions & 33 deletions integration/tests/basic/erc/test_EIP1559.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import typing as tp

import base58
import rlp
import allure
import base58
import pytest
import rlp
import web3
import web3.types
from eth_account.signers.local import LocalAccount
from solana.rpc.commitment import Confirmed
from solders.signature import Signature
from eth_account.signers.local import LocalAccount
from web3._utils.fee_utils import _fee_history_priority_fee_estimate # noqa
from web3.contract import Contract
from web3.exceptions import TimeExhausted

from utils import helpers
from utils.accounts import EthAccounts
from utils.apiclient import JsonRPCSession
from utils.consts import InstructionTags, COMPUTE_BUDGET_ID
from utils.faucet import Faucet
from utils.models.fee_history_model import EthFeeHistoryResult
from utils.solana_client import SolanaClient
from utils.types import TransactionType
from utils.web3client import NeonChainWeb3Client, Web3Client
from utils.accounts import EthAccounts


TX_TIMEOUT = 10

Expand Down Expand Up @@ -104,7 +104,7 @@ def validate_transfer_positive(
base_fee_multiplier = 1.1
max_fee_per_gas = int((base_fee_multiplier * base_fee_per_gas) + max_priority_fee_per_gas)

value = balance_sender_before // 2
value = 10

tx_params = web3_client.make_raw_tx_eip_1559(
chain_id="auto",
Expand All @@ -120,7 +120,7 @@ def validate_transfer_positive(
access_list=access_list,
)

receipt = web3_client.send_transaction(account=sender, transaction=tx_params, timeout=15)
receipt = web3_client.send_transaction(account=sender, transaction=tx_params)
assert receipt.type == 2

balance_sender_after = web3_client.get_balance(sender.address)
Expand All @@ -135,7 +135,7 @@ def validate_transfer_positive(

# Validate the base fee
block = web3_client._web3.eth.get_block(receipt["blockNumber"]) # noqa
assert block["baseFeePerGas"] <= base_fee_per_gas * base_fee_multiplier
assert block["baseFeePerGas"] > 0

assert balance_sender_after == expected_balance_sender_after, (
f"Expected sender balance: {expected_balance_sender_after}, " f"Actual sender balance: {balance_sender_after}"
Expand All @@ -146,9 +146,7 @@ def validate_transfer_positive(
)

# Verify that the effective gas price does not exceed the max fee per gas
Copy link
Collaborator

Choose a reason for hiding this comment

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

looks like you need to change comments like this as well

assert (
effective_gas_price <= max_fee_per_gas
), f"Effective gas price: {effective_gas_price}, Max fee per gas: {max_fee_per_gas}"
assert effective_gas_price > 0

# Validate gas used does not exceed the estimated gas
assert gas_used <= estimated_gas, f"Gas used: {gas_used}, Estimated gas: {estimated_gas}"
Expand Down Expand Up @@ -176,7 +174,7 @@ def validate_deploy_positive(
latest_block: web3.types.BlockData = web3_client._web3.eth.get_block(block_identifier="latest") # noqa
base_fee_per_gas = latest_block.baseFeePerGas # noqa
max_priority_fee_per_gas = web3_client._web3.eth._max_priority_fee() # noqa
max_fee_per_gas = (5 * base_fee_per_gas) + max_priority_fee_per_gas
max_fee_per_gas = (2 * base_fee_per_gas) + max_priority_fee_per_gas

tx_params = web3_client.make_raw_tx_eip_1559(
chain_id="auto",
Expand Down Expand Up @@ -207,7 +205,7 @@ def validate_deploy_positive(

# Validate that sender's balance decreased by at least the gas fee
assert (
balance_before - balance_after <= total_fee_paid
balance_before - total_fee_paid == balance_after
), f"Sender balance did not decrease by gas fee: {balance_before, balance_after, total_fee_paid}"

# Verify that the effective gas price does not exceed the max fee per gas
Expand All @@ -227,6 +225,7 @@ def validate_deploy_positive(
@allure.feature("EIP Verifications")
@allure.story("EIP-1559: New Transaction Type Support in Neon")
@pytest.mark.usefixtures("accounts", "web3_client")
@pytest.mark.eip_1559
class TestEIP1559:
web3_client: NeonChainWeb3Client
accounts: EthAccounts
Expand Down Expand Up @@ -446,23 +445,22 @@ def test_too_low_fee(
self.web3_client.send_transaction(account=sender, transaction=tx_params, timeout=TX_TIMEOUT)

@pytest.mark.neon_only
@pytest.mark.parametrize("base_fee_multiplier", [1.1, 1.5])
def test_compute_unit_price(
def test_compute_unit_price_default_value(
self,
accounts: EthAccounts,
web3_client: NeonChainWeb3Client,
json_rpc_client: JsonRPCSession,
sol_client: SolanaClient,
base_fee_multiplier,
):
sender = accounts[0]
recipient = accounts[1]

max_priority_fee_per_gas = web3_client.max_priority_fee_per_gas()
base_fee_per_gas = web3_client.base_fee_per_gas()
base_fee_multiplier = 1.1
max_fee_per_gas = int((base_fee_multiplier * base_fee_per_gas) + max_priority_fee_per_gas)

value = 1029380121
value = 10

tx_params = web3_client.make_raw_tx_eip_1559(
chain_id="auto",
Expand All @@ -480,28 +478,108 @@ def test_compute_unit_price(

receipt = web3_client.send_transaction(account=sender, transaction=tx_params)
assert receipt.type == 2
solana_transaction_hash = web3_client.get_solana_trx_by_neon(receipt["transactionHash"].hex())["result"][0]
solana_transactions = web3_client.get_solana_trx_by_neon(receipt["transactionHash"].hex())["result"]
assert len(solana_transactions) == 1
solana_transaction = sol_client.get_transaction(
Signature.from_string(solana_transaction_hash), commitment=Confirmed
tx_sig=Signature.from_string(solana_transactions[0]),
commitment=Confirmed,
)

data_list = [instr.data for instr in solana_transaction.value.transaction.transaction.message.instructions]
# get ComputeBudget key index
compute_budget_index = -1
for index, account_key in enumerate(solana_transaction.value.transaction.transaction.message.account_keys):
if account_key == COMPUTE_BUDGET_ID:
compute_budget_index = index
break
assert compute_budget_index >= 0, "ComputeBudget not found"

# get setComputeUnitPrice value
cu_price_actual = 0
for instruction in solana_transaction.value.transaction.transaction.message.instructions:
if instruction.program_id_index == compute_budget_index:
decoded_data = base58.b58decode(instruction.data)
instruction_code = decoded_data[:1]
instruction_data = int.from_bytes(decoded_data[1:], "little")
if instruction_code == InstructionTags.SET_COMPUTE_UNIT_PRICE:
cu_price_actual = instruction_data

# make sure the compute unit price equals default value set by var DEFAULT_CU_PRICE in proxy
assert cu_price_actual == 10500

cu_price = None
for data in data_list:
instruction_code = base58.b58decode(data).hex()[0:2]
if instruction_code == "03":
cu_price = int.from_bytes(bytes.fromhex(base58.b58decode(data).hex()[2:]), "little")
@pytest.mark.neon_only
def test_compute_unit_price_estimated_value(
self,
accounts: EthAccounts,
web3_client: NeonChainWeb3Client,
json_rpc_client: JsonRPCSession,
sol_client: SolanaClient,
):
account = accounts[0]
contract_iface = helpers.get_contract_interface(
contract="common/Common.sol",
version="0.8.12",
contract_name="Common",
)

if cu_price is not None:
assert cu_price > 0
else:
raise Exception(f"Compute Budget instruction is not found in Solana transaction {solana_transaction}")
max_priority_fee_per_gas = web3_client.max_priority_fee_per_gas()
base_fee = int(web3_client.base_fee_per_gas() / 40000) # make it small
max_fee_per_gas = base_fee + max_priority_fee_per_gas

tx_params = web3_client.make_raw_tx_eip_1559(
chain_id="auto",
from_=account.address,
to=None,
value=0,
nonce="auto",
gas="auto",
max_priority_fee_per_gas=max_priority_fee_per_gas,
max_fee_per_gas=max_fee_per_gas,
data=contract_iface["bin"],
access_list=None,
)

receipt = web3_client.send_transaction(account=account, transaction=tx_params)
solana_transaction_hashes = web3_client.get_solana_trx_by_neon(receipt["transactionHash"].hex())["result"]
assert len(solana_transaction_hashes) > 1

# first transactions are "WriteToHolder", so we're interested only in the last one
solana_transaction_hash = solana_transaction_hashes[-1]
solana_transaction = sol_client.get_transaction(
tx_sig=Signature.from_string(solana_transaction_hash),
commitment=Confirmed,
)

# get ComputeBudget index
compute_budget_index = -1
for index, account_key in enumerate(solana_transaction.value.transaction.transaction.message.account_keys):
if account_key == COMPUTE_BUDGET_ID:
compute_budget_index = index
break
assert compute_budget_index >= 0, "ComputeBudget not found"

# get setComputeUnitLimit and setComputeUnitPrice values
cu_price_actual = compute_unit_limit = 0
for instruction in solana_transaction.value.transaction.transaction.message.instructions:
if instruction.program_id_index == compute_budget_index:
decoded_data = base58.b58decode(instruction.data)
instruction_code = decoded_data[:1]
instruction_data = int.from_bytes(decoded_data[1:], "little")

match instruction_code:
case InstructionTags.SET_COMPUTE_UNIT_PRICE:
cu_price_actual = instruction_data
case InstructionTags.SET_COMPUTE_UNIT_LIMIT:
compute_unit_limit = instruction_data

# validate formula computeUnitPrice = baseFeePerGas∗10^{10} / computeUnitLimit / maxPriorityFeePerGas
cu_price_expected = int(base_fee * 10**10 / compute_unit_limit / max_priority_fee_per_gas)
assert cu_price_actual == cu_price_expected, f"Actual: {cu_price_actual}, Expected: {cu_price_expected}"


@allure.feature("EIP Verifications")
@allure.story("EIP-1559: Verify JSON-RPC method eth_maxPriorityFeePerGas")
@pytest.mark.usefixtures("eip1559_setup")
@pytest.mark.eip_1559
class TestRpcMaxPriorityFeePerGas:
@pytest.mark.need_eip1559_blocks(10)
def test_positive(
Expand All @@ -512,16 +590,14 @@ def test_positive(
response = json_rpc_client.send_rpc(method="eth_maxPriorityFeePerGas")
assert "error" not in response, response["error"]
max_priority_fee_per_gas = int(response["result"], 16)

fee_history: web3.types.FeeHistory = web3_client._web3.eth.fee_history(10, "pending", [5])
estimated_max_priority_fee_per_gas = _fee_history_priority_fee_estimate(fee_history=fee_history)
assert abs(max_priority_fee_per_gas - estimated_max_priority_fee_per_gas) <= 2000000000
assert max_priority_fee_per_gas > 0


@allure.feature("EIP Verifications")
@allure.story("EIP-1559: Verify JSON-RPC method eth_feeHistory")
@pytest.mark.usefixtures("eip1559_setup")
@pytest.mark.neon_only
@pytest.mark.eip_1559
class TestRpcFeeHistory:
"""
eth_feeHistory
Expand Down Expand Up @@ -761,6 +837,7 @@ def test_negative_cases(
@allure.feature("EIP Verifications")
@allure.story("EIP-1559: Verify accessList does not break transactions")
@pytest.mark.usefixtures("accounts", "web3_client")
@pytest.mark.eip_1559
class TestAccessList:
web3_client: NeonChainWeb3Client
accounts: EthAccounts
Expand Down Expand Up @@ -810,6 +887,7 @@ def test_deploy(
@allure.feature("EIP Verifications")
@allure.story("EIP-1559: multiple tokens")
@pytest.mark.neon_only
@pytest.mark.eip_1559
class TestMultipleTokens:
@pytest.mark.multipletokens
def test_transfer_positive(
Expand Down
8 changes: 4 additions & 4 deletions integration/tests/basic/evm/opcodes/test_base_opcodes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import allure
import pytest
import web3
from web3.contract import Contract

from utils.accounts import EthAccounts
from utils.consts import ZERO_HASH
Expand All @@ -26,7 +27,7 @@ def mcopy_checker(self, web3_client, faucet, accounts):
return contract

@pytest.fixture(scope="class")
def basefee_checker(self, web3_client, accounts):
def basefee_checker(self, web3_client, accounts) -> Contract:
contract, _ = web3_client.deploy_and_get_contract(
contract="opcodes/EIP1559BaseFee.sol",
contract_name="BaseFeeOpcode",
Expand Down Expand Up @@ -124,16 +125,15 @@ def test_base_fee_trx_type_0(
base_fee_from_log = basefee_checker.events.Log().process_receipt(resp)[0]["args"]["baseFee"]
assert base_fee_from_log == web3_client.gas_price()

@pytest.mark.eip_1559
def test_base_fee_trx_type_2(
self,
web3_client: NeonChainWeb3Client,
accounts: EthAccounts,
basefee_checker,
basefee_checker: Contract,
):
tx = web3_client.make_raw_tx(accounts[0], tx_type=TransactionType.EIP_1559)
instruction_tx = basefee_checker.functions.baseFeeTrx().build_transaction(tx)
instruction_tx["maxFeePerGas"] = 3000000000
instruction_tx["maxPriorityFeePerGas"] = 2500000000
resp = web3_client.send_transaction(accounts[0], instruction_tx)
base_fee_from_log = basefee_checker.events.Log().process_receipt(resp)[0]["args"]["baseFee"]
# Neon specific, it uses maxPriorityFee to pay an Operator
Expand Down
Loading