Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BSIP87: Force-Settlement Fee #2151

Merged
merged 8 commits into from
May 7, 2020
9 changes: 9 additions & 0 deletions libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
FC_ASSERT( op.bitasset_opts->feed_lifetime_sec > chain_parameters.block_interval &&
op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval );
}

FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_BSIP87_TIME
|| !op.common_options.extensions.value.force_settle_fee_percent.valid(),
"A BitAsset's FSFP cannot be set before Hardfork BSIP87" );

if( op.is_prediction_market )
{
FC_ASSERT( op.bitasset_opts );
Expand Down Expand Up @@ -299,6 +304,10 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
"Incorrect issuer for asset! (${o.issuer} != ${a.issuer})",
("o.issuer", o.issuer)("a.issuer", a.issuer) );

FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_BSIP87_TIME
|| !o.new_options.extensions.value.force_settle_fee_percent.valid(),
"A BitAsset's FSFP cannot be set before Hardfork BSIP87" );

abitmore marked this conversation as resolved.
Show resolved Hide resolved
const auto& chain_parameters = d.get_global_properties().parameters;

FC_ASSERT( o.new_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
Expand Down
73 changes: 69 additions & 4 deletions libraries/chain/db_market.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,22 +911,58 @@ bool database::fill_call_order( const call_order_object& order, const asset& pay
return collateral_freed.valid();
} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }

/***
* @brief fullfill a settle order in the specified amounts
*
* @details Called from database::match(), this coordinates exchange of debt asset X held in the
* settle order for collateral asset Y held in a call order, and routes fees. Note that we
* don't touch the call order directly, as match() handles this via a separate call to
* fill_call_order(). We are told exactly how much X and Y to exchange, based on details of
* order matching determined higher up the call chain. Thus it is possible that the settle
* order is not completely satisfied at the conclusion of this function.
*
* @param settle the force_settlement object
* @param pays the quantity of market-issued debt asset X which the settler will yield in this
* round (may be less than the full amount indicated in settle object)
* @param receives the quantity of collateral asset Y which the settler will receive in
* exchange for X
* @param fill_price the price at which the settle order will execute (not used - passed through
* to virtual operation)
* @param is_maker TRUE if the settle order is the maker, FALSE if it is the taker (passed
* through to virtual operation)
* @returns TRUE if the settle order was completely filled, FALSE if only partially filled
*/
bool database::fill_settle_order( const force_settlement_object& settle, const asset& pays, const asset& receives,
const price& fill_price, const bool is_maker )
{ try {
bool filled = false;

const account_object* settle_owner_ptr = nullptr;
// The owner of the settle order pays market fees to the issuer of the collateral asset after HF core-1780
// The owner of the settle order pays market fees to the issuer of the collateral asset.
// After HF core-1780, these fees are shared to the referral program, which is flagged to
// pay_market_fees by setting settle_owner_ptr non-null.
abitmore marked this conversation as resolved.
Show resolved Hide resolved
//
// TODO Check whether the HF check can be removed after the HF.
// Note: even if logically it can be removed, perhaps the removal will lead to a small performance
// loss. Needs testing.
if( head_block_time() >= HARDFORK_CORE_1780_TIME )
settle_owner_ptr = &settle.owner(*this);
// Compute and pay the market fees:
asset market_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives );

// Issuer of the settled smartcoin asset lays claim to a force-settlement fee (BSIP87), but
// note that fee is denominated in collateral asset, not the debt asset. Asset object of
// debt asset is passed to the pay function so it knows where to put the fee. Note that
// amount of collateral asset upon which fee is assessed is reduced by market_fees already
// paid to prevent the total fee exceeding total collateral.
asset force_settle_fees = pay_force_settle_fees( get(pays.asset_id), receives - market_fees );
abitmore marked this conversation as resolved.
Show resolved Hide resolved

auto issuer_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives );
auto total_collateral_denominated_fees = market_fees + force_settle_fees;

// TODO: Do we need a something-for-nothing check here? Or does rounding guarantee fees
// strictly less than receives?
abitmore marked this conversation as resolved.
Show resolved Hide resolved

// If we don't consume entire settle order:
if( pays < settle.balance )
{
modify(settle, [&pays](force_settlement_object& s) {
Expand All @@ -935,15 +971,17 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a
} else {
filled = true;
}
adjust_balance(settle.owner, receives - issuer_fees);
// Give released collateral not already taken as fees to settle order owner:
adjust_balance(settle.owner, receives - total_collateral_denominated_fees);

assert( pays.asset_id != receives.asset_id );
push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives, issuer_fees, fill_price, is_maker ) );
push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives, total_collateral_denominated_fees, fill_price, is_maker ) );

if (filled)
remove(settle);

return filled;

} FC_CAPTURE_AND_RETHROW( (settle)(pays)(receives) ) }

/**
Expand Down Expand Up @@ -1293,4 +1331,31 @@ asset database::pay_market_fees(const account_object* seller, const asset_object
return market_fees;
}

/***
* @brief Calculate force-settlement fee and give it to issuer of the settled asset
* @param collecting_asset the smart asset object which should receive the the fee
christophersanborn marked this conversation as resolved.
Show resolved Hide resolved
* @param collat_receives the amount of collateral the settler would expect to receive absent this fee
* (fee is computed as a percentage of this amount)
* @return asset denoting the amount of fee collected
*/
asset database::pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives)
{
const asset_object& recv_asset = get(collat_receives.asset_id);
FC_ASSERT( collecting_asset.get_id() != collat_receives.asset_id );
if( !collecting_asset.options.extensions.value.force_settle_fee_percent.valid()
|| *collecting_asset.options.extensions.value.force_settle_fee_percent == 0 )
return recv_asset.amount(0);
auto value = detail::calculate_percent(collat_receives.amount, *collecting_asset.options.extensions.value.force_settle_fee_percent);
asset settle_fee = recv_asset.amount(value);
// Deposit fee in asset's dynamic data object:
if( value > 0)
{
const auto& asset_dyn_data = collecting_asset.dynamic_asset_data_id(*this);
modify( asset_dyn_data, [&settle_fee]( asset_dynamic_data_object& obj ){
obj.accumulated_fees += settle_fee.amount; // TODO: Wrong place to put fee
christophersanborn marked this conversation as resolved.
Show resolved Hide resolved
});
}
return settle_fee;
}

} }
4 changes: 4 additions & 0 deletions libraries/chain/hardfork.d/CORE_BSIP87.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// bitshares-core BSIP 87: add force-settlement fee percentage:
#ifndef HARDFORK_CORE_BSIP87_TIME
#define HARDFORK_CORE_BSIP87_TIME (fc::time_point_sec( 1679955066 ) ) // Temporary date until actual hardfork date is set
#endif
1 change: 1 addition & 0 deletions libraries/chain/include/graphene/chain/database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ namespace graphene { namespace chain {

asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount);
asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives );
asset pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives);
///@}


Expand Down
2 changes: 2 additions & 0 deletions libraries/protocol/asset_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ void asset_options::validate()const
}
if( extensions.value.reward_percent.valid() )
FC_ASSERT( *extensions.value.reward_percent <= GRAPHENE_100_PERCENT );
if( extensions.value.force_settle_fee_percent.valid() )
FC_ASSERT( *extensions.value.force_settle_fee_percent <= GRAPHENE_100_PERCENT );
}

void asset_claim_fees_operation::validate()const {
Expand Down
4 changes: 3 additions & 1 deletion libraries/protocol/include/graphene/protocol/asset_ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace graphene { namespace protocol {
{
fc::optional<uint16_t> reward_percent;
fc::optional<flat_set<account_id_type>> whitelist_market_fee_sharing;
// MERGE NOTE: BSIP-74 and/or BSIP-81 additions here
fc::optional<uint16_t> force_settle_fee_percent; // BSIP-87
abitmore marked this conversation as resolved.
Show resolved Hide resolved
};
typedef extension<additional_asset_options> additional_asset_options_t;

Expand Down Expand Up @@ -544,7 +546,7 @@ FC_REFLECT( graphene::protocol::bitasset_options,
(extensions)
)

FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing) )
FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing)(force_settle_fee_percent) )
FC_REFLECT( graphene::protocol::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )
FC_REFLECT( graphene::protocol::asset_global_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::protocol::asset_settle_operation::fee_parameters_type, (fee) )
Expand Down