From 43dd211f731bfd692d2afcb0cdc5acddc1cb061c Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Fri, 17 Jan 2025 13:40:02 -0800 Subject: [PATCH 01/10] updated eip-1559 tests --- integration/tests/basic/erc/test_EIP1559.py | 138 ++++++++++++++---- .../basic/evm/opcodes/test_base_opcodes.py | 8 +- .../tests/basic/rpc/test_eip_1559_eth_rpc.py | 7 +- .../tests/basic/rpc/test_eip_1559_neon_rpc.py | 5 +- .../test_send_scheduled_transactions.py | 4 + integration/tests/economy/test_economics.py | 24 ++- .../test_scheduled_transactions.py | 1 + .../test_external_call_for_instructions.py | 1 + .../neon_evm/test_transactions_eip1559.py | 1 + pyproject.toml | 1 + utils/consts.py | 2 + utils/web3client.py | 28 ++-- 12 files changed, 171 insertions(+), 49 deletions(-) diff --git a/integration/tests/basic/erc/test_EIP1559.py b/integration/tests/basic/erc/test_EIP1559.py index d4a068156..dddbc8573 100644 --- a/integration/tests/basic/erc/test_EIP1559.py +++ b/integration/tests/basic/erc/test_EIP1559.py @@ -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 @@ -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", @@ -176,7 +176,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", @@ -207,7 +207,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 @@ -227,6 +227,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 @@ -446,23 +447,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", @@ -480,28 +480,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 + sol_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( + sol_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( @@ -513,15 +593,17 @@ def test_positive( 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 + fee_history: web3.types.FeeHistory = web3_client._web3.eth.fee_history(1, "pending", [100]) + last_reward = fee_history["reward"][-1][-1] + change_10_pct = int(last_reward * 0.1) + assert abs(max_priority_fee_per_gas - last_reward) <= change_10_pct @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 @@ -761,6 +843,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 @@ -810,6 +893,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( diff --git a/integration/tests/basic/evm/opcodes/test_base_opcodes.py b/integration/tests/basic/evm/opcodes/test_base_opcodes.py index 7f55e08ea..cd41d96b2 100644 --- a/integration/tests/basic/evm/opcodes/test_base_opcodes.py +++ b/integration/tests/basic/evm/opcodes/test_base_opcodes.py @@ -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 @@ -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", @@ -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 diff --git a/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py b/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py index 0f595de67..a4b02cd64 100644 --- a/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py +++ b/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py @@ -10,6 +10,7 @@ @allure.feature("EIP Verifications") @allure.story("EIP-1559: Verify new fields in eth_ JSON-RPC methods") @pytest.mark.neon_only +@pytest.mark.eip_1559 class TestRpcEthMethods: def test_get_transaction_by_hash( self, @@ -22,7 +23,7 @@ def test_get_transaction_by_hash( base_fee_per_gas = web3_client.base_fee_per_gas() 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 receipt = web3_client.send_tokens_eip_1559( from_=sender, @@ -145,7 +146,7 @@ def test_get_block_by_hash( assert result is not None block_base_fee = int(result["baseFeePerGas"], 16) - assert block_base_fee <= base_fee_per_gas + assert block_base_fee > 0 @pytest.mark.neon_only @pytest.mark.parametrize( @@ -185,7 +186,7 @@ def test_get_block_by_number( block_base_fee = int(result["baseFeePerGas"], 16) - assert block_base_fee <= base_fee_per_gas + assert block_base_fee > 0 def test_get_transaction_receipt( self, diff --git a/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py b/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py index 60bad80cf..2852297be 100644 --- a/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py +++ b/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py @@ -9,6 +9,7 @@ @allure.feature("EIP Verifications") @allure.story("EIP-1559: Verify new fields in neon_ JSON-RPC methods") @pytest.mark.neon_only +@pytest.mark.eip_1559 class TestRpcNeonMethods: def test_neon_get_transaction_by_sender_nonce( self, @@ -21,7 +22,7 @@ def test_neon_get_transaction_by_sender_nonce( nonce = web3_client.get_nonce(address=sender.address) base_fee_per_gas = web3_client.base_fee_per_gas() 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 web3_client.send_tokens_eip_1559( from_=sender, @@ -75,7 +76,7 @@ def test_neon_get_transaction_receipt( base_fee_per_gas = web3_client.base_fee_per_gas() max_priority_fee_per_gas = web3_client.max_priority_fee_per_gas() - 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 receipt = web3_client.send_tokens_eip_1559( from_=sender, diff --git a/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py b/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py index fb3e51dbb..35e03e114 100644 --- a/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py +++ b/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py @@ -33,6 +33,7 @@ def test_send_simple_single_trx(self, web3_client_sol, neon_user, common_contrac assert pending_trx[hex(tx.nonce)][0]["status"] in ("Done", "InProgress") assert common_contract.functions.getNumber().call() == contract_data + @pytest.mark.eip_1559 def test_multiple_scheduled_trx(self, web3_client_sol, neon_user, common_contract, evm_loader, treasury_pool): nonce = web3_client_sol.get_nonce(neon_user.checksum_address) contract_data = 18 @@ -77,6 +78,7 @@ def test_multiple_scheduled_trx(self, web3_client_sol, neon_user, common_contrac assert pending_trx[hex(nonce)][0]["status"] == "Done" assert pending_trx[hex(nonce)][0]["hash"][2:] == trxs[0].hash().hex() + @pytest.mark.eip_1559 def test_multiple_scheduled_trx_with_failed_trx( self, web3_client_sol, neon_user, treasury_pool, revert_contract_caller, event_caller_contract, evm_loader ): @@ -225,6 +227,7 @@ def test_scheduled_trx_send_tokens_to_sol_chain_contract( assert event_logs[0].args.value == value assert event_logs[0].event == "IndexedArgs" + @pytest.mark.eip_1559 def test_scheduled_trx_with_timestamp( self, block_timestamp_contract, web3_client_sol, neon_user, treasury_pool, evm_loader, json_rpc_client ): @@ -277,6 +280,7 @@ def test_scheduled_trx_with_timestamp( assert added_timestamp <= int(tx_block_timestamp, 16) assert contract.functions.getDataFromMapping(added_timestamp).call() == [v1, v2] + @pytest.mark.eip_1559 def test_scheduled_trx_with_small_gas_limit( self, block_timestamp_contract, web3_client_sol, neon_user, treasury_pool, evm_loader, event_caller_contract ): diff --git a/integration/tests/economy/test_economics.py b/integration/tests/economy/test_economics.py index c3e4cb471..545b8d117 100644 --- a/integration/tests/economy/test_economics.py +++ b/integration/tests/economy/test_economics.py @@ -67,6 +67,7 @@ def test_account_creation(self, client_and_price, operator): assert sol_balance_after == sol_balance_before @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_send_neon_to_non_existent_account( self, account_with_all_tokens: LocalAccount, @@ -94,6 +95,7 @@ def test_send_neon_to_non_existent_account( get_gas_used_percent(w3_client, receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_send_tokens_to_exist_account( self, account_with_all_tokens: LocalAccount, @@ -149,6 +151,7 @@ def test_send_neon_token_without_chain_id( assert_profit(sol_diff, sol_price, token_diff, neon_price, web3_client.native_token_name) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_send_when_not_enough_tokens_to_gas( self, client_and_price: tuple[Web3Client, float], @@ -215,6 +218,7 @@ def test_erc721_mint(self, erc721, client_and_price, account_with_all_tokens, so assert_profit(sol_diff, sol_price, token_diff, token_price, w3_client.native_token_name) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_withdraw_neon_unexisting_ata( self, pytestconfig: Config, @@ -272,6 +276,7 @@ def test_withdraw_neon_unexisting_ata( get_gas_used_percent(web3_client, receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_withdraw_neon_existing_ata( self, pytestconfig: Config, @@ -358,6 +363,7 @@ def test_erc20_transfer( get_gas_used_percent(w3_client, transfer_tx) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_small_contract_less_100tx( self, account_with_all_tokens: LocalAccount, @@ -403,6 +409,7 @@ def test_deploy_small_contract_less_100tx( get_gas_used_percent(w3_client, receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_to_lost_contract_account( self, account_with_all_tokens: LocalAccount, @@ -486,6 +493,7 @@ def test_contract_get_is_free(self, counter_contract, client_and_price, account_ @pytest.mark.xfail(reason="https://neonlabs.atlassian.net/browse/NDEV-699") @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_cost_resize_account( self, neon_price: float, @@ -531,6 +539,7 @@ def test_cost_resize_account( get_gas_used_percent(web3_client, instruction_receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_contract_interact_1000_steps( self, counter_contract: Contract, @@ -562,6 +571,7 @@ def test_contract_interact_1000_steps( get_gas_used_percent(w3_client, instruction_receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_contract_interact_500000_steps( self, counter_contract: Contract, @@ -597,6 +607,7 @@ def test_contract_interact_500000_steps( get_gas_used_percent(w3_client, instruction_receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_send_transaction_with_gas_limit_reached( self, counter_contract: Contract, @@ -624,6 +635,7 @@ def test_send_transaction_with_gas_limit_reached( assert token_balance_after == token_balance_before, "TOKEN Balance incorrect" @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_send_transaction_with_insufficient_funds( self, counter_contract: Contract, @@ -653,6 +665,7 @@ def test_send_transaction_with_insufficient_funds( assert token_balance_after == token_balance_before, "TOKEN Balance incorrect" @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_tx_interact_more_1kb( self, counter_contract: Contract, @@ -686,6 +699,7 @@ def test_tx_interact_more_1kb( get_gas_used_percent(w3_client, instruction_receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_contract_more_1kb( self, client_and_price: tuple[Web3Client, float], @@ -722,6 +736,7 @@ def test_deploy_contract_more_1kb( get_gas_used_percent(w3_client, contract_deploy_tx) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_contract_to_payed( self, client_and_price: tuple[Web3Client, float], @@ -768,6 +783,7 @@ def test_deploy_contract_to_payed( get_gas_used_percent(w3_client, contract_deploy_tx) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_contract_to_exist_unpayed( self, client_and_price: tuple[Web3Client, float], @@ -822,6 +838,7 @@ def test_deploy_contract_to_exist_unpayed( @pytest.mark.slow @pytest.mark.timeout(16 * Time.MINUTE) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_contract_alt_on( self, sol_client: SolanaClient, @@ -870,6 +887,7 @@ def test_deploy_contract_alt_on( get_gas_used_percent(web3_client, receipt) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_deploy_contract_alt_off( self, sol_client: SolanaClient, @@ -932,6 +950,7 @@ def test_deploy_big_contract_with_structures( get_gas_used_percent(w3_client, receipt) # @pytest.mark.skip(reason="work incorrect very often") + @pytest.mark.eip_1559 def test_deploy_big_contract_with_structures_eip_1559( self, web3_client: NeonChainWeb3Client, @@ -964,6 +983,7 @@ def test_deploy_big_contract_with_structures_eip_1559( @pytest.mark.slow @pytest.mark.parametrize("value", [20, 30]) @pytest.mark.parametrize("tx_type", TransactionType) + @pytest.mark.eip_1559 def test_call_contract_with_mapping_updating( self, client_and_price: tuple[Web3Client, float], @@ -1006,6 +1026,7 @@ def test_call_contract_with_mapping_updating( ) @pytest.mark.skip(reason="work incorrect very often") + @pytest.mark.eip_1559 def test_eip_1559_zero_priority_fee( self, client_and_price: tuple[Web3Client, float], @@ -1044,6 +1065,7 @@ def test_eip_1559_zero_priority_fee( assert_profit(sol_diff, sol_price, token_diff, token_price, w3_client.native_token_name) get_gas_used_percent(w3_client, receipt) + @pytest.mark.eip_1559 def test_eip_1559_profit( self, client_and_price: tuple[Web3Client, float], @@ -1083,7 +1105,7 @@ def test_eip_1559_profit( latest_block: web3.types.BlockData = w3_client._web3.eth.get_block(block_identifier="latest") # noqa base_fee_per_gas = latest_block.baseFeePerGas # noqa max_priority_fee_per_gas = w3_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 recipient_2 = w3_client.create_account() w3_client.send_tokens_eip_1559( diff --git a/integration/tests/neon_evm/solana_native/test_scheduled_transactions.py b/integration/tests/neon_evm/solana_native/test_scheduled_transactions.py index 47f7ea776..f7b457ca3 100644 --- a/integration/tests/neon_evm/solana_native/test_scheduled_transactions.py +++ b/integration/tests/neon_evm/solana_native/test_scheduled_transactions.py @@ -116,6 +116,7 @@ def test_scheduled_trx_wrong_index( with pytest.raises(solana.rpc.core.RPCException, match=InstructionAsserts.TRANSACTION_TREE_INVALID_DATA): evm_loader.create_tree_account(neon_user, treasury_pool, tx.encode(), environment.sol_mint_id) + @pytest.mark.eip_1559 def test_send_sol_with_zero_fee( self, evm_loader, diff --git a/integration/tests/neon_evm/test_external_call_for_instructions.py b/integration/tests/neon_evm/test_external_call_for_instructions.py index 28f46b235..f026a7254 100644 --- a/integration/tests/neon_evm/test_external_call_for_instructions.py +++ b/integration/tests/neon_evm/test_external_call_for_instructions.py @@ -58,6 +58,7 @@ def test_execute_from_instruction( assert sender_initial_balance == evm_loader.get_neon_balance(sender_with_tokens.eth_address) + amount assert receiver_initial_balance == evm_loader.get_neon_balance(session_user.eth_address) - amount + @pytest.mark.eip_1559 def test_execute_from_instruction_eip_1559( self, operator_keypair, evm_loader, treasury_pool, sender_with_tokens, session_user, holder_acc ): diff --git a/integration/tests/neon_evm/test_transactions_eip1559.py b/integration/tests/neon_evm/test_transactions_eip1559.py index d1c331203..d8b3c3976 100644 --- a/integration/tests/neon_evm/test_transactions_eip1559.py +++ b/integration/tests/neon_evm/test_transactions_eip1559.py @@ -16,6 +16,7 @@ from utils.types import Caller +@pytest.mark.eip_1559 class TestEIP1559Transactions: def test_contract_interaction_iterative_transactions( self, diff --git a/pyproject.toml b/pyproject.toml index a062bc101..076de4722 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ markers = [ "slow: these tests are slow and should be run separately", "bug: mark for tests containing bugs which need to be fixed or refactored", "neon_only: tests for Neon functionality", + "eip_1559: tests covering EIP-1559 functionality", "need_eip1559_blocks: values passed to this marker will be used by fixture eip1559_setup", "cost_report: enables Cost Report data collection for this test" ] diff --git a/utils/consts.py b/utils/consts.py index 1bdab35dd..630a0708b 100644 --- a/utils/consts.py +++ b/utils/consts.py @@ -96,3 +96,5 @@ class InstructionTags(bytes, Enum): SCHEDULED_TRANSACTION_CREATE = b"\x4A" SCHEDULED_TRANSACTION_CREATE_MULTIPLE = b"\x4B" SCHEDULED_TRANSACTION_DESTROY = b"\x4C" + SET_COMPUTE_UNIT_PRICE = b"\x03" + SET_COMPUTE_UNIT_LIMIT = b"\x02" diff --git a/utils/web3client.py b/utils/web3client.py index 0adcd29ef..dce235d09 100644 --- a/utils/web3client.py +++ b/utils/web3client.py @@ -11,6 +11,7 @@ import web3.types from eth_abi import abi from eth_typing import BlockIdentifier +from web3.contract import Contract from solders.pubkey import Pubkey from web3.exceptions import TransactionNotFound @@ -239,7 +240,7 @@ def make_raw_tx( if gas: transaction["gas"] = gas else: - if gas_price is not None and gas is not None: + if gas_price is not None: max_priority_fee_per_gas, max_fee_per_gas = self.gas_price_to_eip1559_params(gas_price=gas_price) else: max_priority_fee_per_gas = max_fee_per_gas = "auto" @@ -266,9 +267,9 @@ def send_transaction( gas_multiplier: tp.Optional[float] = None, # fix for some event depends transactions timeout: int = 120, ) -> web3.types.TxReceipt: - instruction_tx = self._web3.eth.account.sign_transaction(transaction, account.key) - signature = self._web3.eth.send_raw_transaction(instruction_tx.rawTransaction) - return self._web3.eth.wait_for_transaction_receipt(signature, timeout=timeout) + signed_tx = self._web3.eth.account.sign_transaction(transaction, account.key) + transaction_hash = self._web3.eth.send_raw_transaction(signed_tx.rawTransaction) + return self._web3.eth.wait_for_transaction_receipt(transaction_hash, timeout=timeout) @allure.step("Send the scheduled transaction") def send_scheduled_transaction( @@ -308,6 +309,7 @@ def make_raw_tx_eip_1559( gas: tp.Union[int, tp.Literal["auto"], None], max_priority_fee_per_gas: tp.Union[int, tp.Literal["auto"], None], max_fee_per_gas: tp.Union[int, tp.Literal["auto"], None], + base_fee_per_gas: tp.Union[int, tp.Literal["auto"]] = "auto", base_fee_multiplier: float = 1.1, ) -> web3.types.TxParams: # Handle addresses @@ -321,6 +323,7 @@ def make_raw_tx_eip_1559( kwargs = locals().copy() del kwargs["self"] del kwargs["base_fee_multiplier"] + del kwargs["base_fee_per_gas"] # Move parameters related to gas to the end as they should be handled last for arg_name in ("gas", "max_priority_fee_per_gas", "max_fee_per_gas"): @@ -332,9 +335,7 @@ def make_raw_tx_eip_1559( params = {"type": TransactionType.EIP_1559} # Map parameters with 'auto' value to their corresponding values - base_fee_per_gas = 10 - - if max_priority_fee_per_gas == "auto" or max_fee_per_gas == "auto": + if base_fee_per_gas == "auto": base_fee_per_gas = self.base_fee_per_gas() auto_map = { @@ -376,7 +377,7 @@ def deploy_and_get_contract( gas: tp.Optional[int] = 0, value=0, tx_type: TransactionType = TransactionType.LEGACY, - ) -> tp.Tuple[tp.Any, web3.types.TxReceipt]: + ) -> tp.Tuple[Contract, web3.types.TxReceipt]: contract_interface = helpers.get_contract_interface( contract, version, @@ -395,7 +396,7 @@ def deploy_and_get_contract( tx_type=tx_type, ) - contract = self.eth.contract(address=contract_deploy_tx["contractAddress"], abi=contract_interface["abi"]) + contract = self._web3.eth.contract(address=contract_deploy_tx["contractAddress"], abi=contract_interface["abi"]) return contract, contract_deploy_tx @@ -579,9 +580,12 @@ def get_token_usd_gas_price(self): ).json() return int(resp["result"]["tokenPriceUsd"], 16) / 100000 - def gas_price_to_eip1559_params(self, gas_price: int) -> tuple[int, int]: - base_fee_per_gas = self.base_fee_per_gas() - + def gas_price_to_eip1559_params( + self, + gas_price: int, + base_fee_multiplier: float = 1.1, + ) -> tuple[int, int]: + base_fee_per_gas = int(self.base_fee_per_gas() * base_fee_multiplier) msg = f"gas_price {gas_price} is lower than the baseFeePerGas {base_fee_per_gas}" assert gas_price >= base_fee_per_gas, msg From 35bcfcd70f70ba7638f7fd42c92f6c2391dbd813 Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Tue, 18 Feb 2025 10:39:17 -0800 Subject: [PATCH 02/10] fixes --- integration/tests/basic/erc/test_EIP1559.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tests/basic/erc/test_EIP1559.py b/integration/tests/basic/erc/test_EIP1559.py index dddbc8573..cff5e6312 100644 --- a/integration/tests/basic/erc/test_EIP1559.py +++ b/integration/tests/basic/erc/test_EIP1559.py @@ -483,7 +483,7 @@ def test_compute_unit_price_default_value( solana_transactions = web3_client.get_solana_trx_by_neon(receipt["transactionHash"].hex())["result"] assert len(solana_transactions) == 1 solana_transaction = sol_client.get_transaction( - sol_sig=Signature.from_string(solana_transactions[0]), + tx_sig=Signature.from_string(solana_transactions[0]), commitment=Confirmed, ) @@ -547,7 +547,7 @@ def test_compute_unit_price_estimated_value( # 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( - sol_sig=Signature.from_string(solana_transaction_hash), + tx_sig=Signature.from_string(solana_transaction_hash), commitment=Confirmed, ) From 1a9a02b1e607b8a17e052cca9e3305dfb615321c Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Tue, 18 Feb 2025 13:42:21 -0800 Subject: [PATCH 03/10] increased timeout - fails on devnet --- integration/tests/basic/erc/test_EIP1559.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/basic/erc/test_EIP1559.py b/integration/tests/basic/erc/test_EIP1559.py index cff5e6312..f0ed96d3b 100644 --- a/integration/tests/basic/erc/test_EIP1559.py +++ b/integration/tests/basic/erc/test_EIP1559.py @@ -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) From 753c44efca8437a71603c4411f7dd9046be21f3e Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Wed, 19 Feb 2025 10:56:23 -0800 Subject: [PATCH 04/10] fixed typing --- integration/tests/conftest.py | 64 ++++++++++++++++------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/integration/tests/conftest.py b/integration/tests/conftest.py index 7302cf791..6a8721dcf 100644 --- a/integration/tests/conftest.py +++ b/integration/tests/conftest.py @@ -1,30 +1,26 @@ -import logging import inspect +import logging import os import random import string import time -import allure import typing as tp +import allure import base58 import pytest -from web3.types import TxReceipt from _pytest.config import Config -from solders.keypair import Keypair -from solders.pubkey import Pubkey +from eth_account.signers.local import LocalAccount from solana.rpc import commitment from solana.rpc.types import TxOpts +from solders.keypair import Keypair +from solders.pubkey import Pubkey from web3.contract import Contract - - -from eth_account.signers.local import LocalAccount - -from conftest import EnvironmentConfig +from web3.types import TxReceipt from clickfile import EnvName +from conftest import EnvironmentConfig from utils.accounts import EthAccounts - from utils.apiclient import JsonRPCSession from utils.consts import COUNTER_ID, LAMPORT_PER_SOL, MULTITOKEN_MINTS from utils.erc20 import ERC20 @@ -32,8 +28,8 @@ from utils.evm_loader import EvmLoader from utils.helpers import decode_function_signature, get_selectors from utils.operator import Operator -from utils.solana_client import SolanaClient from utils.prices import get_sol_price_with_retry +from utils.solana_client import SolanaClient from utils.web3client import NeonChainWeb3Client, Web3Client log = logging.getLogger(__name__) @@ -52,14 +48,14 @@ def json_rpc_client(environment: EnvironmentConfig) -> JsonRPCSession: @pytest.fixture(scope="class") -def web3_client(request, web3_client_session) -> tp.Generator[NeonChainWeb3Client, tp.Any, tp.Any]: +def web3_client(request, web3_client_session) -> tp.Generator[NeonChainWeb3Client, None, None]: if inspect.isclass(request.cls): request.cls.web3_client = web3_client_session yield web3_client_session @pytest.fixture(scope="class") -def sol_client(request, sol_client_session) -> tp.Generator[SolanaClient, tp.Any, tp.Any]: +def sol_client(request, sol_client_session) -> tp.Generator[SolanaClient, None, None]: if inspect.isclass(request.cls): request.cls.sol_client = sol_client_session yield sol_client_session @@ -100,7 +96,7 @@ def operator(environment: EnvironmentConfig, web3_client_session: NeonChainWeb3C @pytest.fixture(scope="session") -def bank_account(pytestconfig: Config) -> tp.Generator[Keypair, tp.Any, tp.Any] | None: +def bank_account(pytestconfig: Config) -> tp.Generator[Keypair | None, None, None]: account = None if pytestconfig.environment.use_bank: if pytestconfig.getoption("--network") == "devnet": @@ -115,7 +111,7 @@ def bank_account(pytestconfig: Config) -> tp.Generator[Keypair, tp.Any, tp.Any] @pytest.fixture(scope="session") -def eth_bank_account(pytestconfig: Config, web3_client_session) -> tp.Generator[Keypair, tp.Any, tp.Any] | None: +def eth_bank_account(pytestconfig: Config, web3_client_session) -> tp.Generator[Keypair | None, None, None]: account = None if pytestconfig.environment.eth_bank_account != "": account = web3_client_session.eth.account.from_key(pytestconfig.environment.eth_bank_account) @@ -127,7 +123,7 @@ def eth_bank_account(pytestconfig: Config, web3_client_session) -> tp.Generator[ @pytest.fixture(scope="session") def solana_account( bank_account, environment: EnvironmentConfig, sol_client_session -) -> tp.Generator[Keypair, tp.Any, tp.Any]: +) -> tp.Generator[Keypair, None, None]: account = Keypair() if environment.use_bank: @@ -147,7 +143,7 @@ def solana_account( @pytest.fixture(scope="function") def new_solana_account( bank_account, environment: EnvironmentConfig, sol_client_session -) -> tp.Generator[Keypair, tp.Any, tp.Any]: +) -> tp.Generator[Keypair, None, None]: account = Keypair() if environment.use_bank: sol_client_session.send_sol(bank_account, account.pubkey(), int(0.01 * LAMPORT_PER_SOL)) @@ -179,7 +175,7 @@ def erc20_spl( solana_account, eth_bank_account, accounts_session, -) -> tp.Generator[ERC20Wrapper, tp.Any, tp.Any]: +) -> tp.Generator[ERC20Wrapper, None, None]: symbol = "".join([random.choice(string.ascii_uppercase) for _ in range(3)]) erc20 = ERC20Wrapper( web3_client_session, @@ -210,9 +206,7 @@ def erc20_spl( @pytest.fixture(scope="session") -def erc20_simple( - web3_client_session, faucet, accounts_session, eth_bank_account -) -> tp.Generator[ERC20, tp.Any, tp.Any]: +def erc20_simple(web3_client_session, faucet, accounts_session, eth_bank_account) -> tp.Generator[ERC20, None, None]: erc20 = ERC20( web3_client=web3_client_session, faucet=faucet, bank_account=eth_bank_account, owner=accounts_session[0] ) @@ -227,7 +221,7 @@ def erc20_spl_mintable( solana_account, accounts_session, eth_bank_account, -) -> tp.Generator[ERC20Wrapper, tp.Any, tp.Any]: +) -> tp.Generator[ERC20Wrapper, None, None]: symbol = "".join([random.choice(string.ascii_uppercase) for _ in range(3)]) erc20 = ERC20Wrapper( web3_client_session, @@ -339,7 +333,7 @@ def withdraw_contract(web3_client, faucet, accounts) -> Contract: @pytest.fixture(scope="class") -def common_contract(web3_client, accounts, pytestconfig) -> Contract: +def common_contract(web3_client, accounts, pytestconfig) -> tp.Generator[Contract, None, None]: if pytestconfig.getoption("--network") == "mainnet": address = os.environ.get("MAINNET_COMMON_CONTRACT_ADDRESS") contract = web3_client.get_deployed_contract(address, "common/Common", contract_name="Common") @@ -354,7 +348,7 @@ def common_contract(web3_client, accounts, pytestconfig) -> Contract: @pytest.fixture(scope="class") -def common_caller_contract(web3_client, accounts, common_contract) -> Contract: +def common_caller_contract(web3_client, accounts, common_contract) -> tp.Generator[Contract, None, None]: contract, tx = web3_client.deploy_and_get_contract( contract="common/Common", version="0.8.12", @@ -430,7 +424,7 @@ def wneon(web3_client, accounts) -> Contract: @pytest.fixture(scope="class") -def storage_contract(web3_client, accounts) -> tp.Generator[Contract, tp.Any, tp.Any]: +def storage_contract(web3_client, accounts) -> tp.Generator[Contract, None, None]: contract, _ = web3_client.deploy_and_get_contract( "common/StorageSoliditySource", "0.8.8", @@ -442,7 +436,7 @@ def storage_contract(web3_client, accounts) -> tp.Generator[Contract, tp.Any, tp @pytest.fixture(scope="class") -def storage_contract_with_deploy_tx(web3_client, accounts) -> tp.Generator[tp.Tuple[tp.Any, TxReceipt], tp.Any, tp.Any]: +def storage_contract_with_deploy_tx(web3_client, accounts) -> tp.Generator[tp.Tuple[Contract, TxReceipt], None, None]: contract, contract_deploy_tx = web3_client.deploy_and_get_contract( "common/StorageSoliditySource", "0.8.8", @@ -454,7 +448,7 @@ def storage_contract_with_deploy_tx(web3_client, accounts) -> tp.Generator[tp.Tu @pytest.fixture(scope="class") -def revert_contract(web3_client, accounts) -> tp.Generator[Contract, tp.Any, tp.Any]: +def revert_contract(web3_client, accounts) -> tp.Generator[Contract, None, None]: contract, _ = web3_client.deploy_and_get_contract( contract="common/Revert", version="0.8.10", @@ -465,7 +459,7 @@ def revert_contract(web3_client, accounts) -> tp.Generator[Contract, tp.Any, tp. @pytest.fixture(scope="class") -def revert_contract_caller(web3_client, accounts, revert_contract) -> tp.Generator[Contract, tp.Any, tp.Any]: +def revert_contract_caller(web3_client, accounts, revert_contract) -> tp.Generator[Contract, None, None]: contract, _ = web3_client.deploy_and_get_contract( contract="common/Revert", version="0.8.10", @@ -491,7 +485,7 @@ def neon_price(web3_client_session) -> float: @pytest.fixture(scope="class") -def events_checker_contract(web3_client, accounts) -> tp.Any: +def events_checker_contract(web3_client, accounts) -> tp.Generator[Contract, None, None]: contract, _ = web3_client.deploy_and_get_contract("common/EventsCheckerCaller", "0.8.15", account=accounts[0]) yield contract @@ -503,7 +497,7 @@ def counter_contract(web3_client, accounts) -> Contract: @pytest.fixture(scope="class") -def nested_call_contracts(accounts, web3_client): +def nested_call_contracts(accounts, web3_client) -> tp.Generator[tuple[Contract, Contract, Contract], None, None]: contract_a, _ = web3_client.deploy_and_get_contract( "common/NestedCallsChecker", "0.8.12", accounts[0], contract_name="A" ) @@ -517,7 +511,7 @@ def nested_call_contracts(accounts, web3_client): @pytest.fixture(scope="function") -def recursion_factory(accounts, web3_client): +def recursion_factory(accounts, web3_client) -> tp.Generator[Contract, None, None]: sender_account = accounts[0] contract, _ = web3_client.deploy_and_get_contract( "common/Recursion", @@ -530,7 +524,7 @@ def recursion_factory(accounts, web3_client): @pytest.fixture(scope="function") -def destroyable_contract(accounts, web3_client): +def destroyable_contract(accounts, web3_client) -> tp.Generator[Contract, None, None]: sender_account = accounts[0] contract, _ = web3_client.deploy_and_get_contract( "opcodes/SelfDestroyable", "0.8.10", sender_account, "SelfDestroyable" @@ -539,7 +533,7 @@ def destroyable_contract(accounts, web3_client): @pytest.fixture(scope="class") -def expected_error_checker(accounts, web3_client): +def expected_error_checker(accounts, web3_client) -> tp.Generator[Contract, None, None]: contract, _ = web3_client.deploy_and_get_contract( "common/ExpectedErrorsChecker", "0.8.12", accounts[0], contract_name="A" ) @@ -561,7 +555,7 @@ def call_solana_caller(accounts, web3_client): @pytest.fixture(scope="class") -def counter_resource_address(call_solana_caller, accounts, web3_client) -> bytes: +def counter_resource_address(call_solana_caller, accounts, web3_client) -> tp.Generator[bytes, None, None]: tx = web3_client.make_raw_tx(accounts[0].address) salt = web3_client.text_to_bytes32("".join(random.choices(string.ascii_letters, k=5))) instruction_tx = call_solana_caller.functions.createResource(salt, 8, 100000, bytes(COUNTER_ID)).build_transaction( From 38feb42e453c99a9edb57ba175f00c7145ab8ca2 Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Wed, 19 Feb 2025 10:39:51 -0800 Subject: [PATCH 05/10] addressed changes - Neon can use any fees ignoring user limits --- integration/tests/basic/erc/test_EIP1559.py | 6 +----- integration/tests/basic/rpc/test_eip_1559_eth_rpc.py | 12 ++++++------ .../tests/basic/rpc/test_eip_1559_neon_rpc.py | 7 +++---- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/integration/tests/basic/erc/test_EIP1559.py b/integration/tests/basic/erc/test_EIP1559.py index f0ed96d3b..3afe88030 100644 --- a/integration/tests/basic/erc/test_EIP1559.py +++ b/integration/tests/basic/erc/test_EIP1559.py @@ -592,11 +592,7 @@ 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(1, "pending", [100]) - last_reward = fee_history["reward"][-1][-1] - change_10_pct = int(last_reward * 0.1) - assert abs(max_priority_fee_per_gas - last_reward) <= change_10_pct + assert max_priority_fee_per_gas > 0 @allure.feature("EIP Verifications") diff --git a/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py b/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py index a4b02cd64..d5bfc9d72 100644 --- a/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py +++ b/integration/tests/basic/rpc/test_eip_1559_eth_rpc.py @@ -40,8 +40,8 @@ def test_get_transaction_by_hash( assert "error" not in response, response["error"] result = response.get("result") assert result is not None - assert int(result["maxFeePerGas"], 16) == max_fee_per_gas - assert int(result["maxPriorityFeePerGas"], 16) == max_priority_fee_per_gas + assert int(result["maxFeePerGas"], 16) > 0 + assert int(result["maxPriorityFeePerGas"], 16) > 0 def test_get_transaction_by_block_hash_and_index( self, @@ -72,8 +72,8 @@ def test_get_transaction_by_block_hash_and_index( assert "error" not in response, response["error"] result = response.get("result") assert result is not None - assert int(result["maxFeePerGas"], 16) == max_fee_per_gas - assert int(result["maxPriorityFeePerGas"], 16) == max_priority_fee_per_gas + assert int(result["maxFeePerGas"], 16) > 0 + assert int(result["maxPriorityFeePerGas"], 16) > 0 def test_get_transaction_by_block_number_and_index( self, @@ -105,8 +105,8 @@ def test_get_transaction_by_block_number_and_index( assert "error" not in response, response["error"] result = response.get("result") assert result is not None - assert int(result["maxFeePerGas"], 16) == max_fee_per_gas - assert int(result["maxPriorityFeePerGas"], 16) == max_priority_fee_per_gas + assert int(result["maxFeePerGas"], 16) > 0 + assert int(result["maxPriorityFeePerGas"], 16) > 0 @pytest.mark.neon_only @pytest.mark.parametrize( diff --git a/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py b/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py index 2852297be..f78cb21ef 100644 --- a/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py +++ b/integration/tests/basic/rpc/test_eip_1559_neon_rpc.py @@ -38,11 +38,11 @@ def test_neon_get_transaction_by_sender_nonce( assert "error" not in response, response["error"] max_priority_fee_per_gas_response = response["result"].get("maxPriorityFeePerGas") assert max_priority_fee_per_gas_response is not None - assert int(max_priority_fee_per_gas_response, 16) == max_priority_fee_per_gas + assert int(max_priority_fee_per_gas_response, 16) > 0 max_fee_per_gas_response = response["result"].get("maxFeePerGas") assert max_fee_per_gas is not None - assert int(max_fee_per_gas_response, 16) == max_fee_per_gas + assert int(max_fee_per_gas_response, 16) > 0 def test_neon_get_solana_transaction_by_neon_transaction( self, @@ -92,5 +92,4 @@ def test_neon_get_transaction_receipt( assert "error" not in response, response["error"] actual_effective_gas_price = int(response["result"].get("effectiveGasPrice"), 16) - assert actual_effective_gas_price >= max_priority_fee_per_gas - assert actual_effective_gas_price <= max_fee_per_gas + assert actual_effective_gas_price > 0 From 605c68fad385cf665811b4e0e4c0ab8d60e05cb8 Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Thu, 20 Feb 2025 09:04:46 -0800 Subject: [PATCH 06/10] fixes --- conftest.py | 33 +++++++++++++++++++-------------- integration/tests/conftest.py | 6 +++++- utils/solana_client.py | 22 +++++++++++----------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/conftest.py b/conftest.py index c20252f98..be2d350d8 100644 --- a/conftest.py +++ b/conftest.py @@ -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 @@ -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() @@ -264,12 +262,19 @@ 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") != "mainnet": if balance < 5 * LAMPORT_PER_SOL: - evm_loader.request_airdrop(user.solana_account.pubkey(), 5 * LAMPORT_PER_SOL, commitment=Confirmed) + slow_envs = EnvName.MAINNET, EnvName.DEVNET, EnvName.TESTNET + timeout_sec = 180 if environment.name in slow_envs else 30 + evm_loader.request_airdrop( + pubkey=user.solana_account.pubkey(), + lamports=5 * LAMPORT_PER_SOL, + commitment=Confirmed, + timeout_sec=timeout_sec, + ) return user diff --git a/integration/tests/conftest.py b/integration/tests/conftest.py index 6a8721dcf..2b878c3f8 100644 --- a/integration/tests/conftest.py +++ b/integration/tests/conftest.py @@ -287,12 +287,16 @@ def account_with_all_tokens( neon_mint, operator_keypair, evm_loader_keypair, - bank_account, + bank_account: Keypair | None, ) -> LocalAccount: neon_account = web3_client.create_account_with_balance(faucet, bank_account=eth_bank_account, amount=500) if web3_client_sol: lamports = 10 * LAMPORT_PER_SOL if environment.use_bank: + bank_account: Keypair + log.debug(f"bank_account pubkey: {bank_account.pubkey()}") + log.debug(f"bank_account private key: {bank_account.secret()}") + log.debug(f"bank_account balance: {evm_loader.get_solana_balance(bank_account.pubkey())}") evm_loader.send_sol(bank_account, solana_account.pubkey(), lamports) else: evm_loader.request_airdrop(solana_account.pubkey(), lamports) diff --git a/utils/solana_client.py b/utils/solana_client.py index 5ec1a6cb2..876ceb166 100644 --- a/utils/solana_client.py +++ b/utils/solana_client.py @@ -1,30 +1,29 @@ import json +import pathlib import time import typing as tp import uuid import allure import requests -import pathlib - import solana.rpc.api import spl.token.client -from solders.transaction_status import EncodedConfirmedTransactionWithStatusMeta -from spl.token.client import Token -from solders.keypair import Keypair -from solders.pubkey import Pubkey from solana.rpc.commitment import Commitment, Finalized, Confirmed from solana.rpc.types import TxOpts -from solders.rpc.responses import GetTransactionResp -from solders.signature import Signature -from solders.system_program import TransferParams, transfer, create_account, CreateAccountParams from solana.transaction import Transaction +from solders.keypair import Keypair +from solders.pubkey import Pubkey from solders.rpc.errors import InternalErrorMessage +from solders.rpc.responses import GetTransactionResp from solders.rpc.responses import RequestAirdropResp +from solders.signature import Signature +from solders.system_program import TransferParams, transfer, create_account, CreateAccountParams +from solders.transaction_status import EncodedConfirmedTransactionWithStatusMeta +from spl.token.client import Token +from spl.token.constants import TOKEN_PROGRAM_ID from spl.token.instructions import get_associated_token_address, create_associated_token_account from utils.helpers import wait_condition -from spl.token.constants import TOKEN_PROGRAM_ID class SolanaClient(solana.rpc.api.Client): @@ -40,6 +39,7 @@ def request_airdrop( pubkey: Pubkey, lamports: int, commitment: tp.Optional[Commitment] = None, + timeout_sec: int = 30, ) -> RequestAirdropResp: airdrop_resp = None for _ in range(5): @@ -51,7 +51,7 @@ def request_airdrop( break else: raise AssertionError(f"Can't get airdrop from solana: {airdrop_resp}") - wait_condition(lambda: self.get_balance(pubkey).value >= lamports, timeout_sec=30) + wait_condition(lambda: self.get_balance(pubkey).value >= lamports, timeout_sec=timeout_sec) return airdrop_resp def send_sol(self, from_: Keypair, to: Pubkey, amount_lamports: int): From 6322a5e34142c757e36a05add8d3c8005219c2a8 Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Thu, 20 Feb 2025 09:05:09 -0800 Subject: [PATCH 07/10] addressed changes - Neon can use any fees ignoring user limits --- integration/tests/basic/erc/test_EIP1559.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/integration/tests/basic/erc/test_EIP1559.py b/integration/tests/basic/erc/test_EIP1559.py index 3afe88030..d777a6001 100644 --- a/integration/tests/basic/erc/test_EIP1559.py +++ b/integration/tests/basic/erc/test_EIP1559.py @@ -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}" @@ -146,9 +146,7 @@ def validate_transfer_positive( ) # Verify that the effective gas price does not exceed the max fee per gas - 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}" From 5447b050e7615e74fac8ac32fef8f82378b729e6 Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Thu, 20 Feb 2025 11:39:37 -0800 Subject: [PATCH 08/10] fixes --- conftest.py | 4 ++-- integration/tests/conftest.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/conftest.py b/conftest.py index be2d350d8..96d93df0d 100644 --- a/conftest.py +++ b/conftest.py @@ -268,7 +268,7 @@ def neon_user(evm_loader: EvmLoader, pytestconfig, bank_account, faucet, environ if pytestconfig.getoption("--network") != "mainnet": if balance < 5 * LAMPORT_PER_SOL: slow_envs = EnvName.MAINNET, EnvName.DEVNET, EnvName.TESTNET - timeout_sec = 180 if environment.name in slow_envs else 30 + timeout_sec = 360 if environment.name in slow_envs else 30 evm_loader.request_airdrop( pubkey=user.solana_account.pubkey(), lamports=5 * LAMPORT_PER_SOL, @@ -279,7 +279,7 @@ def neon_user(evm_loader: EvmLoader, pytestconfig, bank_account, faucet, environ @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": diff --git a/integration/tests/conftest.py b/integration/tests/conftest.py index 2b878c3f8..7c8575036 100644 --- a/integration/tests/conftest.py +++ b/integration/tests/conftest.py @@ -295,7 +295,6 @@ def account_with_all_tokens( if environment.use_bank: bank_account: Keypair log.debug(f"bank_account pubkey: {bank_account.pubkey()}") - log.debug(f"bank_account private key: {bank_account.secret()}") log.debug(f"bank_account balance: {evm_loader.get_solana_balance(bank_account.pubkey())}") evm_loader.send_sol(bank_account, solana_account.pubkey(), lamports) else: From 0b81b44b9a7ed0ee8b985feb360a155278747ece Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Fri, 21 Feb 2025 08:48:05 -0800 Subject: [PATCH 09/10] fixes --- conftest.py | 16 ++++++++++------ integration/tests/conftest.py | 6 ++---- utils/solana_client.py | 3 +-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/conftest.py b/conftest.py index 96d93df0d..07c8793de 100644 --- a/conftest.py +++ b/conftest.py @@ -265,15 +265,19 @@ def accounts_session(pytestconfig: Config, web3_client_session, faucet, eth_bank 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") != "mainnet": - if balance < 5 * LAMPORT_PER_SOL: - slow_envs = EnvName.MAINNET, EnvName.DEVNET, EnvName.TESTNET - timeout_sec = 360 if environment.name in slow_envs else 30 + + 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) + else: + lamports = 5 * LAMPORT_PER_SOL + if balance < lamports: evm_loader.request_airdrop( pubkey=user.solana_account.pubkey(), - lamports=5 * LAMPORT_PER_SOL, + lamports=lamports, commitment=Confirmed, - timeout_sec=timeout_sec, ) return user diff --git a/integration/tests/conftest.py b/integration/tests/conftest.py index 7c8575036..dff444f4b 100644 --- a/integration/tests/conftest.py +++ b/integration/tests/conftest.py @@ -273,7 +273,7 @@ def evm_loader(environment: EnvironmentConfig) -> EvmLoader: ) -@pytest.fixture(scope="class") +@pytest.fixture(scope="session") def account_with_all_tokens( evm_loader, solana_account, @@ -291,11 +291,9 @@ def account_with_all_tokens( ) -> LocalAccount: neon_account = web3_client.create_account_with_balance(faucet, bank_account=eth_bank_account, amount=500) if web3_client_sol: - lamports = 10 * LAMPORT_PER_SOL + lamports = 1 * LAMPORT_PER_SOL if environment.use_bank: bank_account: Keypair - log.debug(f"bank_account pubkey: {bank_account.pubkey()}") - log.debug(f"bank_account balance: {evm_loader.get_solana_balance(bank_account.pubkey())}") evm_loader.send_sol(bank_account, solana_account.pubkey(), lamports) else: evm_loader.request_airdrop(solana_account.pubkey(), lamports) diff --git a/utils/solana_client.py b/utils/solana_client.py index 876ceb166..74778826b 100644 --- a/utils/solana_client.py +++ b/utils/solana_client.py @@ -39,7 +39,6 @@ def request_airdrop( pubkey: Pubkey, lamports: int, commitment: tp.Optional[Commitment] = None, - timeout_sec: int = 30, ) -> RequestAirdropResp: airdrop_resp = None for _ in range(5): @@ -51,7 +50,7 @@ def request_airdrop( break else: raise AssertionError(f"Can't get airdrop from solana: {airdrop_resp}") - wait_condition(lambda: self.get_balance(pubkey).value >= lamports, timeout_sec=timeout_sec) + wait_condition(lambda: self.get_balance(pubkey).value >= lamports, timeout_sec=30) return airdrop_resp def send_sol(self, from_: Keypair, to: Pubkey, amount_lamports: int): From aa4e389a7641119b4ffc8b9eafdd82bc95ecb4dc Mon Sep 17 00:00:00 2001 From: Ivan Adamov Date: Fri, 21 Feb 2025 11:28:19 -0800 Subject: [PATCH 10/10] fixes --- .../solana_signature/test_send_scheduled_transactions.py | 8 -------- integration/tests/conftest.py | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py b/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py index 80a0ccf65..13d73ded1 100644 --- a/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py +++ b/integration/tests/basic/solana_signature/test_send_scheduled_transactions.py @@ -38,7 +38,6 @@ def test_send_simple_single_trx(self, web3_client_sol, neon_user, common_contrac assert pending_trx[hex(tx.nonce)][0]["status"] in ("Done", "InProgress") assert common_contract.functions.getNumber().call() == contract_data - @pytest.mark.eip_1559 def test_multiple_scheduled_trx(self, web3_client_sol, neon_user, common_contract, evm_loader, treasury_pool): nonce = web3_client_sol.get_nonce(neon_user.checksum_address) contract_data = 18 @@ -83,7 +82,6 @@ def test_multiple_scheduled_trx(self, web3_client_sol, neon_user, common_contrac assert pending_trx[hex(nonce)][0]["status"] == "Done" assert pending_trx[hex(nonce)][0]["hash"][2:] == trxs[0].hash().hex() - @pytest.mark.eip_1559 def test_multiple_scheduled_trx_with_failed_trx( self, web3_client_sol, neon_user, treasury_pool, revert_contract_caller, event_caller_contract, evm_loader ): @@ -232,7 +230,6 @@ def test_scheduled_trx_send_tokens_to_sol_chain_contract( assert event_logs[0].args.value == value assert event_logs[0].event == "IndexedArgs" - @pytest.mark.eip_1559 def test_scheduled_trx_with_timestamp( self, block_timestamp_contract, web3_client_sol, neon_user, treasury_pool, evm_loader, json_rpc_client ): @@ -285,7 +282,6 @@ def test_scheduled_trx_with_timestamp( assert added_timestamp <= int(tx_block_timestamp, 16) assert contract.functions.getDataFromMapping(added_timestamp).call() == [v1, v2] - @pytest.mark.eip_1559 def test_scheduled_trx_with_small_gas_limit( self, block_timestamp_contract, web3_client_sol, neon_user, treasury_pool, evm_loader, event_caller_contract ): @@ -364,7 +360,6 @@ def test_long_chain_iterative_scheduled_trx( @allure.feature("Solana native") @allure.story("Test sending scheduled transaction ERC20ForSplNew") class TestScheduledTrxERC20new: - def test_scheduled_trx_pda_balance( self, web3_client_sol, neon_user, erc20_spl_mintable_new, evm_loader, treasury_pool ): @@ -502,7 +497,6 @@ def test_scheduled_trx_both_pda_and_ata_used( def test_scheduled_trx_transferSolana_ata_balance_not_used( self, web3_client_sol, neon_user, erc20_spl_mintable_new, evm_loader, treasury_pool ): - token_mint = Pubkey(erc20_spl_mintable_new.contract.functions.tokenMint().call()) erc20_spl_mintable_new.pop_up_balance(evm_loader, recipient=neon_user, pda_amount=5, ata_amount=2000) @@ -541,7 +535,6 @@ def test_scheduled_trx_transferSolana_ata_balance_not_used( def test_multiple_transactions_with_transfer_from_solana( self, web3_client_sol, neon_user, erc20_spl_mintable_new, evm_loader, treasury_pool ): - token_mint = Pubkey(erc20_spl_mintable_new.contract.functions.tokenMint().call()) my_ata = get_associated_token_address(neon_user.solana_account.pubkey(), token_mint) @@ -695,7 +688,6 @@ def test_multiple_transactions_with_tree_actions_dependent_trx( def test_multiple_transactions_with_tree_actions_independent( self, web3_client_sol, neon_user, erc20_spl_mintable_new, evm_loader, treasury_pool ): - recipient = NeonUser(evm_loader.loader_id) amount_to_transfer = 1_000 diff --git a/integration/tests/conftest.py b/integration/tests/conftest.py index f68e8a9ec..052dd2fec 100644 --- a/integration/tests/conftest.py +++ b/integration/tests/conftest.py @@ -9,7 +9,6 @@ import allure import base58 import pytest - from _pytest.config import Config from eth_account.signers.local import LocalAccount from solana.rpc import commitment @@ -340,7 +339,7 @@ def evm_loader(environment: EnvironmentConfig) -> EvmLoader: def account_with_all_tokens( evm_loader, solana_account, - web3_client, + web3_client_session, web3_client_usdt, web3_client_eth, web3_client_sol, @@ -352,7 +351,7 @@ def account_with_all_tokens( evm_loader_keypair, bank_account: Keypair | None, ) -> LocalAccount: - neon_account = web3_client.create_account_with_balance(faucet, bank_account=eth_bank_account, amount=500) + neon_account = web3_client_session.create_account_with_balance(faucet, bank_account=eth_bank_account, amount=500) if web3_client_sol: lamports = 2 * LAMPORT_PER_SOL if environment.use_bank: