diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 353810e4..ebcd5c2b 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -236,6 +236,10 @@ struct get_impacted_account_visitor { _impacted.insert( op.bettor_id ); } + void operator()( const bet_adjusted_operation& op ) + { + _impacted.insert( op.bettor_id ); + } void operator()( const bet_matched_operation& op ) { _impacted.insert( op.bettor_id ); diff --git a/libraries/chain/betting_market_evaluator.cpp b/libraries/chain/betting_market_evaluator.cpp index 1cf73d8a..8cbb8bfb 100644 --- a/libraries/chain/betting_market_evaluator.cpp +++ b/libraries/chain/betting_market_evaluator.cpp @@ -291,26 +291,14 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op) simulated_bet.betting_market_id = op.betting_market_id; simulated_bet.amount_to_bet = op.amount_to_bet; simulated_bet.backer_multiplier = op.backer_multiplier; - simulated_bet.amount_reserved_for_fees = op.amount_reserved_for_fees; simulated_bet.back_or_lay = op.back_or_lay; share_type required_deposit = get_required_deposit_for_bet(simulated_bet); #endif - // verify they reserved enough to cover the percentage fee - uint16_t percentage_fee = current_params.current_fees->get().percentage_fee; - fc::uint128_t minimum_percentage_fee_calculation = op.amount_to_bet.amount.value; - minimum_percentage_fee_calculation *= percentage_fee; - minimum_percentage_fee_calculation += GRAPHENE_100_PERCENT - 1; // round up - minimum_percentage_fee_calculation /= GRAPHENE_100_PERCENT; - share_type minimum_percentage_fee = minimum_percentage_fee_calculation.to_uint64(); - FC_ASSERT(op.amount_reserved_for_fees >= minimum_percentage_fee, "insufficient fees", - ("fee_provided", op.amount_reserved_for_fees)("fee_required", minimum_percentage_fee)); - // do they have enough in their account to place the bet - _stake_plus_fees = op.amount_to_bet.amount + op.amount_reserved_for_fees; - FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= _stake_plus_fees, "insufficient balance", - ("balance", d.get_balance(*fee_paying_account, *_asset))("stake_plus_fees", _stake_plus_fees) ); + FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= op.amount_to_bet.amount, "insufficient balance", + ("balance", d.get_balance(*fee_paying_account, *_asset))("amount_to_bet", op.amount_to_bet.amount) ); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -324,15 +312,16 @@ object_id_type bet_place_evaluator::do_apply(const bet_place_operation& op) bet_obj.betting_market_id = op.betting_market_id; bet_obj.amount_to_bet = op.amount_to_bet; bet_obj.backer_multiplier = op.backer_multiplier; - bet_obj.amount_reserved_for_fees = op.amount_reserved_for_fees; bet_obj.back_or_lay = op.back_or_lay; }); - d.adjust_balance(fee_paying_account->id, asset(-_stake_plus_fees, _betting_market_group->asset_id)); + bet_id_type new_bet_id = new_bet.id; // save the bet id here, new_bet may be deleted during place_bet() + + d.adjust_balance(fee_paying_account->id, -op.amount_to_bet); bool bet_matched = d.place_bet(new_bet); - return new_bet.id; + return new_bet_id; } FC_CAPTURE_AND_RETHROW( (op) ) } void_result bet_cancel_evaluator::do_evaluate(const bet_cancel_operation& op) diff --git a/libraries/chain/db_bet.cpp b/libraries/chain/db_bet.cpp index e0918f5e..dd9d85e9 100644 --- a/libraries/chain/db_bet.cpp +++ b/libraries/chain/db_bet.cpp @@ -11,15 +11,12 @@ namespace graphene { namespace chain { void database::cancel_bet( const bet_object& bet, bool create_virtual_op ) { asset amount_to_refund = bet.amount_to_bet; - amount_to_refund += bet.amount_reserved_for_fees; //TODO: update global statistics - adjust_balance(bet.bettor_id, amount_to_refund); //return unmatched stake + fees - //TODO: do special fee accounting as required + adjust_balance(bet.bettor_id, amount_to_refund); if (create_virtual_op) { bet_canceled_operation bet_canceled_virtual_op(bet.bettor_id, bet.id, - bet.amount_to_bet, - bet.amount_reserved_for_fees); + bet.amount_to_bet); //idump((bet_canceled_virtual_op)); push_applied_operation(std::move(bet_canceled_virtual_op)); } @@ -127,7 +124,6 @@ void database::resolve_betting_market_group(const betting_market_group_object& b uint16_t rake_fee_percentage = get_global_properties().parameters.betting_rake_fee_percentage; share_type net_profits; share_type payout_amounts; - share_type fees_collected; account_id_type bettor_id = bettor_positions_pair.first; const std::vector& bettor_positions = bettor_positions_pair.second; @@ -149,7 +145,6 @@ void database::resolve_betting_market_group(const betting_market_group_object& b { share_type total_payout = position->pay_if_payout_condition + position->pay_if_not_canceled; payout_amounts += total_payout; - fees_collected += position->fees_collected; net_profits += total_payout - position->pay_if_canceled; break; } @@ -157,12 +152,11 @@ void database::resolve_betting_market_group(const betting_market_group_object& b { share_type total_payout = position->pay_if_not_payout_condition + position->pay_if_not_canceled; payout_amounts += total_payout; - fees_collected += position->fees_collected; net_profits += total_payout - position->pay_if_canceled; break; } case betting_market_resolution_type::cancel: - payout_amounts += position->pay_if_canceled + position->fees_collected; + payout_amounts += position->pay_if_canceled; break; default: continue; @@ -179,9 +173,6 @@ void database::resolve_betting_market_group(const betting_market_group_object& b adjust_balance(*rake_account_id, asset(rake_amount, betting_market_group.asset_id)); } - if (fees_collected.value) - adjust_balance(*rake_account_id, asset(fees_collected, betting_market_group.asset_id)); - // pay winning - rake adjust_balance(bettor_id, asset(payout_amounts - rake_amount, betting_market_group.asset_id)); // [ROL] @@ -191,7 +182,7 @@ void database::resolve_betting_market_group(const betting_market_group_object& b betting_market_group.id, resolutions, payout_amounts, - fees_collected)); + rake_amount)); } betting_market_itr = betting_market_index.lower_bound(betting_market_group.id); @@ -213,27 +204,7 @@ void database::get_required_deposit_for_bet(const betting_market_object& betting } #endif -bool maybe_cull_small_bet( database& db, const bet_object& bet_object_to_cull ) -{ - /** - * There are times when this bet can't be even partially matched at its stated odds - * For example, say it's a back bet for 20 satoshis at 1.92 odds (which comes out to 25:23 - * odds). It's not possible for it to match at exact odds since it's < 25 - * - * Remove these bets from the books to reduce clutter. - */ - share_type minimum_matchable_amount = bet_object_to_cull.get_minimum_matchable_amount(); - if (bet_object_to_cull.amount_to_bet.amount < minimum_matchable_amount) - { - dlog("culling small bet of ${amount}, smaller than the minimum ${minimum_matchable_amount} at odds ${odds}", - ("amount", bet_object_to_cull.amount_to_bet.amount)("minimum_matchable_amount", minimum_matchable_amount)("odds", bet_object_to_cull.backer_multiplier)); - db.cancel_bet(bet_object_to_cull); - return true; - } - return false; -} - -share_type adjust_betting_position(database& db, account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, share_type bet_amount, share_type matched_amount, share_type fees_collected) +share_type adjust_betting_position(database& db, account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, share_type bet_amount, share_type matched_amount) { try { assert(bet_amount >= 0); @@ -253,7 +224,6 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett position.pay_if_not_payout_condition = back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0; position.pay_if_canceled = bet_amount; position.pay_if_not_canceled = 0; - position.fees_collected = fees_collected; // this should not be reducible }); } else { @@ -263,7 +233,6 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett position.pay_if_payout_condition += back_or_lay == bet_type::back ? bet_amount + matched_amount : 0; position.pay_if_not_payout_condition += back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0; position.pay_if_canceled += bet_amount; - position.fees_collected += fees_collected; guaranteed_winnings_returned = position.reduce(); }); @@ -275,29 +244,21 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett // called twice when a bet is matched, once for the taker, once for the maker bool bet_was_matched(database& db, const bet_object& bet, share_type amount_bet, share_type amount_matched, - bet_multiplier_type actual_multiplier, bool cull_if_small) + bet_multiplier_type actual_multiplier) { - // calculate the percentage fee paid - fc::uint128_t percentage_fee_128 = bet.amount_reserved_for_fees.value; - percentage_fee_128 *= amount_bet.value; - percentage_fee_128 += bet.amount_to_bet.amount.value - 1; - percentage_fee_128 /= bet.amount_to_bet.amount.value; - share_type fee_paid = percentage_fee_128.to_uint64(); - // record their bet, modifying their position, and return any winnings share_type guaranteed_winnings_returned = adjust_betting_position(db, bet.bettor_id, bet.betting_market_id, - bet.back_or_lay, amount_bet, amount_matched, fee_paid); + bet.back_or_lay, amount_bet, amount_matched); db.adjust_balance(bet.bettor_id, asset(guaranteed_winnings_returned, bet.amount_to_bet.asset_id)); // generate a virtual "match" op asset asset_amount_bet(amount_bet, bet.amount_to_bet.asset_id); - bet_matched_operation bet_matched_virtual_op(bet.bettor_id, bet.id, bet.betting_market_id, + bet_matched_operation bet_matched_virtual_op(bet.bettor_id, bet.id, asset_amount_bet, - fee_paid, actual_multiplier, guaranteed_winnings_returned); - //idump((bet_matched_virtual_op)); + //edump((bet_matched_virtual_op)); db.push_applied_operation(std::move(bet_matched_virtual_op)); // update the bet on the books @@ -310,12 +271,7 @@ bool bet_was_matched(database& db, const bet_object& bet, { db.modify(bet, [&](bet_object& bet_obj) { bet_obj.amount_to_bet -= asset_amount_bet; - bet_obj.amount_reserved_for_fees -= fee_paid; }); - //TODO: cull_if_small is currently always true, remove the parameter if we don't find a - // need for it soon - if (cull_if_small) - return maybe_cull_small_bet(db, bet); return false; } } @@ -334,7 +290,7 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker { assert(taker_bet.amount_to_bet.asset_id == maker_bet.amount_to_bet.asset_id); assert(taker_bet.amount_to_bet.amount > 0 && maker_bet.amount_to_bet.amount > 0); - assert(taker_bet.backer_multiplier >= maker_bet.backer_multiplier); + assert(taker_bet.back_or_lay == bet_type::back ? taker_bet.backer_multiplier <= maker_bet.backer_multiplier : taker_bet.backer_multiplier >= maker_bet.backer_multiplier); assert(taker_bet.back_or_lay != maker_bet.back_or_lay); int result = 0; @@ -381,37 +337,80 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker //idump((taker_amount_to_match)(maker_amount_to_match)); - result |= bet_was_matched(db, taker_bet, taker_amount_to_match, maker_amount_to_match, maker_bet.backer_multiplier, true); - result |= bet_was_matched(db, maker_bet, maker_amount_to_match, taker_amount_to_match, maker_bet.backer_multiplier, true) << 1; + result |= bet_was_matched(db, taker_bet, taker_amount_to_match, maker_amount_to_match, maker_bet.backer_multiplier); + result |= bet_was_matched(db, maker_bet, maker_amount_to_match, taker_amount_to_match, maker_bet.backer_multiplier) << 1; assert(result != 0); return result; } + +// called from the bet_place_evaluator bool database::place_bet(const bet_object& new_bet_object) { - bet_id_type bet_id = new_bet_object.id; - const asset_object& bet_asset = get(new_bet_object.amount_to_bet.asset_id); - const auto& bet_odds_idx = get_index_type().indices().get(); bet_type bet_type_to_match = new_bet_object.back_or_lay == bet_type::back ? bet_type::lay : bet_type::back; auto book_itr = bet_odds_idx.lower_bound(std::make_tuple(new_bet_object.betting_market_id, bet_type_to_match)); auto book_end = bet_odds_idx.upper_bound(std::make_tuple(new_bet_object.betting_market_id, bet_type_to_match, new_bet_object.backer_multiplier)); + // ilog(""); + // ilog("------------ order book ------------------"); + // for (auto itr = book_itr; itr != book_end; ++itr) + // idump((*itr)); + // ilog("------------ order book ------------------"); + int orders_matched_flags = 0; bool finished = false; while (!finished && book_itr != book_end) { auto old_book_itr = book_itr; ++book_itr; - // match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop. orders_matched_flags = match_bet(*this, new_bet_object, *old_book_itr); + + // we continue if the maker bet was completely consumed AND the taker bet was not finished = orders_matched_flags != 2; } - return (orders_matched_flags & 1) != 0; + if (!(orders_matched_flags & 1)) + { + // if the new (taker) bet was not completely consumed, we need to put whatever remains + // of it on the books. But we only allow bets that can be exactly matched + // on the books, so round the amount down if necessary + share_type minimum_matchable_amount = new_bet_object.get_minimum_matchable_amount(); + share_type scale_factor = new_bet_object.amount_to_bet.amount / minimum_matchable_amount; + share_type rounded_bet_amount = scale_factor * minimum_matchable_amount; + //idump((new_bet_object.amount_to_bet.amount)(rounded_bet_amount)(minimum_matchable_amount)(scale_factor)); + + if (rounded_bet_amount == share_type()) + { + // the remainder of the bet was too small to match, cancel the bet + cancel_bet(new_bet_object, true); + return true; + } + else if (rounded_bet_amount != new_bet_object.amount_to_bet.amount) + { + asset stake_returned = new_bet_object.amount_to_bet; + stake_returned.amount -= rounded_bet_amount; + + modify(new_bet_object, [&rounded_bet_amount](bet_object& modified_bet_object) { + modified_bet_object.amount_to_bet.amount = rounded_bet_amount; + }); + + adjust_balance(new_bet_object.bettor_id, stake_returned); + // TODO: update global statistics + bet_adjusted_operation bet_adjusted_op(new_bet_object.bettor_id, new_bet_object.id, + stake_returned); + // idump((bet_adjusted_op)(new_bet_object)); + push_applied_operation(std::move(bet_adjusted_op)); + return false; + } + else + return false; + } + else + return true; } } } diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 34810391..906d23a6 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -210,6 +210,7 @@ struct get_impacted_account_visitor void operator()(const bet_matched_operation &){} void operator()(const bet_cancel_operation&){} void operator()(const bet_canceled_operation &){} + void operator()(const bet_adjusted_operation &){} void operator()( const tournament_create_operation& op ) { diff --git a/libraries/chain/include/graphene/chain/betting_market_object.hpp b/libraries/chain/include/graphene/chain/betting_market_object.hpp index 4e944871..2f8c97c5 100644 --- a/libraries/chain/include/graphene/chain/betting_market_object.hpp +++ b/libraries/chain/include/graphene/chain/betting_market_object.hpp @@ -94,8 +94,6 @@ class bet_object : public graphene::db::abstract_object< bet_object > bet_multiplier_type backer_multiplier; - share_type amount_reserved_for_fees; // same asset type as amount_to_bet - bet_type back_or_lay; static share_type get_approximate_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay, bool round_up = false); @@ -241,7 +239,10 @@ struct compare_bet_by_odds { return true; if (lhs_bet_type > rhs_bet_type) return false; - return lhs_backer_multiplier < rhs_backer_multiplier; + if (lhs_bet_type == bet_type::back) + return lhs_backer_multiplier < rhs_backer_multiplier; + else + return lhs_backer_multiplier > rhs_backer_multiplier; } bool compare(const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type, bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id, @@ -257,9 +258,9 @@ struct compare_bet_by_odds { if (lhs_bet_type > rhs_bet_type) return false; if (lhs_backer_multiplier < rhs_backer_multiplier) - return true; + return lhs_bet_type == bet_type::back; if (lhs_backer_multiplier > rhs_backer_multiplier) - return false; + return lhs_bet_type == bet_type::lay; return lhs_bet_id < rhs_bet_id; } }; @@ -381,7 +382,10 @@ struct compare_bet_by_bettor_then_odds { return true; if (lhs_bet_type > rhs_bet_type) return false; - return lhs_backer_multiplier < rhs_backer_multiplier; + if (lhs_bet_type == bet_type::back) + return lhs_backer_multiplier < rhs_backer_multiplier; + else + return lhs_backer_multiplier > rhs_backer_multiplier; } bool compare(const betting_market_id_type& lhs_betting_market_id, const account_id_type& lhs_bettor_id, bet_type lhs_bet_type, @@ -403,9 +407,10 @@ struct compare_bet_by_bettor_then_odds { if (lhs_bet_type > rhs_bet_type) return false; if (lhs_backer_multiplier < rhs_backer_multiplier) - return true; + return lhs_bet_type == bet_type::back; if (lhs_backer_multiplier > rhs_backer_multiplier) - return false; + return lhs_bet_type == bet_type::lay; + return lhs_bet_id < rhs_bet_id; } }; @@ -444,6 +449,6 @@ typedef generic_index operation; /// @} // operations group diff --git a/libraries/plugins/bookie/bookie_api.cpp b/libraries/plugins/bookie/bookie_api.cpp index 375cbbc5..1af4fc5b 100644 --- a/libraries/plugins/bookie/bookie_api.cpp +++ b/libraries/plugins/bookie/bookie_api.cpp @@ -80,13 +80,15 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin } }; + // iterate through both sides of the order book (backs at increasing odds then lays at decreasing odds) for (auto bet_odds_iter = bet_odds_idx.lower_bound(std::make_tuple(betting_market_id)); bet_odds_iter != bet_odds_idx.end() && betting_market_id == bet_odds_iter->betting_market_id; ++bet_odds_iter) { if (current_bin && - (bet_odds_iter->back_or_lay == current_bin->back_or_lay || - bet_odds_iter->backer_multiplier > current_bin->backer_multiplier)) + (bet_odds_iter->back_or_lay == current_bin->back_or_lay /* we have switched from back to lay bets */ || + (bet_odds_iter->back_or_lay == bet_type::back ? bet_odds_iter->backer_multiplier > current_bin->backer_multiplier : + bet_odds_iter->backer_multiplier < current_bin->backer_multiplier))) flush_current_bin(); if (!current_bin) @@ -94,9 +96,21 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin // if there is no current bin, create one appropriate for the bet we're processing current_bin = graphene::chain::bet_object(); - current_bin->backer_multiplier = (bet_odds_iter->backer_multiplier + bin_size - 1) / bin_size * bin_size; - current_bin->backer_multiplier = std::min(current_bin->backer_multiplier, current_params.max_bet_multiplier); - current_bin->back_or_lay = bet_odds_iter->back_or_lay == bet_type::back ? bet_type::lay : bet_type::back; + // for back bets, we want to group all bets with odds from 3.0001 to 4 into the "4" bin + // for lay bets, we want to group all bets with odds from 3 to 3.9999 into the "3" bin + if (bet_odds_iter->back_or_lay == bet_type::back) + { + current_bin->backer_multiplier = (bet_odds_iter->backer_multiplier + bin_size - 1) / bin_size * bin_size; + current_bin->backer_multiplier = std::min(current_bin->backer_multiplier, current_params.max_bet_multiplier); + current_bin->back_or_lay = bet_type::lay; + } + else + { + current_bin->backer_multiplier = bet_odds_iter->backer_multiplier / bin_size * bin_size; + current_bin->backer_multiplier = std::max(current_bin->backer_multiplier, current_params.min_bet_multiplier); + current_bin->back_or_lay = bet_type::back; + } + current_bin->amount_to_bet.amount = 0; } diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index 70cee06e..681f18a2 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -55,7 +55,7 @@ namespace graphene { namespace bookie { namespace detail { -class persistent_event_object : public graphene::db::abstract_object +class persistent_event_object : public graphene::db::abstract_object { public: static const uint8_t space_id = bookie_objects; @@ -124,6 +124,117 @@ void events_by_competitor_index::object_modified( const object& after ) } #endif +//////////// betting_market_group_object ////////////////// +class persistent_betting_market_group_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = bookie_objects; + static const uint8_t type_id = persistent_betting_market_group_object_type; + + betting_market_group_object ephemeral_betting_market_group_object; + + share_type total_matched_bets_amount; + + betting_market_group_id_type get_betting_market_group_id() const { return ephemeral_betting_market_group_object.id; } +}; + +struct by_betting_market_group_id; +typedef multi_index_container< + persistent_betting_market_group_object, + indexed_by< + ordered_unique, member >, + ordered_unique, const_mem_fun > > > persistent_betting_market_group_multi_index_type; + +typedef generic_index persistent_betting_market_group_index; + +//////////// betting_market_object ////////////////// +class persistent_betting_market_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = bookie_objects; + static const uint8_t type_id = persistent_betting_market_object_type; + + betting_market_object ephemeral_betting_market_object; + + share_type total_matched_bets_amount; + + betting_market_id_type get_betting_market_id() const { return ephemeral_betting_market_object.id; } +}; + +struct by_betting_market_id; +typedef multi_index_container< + persistent_betting_market_object, + indexed_by< + ordered_unique, member >, + ordered_unique, const_mem_fun > > > persistent_betting_market_multi_index_type; + +typedef generic_index persistent_betting_market_index; + +//////////// bet_object ////////////////// +class persistent_bet_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = bookie_objects; + static const uint8_t type_id = persistent_bet_object_type; + + bet_object ephemeral_bet_object; + + bet_id_type get_bet_id() const { return ephemeral_bet_object.id; } +}; + +struct by_bet_id; +typedef multi_index_container< + persistent_bet_object, + indexed_by< + ordered_unique, member >, + ordered_unique, const_mem_fun > > > persistent_bet_multi_index_type; + + +typedef generic_index persistent_bet_index; + +/* As a plugin, we get notified of new/changed objects at the end of every block processed. + * For most objects, that's fine, because we expect them to always be around until the end of + * the block. However, with bet objects, it's possible that the user places a bet and it fills + * and is removed during the same block, so need another strategy to detect them immediately after + * they are created. + * We do this by creating a secondary index on bet_object. We don't actually use it + * to index any property of the bet, we just use it to register for callbacks. + */ +class persistent_bet_object_helper : public secondary_index +{ + public: + virtual ~persistent_bet_object_helper() {} + + virtual void object_inserted(const object& obj) override; + //virtual void object_removed( const object& obj ) override; + //virtual void about_to_modify( const object& before ) override; + virtual void object_modified(const object& after) override; + void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; } + private: + bookie_plugin* _bookie_plugin; +}; + +void persistent_bet_object_helper::object_inserted(const object& obj) +{ + const bet_object& bet_obj = *boost::polymorphic_downcast(&obj); + _bookie_plugin->database().create([&](persistent_bet_object& saved_bet_obj) { + saved_bet_obj.ephemeral_bet_object = bet_obj; + }); +} +void persistent_bet_object_helper::object_modified(const object& after) +{ + database& db = _bookie_plugin->database(); + auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); + const bet_object& bet_obj = *boost::polymorphic_downcast(&after); + auto iter = persistent_bets_by_bet_id.find(bet_obj.id); + assert (iter != persistent_bets_by_bet_id.end()); + if (iter != persistent_bets_by_bet_id.end()) + db.modify(*iter, [&](persistent_bet_object& saved_bet_obj) { + saved_bet_obj.ephemeral_bet_object = bet_obj; + }); +} + +//////////// end bet_object /////////////////// class bookie_plugin_impl { public: @@ -175,13 +286,14 @@ class bookie_plugin_impl bookie_plugin_impl::~bookie_plugin_impl() { - return; } void bookie_plugin_impl::on_objects_changed(const vector& changed_object_ids) { graphene::chain::database& db = database(); auto& event_id_index = db.get_index_type().indices().get(); + auto& betting_market_group_id_index = db.get_index_type().indices().get(); + auto& betting_market_id_index = db.get_index_type().indices().get(); for (const object_id_type& changed_object_id : changed_object_ids) { @@ -232,6 +344,78 @@ void bookie_plugin_impl::on_objects_changed(const vector& change }); } } + else if (changed_object_id.space() == betting_market_group_object::space_id && + changed_object_id.type() == betting_market_group_object::type_id) + { + betting_market_group_id_type changed_betting_market_group_id = changed_object_id; + const betting_market_group_object* new_betting_market_group_obj = nullptr; + try + { + new_betting_market_group_obj = &changed_betting_market_group_id(db); + } + catch (fc::exception& e) + { + } + // new_betting_market_group_obj should point to the now-changed event_object, or null if it was removed from the database + + const persistent_betting_market_group_object* old_betting_market_group_obj = nullptr; + + auto persistent_betting_market_group_iter = betting_market_group_id_index.find(changed_betting_market_group_id); + if (persistent_betting_market_group_iter != betting_market_group_id_index.end()) + old_betting_market_group_obj = &*persistent_betting_market_group_iter; + + // and old_betting_market_group_obj is a pointer to our saved copy, or nullptr if it is a new object + if (old_betting_market_group_obj && new_betting_market_group_obj) + { + ilog("Modifying persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id)); + db.modify(*old_betting_market_group_obj, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) { + saved_betting_market_group_obj.ephemeral_betting_market_group_object = *new_betting_market_group_obj; + }); + } + else if (new_betting_market_group_obj) + { + ilog("Creating new persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id)); + db.create([&](persistent_betting_market_group_object& saved_betting_market_group_obj) { + saved_betting_market_group_obj.ephemeral_betting_market_group_object = *new_betting_market_group_obj; + }); + } + } + else if (changed_object_id.space() == betting_market_object::space_id && + changed_object_id.type() == betting_market_object::type_id) + { + betting_market_id_type changed_betting_market_id = changed_object_id; + const betting_market_object* new_betting_market_obj = nullptr; + try + { + new_betting_market_obj = &changed_betting_market_id(db); + } + catch (fc::exception& e) + { + } + // new_betting_market_obj should point to the now-changed event_object, or null if it was removed from the database + + const persistent_betting_market_object* old_betting_market_obj = nullptr; + + auto persistent_betting_market_iter = betting_market_id_index.find(changed_betting_market_id); + if (persistent_betting_market_iter != betting_market_id_index.end()) + old_betting_market_obj = &*persistent_betting_market_iter; + + // and old_betting_market_obj is a pointer to our saved copy, or nullptr if it is a new object + if (old_betting_market_obj && new_betting_market_obj) + { + ilog("Modifying persistent betting_market object ${id}", ("id", changed_betting_market_id)); + db.modify(*old_betting_market_obj, [&](persistent_betting_market_object& saved_betting_market_obj) { + saved_betting_market_obj.ephemeral_betting_market_object = *new_betting_market_obj; + }); + } + else if (new_betting_market_obj) + { + ilog("Creating new persistent betting_market object ${id}", ("id", changed_betting_market_id)); + db.create([&](persistent_betting_market_object& saved_betting_market_obj) { + saved_betting_market_obj.ephemeral_betting_market_object = *new_betting_market_obj; + }); + } + } } } @@ -239,29 +423,36 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) { graphene::chain::database& db = database(); const vector >& hist = db.get_applied_operations(); - for( const optional< operation_history_object >& o_op : hist ) + for( const optional& o_op : hist ) { if( !o_op.valid() ) { continue; } const operation_history_object& op = *o_op; - if( op.op.which() == operation::tag< bet_matched_operation >::value ) + if( op.op.which() == operation::tag::value ) { const bet_matched_operation& bet_matched_op = op.op.get(); idump((bet_matched_op)); const asset& amount_bet = bet_matched_op.amount_bet; // object may no longer exist //const bet_object& bet = bet_matched_op.bet_id(db); - const betting_market_object& betting_market = bet_matched_op.betting_market_id(db); - const betting_market_group_object& betting_market_group = betting_market.group_id(db); - db.modify( betting_market_group, [&]( betting_market_group_object& obj ){ - obj.total_matched_bets_amount += amount_bet.amount; - }); + auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); + auto bet_iter = persistent_bets_by_bet_id.find(bet_matched_op.bet_id); + assert(bet_iter != persistent_bets_by_bet_id.end()); + if (bet_iter != persistent_bets_by_bet_id.end()) + { + const bet_object& bet_obj = bet_iter->ephemeral_bet_object; + const betting_market_object& betting_market = bet_obj.betting_market_id(db); // TODO: this needs to look at the persistent version + const betting_market_group_object& betting_market_group = betting_market.group_id(db); // TODO: as does this + db.modify( betting_market_group, [&]( betting_market_group_object& obj ){ + obj.total_matched_bets_amount += amount_bet.amount; + }); + } } - else if( op.op.which() == operation::tag< event_create_operation >::value ) + else if( op.op.which() == operation::tag::value ) { - FC_ASSERT(op.result.which() == operation_result::tag< object_id_type >::value); + FC_ASSERT(op.result.which() == operation_result::tag::value); //object_id_type object_id = op.result.get(); event_id_type object_id = op.result.get(); FC_ASSERT( db.find_object(object_id), "invalid event specified" ); @@ -271,7 +462,7 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) localized_event_strings[pair.first].insert(event_string(object_id, pair.second)); } } - else if( op.op.which() == operation::tag< event_update_operation >::value ) + else if( op.op.which() == operation::tag::value ) { const event_update_operation& event_create_op = op.op.get(); if (!event_create_op.new_name.valid()) @@ -345,10 +536,8 @@ std::string bookie_plugin::plugin_name()const return "bookie"; } -void bookie_plugin::plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg - ) +void bookie_plugin::plugin_set_program_options(boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) { //cli.add_options() // ("track-account", boost::program_options::value>()->composing()->multitoken(), "Account ID to track history for (may specify multiple times)") @@ -363,8 +552,13 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma database().changed_objects.connect([&](const vector& changed_object_ids, const fc::flat_set& impacted_accounts){ my->on_objects_changed(changed_object_ids); }); //auto event_index = database().add_index >(); - //event_index->add_secondary_index(); - //LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type); + database().add_index >(); + database().add_index >(); + database().add_index >(); + const primary_index& bet_object_idx = database().get_index_type >(); + primary_index& nonconst_bet_object_idx = const_cast&>(bet_object_idx); + detail::persistent_bet_object_helper* persistent_bet_object_helper_index = nonconst_bet_object_idx.add_secondary_index(); + persistent_bet_object_helper_index->set_plugin_instance(this); ilog("bookie plugin: plugin_startup() end"); } @@ -392,5 +586,8 @@ void bookie_plugin::get_events_containing_sub_string(std::vector& } } } -FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(scores) ) +FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(scores) ) +FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_group_object, (graphene::db::object), (ephemeral_betting_market_group_object)(total_matched_bets_amount) ) +FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_object, (graphene::db::object), (ephemeral_betting_market_object) ) +FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_bet_object, (graphene::db::object), (ephemeral_bet_object) ) diff --git a/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp b/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp index c5a0c36b..0bd25a2b 100644 --- a/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp +++ b/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp @@ -47,13 +47,16 @@ enum spaces { enum bookie_object_type { persistent_event_object_type, + persistent_betting_market_group_object_type, + persistent_betting_market_object_type, + persistent_bet_object_type, BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; namespace detail { - class bookie_plugin_impl; + class bookie_plugin_impl; } class bookie_plugin : public graphene::app::plugin @@ -63,9 +66,8 @@ class bookie_plugin : public graphene::app::plugin virtual ~bookie_plugin(); std::string plugin_name()const override; - virtual void plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg) override; + virtual void plugin_set_program_options(boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) override; virtual void plugin_initialize(const boost::program_options::variables_map& options) override; virtual void plugin_startup() override; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index fec081cb..f7d41f30 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1685,7 +1685,6 @@ class wallet_api bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier, - share_type amount_reserved_for_fees, bool broadcast = false); signed_transaction propose_resolve_betting_market_group( diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 08459abc..2cd1ce69 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -5406,7 +5406,6 @@ signed_transaction wallet_api::place_bet( bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier, - share_type amount_reserved_for_fees, bool broadcast /*= false*/) { FC_ASSERT( !is_locked() ); @@ -5417,7 +5416,6 @@ signed_transaction wallet_api::place_bet( bet_place_op.betting_market_id = betting_market_id; bet_place_op.amount_to_bet = amount_to_bet; bet_place_op.backer_multiplier = backer_multiplier; - bet_place_op.amount_reserved_for_fees = amount_reserved_for_fees; bet_place_op.back_or_lay = back_or_lay; signed_transaction tx; diff --git a/tests/betting/betting_tests.cpp b/tests/betting/betting_tests.cpp index 49053ceb..764be8f3 100644 --- a/tests/betting/betting_tests.cpp +++ b/tests/betting/betting_tests.cpp @@ -135,12 +135,12 @@ BOOST_AUTO_TEST_CASE(simple_bet_win) transfer(account_id_type(), bob_id, asset(10000)); // place bets at 10:1 - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 2); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 20); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION); // reverse positions at 1:1 - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22); - place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); } FC_LOG_AND_RETHROW() } @@ -159,11 +159,11 @@ BOOST_AUTO_TEST_CASE(binned_order_books) transfer(account_id_type(), bob_id, asset(10000)); // place back bets at decimal odds of 1.55, 1.6, 1.65, 1.66, and 1.67 - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100); const auto& bet_odds_idx = db.get_index_type().indices().get(); @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(binned_order_books) // compute the matching lay order share_type lay_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::back, false /* round down */); ilog("Alice is laying with ${lay_amount} at odds ${odds} to match the binned back amount ${back_amount}", ("lay_amount", lay_amount)("odds", binned_order.backer_multiplier)("back_amount", binned_order.amount_to_bet)); - place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(lay_amount, asset_id_type()), binned_order.backer_multiplier, 2); + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(lay_amount, asset_id_type()), binned_order.backer_multiplier); } bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id)); @@ -201,11 +201,11 @@ BOOST_AUTO_TEST_CASE(binned_order_books) BOOST_CHECK(bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id)) == bet_odds_idx.end()); // place lay bets at decimal odds of 1.55, 1.6, 1.65, 1.66, and 1.67 - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100); binned_orders_point_one = bookie_api.get_binned_order_book(capitals_win_market.id, 1); idump((binned_orders_point_one)); @@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(binned_order_books) // compute the matching lay order share_type back_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::lay, false /* round down */); ilog("Alice is backing with ${back_amount} at odds ${odds} to match the binned lay amount ${lay_amount}", ("back_amount", back_amount)("odds", binned_order.backer_multiplier)("lay_amount", binned_order.amount_to_bet)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(back_amount, asset_id_type()), binned_order.backer_multiplier, 2); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(back_amount, asset_id_type()), binned_order.backer_multiplier); } bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id)); @@ -247,12 +247,12 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test ) transfer(account_id_type(), bob_id, asset(10000000)); // have bob lay a bet for 1M (+20k fees) at 1:1 odds - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); // have alice back a matching bet at 1:1 odds (also costing 1.02M) - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // caps win resolve_betting_market_group(moneyline_betting_markets.id, @@ -263,8 +263,8 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test ) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -281,25 +281,66 @@ BOOST_AUTO_TEST_CASE( cancel_unmatched_in_betting_group_test ) transfer(account_id_type(), bob_id, asset(10000000)); // have bob lay a bet for 1M (+20k fees) at 1:1 odds - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); // have alice back a matching bet at 1:1 odds (also costing 1.02M) - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); // place unmatched - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10); - place_bet(bob_id, blackhawks_win_market.id, bet_type::lay, asset(600, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, blackhawks_win_market.id, bet_type::lay, asset(600, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 500 - 10); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 600 - 20); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 500); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 600); // cancel unmatched cancel_unmatched_bets(moneyline_betting_markets.id); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(inexact_odds) +{ + try + { + ACTORS( (alice)(bob) ); + CREATE_ICE_HOCKEY_BETTING_MARKET(); + + transfer(account_id_type(), alice_id, asset(10000000)); + share_type alice_expected_balance = 10000000; + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value); + + // lay 47 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + alice_expected_balance -= 47; + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value); + + // lay 100 at 1.91 odds (100:91) -- this is an inexact match, we should get refunded 9 and leave a bet for 91 on the books + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + alice_expected_balance -= 91; + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value); + + + transfer(account_id_type(), bob_id, asset(10000000)); + share_type bob_expected_balance = 10000000; + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value); + + // now have bob match it with a back of 300 at 1.91 + // This should: + // match the full 47 @ 1.94 with 50 + // match the full 91 @ 1.91 with 100 + // leaving 150 + // back bets at 100:91 must be a multiple of 100, so refund 50 + // leaves a back bet of 100 @ 1.91 on the books + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(300, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + bob_expected_balance -= 250; + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value); + } + FC_LOG_AND_RETHROW() +} + + BOOST_AUTO_TEST_CASE( chained_market_create_test ) { // Often you will want to create several objects that reference each other at the same time. @@ -430,12 +471,12 @@ struct simple_bet_test_fixture : database_fixture { transfer(account_id_type(), bob_id, asset(10000)); // place bets at 10:1 - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 2); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 20); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION); // reverse positions at 1:1 - place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22); - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22); + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); capitals_win_betting_market_id = capitals_win_market.id; blackhawks_win_betting_market_id = blackhawks_win_market.id; @@ -459,13 +500,13 @@ BOOST_AUTO_TEST_CASE( win ) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value; //rake_value = (-100 + 1100 - 1100) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; - // alice starts with 10000, pays 100 (bet) + 2 (fee), wins 1100, then pays 1100 (bet) + 22 (fee), wins 0 - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 - 2 + 1100 - 1100 - 22 + 0); + // alice starts with 10000, pays 100 (bet), wins 1100, then pays 1100 (bet), wins 0 + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 + 1100 - 1100 + 0); rake_value = (-1000 - 1100 + 2200) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; - // bob starts with 10000, pays 1000 (bet) + 20 (fee), wins 0, then pays 1100 (bet) + 22 (fee), wins 2200 + // bob starts with 10000, pays 1000 (bet), wins 0, then pays 1100 (bet), wins 2200 BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 - 20 + 0 - 1100 - 22 + 2200 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 + 0 - 1100 + 2200 - rake_value); } FC_LOG_AND_RETHROW() } @@ -482,13 +523,13 @@ BOOST_AUTO_TEST_CASE( not_win ) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-100 - 1100 + 2200) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; - // alice starts with 10000, pays 100 (bet) + 2 (fee), wins 0, then pays 1100 (bet) + 22 (fee), wins 2200 + // alice starts with 10000, pays 100 (bet), wins 0, then pays 1100 (bet), wins 2200 BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 - 2 + 0 - 1100 - 22 + 2200 - rake_value); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 + 0 - 1100 + 2200 - rake_value); //rake_value = (-1000 + 1100 - 1100) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; - // bob starts with 10000, pays 1000 (bet) + 20 (fee), wins 1100, then pays 1100 (bet) + 22 (fee), wins 0 - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 - 20 + 1100 - 1100 - 22 + 0); + // bob starts with 10000, pays 1000 (bet), wins 1100, then pays 1100 (bet), wins 0 + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 + 1100 - 1100 + 0); } FC_LOG_AND_RETHROW() } @@ -523,21 +564,21 @@ struct simple_bet_test_fixture_2 : database_fixture { transfer(account_id_type(), bob_id, asset(10000)); // alice backs 1000 at 1:1, matches - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); // now alice lays at 2500 at 1:1. This should require a deposit of 500, with the remaining 200 being funded from exposure - place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(2500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 50); + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(2500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); // match the bet bit by bit. bob matches 500 of alice's 2500 bet. This effectively cancels half of bob's lay position // so he immediately gets 500 back. It reduces alice's back position, but doesn't return any money to her (all 2000 of her exposure // was already "promised" to her lay bet, so the 500 she would have received is placed in her refundable_unmatched_bets) - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); // match another 500, which will fully cancel bob's lay position and return the other 500 he had locked up in his position. // alice's back position is now canceled, 1500 remains of her unmatched lay bet, and the 500 from canceling her position has // been moved to her refundable_unmatched_bets - place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10); + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); capitals_win_betting_market_id = capitals_win_market.id; } @@ -554,9 +595,9 @@ BOOST_AUTO_TEST_CASE(sport_update_test) update_sport(ice_hockey.id, {{"en", "Hockey on Ice"}, {"zh_Hans", "冰"}, {"ja", "アイスホッケ"}}); transfer(account_id_type(), alice_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -571,7 +612,7 @@ BOOST_AUTO_TEST_CASE(event_group_update_test) transfer(account_id_type(), alice_id, asset(10000000)); transfer(account_id_type(), bob_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); const sport_object& ice_on_hockey = create_sport({{"en", "Hockey on Ice"}, {"zh_Hans", "冰球"}, {"ja", "アイスホッケー"}}); \ fc::optional sport_id = ice_on_hockey.id; @@ -582,10 +623,10 @@ BOOST_AUTO_TEST_CASE(event_group_update_test) update_event_group(nhl.id, sport_id, fc::optional()); update_event_group(nhl.id, sport_id, name); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // caps win resolve_betting_market_group(moneyline_betting_markets.id, @@ -596,8 +637,8 @@ BOOST_AUTO_TEST_CASE(event_group_update_test) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -613,7 +654,7 @@ BOOST_AUTO_TEST_CASE(event_update_test) transfer(account_id_type(), alice_id, asset(10000000)); transfer(account_id_type(), bob_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); fc::optional empty; fc::optional name = internationalized_string_type({{"en", "Washington Capitals vs. Chicago Blackhawks"}, {"zh_Hans", "華盛頓首都隊/芝加哥黑"}, {"ja", "ワシントン・キャピタルズ/シカゴ・ブラックホーク"}}); @@ -630,10 +671,10 @@ BOOST_AUTO_TEST_CASE(event_update_test) update_event(capitals_vs_blackhawks.id, event_group_id , empty, empty); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // caps win resolve_betting_market_group(moneyline_betting_markets.id, @@ -644,8 +685,8 @@ BOOST_AUTO_TEST_CASE(event_update_test) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -666,9 +707,9 @@ BOOST_AUTO_TEST_CASE(betting_market_rules_update_test) update_betting_market_rules(betting_market_rules.id, name, desc); transfer(account_id_type(), alice_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -681,7 +722,7 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test) CREATE_ICE_HOCKEY_BETTING_MARKET(); transfer(account_id_type(), alice_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); fc::optional dempty; fc::optional empty_object_id; @@ -701,10 +742,10 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test) update_betting_market_group(moneyline_betting_markets.id, new_desc, new_event, new_rule, freeze); transfer(account_id_type(), bob_id, asset(10000000)); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // caps win resolve_betting_market_group(moneyline_betting_markets.id, @@ -715,8 +756,8 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -729,7 +770,7 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test) CREATE_ICE_HOCKEY_BETTING_MARKET(); transfer(account_id_type(), alice_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); const betting_market_group_object& new_moneyline_betting_markets = create_betting_market_group({{"en", "New Moneyline"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type()); \ fc::optional betting_market_group = new_moneyline_betting_markets.id; @@ -743,10 +784,10 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test) update_betting_market(blackhawks_win_market.id, betting_market_group, fc::optional()); transfer(account_id_type(), bob_id, asset(10000000)); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // caps win resolve_betting_market_group(new_moneyline_betting_markets.id, @@ -757,8 +798,8 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -779,9 +820,9 @@ BOOST_AUTO_TEST_SUITE(other_betting_tests) { \ if (1) \ { \ - edump(("###")); \ + elog("###"); \ edump((e.to_detail_string())); \ - edump(("###")); \ + elog("###"); \ } \ FC_ASSERT(e.to_detail_string().find(reason) != \ std::string::npos, "expected error hasn't occured");\ @@ -802,7 +843,7 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture) transfer(account_id_type(), alice_id, asset(10000000)); transfer(account_id_type(), bob_id, asset(10000000)); - place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); fc::optional name = internationalized_string_type({{"en", "IBM"}, {"zh_Hans", "國家冰球聯"}, {"ja", "ナショナルホッケーリー"}}); @@ -831,10 +872,10 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture) //GRAPHENE_REQUIRE_THROW(try_update_event_group(nhl.id, sport_id, fc::optional()), fc::exception); TRY_EXPECT_THROW(try_update_event_group(nhl.id, sport_id, fc::optional()), fc::exception, "invalid sport specified"); - place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // caps win resolve_betting_market_group(moneyline_betting_markets.id, @@ -845,8 +886,8 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture) uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage; uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); } FC_LOG_AND_RETHROW() } @@ -894,17 +935,17 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test ) BOOST_TEST_MESSAGE("cilic_wins_market " << fc::variant(cilic_wins_market.id).as()); BOOST_TEST_MESSAGE("querrey_wins_market " << fc::variant(querrey_wins_market.id).as()); - place_bet(alice_id, berdych_wins_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); - place_bet(bob_id, berdych_wins_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, berdych_wins_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, berdych_wins_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); - place_bet(alice_id, cilic_wins_market.id, bet_type::back, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 100000 / 50 /* chain defaults to 2% fees */); - place_bet(bob_id, cilic_wins_market.id, bet_type::lay, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 100000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, cilic_wins_market.id, bet_type::back, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, cilic_wins_market.id, bet_type::lay, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 100000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 100000); // federer wins resolve_betting_market_group(moneyline_berdych_vs_federer.id, @@ -914,8 +955,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test ) uint32_t bob_rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Bob's rake value " + std::to_string(bob_rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000 + 2000000 - bob_rake_value); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 100000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 100000 + 2000000 - bob_rake_value); // cilic wins resolve_betting_market_group(moneyline_cilic_vs_querrey.id, @@ -925,8 +966,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test ) uint32_t alice_rake_value = (-100000 + 200000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Alice rake value " + std::to_string(alice_rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000 + 200000 - alice_rake_value); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000 + 2000000 - bob_rake_value); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 100000 + 200000 - alice_rake_value); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 100000 + 2000000 - bob_rake_value); } FC_LOG_AND_RETHROW() } @@ -948,8 +989,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test ) BOOST_TEST_MESSAGE("federer_wins_final_market " << fc::variant(federer_wins_final_market.id).as()); BOOST_TEST_MESSAGE("cilic_wins_final_market " << fc::variant(cilic_wins_final_market.id).as()); - place_bet(alice_id, cilic_wins_final_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); - place_bet(bob_id, cilic_wins_final_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */); + place_bet(alice_id, cilic_wins_final_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); + place_bet(bob_id, cilic_wins_final_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION); betting_market_group_id_type moneyline_cilic_vs_federer_id = moneyline_cilic_vs_federer.id; auto cilic_wins_final_market_id = cilic_wins_final_market.id; @@ -965,8 +1006,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test ) const betting_market_group_object& betting_market_group = moneyline_cilic_vs_federer_id(db); BOOST_CHECK_EQUAL(betting_market_group.total_matched_bets_amount.value, 2000000); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000); // federer wins resolve_betting_market_group(moneyline_cilic_vs_federer_id, @@ -976,8 +1017,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test ) uint32_t bob_rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100; BOOST_TEST_MESSAGE("Bob's rake value " + std::to_string(bob_rake_value)); - BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000); - BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - bob_rake_value); + BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000); + BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 + 2000000 - bob_rake_value); } FC_LOG_AND_RETHROW() } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index f6881f36..7ac43736 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -225,7 +225,6 @@ void database_fixture::verify_asset_supplies( const database& db ) for (const bet_object& o : db.get_index_type().indices()) { total_balances[o.amount_to_bet.asset_id] += o.amount_to_bet.amount; - total_balances[o.amount_to_bet.asset_id] += o.amount_reserved_for_fees; } for (const betting_market_position_object& o : db.get_index_type().indices()) { @@ -1394,14 +1393,13 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark } FC_CAPTURE_AND_RETHROW( (betting_market_id) (group_id) (payout_condition) ) } - void database_fixture::place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier, share_type amount_reserved_for_fees) + void database_fixture::place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier) { try { bet_place_operation bet_place_op; bet_place_op.bettor_id = bettor_id; bet_place_op.betting_market_id = betting_market_id; bet_place_op.amount_to_bet = amount_to_bet; bet_place_op.backer_multiplier = backer_multiplier; - bet_place_op.amount_reserved_for_fees = amount_reserved_for_fees; bet_place_op.back_or_lay = back_or_lay; trx.operations.push_back(bet_place_op); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index a8241a96..aedb2dab 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -318,7 +318,7 @@ struct database_fixture { /*fc::optional description,*/ fc::optional payout_condition); - void place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier, share_type amount_reserved_for_fees); + void place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier); void resolve_betting_market_group(betting_market_group_id_type betting_market_group_id, std::map resolutions); void cancel_unmatched_bets(betting_market_group_id_type betting_market_group_id);