From 419597787b3c479e1766916f64001b6e9e083019 Mon Sep 17 00:00:00 2001 From: Sunday <38057360+quocle108@users.noreply.github.com> Date: Thu, 23 Nov 2023 07:56:54 +0700 Subject: [PATCH] adjust chain inflation (#84) * adjust chain inflation * add more tests * burn the excess inflation token * update --- contracts/eosio.system/src/producer_pay.cpp | 29 ++++- tests/eosio.system_tester.hpp | 14 +- tests/eosio.system_tests.cpp | 135 ++++++++++++++++++++ 3 files changed, 172 insertions(+), 6 deletions(-) diff --git a/contracts/eosio.system/src/producer_pay.cpp b/contracts/eosio.system/src/producer_pay.cpp index 0a5c3684..d4a55bd2 100644 --- a/contracts/eosio.system/src/producer_pay.cpp +++ b/contracts/eosio.system/src/producer_pay.cpp @@ -79,15 +79,24 @@ namespace eosiosystem { const auto usecs_since_last_fill = (ct - _gstate.last_pervote_bucket_fill).count(); if( usecs_since_last_fill > 0 && _gstate.last_pervote_bucket_fill > time_point() ) { - auto new_tokens = static_cast( (continuous_rate * double(token_supply.amount) * double(usecs_since_last_fill)) / double(useconds_per_year) ); + auto current_fees = eosio::token::get_balance(token_account, fees_account, core_symbol().code() ); + auto distribute_tokens = static_cast( (continuous_rate * double(token_supply.amount) * double(usecs_since_last_fill)) / double(useconds_per_year) ); + auto fees_to_use = std::min( distribute_tokens, current_fees.amount ); + auto issue_tokens = distribute_tokens - fees_to_use; // needs to be 2/5 Savings, 2/5 Voters, 1/5 producers - auto to_per_block_pay = new_tokens / 5; + auto to_per_block_pay = distribute_tokens / 5; auto to_voters = 2 * to_per_block_pay; - auto to_savings = new_tokens - (to_voters + to_per_block_pay); + auto to_savings = distribute_tokens - (to_voters + to_per_block_pay); { - token::issue_action issue_act{ token_account, { {get_self(), active_permission} } }; - issue_act.send( get_self(), asset(new_tokens, core_symbol()), "issue tokens for producer pay and savings" ); + if( issue_tokens > 0 ){ + token::issue_action issue_act{ token_account, { {get_self(), active_permission} } }; + issue_act.send( get_self(), asset(issue_tokens, core_symbol()), "issue tokens for producer pay and savings" ); + } + if( fees_to_use > 0 ){ + token::transfer_action transfer_act{ token_account, { {fees_account, active_permission} } }; + transfer_act.send( fees_account, get_self(), asset(fees_to_use, core_symbol()), "collect tokenomic fees" ); + } } { token::transfer_action transfer_act{ token_account, { {get_self(), active_permission} } }; @@ -99,6 +108,16 @@ namespace eosiosystem { _gstate.perblock_bucket += to_per_block_pay; _gstate.voters_bucket += to_voters; _gstate.last_pervote_bucket_fill = ct; + + // burn remaining tokenomic fees + auto burn_fees = std::max(int64_t(0), (current_fees.amount - fees_to_use) ); + if( burn_fees > 0 ){ + token::transfer_action transfer_act{ token_account, { {fees_account, active_permission} } }; + transfer_act.send( fees_account, get_self(), asset(burn_fees, core_symbol()), "burn tokenomic fees" ); + + token::retire_action retire_act{ token_account, { {get_self(), active_permission} } }; + retire_act.send( asset(burn_fees, core_symbol()), "burn tokenomic fees" ); + } } } diff --git a/tests/eosio.system_tester.hpp b/tests/eosio.system_tester.hpp index af579114..162b82a9 100644 --- a/tests/eosio.system_tester.hpp +++ b/tests/eosio.system_tester.hpp @@ -84,7 +84,7 @@ class eosio_system_tester : public TESTER { } } - void remaining_setup() { + void remaining_setup(symbol core_symbol = symbol{CORE_SYM}) { produce_blocks(); // Assumes previous setup steps were done with core token symbol set to CORE_SYM @@ -93,6 +93,10 @@ class eosio_system_tester : public TESTER { create_account_with_resources( "carol1111111"_n, config::system_account_name, core_sym::from_string("1.0000"), false ); BOOST_REQUIRE_EQUAL( core_sym::from_string("1000000000.0000"), get_balance("eosio") + get_balance("eosio.ramfee") + get_balance("eosio.stake") + get_balance("eosio.ram") ); + + // open ram for eosio.fees account + open( "eosio.fees"_n, core_symbol ); + BOOST_REQUIRE_EQUAL( asset(0, core_symbol), get_balance( "eosio.fees", core_symbol ) ); } enum class setup_level { @@ -836,6 +840,14 @@ class eosio_system_tester : public TESTER { ); } + void open( const name& owner, const symbol& symbolname, const name& manager = config::system_account_name) { + base_tester::push_action( "eosio.token"_n, "open"_n, manager, mutable_variant_object() + ("owner", owner) + ("symbol", symbolname ) + ("ram_payer", manager) + ); + } + void transfer( const name& from, std::string_view to, const asset& amount, const name& manager = config::system_account_name ) { transfer( from, name(to), amount, manager ); } diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 4cbfa593..0c287854 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -2263,6 +2263,141 @@ BOOST_FIXTURE_TEST_CASE(producer_pay, eosio_system_tester, * boost::unit_test::t } FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(adjust_chain_inflation, eosio_system_tester, * boost::unit_test::tolerance(1e-10)) try { + + const double continuous_rate = 0.04879; + const double usecs_per_year = 52 * 7 * 24 * 3600 * 1000000ll; + const double secs_per_year = 52 * 7 * 24 * 3600; + const int64_t useconds_per_day = (int64_t)24 * 3600 * 1000'000ll; + + const asset large_asset = core_sym::from_string("80.0000"); + create_account_with_resources( "defproducera"_n, config::system_account_name, core_sym::from_string("1.0000"), false, large_asset, large_asset ); + create_account_with_resources( "defproducerb"_n, config::system_account_name, core_sym::from_string("1.0000"), false, large_asset, large_asset ); + create_account_with_resources( "defproducerc"_n, config::system_account_name, core_sym::from_string("1.0000"), false, large_asset, large_asset ); + + create_account_with_resources( "producvotera"_n, config::system_account_name, core_sym::from_string("1.0000"), false, large_asset, large_asset ); + create_account_with_resources( "producvoterb"_n, config::system_account_name, core_sym::from_string("1.0000"), false, large_asset, large_asset ); + + BOOST_REQUIRE_EQUAL(success(), regproducer("defproducera"_n)); + produce_block(fc::hours(24)); + auto prod = get_producer_info( "defproducera"_n ); + BOOST_REQUIRE_EQUAL("defproducera", prod["owner"].as_string()); + BOOST_REQUIRE_EQUAL(0, prod["total_votes"].as_double()); + + transfer( config::system_account_name, "producvotera", core_sym::from_string("400000000.0000"), config::system_account_name); + BOOST_REQUIRE_EQUAL(success(), stake("producvotera", core_sym::from_string("100000000.0000"), core_sym::from_string("100000000.0000"))); + BOOST_REQUIRE_EQUAL(success(), vote( "producvotera"_n, { "defproducera"_n })); + + // Reduce Inflation Rate + // Simulates a scenario where the tokenomics can adjust the inflation rate. + { + produce_blocks(50); + + const auto initial_global_state = get_global_state(); + const uint64_t initial_claim_time = microseconds_since_epoch_of_iso_string( initial_global_state["last_pervote_bucket_fill"] ); + const int64_t initial_pervote_bucket = initial_global_state["pervote_bucket"].as(); + const int64_t initial_perblock_bucket = initial_global_state["perblock_bucket"].as(); + const int64_t initial_savings = get_balance("eosio.saving"_n).get_amount(); + + prod = get_producer_info("defproducera"); + const uint32_t unpaid_blocks = prod["unpaid_blocks"].as(); + BOOST_REQUIRE(1 < unpaid_blocks); + + + const asset initial_supply = get_token_supply(); + const asset initial_balance = get_balance("defproducera"_n); + transfer(config::system_account_name, "eosio.fees", core_sym::from_string("1.0000"), config::system_account_name); + const asset init_account_fees = get_balance("eosio.fees"_n); + BOOST_TEST_MESSAGE("init_account_fees: " << init_account_fees.get_amount()); + + BOOST_REQUIRE_EQUAL(success(), push_action("defproducera"_n, "claimrewards"_n, mvo()("owner", "defproducera"))); + + const auto global_state = get_global_state(); + const uint64_t claim_time = microseconds_since_epoch_of_iso_string( global_state["last_pervote_bucket_fill"] ); + const int64_t pervote_bucket = global_state["pervote_bucket"].as(); + const int64_t perblock_bucket = global_state["perblock_bucket"].as(); + const int64_t savings = get_balance("eosio.saving"_n).get_amount(); + + prod = get_producer_info("defproducera"); + const asset supply = get_token_supply(); + const asset balance = get_balance("defproducera"_n); + const asset account_fees = get_balance("eosio.fees"_n); + BOOST_REQUIRE_EQUAL(0, account_fees.get_amount()); + + BOOST_REQUIRE_EQUAL(claim_time, microseconds_since_epoch_of_iso_string( prod["last_claim_time"] )); + + auto usecs_between_fills = claim_time - initial_claim_time; + int32_t secs_between_fills = usecs_between_fills/1000000; + uint64_t distribute_tokens = (initial_supply.get_amount() * double(secs_between_fills) * continuous_rate) / secs_per_year; + BOOST_REQUIRE_EQUAL(0, initial_savings); + BOOST_REQUIRE_EQUAL(0, initial_perblock_bucket); + BOOST_REQUIRE_EQUAL(0, initial_pervote_bucket); + + BOOST_REQUIRE_EQUAL(distribute_tokens - init_account_fees.get_amount(), supply.get_amount() - initial_supply.get_amount()); + BOOST_REQUIRE_EQUAL(int64_t(distribute_tokens - (distribute_tokens / 5) * 3), savings - initial_savings); + BOOST_REQUIRE_EQUAL(int64_t(distribute_tokens / 5), balance.get_amount() - initial_balance.get_amount()); + + int64_t from_perblock_bucket = int64_t( initial_supply.get_amount() * double(secs_between_fills) * (continuous_rate / 5.) / secs_per_year ) ; + int64_t from_pervote_bucket = 0; + + + if (from_pervote_bucket >= 100 * 10000) { + BOOST_REQUIRE_EQUAL(from_perblock_bucket + from_pervote_bucket, balance.get_amount() - initial_balance.get_amount()); + BOOST_REQUIRE_EQUAL(0, pervote_bucket); + } else { + BOOST_REQUIRE_EQUAL(from_perblock_bucket, balance.get_amount() - initial_balance.get_amount()); + BOOST_REQUIRE_EQUAL(from_pervote_bucket, pervote_bucket); + } + } + + // Wait 1 day + produce_block(fc::seconds(24 * 3600)); + + // No Inflation Rate Adjustment with Token Burn + // Evaluates the situation where the tokenomics covers inflation rate and tests the burning mechanism for remaining tokens. + { + produce_blocks(100); + + const auto initial_global_state = get_global_state(); + const uint64_t initial_claim_time = microseconds_since_epoch_of_iso_string( initial_global_state["last_pervote_bucket_fill"] ); + const int64_t initial_pervote_bucket = initial_global_state["pervote_bucket"].as(); + const int64_t initial_perblock_bucket = initial_global_state["perblock_bucket"].as(); + const int64_t initial_savings = get_balance("eosio.saving"_n).get_amount(); + + prod = get_producer_info("defproducera"); + const uint32_t unpaid_blocks = prod["unpaid_blocks"].as(); + BOOST_REQUIRE(1 < unpaid_blocks); + + const asset initial_supply = get_token_supply(); + transfer(config::system_account_name, "eosio.fees", core_sym::from_string("200000.0000"), config::system_account_name); + const asset init_account_fees = get_balance("eosio.fees"_n); + + BOOST_REQUIRE_EQUAL(success(), push_action("defproducera"_n, "claimrewards"_n, mvo()("owner", "defproducera"))); + + const auto global_state = get_global_state(); + const uint64_t claim_time = microseconds_since_epoch_of_iso_string( global_state["last_pervote_bucket_fill"] ); + const int64_t pervote_bucket = global_state["pervote_bucket"].as(); + const int64_t perblock_bucket = global_state["perblock_bucket"].as(); + const int64_t savings = get_balance("eosio.saving"_n).get_amount(); + + prod = get_producer_info("defproducera"); + const asset supply = get_token_supply(); + const asset account_fees = get_balance("eosio.fees"_n); + BOOST_REQUIRE_EQUAL(account_fees.get_amount(), 0); + + BOOST_REQUIRE_EQUAL(claim_time, microseconds_since_epoch_of_iso_string( prod["last_claim_time"] )); + + auto usecs_between_fills = claim_time - initial_claim_time; + int32_t secs_between_fills = usecs_between_fills/1000000; + int64_t distribute_tokens = (initial_supply.get_amount() * double(usecs_between_fills) * continuous_rate) / usecs_per_year; + + BOOST_REQUIRE_EQUAL(init_account_fees.get_amount() - distribute_tokens, initial_supply.get_amount() - supply.get_amount()); + BOOST_REQUIRE_EQUAL(int64_t(distribute_tokens - (distribute_tokens / 5) * 3), savings - initial_savings); + + } +} FC_LOG_AND_RETHROW() + BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::unit_test::tolerance(1e-10)) try { auto within_one = [](int64_t a, int64_t b) -> bool { return std::abs( a - b ) <= 1; };