more changes
This commit is contained in:
parent
39e6d6ef99
commit
6c206a517d
19 changed files with 446 additions and 24 deletions
|
|
@ -539,6 +539,12 @@ namespace graphene { namespace app {
|
|||
break;
|
||||
case impl_global_betting_statistics_object_type:
|
||||
break;
|
||||
case impl_collateral_bid_object_type:{
|
||||
const auto& aobj = dynamic_cast<const collateral_bid_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->bidder );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -733,25 +733,49 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::
|
|||
const auto& bitasset = asset_to_settle->bitasset_data(d);
|
||||
if( bitasset.has_settlement() )
|
||||
{
|
||||
auto settled_amount = op.amount * bitasset.settlement_price;
|
||||
FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund );
|
||||
|
||||
d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_fund -= settled_amount.amount;
|
||||
});
|
||||
|
||||
d.adjust_balance(op.account, settled_amount);
|
||||
|
||||
const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d);
|
||||
auto settled_amount = op.amount * bitasset.settlement_price;
|
||||
if( op.amount.amount == mia_dyn.current_supply )
|
||||
settled_amount.amount = bitasset.settlement_fund; // avoid rounding problems
|
||||
else
|
||||
FC_ASSERT( settled_amount.amount < bitasset.settlement_fund );
|
||||
|
||||
if( settled_amount.amount == 0 && !bitasset.is_prediction_market )
|
||||
{
|
||||
if( d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_184_TIME )
|
||||
FC_THROW( "Settle amount is too small to receive anything due to rounding" );
|
||||
else // TODO remove this warning after hard fork core-184
|
||||
wlog( "Something for nothing issue (#184, variant F) occurred at block #${block}", ("block",d.head_block_num()) );
|
||||
}
|
||||
|
||||
asset pays = op.amount;
|
||||
if( op.amount.amount != mia_dyn.current_supply
|
||||
&& settled_amount.amount != 0
|
||||
&& d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_342_TIME )
|
||||
{
|
||||
pays = settled_amount.multiply_and_round_up( bitasset.settlement_price );
|
||||
}
|
||||
|
||||
d.adjust_balance( op.account, -pays );
|
||||
|
||||
if( settled_amount.amount > 0 )
|
||||
{
|
||||
d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_fund -= settled_amount.amount;
|
||||
});
|
||||
|
||||
d.adjust_balance( op.account, settled_amount );
|
||||
}
|
||||
|
||||
d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.current_supply -= op.amount.amount;
|
||||
});
|
||||
obj.current_supply -= pays.amount;
|
||||
});
|
||||
|
||||
return settled_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
d.adjust_balance( op.account, -op.amount );
|
||||
return d.create<force_settlement_object>([&](force_settlement_object& s) {
|
||||
s.owner = op.account;
|
||||
s.balance = op.amount;
|
||||
|
|
@ -769,7 +793,10 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_
|
|||
FC_ASSERT(base.is_market_issued());
|
||||
|
||||
const asset_bitasset_data_object& bitasset = base.bitasset_data(d);
|
||||
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
|
||||
if( bitasset.is_prediction_market || d.head_block_time() <= HARDFORK_CORE_216_TIME )
|
||||
{
|
||||
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
|
||||
}
|
||||
|
||||
FC_ASSERT( o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset );
|
||||
if( d.head_block_time() > HARDFORK_480_TIME )
|
||||
|
|
@ -820,7 +847,19 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
|
|||
});
|
||||
|
||||
if( !(old_feed == bad.current_feed) )
|
||||
{
|
||||
if( bad.has_settlement() ) // implies head_block_time > HARDFORK_CORE_216_TIME
|
||||
{
|
||||
const auto& mia_dyn = base.dynamic_asset_data_id(d);
|
||||
if( !bad.current_feed.settlement_price.is_null()
|
||||
&& ( mia_dyn.current_supply == 0
|
||||
|| ~price::call_price(asset(mia_dyn.current_supply, o.asset_id),
|
||||
asset(bad.settlement_fund, bad.options.short_backing_asset),
|
||||
bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price ) )
|
||||
d.revive_bitasset(base);
|
||||
}
|
||||
db().check_call_orders(base);
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW((o)) }
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ void database::debug_dump()
|
|||
|
||||
const auto& balance_index = db.get_index_type<account_balance_index>().indices();
|
||||
const auto& statistics_index = db.get_index_type<account_stats_index>().indices();
|
||||
const auto& bids = db.get_index_type<collateral_bid_index>().indices();
|
||||
map<asset_id_type,share_type> total_balances;
|
||||
map<asset_id_type,share_type> total_debts;
|
||||
share_type core_in_orders;
|
||||
|
|
@ -58,6 +59,8 @@ void database::debug_dump()
|
|||
// idump(("statistics")(s));
|
||||
reported_core_in_orders += s.total_core_in_orders;
|
||||
}
|
||||
for( const collateral_bid_object& b : bids )
|
||||
total_balances[b.inv_swan_price.base.asset_id] += b.inv_swan_price.base.amount;
|
||||
for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )
|
||||
{
|
||||
// idump(("limit_order")(o));
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@ void database::initialize_evaluators()
|
|||
register_evaluator<sidechain_transaction_send_evaluator>();
|
||||
register_evaluator<sidechain_transaction_settle_evaluator>();
|
||||
register_evaluator<random_number_store_evaluator>();
|
||||
register_evaluator<bid_collateral_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -419,6 +420,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<nft_lottery_balance_index > >();
|
||||
add_index< primary_index<son_stats_index > >();
|
||||
add_index< primary_index<random_number_index > >();
|
||||
add_index< primary_index<collateral_bid_index > >();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1973,6 +1973,57 @@ void update_and_match_call_orders( database& db )
|
|||
wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) );
|
||||
}
|
||||
|
||||
void database::process_bids( const asset_bitasset_data_object& bad )
|
||||
{
|
||||
if( bad.current_feed.settlement_price.is_null() ) return;
|
||||
|
||||
asset_id_type to_revive_id = (asset( 0, bad.options.short_backing_asset ) * bad.settlement_price).asset_id;
|
||||
const asset_object& to_revive = to_revive_id( *this );
|
||||
const asset_dynamic_data_object& bdd = to_revive.dynamic_data( *this );
|
||||
|
||||
const auto& bid_idx = get_index_type< collateral_bid_index >().indices().get<by_price>();
|
||||
const auto start = bid_idx.lower_bound( boost::make_tuple( to_revive_id, price::max( bad.options.short_backing_asset, to_revive_id ), collateral_bid_id_type() ) );
|
||||
|
||||
share_type covered = 0;
|
||||
auto itr = start;
|
||||
while( itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == to_revive_id )
|
||||
{
|
||||
const collateral_bid_object& bid = *itr;
|
||||
asset total_collateral = bid.inv_swan_price.quote * bad.settlement_price;
|
||||
total_collateral += bid.inv_swan_price.base;
|
||||
price call_price = price::call_price( bid.inv_swan_price.quote, total_collateral, bad.current_feed.maintenance_collateral_ratio );
|
||||
if( ~call_price >= bad.current_feed.settlement_price ) break;
|
||||
covered += bid.inv_swan_price.quote.amount;
|
||||
++itr;
|
||||
if( covered >= bdd.current_supply ) break;
|
||||
}
|
||||
if( covered < bdd.current_supply ) return;
|
||||
|
||||
const auto end = itr;
|
||||
share_type to_cover = bdd.current_supply;
|
||||
share_type remaining_fund = bad.settlement_fund;
|
||||
for( itr = start; itr != end; )
|
||||
{
|
||||
const collateral_bid_object& bid = *itr;
|
||||
++itr;
|
||||
share_type debt = bid.inv_swan_price.quote.amount;
|
||||
share_type collateral = (bid.inv_swan_price.quote * bad.settlement_price).amount;
|
||||
if( bid.inv_swan_price.quote.amount >= to_cover )
|
||||
{
|
||||
debt = to_cover;
|
||||
collateral = remaining_fund;
|
||||
}
|
||||
to_cover -= debt;
|
||||
remaining_fund -= collateral;
|
||||
execute_bid( bid, debt, collateral, bad.current_feed );
|
||||
}
|
||||
|
||||
FC_ASSERT( remaining_fund == 0 );
|
||||
FC_ASSERT( to_cover == 0 );
|
||||
|
||||
_cancel_bids_and_revive_mpa( to_revive, bad );
|
||||
}
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{ try {
|
||||
const auto& gpo = get_global_properties();
|
||||
|
|
@ -2244,9 +2295,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
update_and_match_call_orders(*this);
|
||||
|
||||
// Reset all BitAsset force settlement volumes to zero
|
||||
//for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
|
||||
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
|
||||
{
|
||||
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
|
||||
if( d.has_settlement() )
|
||||
process_bids(d);
|
||||
}
|
||||
// Ideally we have to do this after every block but that leads to longer block applicaiton/replay times.
|
||||
// So keep it here as it is not critical. valid_to check ensures
|
||||
// these custom account auths and account roles are not usable.
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
wlog( "Something for nothing issue (#184, variant E) occurred at block #${block}", ("block",head_block_num()) );
|
||||
}
|
||||
else
|
||||
pays = call_itr->get_debt() ^ settlement_price; // round up, in favor of global settlement fund
|
||||
pays = call_itr->get_debt().multiply_and_round_up( settlement_price ); // round up, in favor of global settlement fund
|
||||
|
||||
if( pays > call_itr->get_collateral() )
|
||||
pays = call_itr->get_collateral();
|
||||
|
|
@ -106,6 +106,91 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
|
||||
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
|
||||
|
||||
void database::revive_bitasset( const asset_object& bitasset )
|
||||
{ try {
|
||||
FC_ASSERT( bitasset.is_market_issued() );
|
||||
const asset_bitasset_data_object& bad = bitasset.bitasset_data(*this);
|
||||
FC_ASSERT( bad.has_settlement() );
|
||||
const asset_dynamic_data_object& bdd = bitasset.dynamic_asset_data_id(*this);
|
||||
FC_ASSERT( !bad.is_prediction_market );
|
||||
FC_ASSERT( !bad.current_feed.settlement_price.is_null() );
|
||||
|
||||
if( bdd.current_supply > 0 )
|
||||
{
|
||||
// Create + execute a "bid" with 0 additional collateral
|
||||
const collateral_bid_object& pseudo_bid = create<collateral_bid_object>([&](collateral_bid_object& bid) {
|
||||
bid.bidder = bitasset.issuer;
|
||||
bid.inv_swan_price = asset(0, bad.options.short_backing_asset)
|
||||
/ asset(bdd.current_supply, bitasset.id);
|
||||
});
|
||||
execute_bid( pseudo_bid, bdd.current_supply, bad.settlement_fund, bad.current_feed );
|
||||
} else
|
||||
FC_ASSERT( bad.settlement_fund == 0 );
|
||||
|
||||
_cancel_bids_and_revive_mpa( bitasset, bad );
|
||||
} FC_CAPTURE_AND_RETHROW( (bitasset) ) }
|
||||
|
||||
void database::_cancel_bids_and_revive_mpa( const asset_object& bitasset, const asset_bitasset_data_object& bad )
|
||||
{ try {
|
||||
FC_ASSERT( bitasset.is_market_issued() );
|
||||
FC_ASSERT( bad.has_settlement() );
|
||||
FC_ASSERT( !bad.is_prediction_market );
|
||||
|
||||
// cancel remaining bids
|
||||
const auto& bid_idx = get_index_type< collateral_bid_index >().indices().get<by_price>();
|
||||
auto itr = bid_idx.lower_bound( boost::make_tuple( bitasset.id, price::max( bad.options.short_backing_asset, bitasset.id ), collateral_bid_id_type() ) );
|
||||
while( itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == bitasset.id )
|
||||
{
|
||||
const collateral_bid_object& bid = *itr;
|
||||
++itr;
|
||||
cancel_bid( bid );
|
||||
}
|
||||
|
||||
// revive
|
||||
modify( bad, [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_price = price();
|
||||
obj.settlement_fund = 0;
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW( (bitasset) ) }
|
||||
|
||||
void database::cancel_bid(const collateral_bid_object& bid, bool create_virtual_op)
|
||||
{
|
||||
adjust_balance(bid.bidder, bid.inv_swan_price.base);
|
||||
|
||||
if( create_virtual_op )
|
||||
{
|
||||
bid_collateral_operation vop;
|
||||
vop.bidder = bid.bidder;
|
||||
vop.additional_collateral = bid.inv_swan_price.base;
|
||||
vop.debt_covered = asset( 0, bid.inv_swan_price.quote.asset_id );
|
||||
push_applied_operation( vop );
|
||||
}
|
||||
remove(bid);
|
||||
}
|
||||
|
||||
void database::execute_bid( const collateral_bid_object& bid, share_type debt_covered, share_type collateral_from_fund, const price_feed& current_feed )
|
||||
{
|
||||
const call_order_object& call_obj = create<call_order_object>( [&](call_order_object& call ){
|
||||
call.borrower = bid.bidder;
|
||||
call.collateral = bid.inv_swan_price.base.amount + collateral_from_fund;
|
||||
call.debt = debt_covered;
|
||||
call.call_price = price::call_price(asset(debt_covered, bid.inv_swan_price.quote.asset_id),
|
||||
asset(call.collateral, bid.inv_swan_price.base.asset_id),
|
||||
current_feed.maintenance_collateral_ratio);
|
||||
|
||||
});
|
||||
|
||||
if( bid.inv_swan_price.base.asset_id == asset_id_type() )
|
||||
modify(bid.bidder(*this).statistics(*this), [&](account_statistics_object& stats) {
|
||||
stats.total_core_in_orders += call_obj.collateral;
|
||||
});
|
||||
|
||||
push_applied_operation( execute_bid_operation( bid.bidder, asset( call_obj.collateral, bid.inv_swan_price.quote.asset_id ),
|
||||
asset( debt_covered, bid.inv_swan_price.base.asset_id ) ) );
|
||||
|
||||
remove(bid);
|
||||
}
|
||||
|
||||
void database::cancel_settle_order(const force_settlement_object& order, bool create_virtual_op)
|
||||
{
|
||||
adjust_balance(order.owner, order.balance);
|
||||
|
|
@ -469,7 +554,7 @@ int database::match( const limit_order_object& usd, const limit_order_object& co
|
|||
// so we should cull the order in fill_limit_order() below.
|
||||
// The order would receive 0 even at `match_price`, so it would receive 0 at its own price,
|
||||
// so calling maybe_cull_small() will always cull it.
|
||||
core_receives = usd_receives ^ match_price;
|
||||
core_receives = usd_receives.multiply_and_round_up( match_price );
|
||||
cull_taker = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -486,7 +571,7 @@ int database::match( const limit_order_object& usd, const limit_order_object& co
|
|||
else
|
||||
// The remaining amount in order `core` would be too small,
|
||||
// so the order will be culled in fill_limit_order() below
|
||||
usd_receives = core_receives ^ match_price;
|
||||
usd_receives = core_receives.multiply_and_round_up( match_price );
|
||||
}
|
||||
|
||||
core_pays = usd_receives;
|
||||
|
|
@ -550,7 +635,7 @@ int database::match( const limit_order_object& bid, const call_order_object& ask
|
|||
// so we should cull the order in fill_limit_order() below.
|
||||
// The order would receive 0 even at `match_price`, so it would receive 0 at its own price,
|
||||
// so calling maybe_cull_small() will always cull it.
|
||||
call_receives = order_receives ^ match_price;
|
||||
call_receives = order_receives.multiply_and_round_up( match_price );
|
||||
cull_taker = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -565,7 +650,7 @@ int database::match( const limit_order_object& bid, const call_order_object& ask
|
|||
return 1;
|
||||
}
|
||||
else // has hardfork core-342
|
||||
order_receives = usd_to_buy ^ match_price; // round up here, in favor of limit order
|
||||
order_receives = usd_to_buy.multiply_and_round_up( match_price ); // round up here, in favor of limit order
|
||||
}
|
||||
|
||||
call_pays = order_receives;
|
||||
|
|
@ -629,7 +714,7 @@ asset database::match( const call_order_object& call,
|
|||
{
|
||||
if( call_receives == call_debt ) // the call order is smaller than or equal to the settle order
|
||||
{
|
||||
call_pays = call_receives ^ match_price; // round up here, in favor of settle order
|
||||
call_pays = call_receives.multiply_and_round_up( match_price ); // round up here, in favor of settle order
|
||||
// be here, we should have: call_pays <= call_collateral
|
||||
}
|
||||
else
|
||||
|
|
@ -642,7 +727,7 @@ asset database::match( const call_order_object& call,
|
|||
cull_settle_order = true;
|
||||
// else do nothing, since we can't cull the settle order
|
||||
|
||||
call_receives = call_pays ^ match_price; // round up here to mitigate rouding issue (core-342)
|
||||
call_receives = call_pays.multiply_and_round_up( match_price ); // round up here to mitigate rouding issue (core-342)
|
||||
|
||||
if( call_receives == settle.balance ) // the settle order will be completely filled, no need to cull
|
||||
cull_settle_order = false;
|
||||
|
|
@ -955,7 +1040,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
// so we should cull the order in fill_limit_order() below.
|
||||
// The order would receive 0 even at `match_price`, so it would receive 0 at its own price,
|
||||
// so calling maybe_cull_small() will always cull it.
|
||||
call_receives = order_receives ^ match_price;
|
||||
call_receives = order_receives.multiply_and_round_up( match_price );
|
||||
filled_limit = true;
|
||||
} else { // fill call
|
||||
call_receives = usd_to_buy;
|
||||
|
|
@ -968,7 +1053,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
wlog( "Something for nothing issue (#184, variant D) occurred at block #${block}", ("block",head_block_num()) );
|
||||
}
|
||||
else
|
||||
order_receives = usd_to_buy ^ match_price; // round up, in favor of limit order
|
||||
order_receives = usd_to_buy.multiply_and_round_up( match_price ); // round up, in favor of limit order
|
||||
|
||||
filled_call = true; // this is safe, since BSIP38 (hard fork core-834) depends on BSIP31 (hard fork core-343)
|
||||
if( usd_to_buy == usd_for_sale )
|
||||
|
|
|
|||
|
|
@ -431,6 +431,12 @@ struct get_impacted_account_visitor
|
|||
void operator()( const random_number_store_operation& op ) {
|
||||
_impacted.insert( op.account );
|
||||
}
|
||||
void operator()( const bid_collateral_operation& op ) {
|
||||
_impacted.insert( op.bidder );
|
||||
}
|
||||
void operator()( const execute_bid_operation& op ) {
|
||||
_impacted.insert( op.bidder );
|
||||
}
|
||||
};
|
||||
|
||||
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
|
||||
|
|
|
|||
|
|
@ -409,6 +409,9 @@ namespace graphene { namespace chain {
|
|||
void globally_settle_asset( const asset_object& bitasset, const price& settle_price );
|
||||
void cancel_settle_order(const force_settlement_object& order, bool create_virtual_op = true);
|
||||
void cancel_limit_order(const limit_order_object& order, bool create_virtual_op = true, bool skip_cancel_fee = false);
|
||||
void revive_bitasset( const asset_object& bitasset );
|
||||
void cancel_bid(const collateral_bid_object& bid, bool create_virtual_op = true);
|
||||
void execute_bid( const collateral_bid_object& bid, share_type debt_covered, share_type collateral_from_fund, const price_feed& current_feed );
|
||||
|
||||
/**
|
||||
* @brief Process a new limit order through the markets
|
||||
|
|
@ -527,6 +530,7 @@ namespace graphene { namespace chain {
|
|||
private:
|
||||
void _apply_block( const signed_block& next_block );
|
||||
processed_transaction _apply_transaction( const signed_transaction& trx );
|
||||
void _cancel_bids_and_revive_mpa( const asset_object& bitasset, const asset_bitasset_data_object& bad );
|
||||
|
||||
///Steps involved in applying a new block
|
||||
///@{
|
||||
|
|
@ -579,6 +583,7 @@ namespace graphene { namespace chain {
|
|||
void update_son_statuses( const vector<son_info>& cur_active_sons, const vector<son_info>& new_active_sons );
|
||||
void update_son_wallet( const vector<son_info>& new_active_sons );
|
||||
void update_worker_votes();
|
||||
void process_bids( const asset_bitasset_data_object& bad );
|
||||
|
||||
public:
|
||||
double calculate_vesting_factor(const account_object& stake_account);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ namespace graphene { namespace chain {
|
|||
class asset_object;
|
||||
class asset_bitasset_data_object;
|
||||
class call_order_object;
|
||||
struct bid_collateral_operation;
|
||||
struct call_order_update_operation;
|
||||
struct limit_order_cancel_operation;
|
||||
struct limit_order_create_operation;
|
||||
|
|
@ -88,4 +89,18 @@ namespace graphene { namespace chain {
|
|||
const asset_bitasset_data_object* _bitasset_data = nullptr;
|
||||
};
|
||||
|
||||
class bid_collateral_evaluator : public evaluator<bid_collateral_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bid_collateral_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bid_collateral_operation& o );
|
||||
void_result do_apply( const bid_collateral_operation& o );
|
||||
|
||||
const asset_object* _debt_asset = nullptr;
|
||||
const asset_bitasset_data_object* _bitasset_data = nullptr;
|
||||
const account_object* _paying_account = nullptr;
|
||||
const collateral_bid_object* _bid = nullptr;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -157,6 +157,27 @@ class force_settlement_object : public abstract_object<force_settlement_object>
|
|||
{ return balance.asset_id; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class collateral_bid_object
|
||||
* @brief bids of collateral for debt after a black swan
|
||||
*
|
||||
* There should only be one collateral_bid_object per asset per account, and
|
||||
* only for smartcoin assets that have a global settlement_price.
|
||||
*/
|
||||
class collateral_bid_object : public abstract_object<collateral_bid_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_collateral_bid_object_type;
|
||||
|
||||
asset get_additional_collateral()const { return inv_swan_price.base; }
|
||||
asset get_debt_covered()const { return inv_swan_price.quote; }
|
||||
asset_id_type debt_type()const { return inv_swan_price.quote.asset_id; }
|
||||
|
||||
account_id_type bidder;
|
||||
price inv_swan_price; // Collateral / Debt
|
||||
};
|
||||
|
||||
struct by_collateral;
|
||||
struct by_account;
|
||||
struct by_price;
|
||||
|
|
@ -208,8 +229,31 @@ typedef multi_index_container<
|
|||
>
|
||||
> force_settlement_object_multi_index_type;
|
||||
|
||||
typedef multi_index_container<
|
||||
collateral_bid_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_account>,
|
||||
composite_key< collateral_bid_object,
|
||||
const_mem_fun< collateral_bid_object, asset_id_type, &collateral_bid_object::debt_type>,
|
||||
member< collateral_bid_object, account_id_type, &collateral_bid_object::bidder>
|
||||
>
|
||||
>,
|
||||
ordered_unique< tag<by_price>,
|
||||
composite_key< collateral_bid_object,
|
||||
const_mem_fun< collateral_bid_object, asset_id_type, &collateral_bid_object::debt_type>,
|
||||
member< collateral_bid_object, price, &collateral_bid_object::inv_swan_price >,
|
||||
member< object, object_id_type, &object::id >
|
||||
>,
|
||||
composite_key_compare< std::less<asset_id_type>, std::greater<price>, std::less<object_id_type> >
|
||||
>
|
||||
>
|
||||
> collateral_bid_object_multi_index_type;
|
||||
|
||||
typedef generic_index<call_order_object, call_order_multi_index_type> call_order_index;
|
||||
typedef generic_index<force_settlement_object, force_settlement_object_multi_index_type> force_settlement_index;
|
||||
typedef generic_index<collateral_bid_object, collateral_bid_object_multi_index_type> collateral_bid_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -225,7 +269,10 @@ FC_REFLECT_DERIVED( graphene::chain::force_settlement_object,
|
|||
(graphene::db::object),
|
||||
(owner)(balance)(settlement_date)
|
||||
)
|
||||
FC_REFLECT_DERIVED( graphene::chain::collateral_bid_object, (graphene::db::object),
|
||||
(bidder)(inv_swan_price) )
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_object )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_object )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::force_settlement_object )
|
||||
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::collateral_bid_object )
|
||||
|
|
@ -29,6 +29,8 @@ namespace graphene { namespace chain {
|
|||
|
||||
extern const int64_t scaled_precision_lut[];
|
||||
|
||||
struct price;
|
||||
|
||||
struct asset
|
||||
{
|
||||
asset( share_type a = 0, asset_id_type id = asset_id_type() )
|
||||
|
|
@ -94,6 +96,8 @@ namespace graphene { namespace chain {
|
|||
FC_ASSERT( precision < 19 );
|
||||
return scaled_precision_lut[ precision ];
|
||||
}
|
||||
|
||||
asset multiply_and_round_up( const price& p )const; ///< Multiply and round up
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -172,17 +172,69 @@ namespace graphene { namespace chain {
|
|||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
*
|
||||
* This operation can be used after a black swan to bid collateral for
|
||||
* taking over part of the debt and the settlement_fund (see BSIP-0018).
|
||||
*/
|
||||
struct bid_collateral_operation : public base_operation
|
||||
{
|
||||
/** should be equivalent to call_order_update fee */
|
||||
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
|
||||
asset fee;
|
||||
account_id_type bidder; ///< pays fee and additional collateral
|
||||
asset additional_collateral; ///< the amount of collateral to bid for the debt
|
||||
asset debt_covered; ///< the amount of debt to take over
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return bidder; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
*
|
||||
* @note This is a virtual operation that is created while reviving a
|
||||
* bitasset from collateral bids.
|
||||
*/
|
||||
struct execute_bid_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
execute_bid_operation(){}
|
||||
execute_bid_operation( account_id_type a, asset d, asset c )
|
||||
: bidder(a), debt(d), collateral(c) {}
|
||||
|
||||
asset fee;
|
||||
account_id_type bidder;
|
||||
asset debt;
|
||||
asset collateral;
|
||||
|
||||
account_id_type fee_payer()const { return bidder; }
|
||||
void validate()const { FC_ASSERT( !"virtual operation" ); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::limit_order_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::limit_order_cancel_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::call_order_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bid_collateral_operation::fee_parameters_type, (fee) )
|
||||
/// THIS IS THE ONLY VIRTUAL OPERATION THUS FAR...
|
||||
FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, ) // VIRTUAL
|
||||
FC_REFLECT( graphene::chain::execute_bid_operation::fee_parameters_type, ) // VIRTUAL
|
||||
FC_REFLECT( graphene::chain::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions))
|
||||
FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) )
|
||||
FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(extensions) )
|
||||
FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives)(fill_price)(is_maker) )
|
||||
FC_REFLECT( graphene::chain::bid_collateral_operation, (fee)(bidder)(additional_collateral)(debt_covered)(extensions) )
|
||||
FC_REFLECT( graphene::chain::execute_bid_operation, (fee)(bidder)(debt)(collateral) )
|
||||
|
||||
FC_REFLECT( graphene::chain::call_order_update_operation::options_type, (target_collateral_ratio) )
|
||||
|
||||
|
|
@ -194,3 +246,5 @@ GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_create_ope
|
|||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_cancel_operation )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_update_operation )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fill_order_operation )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::bid_collateral_operation )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::execute_bid_operation )
|
||||
|
|
|
|||
|
|
@ -190,7 +190,9 @@ namespace graphene { namespace chain {
|
|||
nft_lottery_token_purchase_operation,
|
||||
nft_lottery_reward_operation,
|
||||
nft_lottery_end_operation,
|
||||
random_number_store_operation
|
||||
random_number_store_operation,
|
||||
bid_collateral_operation,
|
||||
execute_bid_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@ namespace graphene { namespace chain {
|
|||
impl_offer_history_object_type,
|
||||
impl_son_statistics_object_type,
|
||||
impl_son_schedule_object_type,
|
||||
impl_nft_lottery_balance_object_type
|
||||
impl_nft_lottery_balance_object_type,
|
||||
impl_collateral_bid_object_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -328,6 +329,7 @@ namespace graphene { namespace chain {
|
|||
class nft_lottery_balance_object;
|
||||
class son_statistics_object;
|
||||
class son_schedule_object;
|
||||
class collateral_bid_object;
|
||||
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
|
|
@ -360,6 +362,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< implementation_ids, impl_nft_lottery_balance_object_type, nft_lottery_balance_object> nft_lottery_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
|
||||
typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -534,6 +537,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_son_statistics_object_type)
|
||||
(impl_son_schedule_object_type)
|
||||
(impl_nft_lottery_balance_object_type)
|
||||
(impl_collateral_bid_object_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
|
@ -592,6 +596,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::random_number_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::collateral_bid_id_type )
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
||||
|
|
|
|||
|
|
@ -314,4 +314,61 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result bid_collateral_evaluator::do_evaluate(const bid_collateral_operation& o)
|
||||
{ try {
|
||||
database& d = db();
|
||||
|
||||
FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_216_TIME, "Not yet!" );
|
||||
|
||||
_paying_account = &o.bidder(d);
|
||||
_debt_asset = &o.debt_covered.asset_id(d);
|
||||
FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
|
||||
("sym", _debt_asset->symbol) );
|
||||
|
||||
_bitasset_data = &_debt_asset->bitasset_data(d);
|
||||
|
||||
FC_ASSERT( _bitasset_data->has_settlement() );
|
||||
|
||||
FC_ASSERT( o.additional_collateral.asset_id == _bitasset_data->options.short_backing_asset );
|
||||
|
||||
FC_ASSERT( !_bitasset_data->is_prediction_market, "Cannot bid on a prediction market!" );
|
||||
|
||||
if( o.additional_collateral.amount > 0 )
|
||||
{
|
||||
FC_ASSERT( d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)) >= o.additional_collateral,
|
||||
"Cannot bid ${c} collateral when payer only has ${b}", ("c", o.additional_collateral.amount)
|
||||
("b", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );
|
||||
}
|
||||
|
||||
const collateral_bid_index& bids = d.get_index_type<collateral_bid_index>();
|
||||
const auto& index = bids.indices().get<by_account>();
|
||||
const auto& bid = index.find( boost::make_tuple( o.debt_covered.asset_id, o.bidder ) );
|
||||
if( bid != index.end() )
|
||||
_bid = &(*bid);
|
||||
else
|
||||
FC_ASSERT( o.debt_covered.amount > 0, "Can't find bid to cancel?!");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
void_result bid_collateral_evaluator::do_apply(const bid_collateral_operation& o)
|
||||
{ try {
|
||||
database& d = db();
|
||||
|
||||
if( _bid )
|
||||
d.cancel_bid( *_bid, false );
|
||||
|
||||
if( o.debt_covered.amount == 0 ) return void_result();
|
||||
|
||||
d.adjust_balance( o.bidder, -o.additional_collateral );
|
||||
|
||||
_bid = &d.create<collateral_bid_object>([&]( collateral_bid_object& bid ) {
|
||||
bid.bidder = o.bidder;
|
||||
bid.inv_swan_price = o.additional_collateral / o.debt_covered;
|
||||
});
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -93,6 +93,18 @@ namespace graphene { namespace chain {
|
|||
FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset * price", ("asset",a)("price",b) );
|
||||
}
|
||||
|
||||
asset asset::multiply_and_round_up( const price& b )const
|
||||
{
|
||||
const asset& a = *this;
|
||||
if( a.asset_id == b.base.asset_id )
|
||||
{
|
||||
FC_ASSERT( b.base.amount.value > 0 );
|
||||
FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
return asset( result.convert_to<int64_t>(), b.base.asset_id );
|
||||
}
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset::multiply_and_round_up(price)", ("asset",a)("price",b) );
|
||||
}
|
||||
|
||||
price operator / ( const asset& base, const asset& quote )
|
||||
{ try {
|
||||
FC_ASSERT( base.asset_id != quote.asset_id );
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ void call_order_update_operation::validate()const
|
|||
FC_ASSERT( delta_collateral.amount != 0 || delta_debt.amount != 0 );
|
||||
} FC_CAPTURE_AND_RETHROW((*this)) }
|
||||
|
||||
void bid_collateral_operation::validate()const
|
||||
{ try {
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
FC_ASSERT( debt_covered.amount == 0 || (debt_covered.amount > 0 && additional_collateral.amount > 0) );
|
||||
} FC_CAPTURE_AND_RETHROW((*this)) }
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_create_operation::fee_parameters_type )
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
const auto& settle_index = db.get_index_type<force_settlement_index>().indices();
|
||||
const auto& tournaments_index = db.get_index_type<tournament_index>().indices();
|
||||
const auto& asst_index = db.get_index_type<asset_index>().indices();
|
||||
const auto& bids = db.get_index_type<collateral_bid_index>().indices();
|
||||
|
||||
map<asset_id_type,share_type> total_balances;
|
||||
map<asset_id_type,share_type> total_debts;
|
||||
|
|
@ -273,6 +274,8 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
total_balances[b.asset_type] += b.balance;
|
||||
for( const force_settlement_object& s : settle_index )
|
||||
total_balances[s.balance.asset_id] += s.balance.amount;
|
||||
for( const collateral_bid_object& b : bids )
|
||||
total_balances[b.inv_swan_price.base.asset_id] += b.inv_swan_price.base.amount;
|
||||
for( const account_statistics_object& a : statistics_index )
|
||||
{
|
||||
reported_core_in_orders += a.total_core_in_orders;
|
||||
|
|
@ -1044,6 +1047,22 @@ void database_fixture::cover(const account_object& who, asset what, asset collat
|
|||
verify_asset_supplies(db);
|
||||
} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral) ) }
|
||||
|
||||
void database_fixture::bid_collateral(const account_object& who, const asset& to_bid, const asset& to_cover)
|
||||
{ try {
|
||||
set_expiration( db, trx );
|
||||
trx.operations.clear();
|
||||
bid_collateral_operation bid;
|
||||
bid.bidder = who.id;
|
||||
bid.additional_collateral = to_bid;
|
||||
bid.debt_covered = to_cover;
|
||||
trx.operations.push_back(bid);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
trx.validate();
|
||||
db.push_transaction(trx, ~0);
|
||||
trx.operations.clear();
|
||||
verify_asset_supplies(db);
|
||||
} FC_CAPTURE_AND_RETHROW( (who.name)(to_bid)(to_cover) ) }
|
||||
|
||||
void database_fixture::fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount )
|
||||
{
|
||||
asset_fund_fee_pool_operation fund;
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ struct database_fixture {
|
|||
void cover(account_id_type who, asset what, asset collateral_freed)
|
||||
{ cover(who(db), what, collateral_freed); }
|
||||
void cover(const account_object& who, asset what, asset collateral_freed);
|
||||
void bid_collateral(const account_object& who, const asset& to_bid, const asset& to_cover);
|
||||
|
||||
const asset_object& get_asset( const string& symbol )const;
|
||||
const account_object& get_account( const string& name )const;
|
||||
|
|
|
|||
Loading…
Reference in a new issue