diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index c765de39..f2ca49c9 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -275,6 +275,11 @@ struct get_impacted_account_visitor { _impacted.insert( op.payout_account_id ); } + void operator()( const affiliate_payout_operation& op ) + { + _impacted.insert( op.affiliate ); + } + void operator()( const affiliate_referral_payout_operation& op ) { } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 399720fa..cd3c29a2 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -108,6 +108,8 @@ add_library( graphene_chain betting_market_object.cpp betting_market_group_object.cpp + affiliate_payout.cpp + ${HEADERS} ${PROTOCOL_HEADERS} "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp" diff --git a/libraries/chain/affiliate_payout.cpp b/libraries/chain/affiliate_payout.cpp new file mode 100644 index 00000000..9dd5ae4b --- /dev/null +++ b/libraries/chain/affiliate_payout.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include + +namespace graphene { namespace chain { + + share_type affiliate_payout_helper::payout( account_id_type player, const asset& amount ) + { + return payout( player(_db), amount ); + } + + share_type affiliate_payout_helper::payout( const account_object& player, const asset& amount ) + { + if( !player.affiliate_distributions.valid() ) + return 0; + const auto& dist = player.affiliate_distributions->_dists.find( tag ); + if( dist == player.affiliate_distributions->_dists.end() || dist->second._dist.empty() ) + return 0; + + share_type to_pay = amount.amount.value / 5; // 20% fixed + if( to_pay <= 0 ) + return 0; + + uint16_t remaining = GRAPHENE_100_PERCENT; + share_type paid = 0; + for( const auto& entry : dist->second._dist ) + { + const account_id_type affiliate = entry.first; + const uint16_t share = entry.second; + fc::uint128_t payout = to_pay.value; + if( share != remaining ) + { + FC_ASSERT( share < remaining ); + payout *= share; + payout /= remaining; + remaining -= share; + } + FC_ASSERT( payout.to_uint64() <= to_pay ); + if( payout > 0 ) + { + accumulator[affiliate] += asset( payout.to_uint64(), amount.asset_id ); + to_pay -= payout.to_uint64(); + paid += payout.to_uint64(); + } + } + FC_ASSERT( to_pay == 0 ); + + _db.push_applied_operation( affiliate_referral_payout_operation( player.id, amount ) ); + + return paid; + } + + void affiliate_payout_helper::commit() + { + for( const auto& entry : accumulator ) + { + _db.adjust_balance( entry.first, entry.second ); + _db.push_applied_operation( affiliate_payout_operation( entry.first, tag, entry.second ) ); + } + } + +} } // graphene::chain diff --git a/libraries/chain/db_bet.cpp b/libraries/chain/db_bet.cpp index 337b28c5..f4d34d49 100644 --- a/libraries/chain/db_bet.cpp +++ b/libraries/chain/db_bet.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -167,6 +168,8 @@ void database::settle_betting_market_group(const betting_market_group_object& be rake_account_id = core_asset_dividend_data_obj.dividend_distribution_account; } + affiliate_payout_helper payout_helper( *this, betting_market_group ); + // collect the resolutions of all markets in the BMG: they were previously published and // stored in the individual betting markets std::map resolutions_by_market_id; @@ -261,6 +264,9 @@ void database::settle_betting_market_group(const betting_market_group_object& be if (net_profits.value > 0 && rake_account_id) { rake_amount = ((fc::uint128_t(net_profits.value) * rake_fee_percentage + GRAPHENE_100_PERCENT - 1) / GRAPHENE_100_PERCENT).to_uint64(); + if (rake_amount.value) + rake_amount -= payout_helper.payout( bettor_id, asset( rake_amount, betting_market_group.asset_id ) ); + FC_ASSERT( rake_amount.value >= 0 ); if (rake_amount.value) adjust_balance(*rake_account_id, asset(rake_amount, betting_market_group.asset_id)); } @@ -323,6 +329,8 @@ void database::remove_completed_events() fc_dlog(fc::logger::get("betting"), "removing settled event ${id}", ("id", event.id)); remove(event); } + + payout_helper.commit(); } share_type adjust_betting_position(database& db, diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 9b7fc250..1251e091 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -262,6 +262,11 @@ struct get_impacted_account_visitor { _impacted.insert( op.payout_account_id ); } + void operator()( const affiliate_payout_operation& op ) + { + _impacted.insert( op.affiliate ); + } + void operator()( const affiliate_referral_payout_operation& op ) { } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/include/graphene/chain/affiliate_payout.hpp b/libraries/chain/include/graphene/chain/affiliate_payout.hpp new file mode 100644 index 00000000..8558e2ec --- /dev/null +++ b/libraries/chain/include/graphene/chain/affiliate_payout.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include +#include +#include + +namespace graphene { namespace chain { + class database; + + namespace impl { + class game_type_visitor { + public: + typedef app_tag result_type; + + inline app_tag operator()( const rock_paper_scissors_game_options& o )const { return rps; } + }; + } + + template + app_tag get_tag_for_game( const GAME& game ); + + template<> + inline app_tag get_tag_for_game( const betting_market_group_object& game ) + { + return bookie; + } + + template<> + inline app_tag get_tag_for_game( const tournament_object& game ) + { + return game.options.game_options.visit( impl::game_type_visitor() ); + } + + class affiliate_payout_helper { + public: + template + affiliate_payout_helper( database& db, const GAME& game ) + : tag( get_tag_for_game( game ) ), _db(db) {} + + share_type payout( account_id_type player, const asset& amount ); + share_type payout( const account_object& player, const asset& amount ); + void commit(); + + private: + database& _db; + app_tag tag; + std::map accumulator; + }; + +} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/protocol/affiliate.hpp b/libraries/chain/include/graphene/chain/protocol/affiliate.hpp new file mode 100644 index 00000000..b2c3ee88 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/affiliate.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + + /** + * Virtual op generated when an affiliate receives payout. + */ + + struct affiliate_payout_operation : public base_operation + { + affiliate_payout_operation(){} + affiliate_payout_operation( account_id_type a, app_tag t, const asset& amount ) + : affiliate(a), tag(t), payout(amount) {} + + struct fee_parameters_type { }; + asset fee; + + // Account of the receiving affiliate + account_id_type affiliate; + // App-tag for which the payout was generated + app_tag tag; + // Payout amount + asset payout; + + account_id_type fee_payer()const { return affiliate; } + void validate()const { + FC_ASSERT( false, "Virtual operation" ); + } + + share_type calculate_fee(const fee_parameters_type& params)const + { return 0; } + }; + + /** + * Virtual op generated when a player generates an affiliate payout + */ + struct affiliate_referral_payout_operation : public base_operation + { + affiliate_referral_payout_operation(){} + affiliate_referral_payout_operation( account_id_type p, const asset& amount ) + : player(p), payout(amount) {} + + struct fee_parameters_type { }; + asset fee; + + // Account of the winning player + account_id_type player; + // Payout amount + asset payout; + + account_id_type fee_payer()const { return player; } + void validate()const { + FC_ASSERT( false, "virtual operation" ); + } + + share_type calculate_fee(const fee_parameters_type& params)const + { return 0; } + }; + +} } // graphene::chain + +FC_REFLECT( graphene::chain::affiliate_payout_operation::fee_parameters_type, ) +FC_REFLECT( graphene::chain::affiliate_referral_payout_operation::fee_parameters_type, ) + +FC_REFLECT( graphene::chain::affiliate_payout_operation, (fee)(affiliate)(tag)(payout) ) +FC_REFLECT( graphene::chain::affiliate_referral_payout_operation, (fee)(player)(payout) ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 58cc2cba..4fac8992 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -24,6 +24,7 @@ #pragma once #include #include +#include #include #include #include @@ -124,7 +125,9 @@ namespace graphene { namespace chain { bet_canceled_operation, // VIRTUAL betting_market_group_update_operation, betting_market_update_operation, - event_update_status_operation + event_update_status_operation, + affiliate_payout_operation, // VIRTUAL + affiliate_referral_payout_operation // VIRTUAL > operation; /// @} // operations group diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index c49d9174..0f0195aa 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -333,7 +334,15 @@ namespace graphene { namespace chain { event.db.push_applied_operation(op); } - if (dividend_id && rake_amount.value) + if (rake_amount.value) + { + affiliate_payout_helper payout_helper( event.db, tournament_obj ); + rake_amount -= payout_helper.payout( winner, asset( rake_amount, tournament_obj.options.buy_in.asset_id ) ); + payout_helper.commit(); + FC_ASSERT( rake_amount.value >= 0 ); + } + + if (rake_amount.value) { // Adjusting balance of dividend_distribution_account const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id;