diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 71ee28de..51b5c917 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -76,9 +76,16 @@ void account_statistics_object::process_fees(const account_object& a, database& share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage); share_type referral = core_fee_total - network_cut - lifetime_cut; - d.modify( d.get_core_dynamic_data(), [network_cut](asset_dynamic_data_object& addo) { + if(d.head_block_time() >= HARDFORK_FEES_AS_DIVIDENDS_TIME ) { + const asset_object& ao = d.get_core_asset(); + const asset_dividend_data_object& core_asset_dividend_data_obj = (*ao.dividend_data_id)(d); + account_id_type rake_account_id = core_asset_dividend_data_obj.dividend_distribution_account; + d.adjust_balance(rake_account_id, network_cut); + } else { + d.modify( d.get_core_dynamic_data(), [network_cut](asset_dynamic_data_object& addo) { addo.accumulated_fees += network_cut; - }); + }); + } // Potential optimization: Skip some of this math and object lookups by special casing on the account type. // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index 0e273936..2ae8f0e7 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -128,19 +128,6 @@ database& generic_evaluator::db()const { return trx_state->db(); } db().adjust_balance(fee_payer, fee_from_account); } - void generic_evaluator::pay_fee_to_dividend() - { - const database& d = db(); - - if(d.head_block_time() > HARDFORK_FEES_AS_DIVIDENDS_TIME) { - const asset_object& ao = (*fee_asset).get_id()(d); - const asset_dividend_data_object& core_asset_dividend_data_obj = (*ao.dividend_data_id)(d); - fc::optional rake_account_id = core_asset_dividend_data_obj.dividend_distribution_account; - db_adjust_balance(*rake_account_id, fee_from_account); - } - - } - object_id_type generic_evaluator::get_relative_id( object_id_type rel_id )const { if (!is_relative(rel_id)) diff --git a/libraries/chain/hardfork.d/FEES_AS_DIVIDENDS.hf b/libraries/chain/hardfork.d/FEES_AS_DIVIDENDS.hf index d9e5ab2c..b4acd3ee 100644 --- a/libraries/chain/hardfork.d/FEES_AS_DIVIDENDS.hf +++ b/libraries/chain/hardfork.d/FEES_AS_DIVIDENDS.hf @@ -1,7 +1,7 @@ #ifndef HARDFORK_FEES_AS_DIVIDENDS_TIME #ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_FEES_AS_DIVIDENDS_TIME (fc::time_point_sec::from_iso_string("2022-02-20T00:00:00")) +#define HARDFORK_FEES_AS_DIVIDENDS_TIME (fc::time_point_sec::from_iso_string("2022-04-20T00:00:00")) #else -#define HARDFORK_FEES_AS_DIVIDENDS_TIME (fc::time_point_sec::from_iso_string("2022-02-20T00:00:00")) +#define HARDFORK_FEES_AS_DIVIDENDS_TIME (fc::time_point_sec::from_iso_string("2022-04-20T00:00:00")) #endif #endif diff --git a/libraries/chain/include/graphene/chain/evaluator.hpp b/libraries/chain/include/graphene/chain/evaluator.hpp index cc609edb..af90517e 100644 --- a/libraries/chain/include/graphene/chain/evaluator.hpp +++ b/libraries/chain/include/graphene/chain/evaluator.hpp @@ -107,7 +107,6 @@ namespace graphene { namespace chain { // cause a circular dependency share_type calculate_fee_for_operation(const operation& op) const; void db_adjust_balance(const account_id_type& fee_payer, asset fee_from_account); - void pay_fee_to_dividend(); asset fee_from_account; share_type core_fee_paid; @@ -171,7 +170,6 @@ namespace graphene { namespace chain { auto result = eval->do_apply(op); db_adjust_balance(op.fee_payer(), -fee_from_account); - pay_fee_to_dividend(); return result; } diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp index f716281e..c1daaf51 100644 --- a/tests/tests/gpos_tests.cpp +++ b/tests/tests/gpos_tests.cpp @@ -1522,6 +1522,89 @@ BOOST_AUTO_TEST_CASE( no_proposal ) } } +BOOST_AUTO_TEST_CASE( dividend_distribution_fee ) +{ + try { + // advance to HF + generate_blocks(HARDFORK_FEES_AS_DIVIDENDS_TIME); + generate_block(); + + // update default gpos global parameters to 4 days + auto now = db.head_block_time(); + update_gpos_global(345600, 86400, now); + + generate_block(); + set_expiration(db, trx); + + ACTORS((nathan)(alice)(voter1)(voter2)); + + const auto& core = asset_id_type()(db); + + // get core asset object + const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); + + // by default core token pays dividends once per month + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days + + // update the payout interval to 1 day for speed purposes of the test + update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day + + // get the dividend distribution account + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + + // transfer to voters and nathan without fee. We can't enable fee yet, since create worker is a custom + // function which doesn't take the fee in transaction so by enabling it we will end up with not sufficient fee + transfer( committee_account, voter1_id, core.amount( 2000 * GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, voter2_id, core.amount( 3000 * GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, nathan_id, core.amount( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION ) ); + // create some vesting for voters + create_vesting(voter1_id, core.amount(100 * GRAPHENE_BLOCKCHAIN_PRECISION ), vesting_balance_type::gpos); + create_vesting(voter2_id, core.amount(100 * GRAPHENE_BLOCKCHAIN_PRECISION ), vesting_balance_type::gpos); + // create worker + upgrade_to_lifetime_member(nathan_id); + auto worker = create_worker(nathan_id, 50 * GRAPHENE_BLOCKCHAIN_PRECISION, fc::days(2)); + + generate_block(); + + // vote for worker + vote_for(voter1_id, worker.vote_for, voter1_private_key); + + // for now on enable fees + enable_fees(); + + // transfering some coins to alice. + for(int i = 0; i < 50; i++) { + transfer( nathan_id, alice_id, core.amount( 100 * GRAPHENE_BLOCKCHAIN_PRECISION ) ); + } + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // on the dividend account from total fee should be only network cut which is 20% of total fee. + // note that we did 50 transactions. + // Each transaction takes for fee 20 * GRAPHENE_BLOCKCHAIN_PRECISION + int dividend_distribution_account_balance = get_balance(dividend_distribution_account, core); + BOOST_CHECK_EQUAL(dividend_distribution_account_balance, 50 * 20 * GRAPHENE_BLOCKCHAIN_PRECISION * 20 / 100); + + // voters are not yet paid from dividend account, it will be on next maintenance + BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 1900 * GRAPHENE_BLOCKCHAIN_PRECISION); + BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 2900 * GRAPHENE_BLOCKCHAIN_PRECISION); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // verify that voter_1 is payed from dividend account and voter_2 is not since voter_2 didn't vote + // note that we pass two maintenance intervals, so vesting factor is not anymore 1, instead it is 0.75 + int payout_to_voter1 = (dividend_distribution_account_balance / 2 ) * 0.75; + BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 1900 * GRAPHENE_BLOCKCHAIN_PRECISION + payout_to_voter1); + BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 2900 * GRAPHENE_BLOCKCHAIN_PRECISION); + + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( database_api ) { ACTORS((alice)(bob));