From 0cb9dac66b7579842ffb571f591a067a36c3cf4e Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 20 Feb 2025 11:31:35 +0800 Subject: [PATCH 1/2] Add statistics for gas and bridge fee income. --- include/evm_runtime/evm_contract.hpp | 3 ++ include/evm_runtime/tables.hpp | 10 +++++++ src/actions.cpp | 43 ++++++++++++++++++++++++++++ tests/basic_evm_tester.cpp | 8 ++++++ tests/basic_evm_tester.hpp | 10 +++++++ tests/gas_fee_tests.cpp | 19 ++++++++++++ tests/native_token_tests.cpp | 9 ++++++ 7 files changed, 102 insertions(+) diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index a93faaf5..ef6551d2 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -152,6 +152,9 @@ class [[eosio::contract]] evm_contract : public contract void process_tx(const runtime_config& rc, eosio::name miner, const transaction& tx, std::optional min_inclusion_price); void dispatch_tx(const runtime_config& rc, const transaction& tx); + + struct statistics get_statistics() const; + void set_statistics(const struct statistics &v); }; } // namespace evm_runtime diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index 86b7566f..c7da9d59 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -363,4 +363,14 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] price_queue typedef eosio::multi_index<"pricequeue"_n, price_queue> price_queue_table; +struct [[eosio::table("statistics")]] [[eosio::contract("evm_contract")]] statistics +{ + unsigned_int version; // placeholder for future variant index + balance_with_dust gas_fee_income; + balance_with_dust ingress_bridge_fee_income; + + EOSLIB_SERIALIZE(statistics, (version)(gas_fee_income)(ingress_bridge_fee_income)); +}; +typedef eosio::singleton<"statistics"_n, statistics> statistics_singleton; + } //namespace evm_runtime diff --git a/src/actions.cpp b/src/actions.cpp index f607d98e..99168a59 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -195,6 +195,7 @@ Receipt evm_contract::execute_tx(const runtime_config& rc, eosio::name miner, Bl } bool deducted_miner_cut = false; + bool from_self = false; std::optional inevm; auto populate_bridge_accessors = [&]() { @@ -223,6 +224,8 @@ Receipt evm_contract::execute_tx(const runtime_config& rc, eosio::name miner, Bl if (is_reserved_address(*tx.from)) { const name ingress_account(*extract_reserved_address(*tx.from)); + from_self = ingress_account == get_self(); + const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; @@ -330,6 +333,27 @@ Receipt evm_contract::execute_tx(const runtime_config& rc, eosio::name miner, Bl }); } + // Statistics + if (!from_self) { + // Gas income from tx sent from self should not be counted. + // Bridge transfers can generate such txs. + uint64_t tx_gas_used = receipt.cumulative_gas_used; // Only transaction in the "block" so cumulative_gas_used is the tx gas_used. + auto s = get_statistics(); + if (_config->get_evm_version() >= 1) { + intx::uint512 gas_fee = intx::uint256(tx_gas_used) * ep.evm().block().header.base_fee_per_gas.value(); + check(gas_fee < std::numeric_limits::max(), "too much gas"); + s.gas_fee_income += static_cast(gas_fee); + } else { + intx::uint512 gas_fee = intx::uint256(tx_gas_used) * tx.max_fee_per_gas; + check(gas_fee < std::numeric_limits::max(), "too much gas"); + if (gas_fee_miner_portion.has_value()) { + gas_fee -= *gas_fee_miner_portion; + } + s.gas_fee_income += static_cast(gas_fee); + } + set_statistics(s); + } + LOGTIME("EVM EXECUTE"); return receipt; } @@ -653,6 +677,11 @@ void evm_contract::handle_evm_transfer(eosio::asset quantity, const std::string& quantity -= _config->get_ingress_bridge_fee(); eosio::check(quantity.amount > 0, "must bridge more than ingress bridge fee"); + // Statistics + auto s = get_statistics(); + s.ingress_bridge_fee_income.balance += _config->get_ingress_bridge_fee(); + set_statistics(s); + const std::optional address_bytes = from_hex(memo); eosio::check(!!address_bytes, "unable to parse destination address"); @@ -903,4 +932,18 @@ void evm_contract::setgaslimit(uint64_t ingress_gas_limit) { _config->set_ingress_gas_limit(ingress_gas_limit); } +statistics evm_contract::get_statistics() const { + statistics_singleton statistics_v(get_self(), get_self().value); + return statistics_v.get_or_create(get_self(), statistics { + .version = 0, + .ingress_bridge_fee_income = { .balance = eosio::asset(0, _config->get_ingress_bridge_fee().symbol), .dust = 0 }, + .gas_fee_income = { .balance = eosio::asset(0, _config->get_ingress_bridge_fee().symbol), .dust = 0 }, + }); +} + +void evm_contract::set_statistics(const statistics &v) { + statistics_singleton statistics_v(get_self(), get_self().value); + statistics_v.set(v, get_self()); +} + } //evm_runtime diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 2fcd6844..adf04be1 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -320,6 +320,14 @@ config_table_row basic_evm_tester::get_config() const return fc::raw::unpack(d); } +statistics basic_evm_tester::get_statistics() const +{ + static constexpr eosio::chain::name statistics_singleton_name = "statistics"_n; + const vector d = + get_row_by_account(evm_account_name, evm_account_name, statistics_singleton_name, statistics_singleton_name); + return fc::raw::unpack(d); +} + config2_table_row basic_evm_tester::get_config2() const { static constexpr eosio::chain::name config2_singleton_name = "config2"_n; diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index 6753be00..1fceab6c 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -121,6 +121,13 @@ struct balance_and_dust bool operator!=(const balance_and_dust&) const; }; +struct statistics +{ + unsigned_int version; // placeholder for future variant index + balance_and_dust gas_fee_income; + balance_and_dust ingress_bridge_fee_income; +}; + struct account_object { enum class flag : uint32_t { @@ -214,6 +221,7 @@ FC_REFLECT(evm_test::evm_version_type, (pending_version)(cached_version)) FC_REFLECT(evm_test::evm_version_type::pending, (version)(time)) FC_REFLECT(evm_test::config2_table_row,(next_account_id)) FC_REFLECT(evm_test::balance_and_dust, (balance)(dust)); +FC_REFLECT(evm_test::statistics, (version)(gas_fee_income)(ingress_bridge_fee_income)); FC_REFLECT(evm_test::account_object, (id)(address)(nonce)(balance)) FC_REFLECT(evm_test::storage_slot, (id)(key)(value)) FC_REFLECT(evm_test::fee_parameters, (gas_price)(miner_cut)(ingress_bridge_fee)) @@ -433,6 +441,8 @@ class basic_evm_tester : public evm_validating_tester config_table_row get_config() const; config2_table_row get_config2() const; + statistics get_statistics() const; + void setfeeparams(const fee_parameters& fee_params); silkworm::Transaction diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index ba2fb596..acf30600 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -293,6 +293,10 @@ try { const intx::uint256 miner_balance_before{vault_balance(miner_account_name)}; const intx::uint256 faucet_before = evm_balance(faucet_eoa).value(); + // Gas fee statistics + const auto s = get_statistics(); + const auto gas_fee_sum_before = s.gas_fee_income; + auto tx = generate_tx(recipient.address, 1_gwei); faucet_eoa.sign(tx); pushtx(tx, miner_account_name); @@ -305,6 +309,11 @@ try { BOOST_CHECK_EQUAL(static_cast(vault_balance(miner_account_name)), (miner_balance_before + gas_fee_miner_portion)); + // Gas fee statistics + const auto s2 = get_statistics(); + BOOST_CHECK_EQUAL(static_cast(s2.gas_fee_income), + static_cast(gas_fee_sum_before) + gas_fee - gas_fee_miner_portion); + faucet_eoa.next_nonce = 0; }; @@ -464,6 +473,11 @@ try { //21'000 * 50'000'000'000 = 0.00105 BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(10), 50'000'000'000'000ULL})); + // Gas fee statistics + //21'000 * 300'000'000'000 = 0.0063 + const auto s = get_statistics(); + BOOST_REQUIRE(s.gas_fee_income == (balance_and_dust{make_asset(63), 0ULL})); + tx = generate_tx(evm2.address, 1); tx.type = silkworm::TransactionType::kDynamicFee; tx.max_priority_fee_per_gas = 0; @@ -474,6 +488,11 @@ try { //0.00105 + 0 BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(10), 50'000'000'000'000ULL})); + + // Gas fee statistics + // 0.0063 + 0.0063 = 0.0126 + const auto s2 = get_statistics(); + BOOST_REQUIRE(s2.gas_fee_income == (balance_and_dust{make_asset(126), 0ULL})); } FC_LOG_AND_RETHROW() diff --git a/tests/native_token_tests.cpp b/tests/native_token_tests.cpp index 081dc8e3..f6690967 100644 --- a/tests/native_token_tests.cpp +++ b/tests/native_token_tests.cpp @@ -294,6 +294,10 @@ BOOST_FIXTURE_TEST_CASE(basic_eos_evm_bridge, native_token_evm_tester_EOS) try { //transfer 2.0000 EOS from alice to evm1 account, expect 1.9000 to be delivered to evm1 account, 0.1000 to contract balance { + const auto s = get_statistics(); + const auto initial_gas_count = s.gas_fee_income; + BOOST_REQUIRE_EQUAL(s.ingress_bridge_fee_income.balance, make_asset(0)); + const int64_t to_bridge = 2'0000; const int64_t alice_native_before = native_balance("alice"_n); BOOST_REQUIRE(!!evm_balance(evm1)); @@ -309,6 +313,11 @@ BOOST_FIXTURE_TEST_CASE(basic_eos_evm_bridge, native_token_evm_tester_EOS) try { intx::uint256 new_special_balance{initial_special_balance}; new_special_balance += smallest * bridge_fee; BOOST_REQUIRE_EQUAL(static_cast(vault_balance("evm"_n)), new_special_balance); + + const auto s2 = get_statistics(); + BOOST_REQUIRE_EQUAL(s2.ingress_bridge_fee_income.balance, make_asset(bridge_fee)); + // Bridge transfers should not generate gas income as the gas fee for the evm transfer is paid by evm_runtime. + BOOST_REQUIRE(s2.gas_fee_income == initial_gas_count); } } FC_LOG_AND_RETHROW() From 307671cac5301ddc7b76f8298ca02645a5cd1d3a Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 20 Feb 2025 13:58:59 +0800 Subject: [PATCH 2/2] Move tests to dedicated test suite. --- tests/CMakeLists.txt | 1 + tests/gas_fee_tests.cpp | 19 --- tests/native_token_tests.cpp | 9 -- tests/statistics_tests.cpp | 290 +++++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+), 28 deletions(-) create mode 100644 tests/statistics_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7499bf0a..3f18f6be 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ add_eosio_test_executable( unit_test ${CMAKE_SOURCE_DIR}/chainid_tests.cpp ${CMAKE_SOURCE_DIR}/bridge_message_tests.cpp ${CMAKE_SOURCE_DIR}/admin_actions_tests.cpp + ${CMAKE_SOURCE_DIR}/statistics_tests.cpp ${CMAKE_SOURCE_DIR}/main.cpp ${CMAKE_SOURCE_DIR}/../silkworm/silkworm/core/rlp/encode.cpp ${CMAKE_SOURCE_DIR}/../silkworm/silkworm/core/rlp/decode.cpp diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index acf30600..ba2fb596 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -293,10 +293,6 @@ try { const intx::uint256 miner_balance_before{vault_balance(miner_account_name)}; const intx::uint256 faucet_before = evm_balance(faucet_eoa).value(); - // Gas fee statistics - const auto s = get_statistics(); - const auto gas_fee_sum_before = s.gas_fee_income; - auto tx = generate_tx(recipient.address, 1_gwei); faucet_eoa.sign(tx); pushtx(tx, miner_account_name); @@ -309,11 +305,6 @@ try { BOOST_CHECK_EQUAL(static_cast(vault_balance(miner_account_name)), (miner_balance_before + gas_fee_miner_portion)); - // Gas fee statistics - const auto s2 = get_statistics(); - BOOST_CHECK_EQUAL(static_cast(s2.gas_fee_income), - static_cast(gas_fee_sum_before) + gas_fee - gas_fee_miner_portion); - faucet_eoa.next_nonce = 0; }; @@ -473,11 +464,6 @@ try { //21'000 * 50'000'000'000 = 0.00105 BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(10), 50'000'000'000'000ULL})); - // Gas fee statistics - //21'000 * 300'000'000'000 = 0.0063 - const auto s = get_statistics(); - BOOST_REQUIRE(s.gas_fee_income == (balance_and_dust{make_asset(63), 0ULL})); - tx = generate_tx(evm2.address, 1); tx.type = silkworm::TransactionType::kDynamicFee; tx.max_priority_fee_per_gas = 0; @@ -488,11 +474,6 @@ try { //0.00105 + 0 BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(10), 50'000'000'000'000ULL})); - - // Gas fee statistics - // 0.0063 + 0.0063 = 0.0126 - const auto s2 = get_statistics(); - BOOST_REQUIRE(s2.gas_fee_income == (balance_and_dust{make_asset(126), 0ULL})); } FC_LOG_AND_RETHROW() diff --git a/tests/native_token_tests.cpp b/tests/native_token_tests.cpp index f6690967..081dc8e3 100644 --- a/tests/native_token_tests.cpp +++ b/tests/native_token_tests.cpp @@ -294,10 +294,6 @@ BOOST_FIXTURE_TEST_CASE(basic_eos_evm_bridge, native_token_evm_tester_EOS) try { //transfer 2.0000 EOS from alice to evm1 account, expect 1.9000 to be delivered to evm1 account, 0.1000 to contract balance { - const auto s = get_statistics(); - const auto initial_gas_count = s.gas_fee_income; - BOOST_REQUIRE_EQUAL(s.ingress_bridge_fee_income.balance, make_asset(0)); - const int64_t to_bridge = 2'0000; const int64_t alice_native_before = native_balance("alice"_n); BOOST_REQUIRE(!!evm_balance(evm1)); @@ -313,11 +309,6 @@ BOOST_FIXTURE_TEST_CASE(basic_eos_evm_bridge, native_token_evm_tester_EOS) try { intx::uint256 new_special_balance{initial_special_balance}; new_special_balance += smallest * bridge_fee; BOOST_REQUIRE_EQUAL(static_cast(vault_balance("evm"_n)), new_special_balance); - - const auto s2 = get_statistics(); - BOOST_REQUIRE_EQUAL(s2.ingress_bridge_fee_income.balance, make_asset(bridge_fee)); - // Bridge transfers should not generate gas income as the gas fee for the evm transfer is paid by evm_runtime. - BOOST_REQUIRE(s2.gas_fee_income == initial_gas_count); } } FC_LOG_AND_RETHROW() diff --git a/tests/statistics_tests.cpp b/tests/statistics_tests.cpp new file mode 100644 index 00000000..33686464 --- /dev/null +++ b/tests/statistics_tests.cpp @@ -0,0 +1,290 @@ +#include "basic_evm_tester.hpp" + +using namespace eosio::testing; +using namespace evm_test; + +#include + +struct statistics_evm_tester : basic_evm_tester +{ + evm_eoa faucet_eoa; + + static constexpr name miner_account_name = "alice"_n; + + statistics_evm_tester() : + faucet_eoa(evmc::from_hex("a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954").value()) + { + create_accounts({miner_account_name}); + transfer_token(faucet_account_name, miner_account_name, make_asset(100'0000)); + } + + void fund_evm_faucet() + { + transfer_token(faucet_account_name, evm_account_name, make_asset(100'0000), faucet_eoa.address_0x()); + } +}; + +BOOST_AUTO_TEST_SUITE(statistics_evm_tests) + + +BOOST_FIXTURE_TEST_CASE(gas_fee_statistics_miner_cut, statistics_evm_tester) +try { + produce_block(); + control->abort_block(); + + static constexpr uint32_t hundred_percent = 100'000; + + evm_eoa recipient; + + auto dump_accounts = [&]() { + scan_accounts([](evm_test::account_object&& account) -> bool { + idump((account)); + return false; + }); + }; + + struct gas_fee_data + { + uint64_t gas_price; + uint32_t miner_cut; + uint64_t expected_gas_fee_miner_portion; + uint64_t expected_gas_fee_contract_portion; + }; + + std::vector gas_fee_trials = { + {1'000'000'000, 50'000, 10'500'000'000'000, 10'500'000'000'000}, + {1'000'000'000, 0, 0, 21'000'000'000'000}, + {1'000'000'000, 10'000, 2'100'000'000'000, 18'900'000'000'000}, + {1'000'000'000, 90'000, 18'900'000'000'000, 2'100'000'000'000}, + }; + + // EVM contract account acts as the miner + auto run_test_with_contract_as_miner = [this, &recipient](const gas_fee_data& trial) { + speculative_block_starter sb{*this}; + + init(evm_chain_id, trial.gas_price, trial.miner_cut); + fund_evm_faucet(); + + const auto gas_fee = intx::uint256{trial.gas_price * 21000}; + + BOOST_CHECK_EQUAL(gas_fee, + intx::uint256(trial.expected_gas_fee_miner_portion + trial.expected_gas_fee_contract_portion)); + + const intx::uint256 special_balance_before{vault_balance(evm_account_name)}; + const intx::uint256 faucet_before = evm_balance(faucet_eoa).value(); + + auto tx = generate_tx(recipient.address, 1_gwei); + faucet_eoa.sign(tx); + pushtx(tx); + + BOOST_CHECK_EQUAL(*evm_balance(faucet_eoa), (faucet_before - tx.value - gas_fee)); + BOOST_REQUIRE(evm_balance(recipient).has_value()); + BOOST_CHECK_EQUAL(*evm_balance(recipient), tx.value); + BOOST_CHECK_EQUAL(static_cast(vault_balance(evm_account_name)), + (special_balance_before + gas_fee)); + + faucet_eoa.next_nonce = 0; + }; + + for (const auto& trial : gas_fee_trials) { + run_test_with_contract_as_miner(trial); + } + + // alice acts as the miner + auto run_test_with_alice_as_miner = [this, &recipient](const gas_fee_data& trial) { + speculative_block_starter sb{*this}; + + init(evm_chain_id, trial.gas_price, trial.miner_cut); + fund_evm_faucet(); + open(miner_account_name); + + const auto gas_fee = intx::uint256{trial.gas_price * 21000}; + const auto gas_fee_miner_portion = (gas_fee * trial.miner_cut) / hundred_percent; + + BOOST_CHECK_EQUAL(gas_fee_miner_portion, intx::uint256(trial.expected_gas_fee_miner_portion)); + + const auto gas_fee_contract_portion = gas_fee - gas_fee_miner_portion; + BOOST_CHECK_EQUAL(gas_fee_contract_portion, intx::uint256(trial.expected_gas_fee_contract_portion)); + + const intx::uint256 special_balance_before{vault_balance(evm_account_name)}; + const intx::uint256 miner_balance_before{vault_balance(miner_account_name)}; + const intx::uint256 faucet_before = evm_balance(faucet_eoa).value(); + + // Gas fee statistics + const auto s = get_statistics(); + const auto gas_fee_sum_before = s.gas_fee_income; + + auto tx = generate_tx(recipient.address, 1_gwei); + faucet_eoa.sign(tx); + pushtx(tx, miner_account_name); + + BOOST_CHECK_EQUAL(*evm_balance(faucet_eoa), (faucet_before - tx.value - gas_fee)); + BOOST_REQUIRE(evm_balance(recipient).has_value()); + BOOST_CHECK_EQUAL(*evm_balance(recipient), tx.value); + BOOST_CHECK_EQUAL(static_cast(vault_balance(evm_account_name)), + (special_balance_before + gas_fee - gas_fee_miner_portion)); + BOOST_CHECK_EQUAL(static_cast(vault_balance(miner_account_name)), + (miner_balance_before + gas_fee_miner_portion)); + + // Gas fee statistics + const auto s2 = get_statistics(); + BOOST_CHECK_EQUAL(static_cast(s2.gas_fee_income), + static_cast(gas_fee_sum_before) + gas_fee - gas_fee_miner_portion); + + faucet_eoa.next_nonce = 0; + }; + + for (const auto& trial : gas_fee_trials) { + run_test_with_alice_as_miner(trial); + } +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(gas_fee_statistics_miner_cut_v1, statistics_evm_tester) +try { + static constexpr uint64_t base_gas_price = 300'000'000'000; // 300 gwei + + init(); + + auto miner_account = "miner"_n; + create_accounts({miner_account}); + open(miner_account); + + // Set base price + setfeeparams({.gas_price=base_gas_price}); + + auto config = get_config(); + BOOST_REQUIRE(config.miner_cut == suggested_miner_cut); + + // Set version 1 + setversion(1, evm_account_name); + + produce_blocks(3); + + // Fund evm1 address with 10.0000 EOS / trigger version change and sets miner_cut to 0 + evm_eoa evm1; + transfer_token("alice"_n, evm_account_name, make_asset(10'0000), evm1.address_0x()); + + config = get_config(); + BOOST_REQUIRE(config.miner_cut == 0); + + // miner_cut can't be changed when version >= 1 + BOOST_REQUIRE_EXCEPTION(setfeeparams({.miner_cut=10'0000}), + eosio_assert_message_exception, + [](const eosio_assert_message_exception& e) {return testing::expect_assert_message(e, "assertion failure with message: can't set miner_cut");}); + + auto inclusion_price = 50'000'000'000; // 50 gwei + + evm_eoa evm2; + auto tx = generate_tx(evm2.address, 1); + tx.type = silkworm::TransactionType::kDynamicFee; + tx.max_priority_fee_per_gas = inclusion_price*2; + tx.max_fee_per_gas = base_gas_price + inclusion_price; + + BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(0), 0ULL})); + + evm1.sign(tx); + pushtx(tx, miner_account); + + //21'000 * 50'000'000'000 = 0.00105 + BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(10), 50'000'000'000'000ULL})); + + // Gas fee statistics + //21'000 * 300'000'000'000 = 0.0063 + const auto s = get_statistics(); + BOOST_REQUIRE(s.gas_fee_income == (balance_and_dust{make_asset(63), 0ULL})); + + tx = generate_tx(evm2.address, 1); + tx.type = silkworm::TransactionType::kDynamicFee; + tx.max_priority_fee_per_gas = 0; + tx.max_fee_per_gas = base_gas_price + inclusion_price; + + evm1.sign(tx); + pushtx(tx, miner_account); + + //0.00105 + 0 + BOOST_REQUIRE(vault_balance(miner_account) == (balance_and_dust{make_asset(10), 50'000'000'000'000ULL})); + + // Gas fee statistics + // 0.0063 + 0.0063 = 0.0126 + const auto s2 = get_statistics(); + BOOST_REQUIRE(s2.gas_fee_income == (balance_and_dust{make_asset(126), 0ULL})); +} +FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE(gas_fee_statistics_basic_eos_evm_bridge, statistics_evm_tester) try { + + init(); + fund_evm_faucet(); + open(miner_account_name); + + evm_eoa evm1, evm2; + + //reminder: .0001 EOS is 100 szabos + const intx::uint256 smallest = 100_szabo; + + //to start with, there is no ingress bridge fee. should be 1->1 + + const auto s = get_statistics(); + const auto initial_gas_count = s.gas_fee_income; + const auto initial_bridge_count = s.ingress_bridge_fee_income; + + //transfer 1.0000 EOS from alice to evm1 account + { + const int64_t to_bridge = 1'0000; + + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + const auto s2 = get_statistics(); + BOOST_REQUIRE_EQUAL(s2.ingress_bridge_fee_income.balance, make_asset(0)); + // Bridge transfers should not generate gas income as the gas fee for the evm transfer is paid by evm_runtime. + BOOST_REQUIRE(s2.gas_fee_income == initial_gas_count); + } + + //transfer 0.5000 EOS from alice to evm2 account + { + const int64_t to_bridge = 5000; + + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm2.address_0x()); + + const auto s2 = get_statistics(); + BOOST_REQUIRE(s2.ingress_bridge_fee_income == initial_bridge_count); + // Bridge transfers should not generate gas income as the gas fee for the evm transfer is paid by evm_runtime. + BOOST_REQUIRE(s2.gas_fee_income == initial_gas_count); + } + + //transfer 0.1234 EOS from alice to evm1 account + { + const int64_t to_bridge = 1234; + + const intx::uint256 evm1_before = *evm_balance(evm1); + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + const auto s2 = get_statistics(); + BOOST_REQUIRE(s2.ingress_bridge_fee_income == initial_bridge_count); + // Bridge transfers should not generate gas income as the gas fee for the evm transfer is paid by evm_runtime. + BOOST_REQUIRE(s2.gas_fee_income == initial_gas_count); + + } + + //set the bridge free to 0.1000 EOS + const int64_t bridge_fee = 1000; + setfeeparams(fee_parameters{.ingress_bridge_fee = make_asset(bridge_fee)}); + + //transfer 2.0000 EOS from alice to evm1 account, expect 1.9000 to be delivered to evm1 account, 0.1000 to contract balance + { + const int64_t to_bridge = 2'0000; + + const intx::uint256 evm1_before = *evm_balance(evm1); + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + const auto s2 = get_statistics(); + BOOST_REQUIRE_EQUAL(s2.ingress_bridge_fee_income.balance, make_asset(bridge_fee)); + // Bridge transfers should not generate gas income as the gas fee for the evm transfer is paid by evm_runtime. + BOOST_REQUIRE(s2.gas_fee_income == initial_gas_count); + } + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END()