Skip to content

Commit

Permalink
Merge pull request #1419 from openledger/1268-market-fee-sharing
Browse files Browse the repository at this point in the history
Distribute Asset Market Fees to Referral Program
  • Loading branch information
oxarbitrage committed Jan 29, 2019
2 parents 0f10387 + cce205c commit 8714026
Show file tree
Hide file tree
Showing 17 changed files with 1,483 additions and 25 deletions.
18 changes: 18 additions & 0 deletions libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@
#include <locale>

namespace graphene { namespace chain {
namespace detail {
// TODO review and remove code below and links to it after hf_1268
void check_asset_options_hf_1268(const fc::time_point_sec& block_time, const asset_options& options)
{
if( block_time < HARDFORK_1268_TIME )
{
FC_ASSERT( !options.extensions.value.reward_percent.valid(),
"Asset extension reward percent is only available after HARDFORK_1268_TIME!");

FC_ASSERT( !options.extensions.value.whitelist_market_fee_sharing.valid(),
"Asset extension whitelist_market_fee_sharing is only available after HARDFORK_1268_TIME!");
}
}
}

void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op )
{ try {
Expand All @@ -45,6 +59,8 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );

detail::check_asset_options_hf_1268(d.head_block_time(), op.common_options);

// Check that all authorities do exist
for( auto id : op.common_options.whitelist_authorities )
d.get_object(id);
Expand Down Expand Up @@ -277,6 +293,8 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
validate_new_issuer( d, a, *o.new_issuer );
}

detail::check_asset_options_hf_1268(d.head_block_time(), o.new_options);

if( (d.head_block_time() < HARDFORK_572_TIME) || (a.dynamic_asset_data_id(d).current_supply != 0) )
{
// new issuer_permissions must be subset of old issuer permissions
Expand Down
76 changes: 76 additions & 0 deletions libraries/chain/db_balance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <boost/range/algorithm.hpp>

namespace graphene { namespace chain {

Expand Down Expand Up @@ -81,9 +82,81 @@ void database::adjust_balance(account_id_type account, asset delta )

} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }

namespace detail {

/**
* Used as a key to search vesting_balance_object in the index
*/
struct vbo_mfs_key
{
account_id_type account_id;
asset_id_type asset_id;

vbo_mfs_key(const account_id_type& account, const asset_id_type& asset):
account_id(account),
asset_id(asset)
{}

bool operator()(const vbo_mfs_key& k, const vesting_balance_object& vbo)const
{
return ( vbo.balance_type == vesting_balance_type::market_fee_sharing ) &&
( k.asset_id == vbo.balance.asset_id ) &&
( k.account_id == vbo.owner );
}

uint64_t operator()(const vbo_mfs_key& k)const
{
return vbo_mfs_hash(k.account_id, k.asset_id);
}
};
} //detail

asset database::get_market_fee_vesting_balance(const account_id_type &account_id, const asset_id_type &asset_id)
{
auto& vesting_balances = get_index_type<vesting_balance_index>().indices().get<by_vesting_type>();
const auto& key = detail::vbo_mfs_key{account_id, asset_id};
auto vbo_it = vesting_balances.find(key, key, key);

if( vbo_it == vesting_balances.end() )
{
return asset(0, asset_id);
}
return vbo_it->balance;
}

void database::deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta)
{ try {
FC_ASSERT( delta.amount >= 0, "Invalid negative value for balance");

if( delta.amount == 0 )
return;

auto& vesting_balances = get_index_type<vesting_balance_index>().indices().get<by_vesting_type>();
const auto& key = detail::vbo_mfs_key{account_id, delta.asset_id};
auto vbo_it = vesting_balances.find(key, key, key);

auto block_time = head_block_time();

if( vbo_it == vesting_balances.end() )
{
create<vesting_balance_object>([&account_id, &delta, &block_time](vesting_balance_object &vbo) {
vbo.owner = account_id;
vbo.balance = delta;
vbo.balance_type = vesting_balance_type::market_fee_sharing;
vbo.policy = instant_vesting_policy{};
});
} else {
modify( *vbo_it, [&block_time, &delta]( vesting_balance_object& vbo )
{
vbo.deposit_vested(block_time, delta);
});
}
} FC_CAPTURE_AND_RETHROW( (account_id)(delta) ) }

optional< vesting_balance_id_type > database::deposit_lazy_vesting(
const optional< vesting_balance_id_type >& ovbid,
share_type amount, uint32_t req_vesting_seconds,
vesting_balance_type balance_type,
account_id_type req_owner,
bool require_vesting )
{
Expand Down Expand Up @@ -117,6 +190,7 @@ optional< vesting_balance_id_type > database::deposit_lazy_vesting(
{
_vbo.owner = req_owner;
_vbo.balance = amount;
_vbo.balance_type = balance_type;

cdd_vesting_policy policy;
policy.vesting_seconds = req_vesting_seconds;
Expand Down Expand Up @@ -152,6 +226,7 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b
acct.cashback_vb,
amount,
get_global_properties().parameters.cashback_vesting_period_seconds,
vesting_balance_type::cashback,
acct.id,
require_vesting );

Expand Down Expand Up @@ -179,6 +254,7 @@ void database::deposit_witness_pay(const witness_object& wit, share_type amount)
wit.pay_vb,
amount,
get_global_properties().parameters.witness_pay_vesting_seconds,
vesting_balance_type::witness,
wit.witness_account,
true );

Expand Down
77 changes: 70 additions & 7 deletions libraries/chain/db_market.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,21 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/is_authorized_asset.hpp>

#include <fc/uint128.hpp>

namespace graphene { namespace chain {
namespace graphene { namespace chain { namespace detail {

uint64_t calculate_percent(const share_type& value, uint16_t percent)
{
fc::uint128 a(value.value);
a *= percent;
a /= GRAPHENE_100_PERCENT;
return a.to_uint64();
}

} //detail

/**
* All margin positions are force closed at the swan price
Expand Down Expand Up @@ -775,7 +786,10 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p
const account_object& seller = order.seller(*this);
const asset_object& recv_asset = receives.asset_id(*this);

auto issuer_fees = pay_market_fees( recv_asset, receives );
auto issuer_fees = ( head_block_time() < HARDFORK_1268_TIME ) ?
pay_market_fees(recv_asset, receives) :
pay_market_fees(seller, recv_asset, receives);

pay_order( seller, receives - issuer_fees, pays );

assert( pays.asset_id != receives.asset_id );
Expand Down Expand Up @@ -1109,10 +1123,8 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass
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());
auto value = detail::calculate_percent(trade_amount.amount, trade_asset.options.market_fee_percent);
asset percent_fee = trade_asset.amount(value);

if( percent_fee.amount > trade_asset.options.max_market_fee )
percent_fee.amount = trade_asset.options.max_market_fee;
Expand All @@ -1123,7 +1135,7 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass
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 );
FC_ASSERT( issuer_fees <= receives, "Market fee shouldn't be greater than receives");

//Don't dirty undo state if not actually collecting any fees
if( issuer_fees.amount > 0 )
Expand All @@ -1138,4 +1150,55 @@ asset database::pay_market_fees( const asset_object& recv_asset, const asset& re
return issuer_fees;
}

asset database::pay_market_fees(const account_object& seller, const asset_object& recv_asset, const asset& receives )
{
const auto issuer_fees = calculate_market_fee( recv_asset, receives );
FC_ASSERT( issuer_fees <= receives, "Market fee shouldn't be greater than receives");
//Don't dirty undo state if not actually collecting any fees
if ( issuer_fees.amount > 0 )
{
// calculate and pay rewards
asset reward = recv_asset.amount(0);

auto is_rewards_allowed = [&recv_asset, &seller]() {
const auto &white_list = recv_asset.options.extensions.value.whitelist_market_fee_sharing;
return ( !white_list || (*white_list).empty() || ( (*white_list).find(seller.registrar) != (*white_list).end() ) );
};

if ( is_rewards_allowed() )
{
const auto reward_percent = recv_asset.options.extensions.value.reward_percent;
if ( reward_percent && *reward_percent )
{
const auto reward_value = detail::calculate_percent(issuer_fees.amount, *reward_percent);
if ( reward_value > 0 && is_authorized_asset(*this, seller.registrar(*this), recv_asset) )
{
reward = recv_asset.amount(reward_value);
FC_ASSERT( reward < issuer_fees, "Market reward should be less than issuer fees");
// cut referrer percent from reward
const auto referrer_rewards_percentage = seller.referrer_rewards_percentage;
const auto referrer_rewards_value = detail::calculate_percent(reward.amount, referrer_rewards_percentage);
auto registrar_reward = reward;

if ( referrer_rewards_value > 0 && is_authorized_asset(*this, seller.referrer(*this), recv_asset))
{
FC_ASSERT ( referrer_rewards_value <= reward.amount, "Referrer reward shouldn't be greater than total reward" );
const asset referrer_reward = recv_asset.amount(referrer_rewards_value);
registrar_reward -= referrer_reward;
deposit_market_fee_vesting_balance(seller.referrer, referrer_reward);
}
deposit_market_fee_vesting_balance(seller.registrar, registrar_reward);
}
}
}

const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this);
modify( recv_dyn_data, [&issuer_fees, &reward]( asset_dynamic_data_object& obj ){
obj.accumulated_fees += issuer_fees.amount - reward.amount;
});
}

return issuer_fees;
}

} }
4 changes: 4 additions & 0 deletions libraries/chain/hardfork.d/CORE_1268.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// #1268 Distribute Asset Market Fees to Referral Program
#ifndef HARDFORK_1268_TIME
#define HARDFORK_1268_TIME (fc::time_point_sec( 1577880000 )) // Wednesday, January 1, 2020 12:00:00 PM
#endif
12 changes: 12 additions & 0 deletions libraries/chain/include/graphene/chain/database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace graphene { namespace chain {
class transaction_evaluation_state;

struct budget_record;
enum class vesting_balance_type;

/**
* @class database
Expand Down Expand Up @@ -302,6 +303,15 @@ namespace graphene { namespace chain {
*/
void adjust_balance(account_id_type account, asset delta);

void deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta);
/**
* @brief Retrieve a particular account's market fee vesting balance in a given asset
* @param owner Account whose balance should be retrieved
* @param asset_id ID of the asset to get balance in
* @return owner's balance in asset
*/
asset get_market_fee_vesting_balance(const account_id_type &account_id, const asset_id_type &asset_id);

/**
* @brief Helper to make lazy deposit to CDD VBO.
*
Expand All @@ -319,6 +329,7 @@ namespace graphene { namespace chain {
const optional< vesting_balance_id_type >& ovbid,
share_type amount,
uint32_t req_vesting_seconds,
vesting_balance_type balance_type,
account_id_type req_owner,
bool require_vesting );

Expand Down Expand Up @@ -394,6 +405,7 @@ namespace graphene { namespace chain {

asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount);
asset pay_market_fees( const asset_object& recv_asset, const asset& receives );
asset pay_market_fees( const account_object& seller, const asset_object& recv_asset, const asset& receives );


///@{
Expand Down
11 changes: 9 additions & 2 deletions libraries/chain/include/graphene/chain/protocol/asset_ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@

namespace graphene { namespace chain {

struct additional_asset_options
{
fc::optional<uint16_t> reward_percent;
fc::optional<flat_set<account_id_type>> whitelist_market_fee_sharing;
};
typedef extension<additional_asset_options> additional_asset_options_t;

bool is_valid_symbol( const string& symbol );

/**
Expand Down Expand Up @@ -75,7 +82,7 @@ namespace graphene { namespace chain {
* size of description.
*/
string description;
extensions_type extensions;
additional_asset_options_t extensions;

/// Perform internal consistency checks.
/// @throws fc::exception if any check fails
Expand Down Expand Up @@ -535,7 +542,7 @@ FC_REFLECT( graphene::chain::bitasset_options,
(extensions)
)


FC_REFLECT( graphene::chain::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing) )
FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )
FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) )
Expand Down
10 changes: 9 additions & 1 deletion libraries/chain/include/graphene/chain/protocol/vesting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,15 @@ namespace graphene { namespace chain {
cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){}
};

typedef fc::static_variant<linear_vesting_policy_initializer, cdd_vesting_policy_initializer> vesting_policy_initializer;
struct instant_vesting_policy_initializer
{
};

typedef fc::static_variant<
linear_vesting_policy_initializer,
cdd_vesting_policy_initializer,
instant_vesting_policy_initializer
> vesting_policy_initializer;


/**
Expand Down Expand Up @@ -117,4 +124,5 @@ FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_b

FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) )
FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) )
FC_REFLECT_EMPTY( graphene::chain::instant_vesting_policy_initializer )
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer )
Loading

0 comments on commit 8714026

Please sign in to comment.