From 9dbf78c891f32e5f0f9728106a783c2171e19d20 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 09:52:33 -0400 Subject: [PATCH] start refactoring short --- .../chain/include/graphene/chain/asset.hpp | 25 +- .../include/graphene/chain/operations.hpp | 92 +----- .../graphene/chain/short_order_evaluator.hpp | 27 +- .../graphene/chain/short_order_object.hpp | 66 ----- .../chain/include/graphene/chain/types.hpp | 9 +- libraries/chain/operations.cpp | 38 +-- libraries/chain/short_order_evaluator.cpp | 276 +++++------------- 7 files changed, 84 insertions(+), 449 deletions(-) diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index bee277ec..aac46ed1 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -121,7 +121,7 @@ namespace graphene { namespace chain { /** * @class price_feed - * @brief defines market parameters for shorts and margin positions + * @brief defines market parameters for margin positions */ struct price_feed { @@ -131,10 +131,6 @@ namespace graphene { namespace chain { * unreasonable prices. */ price call_limit; - /** - * Short orders will only be matched against bids above this price. - */ - price short_limit; /** * Forced settlements will evaluate using this price. */ @@ -144,20 +140,6 @@ namespace graphene { namespace chain { */ uint32_t max_margin_period_sec = GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC; - /** - * Required maintenance collateral is defined - * as a fixed point number with a maximum value of 10.000 - * and a minimum value of 1.000. - * - * This value must be greater than required_maintenance_collateral or - * a margin call would be triggered immediately. - * - * Default requirement is $2 of collateral per $1 of debt based - * upon the premise that both parties to every trade should bring - * equal value to the table. - */ - uint16_t required_initial_collateral = GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO; - /** * Required maintenance collateral is defined * as a fixed point number with a maximum value of 10.000 @@ -191,6 +173,7 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::asset, (amount)(asset_id) ) FC_REFLECT( graphene::chain::price, (base)(quote) ) -#define GRAPHENE_PRICE_FEED_FIELDS (call_limit)(short_limit)(settlement_price)(max_margin_period_sec)\ - (required_initial_collateral)(required_maintenance_collateral) + +#define GRAPHENE_PRICE_FEED_FIELDS (call_limit)(settlement_price)(max_margin_period_sec)(required_maintenance_collateral) + FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS ) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index b670be1d..ba876c9a 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -827,89 +827,6 @@ namespace graphene { namespace chain { } }; - /** - * @ingroup operations - * - * Define a new short order, if it is filled it will - * be merged with existing call orders for the same - * account. If maintenance_collateral_ratio is set - * it will update any existing open call orders to - * use the new maintenance level. - * - * When shorting you specify the total amount to sell - * and the amount of collateral along with the initial - * ratio. The price it will sell at is (amount_to_sell/(collateral*initial_collateral_ratio/2000)) - */ - struct short_order_create_operation - { - /// The account placing a short order (this account must sign the transaction) - account_id_type seller; - /// The amount of market-issued asset to short sell - asset amount_to_sell; - /// The fee paid by seller - asset fee; - /// The amount of collateral to withdraw from the seller - asset collateral; - /// Fixed point representation of initial collateral ratio, with three digits of precision - /// Must be greater than or equal to the minimum specified by price feed - uint16_t initial_collateral_ratio = GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO; - /// Fixed point representation of maintenance collateral ratio, with three digits of precision - /// Must be greater than or equal to the minimum specified by price feed - uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO; - /// Expiration time for this order. Any unfilled portion of this order which is on the books at or past this time - /// will automatically be canceled. - time_point_sec expiration = time_point_sec::maximum(); - - account_id_type fee_payer()const { return seller; } - void get_required_auth(flat_set& active_auth_set, flat_set&)const; - void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - - pair get_market()const - { - return amount_to_sell.asset_id < collateral.asset_id ? - std::make_pair( amount_to_sell.asset_id, collateral.asset_id ) : - std::make_pair( collateral.asset_id, amount_to_sell.asset_id ); - } - - /** convention: amount_to_sell / amount_to_receive */ - price sell_price()const { return ~price::call_price(amount_to_sell, collateral, initial_collateral_ratio); } - - /** convention: amount_to_sell / amount_to_receive means we are - * selling collateral to receive debt - **/ - price call_price() const { return price::call_price(amount_to_sell, collateral, maintenance_collateral_ratio); } - - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( seller, -collateral ); - } - }; - - /** - * @ingroup operations - * Cancel the short order and return the balance to the - * order->seller account. - */ - struct short_order_cancel_operation - { - short_order_id_type order; - account_id_type fee_paying_account; ///< Must be order->seller - asset fee; ///< paid by order->seller - - account_id_type fee_payer()const { return fee_paying_account; } - void get_required_auth(flat_set& active_auth_set, flat_set&)const; - void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - - void get_balance_delta( balance_accumulator& acc, const operation_result& result )const - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( fee_payer(), result.get() ); - } - }; - /** * @ingroup operations @@ -924,13 +841,15 @@ namespace graphene { namespace chain { * have appropriate asset_ids, even if the amount is zero. * * @note this operation can be used to force a market order using the collateral without requiring outside funds. + * + * @note this operation can be used to issue new bitassets provided there is sufficient collateral */ struct call_order_update_operation { account_id_type funding_account; ///< pays fee, collateral, and cover asset fee; ///< paid by funding_account asset collateral_to_add; ///< the amount of collateral to add to the margin position - asset amount_to_cover; ///< the amount of the debt to be paid off + asset amount_to_cover; ///< the amount of the debt to be paid off, may be negative to issue new debt uint16_t maintenance_collateral_ratio = 0; ///< 0 means don't change, 1000 means feed account_id_type fee_payer()const { return funding_account; } @@ -1393,9 +1312,7 @@ namespace graphene { namespace chain { typedef fc::static_variant< transfer_operation, limit_order_create_operation, - short_order_create_operation, limit_order_cancel_operation, - short_order_cancel_operation, call_order_update_operation, key_create_operation, account_create_operation, @@ -1594,9 +1511,6 @@ FC_REFLECT( graphene::chain::limit_order_create_operation, ) FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) ) FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order) ) -FC_REFLECT( graphene::chain::short_order_cancel_operation,(fee)(fee_paying_account)(order) ) -FC_REFLECT( graphene::chain::short_order_create_operation, (fee)(seller)(amount_to_sell)(collateral) - (initial_collateral_ratio)(maintenance_collateral_ratio)(expiration) ) FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(collateral_to_add)(amount_to_cover)(maintenance_collateral_ratio) ) FC_REFLECT( graphene::chain::transfer_operation, diff --git a/libraries/chain/include/graphene/chain/short_order_evaluator.hpp b/libraries/chain/include/graphene/chain/short_order_evaluator.hpp index acf1acd4..485424fb 100644 --- a/libraries/chain/include/graphene/chain/short_order_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/short_order_evaluator.hpp @@ -22,32 +22,6 @@ namespace graphene { namespace chain { - class short_order_create_evaluator : public evaluator - { - public: - typedef short_order_create_operation operation_type; - - object_id_type do_evaluate( const short_order_create_operation& o ); - object_id_type do_apply( const short_order_create_operation& o ); - - const short_order_create_operation* _op = nullptr; - const account_object* _seller = nullptr; - const asset_object* _sell_asset = nullptr; - const asset_object* _receive_asset = nullptr; - share_type _priority_fee = 0; - }; - - class short_order_cancel_evaluator : public evaluator - { - public: - typedef short_order_cancel_operation operation_type; - - asset do_evaluate( const short_order_cancel_operation& o ); - asset do_apply( const short_order_cancel_operation& o ); - - const short_order_object* _order; - }; - class call_order_update_evaluator : public evaluator { public: @@ -60,6 +34,7 @@ namespace graphene { namespace chain { const asset_object* _debt_asset = nullptr; const account_object* _paying_account = nullptr; const call_order_object* _order = nullptr; + const bitasset_data_object* _bitasset_data = nullptr; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/short_order_object.hpp b/libraries/chain/include/graphene/chain/short_order_object.hpp index acbc59d8..1036b743 100644 --- a/libraries/chain/include/graphene/chain/short_order_object.hpp +++ b/libraries/chain/include/graphene/chain/short_order_object.hpp @@ -26,47 +26,6 @@ namespace graphene { namespace chain { using namespace graphene::db; - /** - * @class short_order_object - * @brief maintains state about requests to short an asset - * - * Short orders are only valid if their sell price is above the - * fair market value of the asset at the feed price. Users can - * place shorts at any price but their order will be ignored - * beyond the feed. - * - * All shorts have a minimial initial collateral ratio requirement that is - * defined by the network, but individuals may choose to have a higher - * initial collateral to avoid the risk of being margin called. - * - * All shorts have a maintenance collateral ratio that must be kept or - * the network will automatically cover the short order. Users can - * specify a higher maintenance collateral ratio as a form of "stop loss" - * and to potentially get ahead of a short squeeze. - */ - class short_order_object : public abstract_object - { - public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = short_order_object_type; - - time_point_sec expiration; - account_id_type seller; - share_type for_sale; - share_type available_collateral; ///< asset_id == sell_price.quote.asset_id - price sell_price; ///< the price the short is currently at = min(limit_price,feed) - price call_price; ///< the price that will be used to trigger margin calls after match, must be 1:1 if prediction market - uint16_t initial_collateral_ratio = 0; ///< may be higher than the network requires - uint16_t maintenance_collateral_ratio = 0; ///< may optionally be higher than the network requires - - asset get_collateral()const { return asset( available_collateral, sell_price.quote.asset_id ); } - /** if the initial_collateral_ratio is 0, then this is a prediction market order which means the - * amount for sale depends upon price and available collateral. - */ - asset amount_for_sale()const { return asset( for_sale, sell_price.base.asset_id ); } - asset amount_to_receive()const { return amount_for_sale() * sell_price; } - }; - /** * @class call_order_object * @brief tracks debt and call price information @@ -115,27 +74,6 @@ namespace graphene { namespace chain { { return balance.asset_id; } }; - struct by_id; - struct by_price; - struct by_account; - struct by_expiration; - struct by_collateral; - typedef multi_index_container< - short_order_object, - indexed_by< - hashed_unique< tag, - member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member< short_order_object, time_point_sec, &short_order_object::expiration> >, - ordered_unique< tag, - composite_key< short_order_object, - member< short_order_object, price, &short_order_object::sell_price>, - member< object, object_id_type, &object::id> - >, - composite_key_compare< std::greater, std::less > - > - > - > short_order_multi_index_type; - typedef multi_index_container< call_order_object, indexed_by< @@ -187,10 +125,6 @@ namespace graphene { namespace chain { typedef generic_index force_settlement_index; } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::short_order_object, (graphene::db::object), - (expiration)(seller)(for_sale)(available_collateral)(sell_price) - (call_price)(initial_collateral_ratio)(maintenance_collateral_ratio) - ) FC_REFLECT_DERIVED( graphene::chain::call_order_object, (graphene::db::object), (borrower)(collateral)(debt)(call_price)(maintenance_collateral_ratio) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 7a7d15d1..a4d2b089 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -108,7 +108,6 @@ namespace graphene { namespace chain { delegate_object_type, witness_object_type, limit_order_object_type, - short_order_object_type, call_order_object_type, custom_object_type, proposal_object_type, @@ -152,7 +151,6 @@ namespace graphene { namespace chain { class force_settlement_object; class key_object; class limit_order_object; - class short_order_object; class call_order_object; class custom_object; class proposal_object; @@ -169,7 +167,6 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, delegate_object_type, delegate_object> delegate_id_type; typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type; typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type; - typedef object_id< protocol_ids, short_order_object_type, short_order_object> short_order_id_type; typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type; typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type; typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type; @@ -358,7 +355,7 @@ namespace graphene { namespace chain { uint32_t witness_withdraw_pay_fee; ///< fee for withdrawing witness pay uint32_t transfer_fee; ///< fee for transferring some asset uint32_t limit_order_fee; ///< fee for placing a limit order in the markets - uint32_t short_order_fee; ///< fee for placing a short order in the markets + uint32_t call_order_fee; ///< fee for placing a call order in the markets uint32_t publish_feed_fee; ///< fee for publishing a price feed uint32_t asset_create_fee; ///< the cost to register the cheapest asset uint32_t asset_update_fee; ///< the cost to modify a registered asset @@ -487,7 +484,6 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (delegate_object_type) (witness_object_type) (limit_order_object_type) - (short_order_object_type) (call_order_object_type) (custom_object_type) (proposal_object_type) @@ -532,7 +528,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (witness_withdraw_pay_fee) (transfer_fee) (limit_order_fee) - (short_order_fee) + (call_order_fee) (publish_feed_fee) (asset_create_fee) (asset_update_fee) @@ -587,7 +583,6 @@ FC_REFLECT_TYPENAME( graphene::chain::force_settlement_id_type ) FC_REFLECT_TYPENAME( graphene::chain::delegate_id_type ) FC_REFLECT_TYPENAME( graphene::chain::witness_id_type ) FC_REFLECT_TYPENAME( graphene::chain::limit_order_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::short_order_id_type ) FC_REFLECT_TYPENAME( graphene::chain::call_order_id_type ) FC_REFLECT_TYPENAME( graphene::chain::custom_id_type ) FC_REFLECT_TYPENAME( graphene::chain::proposal_id_type ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index c08027e2..ce9c2942 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -393,37 +393,6 @@ share_type limit_order_cancel_operation::calculate_fee(const fee_schedule_type& return k.limit_order_fee; } -void short_order_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const -{ - active_auth_set.insert(seller); -} - -void short_order_create_operation::validate()const -{ - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( initial_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); - FC_ASSERT( initial_collateral_ratio > maintenance_collateral_ratio ); - FC_ASSERT( initial_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); -} - -share_type short_order_create_operation::calculate_fee(const fee_schedule_type& k) const -{ - return k.short_order_fee; -} -void short_order_cancel_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const -{ - active_auth_set.insert(fee_paying_account); -} - -void short_order_cancel_operation::validate()const -{ - FC_ASSERT( fee.amount >= 0 ); -} - -share_type short_order_cancel_operation::calculate_fee(const fee_schedule_type& k) const -{ - return k.short_order_fee; -} void call_order_update_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const { @@ -433,18 +402,13 @@ void call_order_update_operation::get_required_auth(flat_set& a void call_order_update_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( collateral_to_add.amount > 0 || amount_to_cover.amount > 0 || maintenance_collateral_ratio > 0 ); - if( amount_to_cover.amount == 0 ) FC_ASSERT( collateral_to_add.amount >= 0 ); - if( collateral_to_add.amount.value <= 0 ) FC_ASSERT( amount_to_cover.amount.value > 0 ); - - FC_ASSERT( amount_to_cover.amount >= 0 ); FC_ASSERT( amount_to_cover.asset_id != collateral_to_add.asset_id ); FC_ASSERT( maintenance_collateral_ratio == 0 || maintenance_collateral_ratio >= 1000 ); } share_type call_order_update_operation::calculate_fee(const fee_schedule_type& k) const { - return k.short_order_fee; + return k.call_order_fee; } proposal_create_operation proposal_create_operation::genesis_proposal(const database& db) diff --git a/libraries/chain/short_order_evaluator.cpp b/libraries/chain/short_order_evaluator.cpp index ab05611a..743fe634 100644 --- a/libraries/chain/short_order_evaluator.cpp +++ b/libraries/chain/short_order_evaluator.cpp @@ -23,139 +23,6 @@ #include namespace graphene { namespace chain { -object_id_type short_order_create_evaluator::do_evaluate( const short_order_create_operation& op ) -{ - database& d = db(); - - FC_ASSERT( op.expiration >= d.head_block_time() ); - - const asset_object& base_asset = op.amount_to_sell.asset_id(d); - const asset_object& quote_asset = op.collateral.asset_id(d); - - FC_ASSERT( base_asset.is_market_issued() ); - FC_ASSERT( quote_asset.id == base_asset.bitasset_data(d).options.short_backing_asset ); - _seller = fee_paying_account; - _receive_asset = "e_asset; - _sell_asset = &base_asset; - - - FC_ASSERT( !(base_asset.options.flags & white_list) || _seller->is_authorized_asset(base_asset) ); - FC_ASSERT( !(quote_asset.options.flags & white_list) || _seller->is_authorized_asset(quote_asset) ); - - const asset_bitasset_data_object& bitasset_data = _sell_asset->bitasset_data(d); - if( bitasset_data.is_prediction_market ) - { - FC_ASSERT( op.initial_collateral_ratio == 0 ); - FC_ASSERT( op.maintenance_collateral_ratio == 0 ); - auto p = op.sell_price(); - - // the maximum price is 1:1, it does not make sense to charge more than - // the collateral backing the position. - FC_ASSERT( p.base.amount < p.quote.amount ); - } - else - { - FC_ASSERT( op.initial_collateral_ratio >= bitasset_data.current_feed.required_initial_collateral ); - FC_ASSERT( op.maintenance_collateral_ratio >= bitasset_data.current_feed.required_maintenance_collateral ); - FC_ASSERT( op.sell_price() >= bitasset_data.current_feed.short_limit ); - } - - return object_id_type(); -} - -object_id_type short_order_create_evaluator::do_apply( const short_order_create_operation& op ) -{ - db().adjust_balance(op.seller, -op.collateral); - - const auto& new_order_object = db().create( [&]( short_order_object& obj ){ - obj.seller = _seller->id; - obj.for_sale = op.amount_to_sell.amount; - obj.available_collateral = op.collateral.amount; - obj.sell_price = op.sell_price(); - obj.call_price = op.call_price(); - obj.initial_collateral_ratio = op.initial_collateral_ratio; - obj.maintenance_collateral_ratio = op.maintenance_collateral_ratio; - obj.expiration = op.expiration; - }); - short_order_id_type new_id = new_order_object.id; - - if( op.collateral.asset_id == asset_id_type() ) - { - auto& bal_obj = fee_paying_account->statistics(db()); - db().modify( bal_obj, [&]( account_statistics_object& obj ){ - obj.total_core_in_orders += op.collateral.amount; - }); - } - - // Possible optimization: We only need to check calls if both are true: - // - The new order is at the front of the book - // - The new order is below the call limit price - db().check_call_orders(*_sell_asset); - - if( !db().find(new_id) ) // then we were filled by call order - return new_id; - - const auto& limit_order_idx = db().get_index_type(); - const auto& limit_price_idx = limit_order_idx.indices().get(); - - auto min_limit_price = ~op.sell_price(); - - auto itr = limit_price_idx.lower_bound( min_limit_price.max() ); - auto end = limit_price_idx.upper_bound( min_limit_price ); - - while( itr != end ) - { - auto old_itr = itr; - ++itr; - if( db().match( *old_itr, new_order_object, old_itr->sell_price ) != 1 ) - break; // 1 means ONLY old iter filled - } - - //Possible optimization: only check calls if the new order completely filled some old order - //Do I need to check both assets? - db().check_call_orders(*_sell_asset); - db().check_call_orders(*_receive_asset); - - return new_id; -} // short_order_evaluator::do_apply - - -asset short_order_cancel_evaluator::do_evaluate( const short_order_cancel_operation& o ) -{ - database& d = db(); - - _order = &o.order(d); - FC_ASSERT( _order->seller == o.fee_paying_account ); - - return _order->get_collateral(); -} - -asset short_order_cancel_evaluator::do_apply( const short_order_cancel_operation& o ) -{ - database& d = db(); - - auto refunded = _order->get_collateral(); - d.adjust_balance(o.fee_paying_account, refunded); - auto base_asset = _order->sell_price.base.asset_id; - auto quote_asset = _order->sell_price.quote.asset_id; - - d.remove( *_order ); - - if( refunded.asset_id == asset_id_type() ) - { - auto& stats_obj = fee_paying_account->statistics(d); - d.modify( stats_obj, [&]( account_statistics_object& obj ){ - obj.total_core_in_orders -= refunded.amount; - }); - } - - // Possible optimization: order can be called by canceling a short order iff the canceled order was at the top of the book. - // Do I need to check calls in both assets? - db().check_call_orders(base_asset(d)); - db().check_call_orders(quote_asset(d)); - - return refunded; -} asset call_order_update_evaluator::do_evaluate(const call_order_update_operation& o) { try { @@ -164,101 +31,104 @@ asset call_order_update_evaluator::do_evaluate(const call_order_update_operation _paying_account = &o.funding_account(d); _debt_asset = &o.amount_to_cover.asset_id(d); - const asset_bitasset_data_object& bitasset_data = _debt_asset->bitasset_data(d); - FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a market-issued asset.", + _bitasset_data = &_debt_asset->bitasset_data(d); + FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.", ("sym", _debt_asset->symbol) ); - FC_ASSERT( o.collateral_to_add.asset_id == bitasset_data.options.short_backing_asset ); + FC_ASSERT( o.collateral_to_add.asset_id == _bitasset_data->options.short_backing_asset ); - if( bitasset_data.is_prediction_market ) + if( _bitasset_data->is_prediction_market ) { - FC_ASSERT( o.collateral_to_add.amount <= 0 ); - FC_ASSERT( -o.collateral_to_add.amount == o.amount_to_cover.amount ); + FC_ASSERT( o.collateral_to_add.amount == -o.amount_to_cover.amount ); FC_ASSERT( o.maintenance_collateral_ratio == 0 ); } else { FC_ASSERT( o.maintenance_collateral_ratio == 0 || - o.maintenance_collateral_ratio > bitasset_data.current_feed.required_maintenance_collateral ); + o.maintenance_collateral_ratio > _bitasset_data->current_feed.required_maintenance_collateral ); } - FC_ASSERT( d.get_balance(*_paying_account, *_debt_asset) >= o.amount_to_cover, - "Cannot cover by ${c} when payer has ${b}", - ("c", o.amount_to_cover.amount)("b", d.get_balance(*_paying_account, *_debt_asset).amount) ); - FC_ASSERT( d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)) >= o.collateral_to_add, - "Cannot increase collateral by ${c} when payer has ${b}", ("c", o.amount_to_cover.amount) - ("b", d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)).amount) ); - - auto& call_idx = d.get_index_type().indices().get(); - auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.amount_to_cover.asset_id) ); - FC_ASSERT( itr != call_idx.end(), "Could not find call order for ${sym} belonging to ${acct}.", - ("sym", _debt_asset->symbol)("acct", _paying_account->name) ); - _order = &*itr; - - FC_ASSERT( o.amount_to_cover.asset_id == _order->debt_type() ); - - FC_ASSERT( o.amount_to_cover.amount <= _order->get_debt().amount ); - - if( o.amount_to_cover.amount < _order->get_debt().amount ) + if( o.amount_to_cover > 0 ) { - FC_ASSERT( (_order->get_debt() - o.amount_to_cover) * - price::call_price(_order->get_debt() - o.amount_to_cover, - _order->get_collateral() + o.collateral_to_add, - o.maintenance_collateral_ratio? o.maintenance_collateral_ratio - : _order->maintenance_collateral_ratio) - < _order->get_collateral(), - "Order would be called immediately following this update. Refusing to apply update." ); - FC_ASSERT( o.amount_to_cover < _order->get_debt(), "Cover amount is greater than debt." ); - } else { - _closing_order = true; - FC_ASSERT( o.collateral_to_add.amount == -_order->get_collateral().amount, "", - ("collateral", _order->get_collateral()) ); - return _order->get_collateral(); + FC_ASSERT( d.get_balance(*_paying_account, *_debt_asset) >= o.amount_to_cover, + "Cannot cover by ${c} when payer only has ${b}", + ("c", o.amount_to_cover.amount)("b", d.get_balance(*_paying_account, *_debt_asset).amount) ); } + if( o.collateral_to_add > 0 ) + { + FC_ASSERT( d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)) >= o.collateral_to_add, + "Cannot increase collateral by ${c} when payer only has ${b}", ("c", o.amount_to_cover.amount) + ("b", d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)).amount) ); + } + return asset(); } FC_CAPTURE_AND_RETHROW( (o) ) } + asset call_order_update_evaluator::do_apply(const call_order_update_operation& o) { database& d = db(); + asset collateral_returned = -o.collateral_to_add; - d.adjust_balance(_paying_account->get_id(), -o.amount_to_cover); + d.adjust_balance( o.funding_account, -o.amount_to_cover); + d.adjust_balance( o.funding_account, -o.collateral_to_add); // Deduct the debt paid from the total supply of the debt asset. - d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) { - dynamic_asset.current_supply -= o.amount_to_cover.amount; - assert(dynamic_asset.current_supply >= 0); - }); - - asset collateral_returned; - if( _closing_order ) + if( o.amount_to_cover != 0 ) { - collateral_returned = _order->get_collateral(); - // Credit the account's balances for his returned collateral. - d.adjust_balance(_paying_account->get_id(), collateral_returned); - d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) { - if( _order->get_collateral().asset_id == asset_id_type() ) - stats.total_core_in_orders -= collateral_returned.amount; + d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) { + dynamic_asset.current_supply -= o.amount_to_cover.amount; + assert(dynamic_asset.current_supply >= 0); }); - // Remove the call order. - d.remove(*_order); - } else { - // Update the call order. - d.modify(*_order, [&o](call_order_object& call) { - call.debt -= o.amount_to_cover.amount; - call.collateral += o.collateral_to_add.amount; - if( o.maintenance_collateral_ratio ) - call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; - call.update_call_price(); - }); - if( o.collateral_to_add.amount > 0 ) - // Deduct the added collateral from the account. - d.adjust_balance(_paying_account->get_id(), -o.collateral_to_add); - d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) { - if( o.collateral_to_add.asset_id == asset_id_type() ) - stats.total_core_in_orders += o.collateral_to_add.amount; - }); } + auto& call_idx = d.get_index_type().indices().get(); + auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.amount_to_cover.asset_id) ); + if( itr == call_idx.end() ) + { + FC_ASSERT( o.collateral_to_add.amount > 0 ); + FC_ASSERT( o.amount_to_cover.amount < 0 ); + d.create( [&](call_order_object& call ){ + call.owner = o.funding_account; + call.collateral = o.collateral_to_add.amount; + call.debt = -o.amount_to_cover.amount; + call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; + // TODO: this is only necessary for non-prediction markets + call.update_call_price(); + FC_ASSERT( call.call_price < _bitasset_data->current_feed.settlement_price ); + }); + } + else + { + if( itr->debt - o.amount_to_cover == 0 ) + { + FC_ASSERT( o.collateral_to_add == 0 ); + collateral_returned = itr->get_collateral(); + d.adjust_balance( o.funding_account, call. + d.remove( *itr ); + } + else + { + FC_ASSERT( (itr->debt - o.amount_to_cover.amount) >= 0 ); + FC_ASSERT( (itr->collateral + o.collateral_to_add.amount) >= 0 ); + + d.modify( *itr, [&]( call_order_object& call ){ + call.collateral += o.collateral_to_add.amount; + call.debt -= o.amount_to_cover.amount; + if( o.maintenance_collateral_ratio ) + call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; + + // TODO: this is only necessary for non-prediction markets + call.update_call_price(); + FC_ASSERT( call.call_price < _bitasset_data->current_feed.settlement_price ); + }); + } + } + if( collateral_returned.asset_id == asset_id_type() && collateral_returned.amount != 0 ) + { + d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) { + stats.total_core_in_orders -= collateral_returned.amount; + }); + } return collateral_returned; }