start refactoring short
This commit is contained in:
parent
1c7a39d3e9
commit
9dbf78c891
7 changed files with 84 additions and 449 deletions
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = "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>( [&]( 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue