void_result asset_update_evaluator::do_apply(const asset_update_operation& o) { database& d = db(); // If we are now disabling force settlements, cancel all open force settlement orders if( o.new_options.flags & disable_force_settle && asset_to_update->can_force_settle() ) { const auto& idx = d.get_index_type<force_settlement_index>().indices().get<by_expiration>(); // Funky iteration code because we're removing objects as we go. We have to re-initialize itr every loop instead // of simply incrementing it. for( auto itr = idx.lower_bound(o.asset_to_update); itr != idx.end() && itr->settlement_asset_id() == o.asset_to_update; itr = idx.lower_bound(o.asset_to_update) ) d.cancel_order(*itr); } d.modify(*asset_to_update, [&](asset_object& a) { if( o.new_issuer ) a.issuer = *o.new_issuer; a.options = o.new_options; }); return void_result(); }
void database::clear_expired_orders() { detail::with_skip_flags( *this, get_node_properties().skip_flags | skip_authority_check, [&](){ transaction_evaluation_state cancel_context(this); //Cancel expired limit orders auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>(); while( !limit_index.empty() && limit_index.begin()->expiration <= head_block_time() ) { limit_order_cancel_operation canceler; const limit_order_object& order = *limit_index.begin(); canceler.fee_paying_account = order.seller; canceler.order = order.id; apply_operation(cancel_context, canceler); } }); //Process expired force settlement orders auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>(); if( !settlement_index.empty() ) { asset_id_type current_asset = settlement_index.begin()->settlement_asset_id(); asset max_settlement_volume; auto next_asset = [¤t_asset, &settlement_index] { auto bound = settlement_index.upper_bound(current_asset); if( bound == settlement_index.end() ) return false; current_asset = bound->settlement_asset_id(); return true; }; // At each iteration, we either consume the current order and remove it, or we move to the next asset for( auto itr = settlement_index.lower_bound(current_asset); itr != settlement_index.end(); itr = settlement_index.lower_bound(current_asset) ) { const force_settlement_object& order = *itr; auto order_id = order.id; current_asset = order.settlement_asset_id(); const asset_object& mia_object = get(current_asset); const asset_bitasset_data_object mia = mia_object.bitasset_data(*this); // Has this order not reached its settlement date? if( order.settlement_date > head_block_time() ) { if( next_asset() ) continue; break; } // Can we still settle in this asset? if( mia.current_feed.settlement_price.is_null() ) { ilog("Canceling a force settlement in ${asset} because settlement price is null", ("asset", mia_object.symbol)); cancel_order(order); continue; } if( max_settlement_volume.asset_id != current_asset ) max_settlement_volume = mia_object.amount(mia.max_force_settlement_volume(mia_object.dynamic_data(*this).current_supply)); if( mia.force_settled_volume >= max_settlement_volume.amount ) { /* ilog("Skipping force settlement in ${asset}; settled ${settled_volume} / ${max_volume}", ("asset", mia_object.symbol)("settlement_price_null",mia.current_feed.settlement_price.is_null()) ("settled_volume", mia.force_settled_volume)("max_volume", max_settlement_volume)); */ if( next_asset() ) continue; break; } auto& pays = order.balance; auto receives = (order.balance * mia.current_feed.settlement_price); receives.amount = (fc::uint128_t(receives.amount.value) * (GRAPHENE_100_PERCENT - mia.options.force_settlement_offset_percent) / GRAPHENE_100_PERCENT).to_uint64(); assert(receives <= order.balance * mia.current_feed.settlement_price); price settlement_price = pays / receives; auto& call_index = get_index_type<call_order_index>().indices().get<by_collateral>(); asset settled = mia_object.amount(mia.force_settled_volume); // Match against the least collateralized short until the settlement is finished or we reach max settlements while( settled < max_settlement_volume && find_object(order_id) ) { auto itr = call_index.lower_bound(boost::make_tuple(price::min(mia_object.bitasset_data(*this).options.short_backing_asset, mia_object.get_id()))); // There should always be a call order, since asset exists! assert(itr != call_index.end() && itr->debt_type() == mia_object.get_id()); asset max_settlement = max_settlement_volume - settled; settled += match(*itr, order, settlement_price, max_settlement); } modify(mia, [settled](asset_bitasset_data_object& b) { b.force_settled_volume = settled.amount; }); } } }