start refactoring short

This commit is contained in:
Daniel Larimer 2015-06-16 09:52:33 -04:00
parent 1c7a39d3e9
commit 9dbf78c891
7 changed files with 84 additions and 449 deletions

View file

@ -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 )

View file

@ -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<account_id_type>& active_auth_set, flat_set<account_id_type>&)const;
void validate()const;
share_type calculate_fee( const fee_schedule_type& k )const;
pair<asset_id_type,asset_id_type> 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<account_id_type>& active_auth_set, flat_set<account_id_type>&)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<asset>() );
}
};
/**
* @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,

View file

@ -22,32 +22,6 @@
namespace graphene { namespace chain {
class short_order_create_evaluator : public evaluator<short_order_create_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<short_order_cancel_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<call_order_update_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

View file

@ -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<short_order_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<by_id>,
member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_expiration>, member< short_order_object, time_point_sec, &short_order_object::expiration> >,
ordered_unique< tag<by_price>,
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<price>, std::less<object_id_type> >
>
>
> 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_object, force_settlement_object_multi_index_type> 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) )

View file

@ -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 )

View file

@ -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<account_id_type>& active_auth_set, flat_set<account_id_type>&) 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<account_id_type>& active_auth_set, flat_set<account_id_type>&) 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<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
{
@ -433,18 +402,13 @@ void call_order_update_operation::get_required_auth(flat_set<account_id_type>& 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)

View file

@ -23,139 +23,6 @@
#include <fc/uint128.hpp>
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 = &quote_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>( [&]( 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<limit_order_index>();
const auto& limit_price_idx = limit_order_idx.indices().get<by_price>();
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<call_order_index>().indices().get<by_account>();
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<call_order_index>().indices().get<by_account>();
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_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;
}