近期需要了解下market费用这块,先来初步理一理,不排除有理解错误的内容,如有还请指正。感谢@abit提供市场费用结算精确代码位置。
Bitshares是去中心化交易所,而market是买卖相关,那market应该是Bitshares中最重要的内容了,也是区分于其它基于Graphene区块链的部分。
market.hpp
/**
* @class limit_order_create_operation
* @brief instructs the blockchain to attempt to sell one asset for another
* @ingroup operations
*
* The blockchain will atempt to sell amount_to_sell.asset_id for as
* much min_to_receive.asset_id as possible. The fee will be paid by
* the seller's account. Market fees will apply as specified by the
* issuer of both the selling asset and the receiving asset as
* a percentage of the amount exchanged.
*
* If either the selling asset or the receiving asset is white list
* restricted, the order will only be created if the seller is on
* the white list of the restricted asset type.
*
* Market orders are matched in the order they are included
* in the block chain.
*/
struct limit_order_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type seller;
asset amount_to_sell;
asset min_to_receive;
/// The order will be removed from the books if not filled by expiration
/// Upon expiration, all unsold asset will be returned to seller
time_point_sec expiration = time_point_sec::maximum();
/// If this flag is set the entire order must be filled or the operation is rejected
bool fill_or_kill = false;
extensions_type extensions;
pair<asset_id_type,asset_id_type> get_market()const
{
return amount_to_sell.asset_id < min_to_receive.asset_id ?
std::make_pair(amount_to_sell.asset_id, min_to_receive.asset_id) :
std::make_pair(min_to_receive.asset_id, amount_to_sell.asset_id);
}
account_id_type fee_payer()const { return seller; }
void validate()const;
price get_price()const { return amount_to_sell / min_to_receive; }
};
在定单创建的operation前有注释,意思是会尽可能的按min_to_receive指定的资产和数量卖掉amount_to_sell指定的资产,市场费用由seller付出,费用数量由买卖资产中的"market_fee_percent"指定。
在cli_wallet中接口定义如下:
signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, string min_to_receive, string symbol_to_receive, uint32_t timeout_sec, bool fill_or_kill, bool broadcast)
定单创建执行:
object_id_type limit_order_create_evaluator::do_apply(const limit_order_create_operation& op)
{
...
db().adjust_balance(op.seller, -op.amount_to_sell);
const auto& new_order_object = db().create<limit_order_object>([&](limit_order_object& obj){
obj.seller = _seller->id;
obj.for_sale = op.amount_to_sell.amount;
obj.sell_price = op.get_price();
obj.expiration = op.expiration;
obj.deferred_fee = _deferred_fee;
});
limit_order_id_type order_id = new_order_object.id; // save this because we may remove the object by filling it
bool filled = db().apply_order(new_order_object);
...
return order_id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
执行定单创建时会减少seller要卖出的资产,生成定单,调用apply_order()函数。
定单撮合主要在以下几个函数中,有时间再详细看。
db_market.cpp
bool database::apply_order(const limit_order_object& new_order_object, bool allow_black_swan)
int database::match( const limit_order_object& bid, const limit_order_object& ask, const price& match_price )
bool database::fill_order( const limit_order_object& order, const asset& pays, const asset& receives, bool cull_if_small,
const price& fill_price, const bool is_maker )
简单看下match函数:
template<typename OrderType>
int database::match( const limit_order_object& usd, const OrderType& core, const price& match_price )
{
assert( usd.sell_price.quote.asset_id == core.sell_price.base.asset_id );
assert( usd.sell_price.base.asset_id == core.sell_price.quote.asset_id );
assert( usd.for_sale > 0 && core.for_sale > 0 );
auto usd_for_sale = usd.amount_for_sale();
auto core_for_sale = core.amount_for_sale();
asset usd_pays, usd_receives, core_pays, core_receives;
if( usd_for_sale <= core_for_sale * match_price )
{
core_receives = usd_for_sale;
usd_receives = usd_for_sale * match_price;
}
else
{
//This line once read: assert( core_for_sale < usd_for_sale * match_price );
//This assert is not always true -- see trade_amount_equals_zero in operation_tests.cpp
//Although usd_for_sale is greater than core_for_sale * match_price, core_for_sale == usd_for_sale * match_price
//Removing the assert seems to be safe -- apparently no asset is created or destroyed.
usd_receives = core_for_sale;
core_receives = core_for_sale * match_price;
}
core_pays = usd_receives;
usd_pays = core_receives;
assert( usd_pays == usd.amount_for_sale() ||
core_pays == core.amount_for_sale() );
int result = 0;
result |= fill_order( usd, usd_pays, usd_receives, false, match_price, false ); // although this function is a template,
result |= fill_order( core, core_pays, core_receives, true, match_price, true ) << 1; // the second param is maker
assert( result != 0 );
return result;
}
match()函数中调用两次fill_order(),买卖总是双方的。
费用计算在calculate_market_fee函数中:
asset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount )
{
assert( trade_asset.id == trade_amount.asset_id );
if( !trade_asset.charges_market_fees() )
return trade_asset.amount(0);
if( trade_asset.options.market_fee_percent == 0 )
return trade_asset.amount(0);
fc::uint128 a(trade_amount.amount.value);
a *= trade_asset.options.market_fee_percent;
a /= GRAPHENE_100_PERCENT;
asset percent_fee = trade_asset.amount(a.to_uint64());
if( percent_fee.amount > trade_asset.options.max_market_fee )
percent_fee.amount = trade_asset.options.max_market_fee;
return percent_fee;
}
charges_market_fees()判断交易资产flags是否指定charge_market_fee,charge_market_fee是资产发行人指定的是否交易需要支付费用给发行方。定义如下:
charge_market_fee = 0x01, /**< an issuer-specified percentage of all market trades in this asset is paid to the issuer */
然后判断资产属性market_fee_percent的比例,例如"CNY"指定如下:
get_asset CNY
{
"id": "1.3.113",
"symbol": "CNY",
"precision": 4,
"issuer": "1.2.0",
"options": {
"max_supply": "1000000000000000",
"market_fee_percent": 10,
"max_market_fee": "1000000000000000",
...
}
费用 = 交易数量*费用比例,不超过max_market_fee指定的值。
calculate_market_fee()函数会在pay_market_fees()中调用。
asset database::pay_market_fees( const asset_object& recv_asset, const asset& receives )
{
auto issuer_fees = calculate_market_fee( recv_asset, receives );
assert(issuer_fees <= receives );
//Don't dirty undo state if not actually collecting any fees
if( issuer_fees.amount > 0 )
{
const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this);
modify( recv_dyn_data, [&]( asset_dynamic_data_object& obj ){
//idump((issuer_fees));
obj.accumulated_fees += issuer_fees.amount;
});
}
return issuer_fees;
}
费用累加到资产的accumulated_fees变量中。
资产累加的费用可以通过asset_claim_fees_operation转到资产发行人帐户中,在bitshares-ui中资产界面应该有这个功能吧(猜的!)!
感谢您阅读 @chaimyu 的帖子,期待您能留言交流!