BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
market_evaluator.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
27 
29 
32 #include <graphene/chain/hardfork.hpp>
34 
36 
37 namespace graphene { namespace chain {
39 { try {
40  const database& d = db();
41 
43 
47 
49  {
52  limit_order_create_market_not_whitelisted,
53  "This market has not been whitelisted by the selling asset", );
54  }
56  {
59  limit_order_create_market_blacklisted,
60  "This market has been blacklisted by the selling asset", );
61  }
62 
64  limit_order_create_selling_asset_unauthorized,
65  "The account is not allowed to transact the selling asset", );
66 
68  limit_order_create_receiving_asset_unauthorized,
69  "The account is not allowed to transact the receiving asset", );
70 
72  limit_order_create_insufficient_balance,
73  "insufficient balance",
74  ("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) );
75 
76  return void_result();
77 } FC_CAPTURE_AND_RETHROW( (op) ) }
78 
80 {
81  if( db().head_block_time() <= HARDFORK_CORE_604_TIME )
83  else
84  if( !trx_state->skip_fee )
85  {
86  if( fee_asset->get_id() != asset_id_type() )
87  {
90  });
91  }
92  }
93 }
94 
96 {
97  if( db().head_block_time() <= HARDFORK_445_TIME )
99  else
100  {
102  if( db().head_block_time() > HARDFORK_CORE_604_TIME && fee_asset->get_id() != asset_id_type() )
104  }
105 }
106 
108 { try {
109  if( op.amount_to_sell.asset_id == asset_id_type() )
110  {
112  bal.total_core_in_orders += op.amount_to_sell.amount;
113  });
114  }
115 
117 
118  const auto& new_order_object = db().create<limit_order_object>([&](limit_order_object& obj){
119  obj.seller = _seller->id;
120  obj.for_sale = op.amount_to_sell.amount;
121  obj.sell_price = op.get_price();
122  obj.expiration = op.expiration;
123  obj.deferred_fee = _deferred_fee;
124  obj.deferred_paid_fee = _deferred_paid_fee;
125  });
126  limit_order_id_type order_id = new_order_object.id; // save this because we may remove the object by filling it
127  bool filled;
128  if( db().get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_625_TIME )
129  filled = db().apply_order_before_hardfork_625( new_order_object );
130  else
131  filled = db().apply_order( new_order_object );
132 
133  GRAPHENE_ASSERT( !op.fill_or_kill || filled,
134  limit_order_create_kill_unfilled,
135  "Killing limit order ${op} due to unable to fill",
136  ("op",op) );
137 
138  return order_id;
139 } FC_CAPTURE_AND_RETHROW( (op) ) }
140 
142 { try {
143  database& d = db();
144 
145  _order = d.find( o.order );
146 
147  GRAPHENE_ASSERT( _order != nullptr,
148  limit_order_cancel_nonexist_order,
149  "Limit order ${oid} does not exist",
150  ("oid", o.order) );
151 
152  GRAPHENE_ASSERT( _order->seller == o.fee_paying_account,
153  limit_order_cancel_owner_mismatch,
154  "Limit order ${oid} is owned by someone else",
155  ("oid", o.order) );
156 
157  return void_result();
158 } FC_CAPTURE_AND_RETHROW( (o) ) }
159 
161 { try {
162  database& d = db();
163 
164  auto base_asset = _order->sell_price.base.asset_id;
165  auto quote_asset = _order->sell_price.quote.asset_id;
166  auto refunded = _order->amount_for_sale();
167 
168  d.cancel_limit_order(*_order, false /* don't create a virtual op*/);
169 
170  if( d.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_606_TIME )
171  {
172  // Possible optimization: order can be called by canceling a limit order iff the canceled order was at the top of the book.
173  // Do I need to check calls in both assets?
174  d.check_call_orders(base_asset(d));
175  d.check_call_orders(quote_asset(d));
176  }
177 
178  return refunded;
179 } FC_CAPTURE_AND_RETHROW( (o) ) }
180 
182 { try {
183  database& d = db();
184 
185  auto next_maintenance_time = d.get_dynamic_global_properties().next_maintenance_time;
186 
187  _paying_account = &o.funding_account(d);
188  _debt_asset = &o.delta_debt.asset_id(d);
189  FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
190  ("sym", _debt_asset->symbol) );
191 
192  FC_ASSERT( o.delta_debt.amount <= 0 || _debt_asset->can_create_new_supply(), "Can not create new supply" );
193 
194  _dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d);
195 
196  /***
197  * There are instances of assets exceeding max_supply before hf 1465, therefore this code must remain.
198  */
199  if (next_maintenance_time > HARDFORK_CORE_1465_TIME)
200  {
201  FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply,
202  "Borrowing this quantity would exceed MAX_SUPPLY" );
203  }
204 
205  FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0,
206  "This transaction would bring current supply below zero.");
207 
208  _bitasset_data = &_debt_asset->bitasset_data(d);
209 
212  FC_ASSERT( !_bitasset_data->has_settlement(), "Cannot update debt position when the asset has been globally settled" );
213 
214  FC_ASSERT( o.delta_collateral.asset_id == _bitasset_data->options.short_backing_asset,
215  "Collateral asset type should be same as backing asset of debt asset" );
216 
217  if( _bitasset_data->is_prediction_market )
219  "Debt amount and collateral amount should be same when updating debt position in a prediction market" );
220  else if( _bitasset_data->current_feed.settlement_price.is_null() )
221  FC_THROW_EXCEPTION(insufficient_feeds, "Cannot borrow asset with no price feed.");
222 
223  // Note: there was code here checking whether the account has enough balance to increase delta collateral,
224  // which is now removed since the check is implicitly done later by `adjust_balance()` in `do_apply()`.
225 
226  return void_result();
227 } FC_CAPTURE_AND_RETHROW( (o) ) }
228 
229 
231 { try {
232  database& d = db();
233 
234  if( o.delta_debt.amount != 0 )
235  {
237 
238  // Deduct the debt paid from the total supply of the debt asset.
239  d.modify(*_dynamic_data_obj, [&](asset_dynamic_data_object& dynamic_asset) {
240  dynamic_asset.current_supply += o.delta_debt.amount;
241  });
242  }
243 
244  if( o.delta_collateral.amount != 0 )
245  {
247 
248  // Adjust the total core in orders accodingly
249  if( o.delta_collateral.asset_id == asset_id_type() )
250  {
251  d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) {
252  stats.total_core_in_orders += o.delta_collateral.amount;
253  });
254  }
255  }
256 
257  const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;
258  bool before_core_hardfork_1270 = ( next_maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue
259 
260  auto& call_idx = d.get_index_type<call_order_index>().indices().get<by_account>();
261  auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) );
262  const call_order_object* call_obj = nullptr;
263  call_order_id_type call_order_id;
264 
265  optional<price> old_collateralization;
266  optional<share_type> old_debt;
267 
268  if( itr == call_idx.end() ) // creating new debt position
269  {
270  FC_ASSERT( o.delta_collateral.amount > 0, "Delta collateral amount of new debt position should be positive" );
271  FC_ASSERT( o.delta_debt.amount > 0, "Delta debt amount of new debt position should be positive" );
272 
273  call_obj = &d.create<call_order_object>( [&o,this,before_core_hardfork_1270]( call_order_object& call ){
274  call.borrower = o.funding_account;
275  call.collateral = o.delta_collateral.amount;
276  call.debt = o.delta_debt.amount;
277  if( before_core_hardfork_1270 ) // before core-1270 hard fork, calculate call_price here and cache it
278  call.call_price = price::call_price( o.delta_debt, o.delta_collateral,
279  _bitasset_data->current_feed.maintenance_collateral_ratio );
280  else // after core-1270 hard fork, set call_price to 1
281  call.call_price = price( asset( 1, o.delta_collateral.asset_id ), asset( 1, o.delta_debt.asset_id ) );
282  call.target_collateral_ratio = o.extensions.value.target_collateral_ratio;
283  });
284  call_order_id = call_obj->id;
285  }
286  else // updating existing debt position
287  {
288  call_obj = &*itr;
289  auto new_collateral = call_obj->collateral + o.delta_collateral.amount;
290  auto new_debt = call_obj->debt + o.delta_debt.amount;
291  call_order_id = call_obj->id;
292 
293  if( new_debt == 0 )
294  {
295  FC_ASSERT( new_collateral == 0, "Should claim all collateral when closing debt position" );
296  d.remove( *call_obj );
297  return call_order_id;
298  }
299 
300  FC_ASSERT( new_collateral > 0 && new_debt > 0,
301  "Both collateral and debt should be positive after updated a debt position if not to close it" );
302 
303  old_collateralization = call_obj->collateralization();
304  old_debt = call_obj->debt;
305 
306  d.modify( *call_obj, [&o,new_debt,new_collateral,this,before_core_hardfork_1270]( call_order_object& call ){
307  call.collateral = new_collateral;
308  call.debt = new_debt;
309  if( before_core_hardfork_1270 ) // don't update call_price after core-1270 hard fork
310  {
311  call.call_price = price::call_price( call.get_debt(), call.get_collateral(),
312  _bitasset_data->current_feed.maintenance_collateral_ratio );
313  }
314  call.target_collateral_ratio = o.extensions.value.target_collateral_ratio;
315  });
316  }
317 
318  // then we must check for margin calls and other issues
319  if( !_bitasset_data->is_prediction_market )
320  {
321  // check to see if the order needs to be margin called now, but don't allow black swans and require there to be
322  // limit orders available that could be used to fill the order.
323  // Note: due to https://github.com/bitshares/bitshares-core/issues/649, before core-343 hard fork,
324  // the first call order may be unable to be updated if the second one is undercollateralized.
325  if( d.check_call_orders( *_debt_asset, false, false, _bitasset_data ) ) // don't allow black swan, not for new limit order
326  {
327  call_obj = d.find(call_order_id);
328  // before hard fork core-583: if we filled at least one call order, we are OK if we totally filled.
329  // after hard fork core-583: we want to allow increasing collateral
330  // Note: increasing collateral won't get the call order itself matched (instantly margin called)
331  // if there is at least a call order get matched but didn't cause a black swan event,
332  // current order must have got matched. in this case, it's OK if it's totally filled.
334  !call_obj,
335  call_order_update_unfilled_margin_call,
336  "Updating call order would trigger a margin call that cannot be fully filled"
337  );
338  }
339  else
340  {
341  call_obj = d.find(call_order_id);
342  // we know no black swan event has occurred
343  FC_ASSERT( call_obj, "no margin call was executed and yet the call object was deleted" );
344  // this HF must remain as-is, as the assert inside the "if" was triggered during push_proposal()
345  if( d.head_block_time() <= HARDFORK_CORE_583_TIME )
346  {
347  // We didn't fill any call orders. This may be because we
348  // aren't in margin call territory, or it may be because there
349  // were no matching orders. In the latter case, we throw.
351  // we know core-583 hard fork is before core-1270 hard fork, it's ok to use call_price here
352  ~call_obj->call_price < _bitasset_data->current_feed.settlement_price,
353  call_order_update_unfilled_margin_call,
354  "Updating call order would trigger a margin call that cannot be fully filled",
355  // we know core-583 hard fork is before core-1270 hard fork, it's ok to use call_price here
356  ("a", ~call_obj->call_price )("b", _bitasset_data->current_feed.settlement_price)
357  );
358  }
359  else // after hard fork core-583, always allow call order to be updated if collateral ratio
360  // is increased and debt is not increased
361  {
362  // We didn't fill any call orders. This may be because we
363  // aren't in margin call territory, or it may be because there
364  // were no matching orders. In the latter case,
365  // if collateral ratio is not increased or debt is increased, we throw.
366  // be here, we know no margin call was executed,
367  // so call_obj's collateral ratio should be set only by op
368  // ------
369  // Before BSIP77, CR of the new/updated position is required to be above MCR;
370  // after BSIP77, CR of the new/updated position is required to be above max(ICR,MCR).
371  // The `current_initial_collateralization` variable has been initialized according to the logic,
372  // so we directly use it here.
373  bool check = ( !before_core_hardfork_1270
374  && call_obj->collateralization() > _bitasset_data->current_initial_collateralization )
375  || ( before_core_hardfork_1270
376  && ~call_obj->call_price < _bitasset_data->current_feed.settlement_price )
377  || ( old_collateralization.valid() && call_obj->debt <= *old_debt
378  && call_obj->collateralization() > *old_collateralization );
379  FC_ASSERT( check,
380  "Can only increase collateral ratio without increasing debt when the debt position's "
381  "collateral ratio is lower than required initial collateral ratio (ICR), "
382  "if not to trigger a margin call that be fully filled immediately",
383  ("old_debt", old_debt)
384  ("new_debt", call_obj->debt)
385  ("old_collateralization", old_collateralization)
386  ("new_collateralization", call_obj->collateralization() )
387  );
388  }
389  }
390  }
391 
392  return call_order_id;
393 } FC_CAPTURE_AND_RETHROW( (o) ) }
394 
396 { try {
397  database& d = db();
398 
399  FC_ASSERT( d.head_block_time() > HARDFORK_CORE_216_TIME, "Not yet!" );
400 
401  _paying_account = &o.bidder(d);
402  _debt_asset = &o.debt_covered.asset_id(d);
403  FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
404  ("sym", _debt_asset->symbol) );
405 
406  _bitasset_data = &_debt_asset->bitasset_data(d);
407 
408  FC_ASSERT( _bitasset_data->has_settlement() );
409 
410  FC_ASSERT( o.additional_collateral.asset_id == _bitasset_data->options.short_backing_asset );
411 
412  FC_ASSERT( !_bitasset_data->is_prediction_market, "Cannot bid on a prediction market!" );
413 
415  const auto& index = bids.indices().get<by_account>();
416  const auto& bid = index.find( boost::make_tuple( o.debt_covered.asset_id, o.bidder ) );
417  if( bid != index.end() )
418  _bid = &(*bid);
419  else
420  FC_ASSERT( o.debt_covered.amount > 0, "Can't find bid to cancel?!");
421 
422  if( o.additional_collateral.amount > 0 )
423  {
424  if( _bid && d.head_block_time() >= HARDFORK_CORE_1692_TIME ) // TODO: see if HF check can be removed after HF
425  {
426  asset delta = o.additional_collateral - _bid->get_additional_collateral();
427  FC_ASSERT( d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)) >= delta,
428  "Cannot increase bid from ${oc} to ${nc} collateral when payer only has ${b}",
429  ("oc", _bid->get_additional_collateral().amount)("nc", o.additional_collateral.amount)
430  ("b", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );
431  } else
432  FC_ASSERT( d.get_balance( *_paying_account,
433  _bitasset_data->options.short_backing_asset(d) ) >= o.additional_collateral,
434  "Cannot bid ${c} collateral when payer only has ${b}", ("c", o.additional_collateral.amount)
435  ("b", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );
436  }
437 
438  return void_result();
439 } FC_CAPTURE_AND_RETHROW( (o) ) }
440 
441 
443 { try {
444  database& d = db();
445 
446  if( _bid )
447  d.cancel_bid( *_bid, false );
448 
449  if( o.debt_covered.amount == 0 ) return void_result();
450 
452 
453  _bid = &d.create<collateral_bid_object>([&]( collateral_bid_object& bid ) {
454  bid.bidder = o.bidder;
455  bid.inv_swan_price = o.additional_collateral / o.debt_covered;
456  });
457 
458  // Note: CORE asset in collateral_bid_object is not counted in account_stats.total_core_in_orders
459 
460  return void_result();
461 } FC_CAPTURE_AND_RETHROW( (o) ) }
462 
463 } } // graphene::chain
const asset_object * fee_asset
Definition: evaluator.hpp:118
void modify(const T &obj, const Lambda &m)
asset additional_collateral
the amount of collateral to bid for the debt
Definition: market.hpp:185
const T * find(object_id_type id) const
void adjust_balance(account_id_type account, asset delta)
Adjust a particular account&#39;s balance in a given asset by a delta.
Definition: db_balance.cpp:54
bool fill_or_kill
If this flag is set the entire order must be filled or the operation is rejected. ...
Definition: market.hpp:62
bool is_authorized_asset(const database &d, const account_object &acct, const asset_object &asset_obj)
optional< uint16_t > target_collateral_ratio
maximum CR to maintain when selling collateral on margin call
void_result do_evaluate(const limit_order_cancel_operation &o)
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
void_result do_evaluate(const call_order_update_operation &o)
Definition: api.cpp:56
tracks debt and call price information
asset get_balance(account_id_type owner, asset_id_type asset_id) const
Retrieve a particular account&#39;s balance in a given asset.
Definition: db_balance.cpp:35
account_id_type bidder
pays fee and additional collateral
Definition: market.hpp:184
const dynamic_global_property_object & get_dynamic_global_properties() const
Definition: db_getter.cpp:54
bool valid() const
Definition: optional.hpp:186
const account_object * fee_paying_account
Definition: evaluator.hpp:116
bool apply_order(const limit_order_object &new_order_object, bool allow_black_swan=true)
Definition: db_market.cpp:427
flat_set< asset_id_type > blacklist_markets
Definition: asset_ops.hpp:86
price call_price
Collateral / Debt.
asset delta_collateral
the amount of collateral to add to the margin position
Definition: market.hpp:126
account_id_type funding_account
pays fee, collateral, and cover
Definition: market.hpp:125
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
share_type debt
call_price.quote.asset_id, access via get_debt
void cancel_bid(const collateral_bid_object &bid, bool create_virtual_op=true)
Definition: db_market.cpp:178
flat_set< asset_id_type > whitelist_markets
Definition: asset_ops.hpp:84
bool apply_order_before_hardfork_625(const limit_order_object &new_order_object, bool allow_black_swan=true)
Process a new limit order through the markets.
Definition: db_market.cpp:359
object_id_type id
Definition: object.hpp:73
void_result do_evaluate(const limit_order_create_operation &o)
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:114
void cancel_limit_order(const limit_order_object &order, bool create_virtual_op=true, bool skip_cancel_fee=false)
Definition: db_market.cpp:238
time_point_sec head_block_time() const
Definition: db_getter.cpp:64
void_result do_apply(const bid_collateral_operation &o)
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
instructs the blockchain to attempt to sell one asset for anotherThe blockchain will atempt to sell a...
Definition: market.hpp:48
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
void_result do_evaluate(const bid_collateral_operation &o)
asset do_apply(const limit_order_cancel_operation &o)
tracks the asset information that changes frequentlyBecause the asset_object is very large it doesn&#39;t...
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:378
bids of collateral for debt after a black swan
share_type collateral
call_price.base.asset_id, access via get_collateral
asset delta_debt
the amount of the debt to be paid off, may be negative to issue new debt
Definition: market.hpp:127
const object & get(object_id_type id) const
Definition: index.hpp:111
virtual const object * find(object_id_type id) const override
asset_id_type asset_id
Definition: asset.hpp:39
account_statistics_id_type statistics
asset_id_type get_id() const
abstract base class for accessing objects indexed in various ways.
Definition: index.hpp:71
void remove(const object &obj)
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
const asset_dynamic_data_object * fee_asset_dyn_data
Definition: evaluator.hpp:119
share_type current_supply
The number of shares currently in existence.
object_id_type do_apply(const limit_order_create_operation &o)
static price call_price(const asset &debt, const asset &collateral, uint16_t collateral_ratio)
Definition: asset.cpp:212
const T & create(F &&constructor)
object_id_type do_apply(const call_order_update_operation &o)
asset debt_covered
the amount of debt to take over
Definition: market.hpp:186
const IndexType & get_index_type() const
bool check_call_orders(const asset_object &mia, bool enable_black_swan=true, bool for_new_limit_order=false, const asset_bitasset_data_object *bitasset_ptr=nullptr)
Definition: db_market.cpp:1100
an offer to sell a amount of a asset at a specified exchange rate by a certain timeThis limit_order_o...
const index_type & indices() const
transaction_evaluation_state * trx_state
Definition: evaluator.hpp:120