ppy marketplace 5 - Add revenue split
This commit is contained in:
parent
db9a35fa17
commit
c0da3a2e84
7 changed files with 129 additions and 17 deletions
|
|
@ -16,6 +16,8 @@ namespace graphene { namespace chain {
|
|||
std::string name;
|
||||
std::string symbol;
|
||||
std::string base_uri;
|
||||
optional<account_id_type> revenue_partner;
|
||||
optional<double> revenue_split;
|
||||
};
|
||||
|
||||
class nft_object : public abstract_object<nft_object>
|
||||
|
|
@ -87,7 +89,9 @@ FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object)
|
|||
(owner)
|
||||
(name)
|
||||
(symbol)
|
||||
(base_uri) )
|
||||
(base_uri)
|
||||
(revenue_partner)
|
||||
(revenue_split) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
|
||||
(nft_metadata_id)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace graphene { namespace chain {
|
|||
std::string name;
|
||||
std::string symbol;
|
||||
std::string base_uri;
|
||||
optional<account_id_type> revenue_partner;
|
||||
optional<double> revenue_split;
|
||||
|
||||
account_id_type fee_payer()const { return owner; }
|
||||
};
|
||||
|
|
@ -27,6 +29,8 @@ namespace graphene { namespace chain {
|
|||
optional<std::string> name;
|
||||
optional<std::string> symbol;
|
||||
optional<std::string> base_uri;
|
||||
optional<account_id_type> revenue_partner;
|
||||
optional<double> revenue_split;
|
||||
|
||||
account_id_type fee_payer()const { return owner; }
|
||||
};
|
||||
|
|
@ -97,8 +101,8 @@ FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_ty
|
|||
FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) )
|
||||
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) )
|
||||
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) )
|
||||
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) )
|
||||
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) )
|
||||
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) )
|
||||
FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) )
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_creat
|
|||
FC_ASSERT( idx_nft_md_by_name.find(op.name) == idx_nft_md_by_name.end(), "NFT name already in use" );
|
||||
const auto& idx_nft_md_by_symbol = db().get_index_type<nft_metadata_index>().indices().get<by_symbol>();
|
||||
FC_ASSERT( idx_nft_md_by_symbol.find(op.symbol) == idx_nft_md_by_symbol.end(), "NFT symbol already in use" );
|
||||
FC_ASSERT( (op.revenue_partner && op.revenue_split) || (!op.revenue_partner && !op.revenue_split), "NFT revenue partner info invalid" );
|
||||
if(op.revenue_partner) {
|
||||
(*op.revenue_partner)(db());
|
||||
FC_ASSERT( *op.revenue_split >= 0.0 && *op.revenue_split <= 1.0, "Revenue split percent invalid" );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -19,6 +24,8 @@ object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_creat
|
|||
obj.name = op.name;
|
||||
obj.symbol = op.symbol;
|
||||
obj.base_uri = op.base_uri;
|
||||
obj.revenue_partner = op.revenue_partner;
|
||||
obj.revenue_split = op.revenue_split;
|
||||
});
|
||||
return new_nft_metadata_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
@ -30,7 +37,11 @@ void_result nft_metadata_update_evaluator::do_evaluate( const nft_metadata_updat
|
|||
auto itr_nft_md = idx_nft_md.find(op.nft_metadata_id);
|
||||
FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" );
|
||||
FC_ASSERT( itr_nft_md->owner == op.owner, "Only owner can modify NFT metadata" );
|
||||
|
||||
FC_ASSERT( (op.revenue_partner && op.revenue_split) || (!op.revenue_partner && !op.revenue_split), "NFT revenue partner info invalid" );
|
||||
if(op.revenue_partner) {
|
||||
(*op.revenue_partner)(db());
|
||||
FC_ASSERT( *op.revenue_split >= 0.0 && *op.revenue_split <= 1.0, "Revenue split percent invalid" );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -43,6 +54,10 @@ void_result nft_metadata_update_evaluator::do_apply( const nft_metadata_update_o
|
|||
obj.symbol = *op.symbol;
|
||||
if( op.base_uri.valid() )
|
||||
obj.base_uri = *op.base_uri;
|
||||
if( op.revenue_partner.valid() )
|
||||
obj.revenue_partner = op.revenue_partner;
|
||||
if( op.revenue_split.valid() )
|
||||
obj.revenue_split = op.revenue_split;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ namespace graphene
|
|||
{
|
||||
namespace chain
|
||||
{
|
||||
|
||||
void_result offer_evaluator::do_evaluate(const offer_operation &op)
|
||||
{
|
||||
try
|
||||
|
|
@ -33,7 +32,7 @@ namespace graphene
|
|||
}
|
||||
else
|
||||
{
|
||||
FC_ASSERT(is_owner || is_approved || is_approved_operator, "Issuer has no authority to sell the item");
|
||||
FC_ASSERT(is_owner, "Issuer has no authority to sell the item");
|
||||
}
|
||||
}
|
||||
FC_ASSERT(op.offer_expiration_date > d.head_block_time(), "Expiration should be in future");
|
||||
|
|
@ -87,7 +86,7 @@ namespace graphene
|
|||
bool is_approved_operator = (std::find(nft_obj.approved_operators.begin(), nft_obj.approved_operators.end(), op.bidder) != nft_obj.approved_operators.end());
|
||||
if (offer.buying_item)
|
||||
{
|
||||
FC_ASSERT(is_owner || is_approved || is_approved_operator, "Bidder has no authority to sell the item");
|
||||
FC_ASSERT(is_owner, "Bidder has no authority to sell the item");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -165,7 +164,6 @@ namespace graphene
|
|||
FC_THROW_EXCEPTION(fc::assert_exception, "finalize_offer_operation: unknown result type.");
|
||||
break;
|
||||
}
|
||||
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
|
|
@ -176,25 +174,61 @@ namespace graphene
|
|||
try
|
||||
{
|
||||
database &d = db();
|
||||
|
||||
offer_object offer = op.offer_id(d);
|
||||
vector<nft_safe_transfer_from_operation> xfer_ops;
|
||||
// Calculate the fees for all revenue partners of the items
|
||||
auto calc_fee = [&](int64_t &tot_fees) {
|
||||
map<account_id_type, asset> fee_map;
|
||||
for (const auto &item : offer.item_ids)
|
||||
{
|
||||
const auto &nft_obj = item(d);
|
||||
const auto &nft_meta_obj = nft_obj.nft_metadata_id(d);
|
||||
if (nft_meta_obj.revenue_partner && *nft_meta_obj.revenue_split > 0.0)
|
||||
{
|
||||
const auto &rev_partner = *nft_meta_obj.revenue_partner;
|
||||
const auto &rev_split = *nft_meta_obj.revenue_split;
|
||||
int64_t item_fee = static_cast<int64_t>((rev_split * (*offer.bid_price).amount.value) / offer.item_ids.size());
|
||||
const auto& fee_asset = asset(item_fee, (*offer.bid_price).asset_id);
|
||||
auto ret_val = fee_map.insert({rev_partner, fee_asset});
|
||||
if ( ret_val.second == false ) {
|
||||
fee_map[rev_partner] += fee_asset;
|
||||
}
|
||||
tot_fees += item_fee;
|
||||
}
|
||||
}
|
||||
return fee_map;
|
||||
};
|
||||
|
||||
if (op.result != result_type::ExpiredNoBid)
|
||||
{
|
||||
int64_t tot_fees = 0;
|
||||
auto &&fee_map = calc_fee(tot_fees);
|
||||
// Transfer all the fee
|
||||
for (const auto &fee_itr : fee_map)
|
||||
{
|
||||
auto &acc = fee_itr.first;
|
||||
auto &acc_fee = fee_itr.second;
|
||||
d.adjust_balance(acc, acc_fee);
|
||||
}
|
||||
// Calculate the remaining seller amount after the fee is deducted
|
||||
auto &&seller_amount = *offer.bid_price - asset(tot_fees, (*offer.bid_price).asset_id);
|
||||
// Buy Offer
|
||||
if (offer.buying_item)
|
||||
{
|
||||
d.adjust_balance(*offer.bidder, *offer.bid_price);
|
||||
// Send the seller his amount
|
||||
d.adjust_balance(*offer.bidder, seller_amount);
|
||||
if (offer.bid_price < offer.maximum_price)
|
||||
{
|
||||
// Send the buyer the delta
|
||||
d.adjust_balance(offer.issuer, offer.maximum_price - *offer.bid_price);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
d.adjust_balance(offer.issuer, *offer.bid_price);
|
||||
// Sell Offer, send the seller his amount
|
||||
d.adjust_balance(offer.issuer, seller_amount);
|
||||
}
|
||||
|
||||
// Safely tranfer the NFTs with the ops
|
||||
for (const auto &item : offer.item_ids)
|
||||
{
|
||||
const auto &nft_obj = item(d);
|
||||
|
|
@ -244,7 +278,6 @@ namespace graphene
|
|||
d.apply_operation(xfer_context, xfer_op);
|
||||
}
|
||||
}
|
||||
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
|
|
|
|||
|
|
@ -1904,6 +1904,8 @@ class wallet_api
|
|||
* @param name Name of the token group
|
||||
* @param symbol Symbol of the token group
|
||||
* @param base_uri Base URI for token URI
|
||||
* @param revenue_partner revenue partner for this type of Token
|
||||
* @param revenue_split revenue split for the sale
|
||||
* @param broadcast true to broadcast transaction to the network
|
||||
* @return Signed transaction transfering the funds
|
||||
*/
|
||||
|
|
@ -1911,6 +1913,8 @@ class wallet_api
|
|||
string name,
|
||||
string symbol,
|
||||
string base_uri,
|
||||
optional<string> revenue_partner,
|
||||
optional<double> revenue_split,
|
||||
bool broadcast);
|
||||
|
||||
/**
|
||||
|
|
@ -1919,6 +1923,8 @@ class wallet_api
|
|||
* @param name Name of the token group
|
||||
* @param symbol Symbol of the token group
|
||||
* @param base_uri Base URI for token URI
|
||||
* @param revenue_partner revenue partner for this type of Token
|
||||
* @param revenue_split revenue split for the sale
|
||||
* @param broadcast true to broadcast transaction to the network
|
||||
* @return Signed transaction transfering the funds
|
||||
*/
|
||||
|
|
@ -1926,6 +1932,8 @@ class wallet_api
|
|||
string name,
|
||||
string symbol,
|
||||
string base_uri,
|
||||
optional<string> revenue_partner,
|
||||
optional<double> revenue_split,
|
||||
bool broadcast);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6161,6 +6161,8 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
|
|||
string name,
|
||||
string symbol,
|
||||
string base_uri,
|
||||
optional<string> revenue_partner,
|
||||
optional<double> revenue_split,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
|
@ -6170,6 +6172,17 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
|
|||
op.name = name;
|
||||
op.symbol = symbol;
|
||||
op.base_uri = base_uri;
|
||||
if( revenue_partner )
|
||||
{
|
||||
account_object partner_account = my->get_account(*revenue_partner);
|
||||
op.revenue_partner = partner_account.id;
|
||||
double rev_split = 0.0;
|
||||
if( revenue_split )
|
||||
{
|
||||
rev_split = *revenue_split;
|
||||
}
|
||||
op.revenue_split = rev_split;
|
||||
}
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
|
|
@ -6183,6 +6196,8 @@ signed_transaction wallet_api::nft_metadata_update(string owner_account_id_or_na
|
|||
string name,
|
||||
string symbol,
|
||||
string base_uri,
|
||||
optional<string> revenue_partner,
|
||||
optional<double> revenue_split,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
|
@ -6192,6 +6207,17 @@ signed_transaction wallet_api::nft_metadata_update(string owner_account_id_or_na
|
|||
op.name = name;
|
||||
op.symbol = symbol;
|
||||
op.base_uri = base_uri;
|
||||
if( revenue_partner )
|
||||
{
|
||||
account_object partner_account = my->get_account(*revenue_partner);
|
||||
op.revenue_partner = partner_account.id;
|
||||
double rev_split = 0.0;
|
||||
if( revenue_split )
|
||||
{
|
||||
rev_split = *revenue_split;
|
||||
}
|
||||
op.revenue_split = rev_split;
|
||||
}
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ BOOST_AUTO_TEST_CASE(nft_metadata_create_test)
|
|||
op.name = "NFT Test";
|
||||
op.symbol = "NFT";
|
||||
op.base_uri = "http://nft.example.com";
|
||||
op.revenue_partner = mdowner_id;
|
||||
op.revenue_split = 0.1;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, mdowner_private_key);
|
||||
|
|
@ -337,10 +339,12 @@ BOOST_AUTO_TEST_CASE(best_buy_bid_for_sell_offer)
|
|||
GET_ACTOR(bob);
|
||||
GET_ACTOR(charlie);
|
||||
GET_ACTOR(operator1);
|
||||
GET_ACTOR(mdowner);
|
||||
|
||||
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
|
||||
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
|
||||
int64_t charlie_balance = get_balance(charlie_id(db), asset_id_type()(db));
|
||||
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
|
||||
const auto &offer_obj = sell_offer(db);
|
||||
|
||||
bid_operation bid_op;
|
||||
|
|
@ -375,8 +379,11 @@ BOOST_AUTO_TEST_CASE(best_buy_bid_for_sell_offer)
|
|||
auto cached_offer_obj = offer_obj;
|
||||
// Generate a block and offer should be finalized with bid
|
||||
generate_block();
|
||||
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
|
||||
(alice_balance + cached_offer_obj.maximum_price.amount).value);
|
||||
(alice_balance + cached_offer_obj.maximum_price.amount).value - partner_fee);
|
||||
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
|
||||
mdowner_balance + partner_fee);
|
||||
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
|
||||
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(oidx.size() == 0);
|
||||
|
|
@ -408,12 +415,17 @@ BOOST_AUTO_TEST_CASE(expire_with_bid_for_sell_offer_test)
|
|||
INVOKE(second_buy_bid_for_sell_offer_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(charlie);
|
||||
GET_ACTOR(mdowner);
|
||||
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
|
||||
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
|
||||
const auto &offer_obj = sell_offer(db);
|
||||
auto cached_offer_obj = offer_obj;
|
||||
generate_blocks(5);
|
||||
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
|
||||
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
|
||||
mdowner_balance + partner_fee);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
|
||||
(alice_balance + (*cached_offer_obj.bid_price).amount).value);
|
||||
(alice_balance + (*cached_offer_obj.bid_price).amount).value - partner_fee);
|
||||
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
|
||||
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(oidx.size() == 0);
|
||||
|
|
@ -624,9 +636,11 @@ BOOST_AUTO_TEST_CASE(best_sell_bid_for_buy_offer)
|
|||
INVOKE(second_sell_bid_for_buy_offer_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
GET_ACTOR(mdowner);
|
||||
|
||||
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
|
||||
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
|
||||
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
|
||||
const auto &offer_obj = buy_offer(db);
|
||||
|
||||
bid_operation bid_op;
|
||||
|
|
@ -658,8 +672,11 @@ BOOST_AUTO_TEST_CASE(best_sell_bid_for_buy_offer)
|
|||
// Generate a block and offer should be finalized with bid
|
||||
generate_block();
|
||||
// Check balances
|
||||
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
|
||||
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
|
||||
mdowner_balance + partner_fee);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
|
||||
(bob_balance + exp_delta_bidder1.amount).value);
|
||||
(bob_balance + exp_delta_bidder1.amount).value - partner_fee);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
|
||||
(alice_balance + exp_delta_bidder2.amount).value);
|
||||
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
|
||||
|
|
@ -693,15 +710,20 @@ BOOST_AUTO_TEST_CASE(expire_with_bid_for_buy_offer_test)
|
|||
INVOKE(second_sell_bid_for_buy_offer_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
GET_ACTOR(mdowner);
|
||||
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
|
||||
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
|
||||
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
|
||||
const auto &offer_obj = buy_offer(db);
|
||||
auto cached_offer_obj = offer_obj;
|
||||
generate_blocks(5);
|
||||
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
|
||||
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
|
||||
mdowner_balance + partner_fee);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
|
||||
(alice_balance + cached_offer_obj.maximum_price.amount - (*cached_offer_obj.bid_price).amount).value);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
|
||||
(bob_balance + (*cached_offer_obj.bid_price).amount).value);
|
||||
(bob_balance + (*cached_offer_obj.bid_price).amount).value - partner_fee);
|
||||
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
|
||||
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(oidx.size() == 0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue