NFT Lottery (#384)
This commit is contained in:
parent
5a5154b260
commit
b99a19bd72
29 changed files with 2005 additions and 129 deletions
|
|
@ -234,7 +234,6 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
||||||
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
|
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
|
||||||
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
|
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
|
||||||
|
|
||||||
|
|
||||||
uint32_t api_limit_get_lower_bound_symbol = 100;
|
uint32_t api_limit_get_lower_bound_symbol = 100;
|
||||||
uint32_t api_limit_get_limit_orders = 300;
|
uint32_t api_limit_get_limit_orders = 300;
|
||||||
uint32_t api_limit_get_limit_orders_by_account = 101;
|
uint32_t api_limit_get_limit_orders_by_account = 101;
|
||||||
|
|
@ -248,6 +247,9 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
||||||
|
|
||||||
// Account Role
|
// Account Role
|
||||||
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
||||||
|
// rng
|
||||||
|
vector<uint64_t> get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const;
|
||||||
|
uint64_t get_random_number(uint64_t bound) const;
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
const account_object* get_account_from_string( const std::string& name_or_id,
|
const account_object* get_account_from_string( const std::string& name_or_id,
|
||||||
|
|
@ -3174,6 +3176,32 @@ vector<account_role_object> database_api_impl::get_account_roles_by_owner(accoun
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Random numbers //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
vector<uint64_t> database_api::get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const
|
||||||
|
{
|
||||||
|
return my->get_random_number_ex(minimum, maximum, selections, duplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uint64_t> database_api_impl::get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const
|
||||||
|
{
|
||||||
|
return _db.get_random_numbers(minimum, maximum, selections, duplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t database_api::get_random_number(uint64_t bound) const
|
||||||
|
{
|
||||||
|
return my->get_random_number(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t database_api_impl::get_random_number(uint64_t bound) const {
|
||||||
|
vector<uint64_t> v = get_random_number_ex(0, bound, 1, false);
|
||||||
|
return v.at(0);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// //
|
// //
|
||||||
// Private methods //
|
// Private methods //
|
||||||
|
|
|
||||||
|
|
@ -934,7 +934,28 @@ class database_api
|
||||||
// ACCOUNT ROLE //
|
// ACCOUNT ROLE //
|
||||||
//////////////////
|
//////////////////
|
||||||
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
||||||
private:
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Random number generator //
|
||||||
|
/////////////////////////////
|
||||||
|
/**
|
||||||
|
* @brief Returns the random number
|
||||||
|
* @param minimum Lower bound of segment containing random number
|
||||||
|
* @param maximum Upper bound of segment containing random number
|
||||||
|
* @param selections Number of random numbers to return
|
||||||
|
* @param duplicates Allow duplicated numbers
|
||||||
|
* @return Vector containing random numbers from segment [minimum, maximum)
|
||||||
|
*/
|
||||||
|
vector<uint64_t> get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the random number
|
||||||
|
* @param bound Upper bound of segment containing random number
|
||||||
|
* @return Random number from segment [0, bound)
|
||||||
|
*/
|
||||||
|
uint64_t get_random_number(uint64_t bound) const;
|
||||||
|
|
||||||
|
private:
|
||||||
std::shared_ptr< database_api_impl > my;
|
std::shared_ptr< database_api_impl > my;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1125,4 +1146,7 @@ FC_API(graphene::app::database_api,
|
||||||
|
|
||||||
// Account Roles
|
// Account Roles
|
||||||
(get_account_roles_by_owner)
|
(get_account_roles_by_owner)
|
||||||
|
// rngs
|
||||||
|
(get_random_number_ex)
|
||||||
|
(get_random_number)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -715,6 +715,7 @@ void database::_apply_block( const signed_block& next_block )
|
||||||
perform_chain_maintenance(next_block, global_props);
|
perform_chain_maintenance(next_block, global_props);
|
||||||
|
|
||||||
check_ending_lotteries();
|
check_ending_lotteries();
|
||||||
|
check_ending_nft_lotteries();
|
||||||
|
|
||||||
create_block_summary(next_block);
|
create_block_summary(next_block);
|
||||||
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ uint32_t database::last_non_undoable_block_num() const
|
||||||
return head_block_num() - _undo_db.size();
|
return head_block_num() - _undo_db.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> database::get_seeds(asset_id_type for_asset, uint8_t count_winners) const
|
std::vector<uint32_t> database::get_seeds( asset_id_type for_asset, uint8_t count_winners ) const
|
||||||
{
|
{
|
||||||
FC_ASSERT( count_winners <= 64 );
|
FC_ASSERT( count_winners <= 64 );
|
||||||
std::string salted_string = std::string(_random_number_generator._seed) + std::to_string(for_asset.instance.value);
|
std::string salted_string = std::string(_random_number_generator._seed) + std::to_string(for_asset.instance.value);
|
||||||
|
|
@ -315,4 +315,36 @@ bool database::is_son_active( son_id_type son_id )
|
||||||
return (it_son != active_son_ids.end());
|
return (it_son != active_son_ids.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<uint64_t> database::get_random_numbers(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates)
|
||||||
|
{
|
||||||
|
FC_ASSERT( selections <= 100000 );
|
||||||
|
if (duplicates == false) {
|
||||||
|
FC_ASSERT( maximum - minimum >= selections );
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uint64_t> v;
|
||||||
|
v.reserve(selections);
|
||||||
|
|
||||||
|
if (duplicates) {
|
||||||
|
for (uint64_t i = 0; i < selections; i++) {
|
||||||
|
int64_t rnd = get_random_bits(maximum - minimum) + minimum;
|
||||||
|
v.push_back(rnd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vector<uint64_t> tmpv;
|
||||||
|
tmpv.reserve(selections);
|
||||||
|
for (uint64_t i = minimum; i < maximum; i++) {
|
||||||
|
tmpv.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint64_t i = 0; (i < selections) && (tmpv.size() > 0); i++) {
|
||||||
|
uint64_t idx = get_random_bits(tmpv.size());
|
||||||
|
v.push_back(tmpv.at(idx));
|
||||||
|
tmpv.erase(tmpv.begin() + idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
#include <graphene/chain/custom_account_authority_object.hpp>
|
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||||
#include <graphene/chain/offer_object.hpp>
|
#include <graphene/chain/offer_object.hpp>
|
||||||
#include <graphene/chain/account_role_object.hpp>
|
#include <graphene/chain/account_role_object.hpp>
|
||||||
|
#include <graphene/chain/random_number_object.hpp>
|
||||||
|
|
||||||
#include <graphene/chain/nft_object.hpp>
|
#include <graphene/chain/nft_object.hpp>
|
||||||
|
|
||||||
|
|
@ -94,12 +95,14 @@
|
||||||
#include <graphene/chain/offer_evaluator.hpp>
|
#include <graphene/chain/offer_evaluator.hpp>
|
||||||
#include <graphene/chain/nft_evaluator.hpp>
|
#include <graphene/chain/nft_evaluator.hpp>
|
||||||
#include <graphene/chain/account_role_evaluator.hpp>
|
#include <graphene/chain/account_role_evaluator.hpp>
|
||||||
|
#include <graphene/chain/nft_lottery_evaluator.hpp>
|
||||||
#include <graphene/chain/son_evaluator.hpp>
|
#include <graphene/chain/son_evaluator.hpp>
|
||||||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||||
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
||||||
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
||||||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||||
|
#include <graphene/chain/random_number_evaluator.hpp>
|
||||||
|
|
||||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||||
|
|
||||||
|
|
@ -203,6 +206,12 @@ const uint8_t offer_history_object::type_id;
|
||||||
const uint8_t account_role_object::space_id;
|
const uint8_t account_role_object::space_id;
|
||||||
const uint8_t account_role_object::type_id;
|
const uint8_t account_role_object::type_id;
|
||||||
|
|
||||||
|
const uint8_t nft_lottery_balance_object::space_id;
|
||||||
|
const uint8_t nft_lottery_balance_object::type_id;
|
||||||
|
|
||||||
|
const uint8_t random_number_object::space_id;
|
||||||
|
const uint8_t random_number_object::type_id;
|
||||||
|
|
||||||
void database::initialize_evaluators()
|
void database::initialize_evaluators()
|
||||||
{
|
{
|
||||||
_operation_evaluators.resize(255);
|
_operation_evaluators.resize(255);
|
||||||
|
|
@ -295,6 +304,9 @@ void database::initialize_evaluators()
|
||||||
register_evaluator<account_role_create_evaluator>();
|
register_evaluator<account_role_create_evaluator>();
|
||||||
register_evaluator<account_role_update_evaluator>();
|
register_evaluator<account_role_update_evaluator>();
|
||||||
register_evaluator<account_role_delete_evaluator>();
|
register_evaluator<account_role_delete_evaluator>();
|
||||||
|
register_evaluator<nft_lottery_token_purchase_evaluator>();
|
||||||
|
register_evaluator<nft_lottery_reward_evaluator>();
|
||||||
|
register_evaluator<nft_lottery_end_evaluator>();
|
||||||
register_evaluator<create_son_evaluator>();
|
register_evaluator<create_son_evaluator>();
|
||||||
register_evaluator<update_son_evaluator>();
|
register_evaluator<update_son_evaluator>();
|
||||||
register_evaluator<deregister_son_evaluator>();
|
register_evaluator<deregister_son_evaluator>();
|
||||||
|
|
@ -314,6 +326,7 @@ void database::initialize_evaluators()
|
||||||
register_evaluator<sidechain_transaction_sign_evaluator>();
|
register_evaluator<sidechain_transaction_sign_evaluator>();
|
||||||
register_evaluator<sidechain_transaction_send_evaluator>();
|
register_evaluator<sidechain_transaction_send_evaluator>();
|
||||||
register_evaluator<sidechain_transaction_settle_evaluator>();
|
register_evaluator<sidechain_transaction_settle_evaluator>();
|
||||||
|
register_evaluator<random_number_store_evaluator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void database::initialize_indexes()
|
void database::initialize_indexes()
|
||||||
|
|
@ -403,7 +416,9 @@ void database::initialize_indexes()
|
||||||
add_index< primary_index<lottery_balance_index > >();
|
add_index< primary_index<lottery_balance_index > >();
|
||||||
add_index< primary_index<sweeps_vesting_balance_index > >();
|
add_index< primary_index<sweeps_vesting_balance_index > >();
|
||||||
add_index< primary_index<offer_history_index > >();
|
add_index< primary_index<offer_history_index > >();
|
||||||
|
add_index< primary_index<nft_lottery_balance_index > >();
|
||||||
add_index< primary_index<son_stats_index > >();
|
add_index< primary_index<son_stats_index > >();
|
||||||
|
add_index< primary_index<random_number_index > >();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include <graphene/chain/witness_schedule_object.hpp>
|
#include <graphene/chain/witness_schedule_object.hpp>
|
||||||
#include <graphene/chain/special_authority_object.hpp>
|
#include <graphene/chain/special_authority_object.hpp>
|
||||||
#include <graphene/chain/operation_history_object.hpp>
|
#include <graphene/chain/operation_history_object.hpp>
|
||||||
|
#include <graphene/chain/nft_object.hpp>
|
||||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||||
|
|
||||||
#include <fc/io/fstream.hpp>
|
#include <fc/io/fstream.hpp>
|
||||||
|
|
@ -304,6 +305,24 @@ void database::check_ending_lotteries()
|
||||||
} catch( ... ) {}
|
} catch( ... ) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void database::check_ending_nft_lotteries()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const auto &nft_lotteries_idx = get_index_type<nft_metadata_index>().indices().get<active_nft_lotteries>();
|
||||||
|
for (auto checking_token : nft_lotteries_idx)
|
||||||
|
{
|
||||||
|
FC_ASSERT(checking_token.is_lottery());
|
||||||
|
const auto &lottery_options = checking_token.lottery_data->lottery_options;
|
||||||
|
FC_ASSERT(lottery_options.is_active);
|
||||||
|
// Check the current supply of lottery tokens
|
||||||
|
auto current_supply = checking_token.get_token_current_supply(*this);
|
||||||
|
if ((lottery_options.ending_on_soldout && (current_supply == checking_token.max_supply)) ||
|
||||||
|
(lottery_options.end_date != time_point_sec() && (lottery_options.end_date <= head_block_time())))
|
||||||
|
checking_token.end_lottery(*this);
|
||||||
|
}
|
||||||
|
} catch( ... ) {}
|
||||||
|
}
|
||||||
|
|
||||||
void database::check_lottery_end_by_participants( asset_id_type asset_id )
|
void database::check_lottery_end_by_participants( asset_id_type asset_id )
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -364,6 +364,13 @@ struct get_impacted_account_visitor
|
||||||
void operator()( const account_role_delete_operation& op ){
|
void operator()( const account_role_delete_operation& op ){
|
||||||
_impacted.insert( op.owner );
|
_impacted.insert( op.owner );
|
||||||
}
|
}
|
||||||
|
void operator()( const nft_lottery_token_purchase_operation& op ){
|
||||||
|
_impacted.insert( op.buyer );
|
||||||
|
}
|
||||||
|
void operator()( const nft_lottery_reward_operation& op ) {
|
||||||
|
_impacted.insert( op.winner );
|
||||||
|
}
|
||||||
|
void operator()( const nft_lottery_end_operation& op ) {}
|
||||||
void operator()( const son_create_operation& op ) {
|
void operator()( const son_create_operation& op ) {
|
||||||
_impacted.insert( op.owner_account );
|
_impacted.insert( op.owner_account );
|
||||||
}
|
}
|
||||||
|
|
@ -421,6 +428,9 @@ struct get_impacted_account_visitor
|
||||||
void operator()( const sidechain_transaction_settle_operation& op ) {
|
void operator()( const sidechain_transaction_settle_operation& op ) {
|
||||||
_impacted.insert( op.payer );
|
_impacted.insert( op.payer );
|
||||||
}
|
}
|
||||||
|
void operator()( const random_number_store_operation& op ) {
|
||||||
|
_impacted.insert( op.account );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
|
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
|
||||||
|
|
@ -590,6 +600,8 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
||||||
break;
|
break;
|
||||||
case impl_fba_accumulator_object_type:
|
case impl_fba_accumulator_object_type:
|
||||||
break;
|
break;
|
||||||
|
case impl_nft_lottery_balance_object_type:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,7 @@ namespace graphene { namespace chain {
|
||||||
|
|
||||||
void check_lottery_end_by_participants( asset_id_type asset_id );
|
void check_lottery_end_by_participants( asset_id_type asset_id );
|
||||||
void check_ending_lotteries();
|
void check_ending_lotteries();
|
||||||
|
void check_ending_nft_lotteries();
|
||||||
|
|
||||||
//////////////////// db_getter.cpp ////////////////////
|
//////////////////// db_getter.cpp ////////////////////
|
||||||
|
|
||||||
|
|
@ -324,6 +325,7 @@ namespace graphene { namespace chain {
|
||||||
|
|
||||||
uint32_t last_non_undoable_block_num() const;
|
uint32_t last_non_undoable_block_num() const;
|
||||||
vector<authority> get_account_custom_authorities(account_id_type account, const operation& op)const;
|
vector<authority> get_account_custom_authorities(account_id_type account, const operation& op)const;
|
||||||
|
vector<uint64_t> get_random_numbers(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates);
|
||||||
//////////////////// db_init.cpp ////////////////////
|
//////////////////// db_init.cpp ////////////////////
|
||||||
|
|
||||||
void initialize_evaluators();
|
void initialize_evaluators();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
#include <graphene/chain/protocol/operations.hpp>
|
||||||
|
#include <graphene/chain/evaluator.hpp>
|
||||||
|
#include <graphene/chain/database.hpp>
|
||||||
|
|
||||||
|
namespace graphene
|
||||||
|
{
|
||||||
|
namespace chain
|
||||||
|
{
|
||||||
|
|
||||||
|
class nft_lottery_token_purchase_evaluator : public evaluator<nft_lottery_token_purchase_evaluator>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nft_lottery_token_purchase_operation operation_type;
|
||||||
|
|
||||||
|
void_result do_evaluate(const nft_lottery_token_purchase_operation &o);
|
||||||
|
object_id_type do_apply(const nft_lottery_token_purchase_operation &o);
|
||||||
|
};
|
||||||
|
|
||||||
|
class nft_lottery_reward_evaluator : public evaluator<nft_lottery_reward_evaluator>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nft_lottery_reward_operation operation_type;
|
||||||
|
|
||||||
|
void_result do_evaluate(const nft_lottery_reward_operation &o);
|
||||||
|
void_result do_apply(const nft_lottery_reward_operation &o);
|
||||||
|
};
|
||||||
|
|
||||||
|
class nft_lottery_end_evaluator : public evaluator<nft_lottery_end_evaluator>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nft_lottery_end_operation operation_type;
|
||||||
|
|
||||||
|
void_result do_evaluate(const nft_lottery_end_operation &o);
|
||||||
|
void_result do_apply(const nft_lottery_end_operation &o);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chain
|
||||||
|
} // namespace graphene
|
||||||
|
|
@ -6,6 +6,29 @@
|
||||||
namespace graphene { namespace chain {
|
namespace graphene { namespace chain {
|
||||||
using namespace graphene::db;
|
using namespace graphene::db;
|
||||||
|
|
||||||
|
class nft_lottery_balance_object : public abstract_object<nft_lottery_balance_object>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const uint8_t space_id = implementation_ids;
|
||||||
|
static const uint8_t type_id = impl_nft_lottery_balance_object_type;
|
||||||
|
|
||||||
|
// Total Progressive jackpot carried over from previous lotteries
|
||||||
|
asset total_progressive_jackpot;
|
||||||
|
// Current total jackpot in this lottery inclusive of the progressive jackpot
|
||||||
|
asset jackpot;
|
||||||
|
// Total tickets sold
|
||||||
|
share_type sweeps_tickets_sold;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nft_lottery_data
|
||||||
|
{
|
||||||
|
nft_lottery_data() {}
|
||||||
|
nft_lottery_data(const nft_lottery_options &options, nft_lottery_balance_id_type lottery_id)
|
||||||
|
: lottery_options(options), lottery_balance_id(lottery_id) {}
|
||||||
|
nft_lottery_options lottery_options;
|
||||||
|
nft_lottery_balance_id_type lottery_balance_id;
|
||||||
|
};
|
||||||
|
|
||||||
class nft_metadata_object : public abstract_object<nft_metadata_object>
|
class nft_metadata_object : public abstract_object<nft_metadata_object>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -21,6 +44,21 @@ namespace graphene { namespace chain {
|
||||||
bool is_transferable = false;
|
bool is_transferable = false;
|
||||||
bool is_sellable = true;
|
bool is_sellable = true;
|
||||||
optional<account_role_id_type> account_role;
|
optional<account_role_id_type> account_role;
|
||||||
|
share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||||
|
optional<nft_lottery_data> lottery_data;
|
||||||
|
|
||||||
|
nft_metadata_id_type get_id() const { return id; }
|
||||||
|
bool is_lottery() const { return lottery_data.valid(); }
|
||||||
|
uint32_t get_owner_num() const { return owner.instance.value; }
|
||||||
|
time_point_sec get_lottery_expiration() const;
|
||||||
|
asset get_lottery_jackpot(const database &db) const;
|
||||||
|
share_type get_token_current_supply(const database &db) const;
|
||||||
|
vector<account_id_type> get_holders(const database &db) const;
|
||||||
|
vector<uint64_t> get_ticket_ids(const database &db) const;
|
||||||
|
void distribute_benefactors_part(database &db);
|
||||||
|
map<account_id_type, vector<uint16_t>> distribute_winners_part(database &db);
|
||||||
|
void distribute_sweeps_holders_part(database &db);
|
||||||
|
void end_lottery(database &db);
|
||||||
};
|
};
|
||||||
|
|
||||||
class nft_object : public abstract_object<nft_object>
|
class nft_object : public abstract_object<nft_object>
|
||||||
|
|
@ -36,8 +74,23 @@ namespace graphene { namespace chain {
|
||||||
std::string token_uri;
|
std::string token_uri;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct nft_lottery_comparer
|
||||||
|
{
|
||||||
|
bool operator()(const nft_metadata_object& lhs, const nft_metadata_object& rhs) const
|
||||||
|
{
|
||||||
|
if ( !lhs.is_lottery() ) return false;
|
||||||
|
if ( !lhs.lottery_data->lottery_options.is_active && !rhs.is_lottery()) return true; // not active lotteries first, just assets then
|
||||||
|
if ( !lhs.lottery_data->lottery_options.is_active ) return false;
|
||||||
|
if ( lhs.lottery_data->lottery_options.is_active && ( !rhs.is_lottery() || !rhs.lottery_data->lottery_options.is_active ) ) return true;
|
||||||
|
return lhs.get_lottery_expiration() > rhs.get_lottery_expiration();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct by_name;
|
struct by_name;
|
||||||
struct by_symbol;
|
struct by_symbol;
|
||||||
|
struct active_nft_lotteries;
|
||||||
|
struct by_nft_lottery;
|
||||||
|
struct by_nft_lottery_owner;
|
||||||
using nft_metadata_multi_index_type = multi_index_container<
|
using nft_metadata_multi_index_type = multi_index_container<
|
||||||
nft_metadata_object,
|
nft_metadata_object,
|
||||||
indexed_by<
|
indexed_by<
|
||||||
|
|
@ -49,6 +102,34 @@ namespace graphene { namespace chain {
|
||||||
>,
|
>,
|
||||||
ordered_unique< tag<by_symbol>,
|
ordered_unique< tag<by_symbol>,
|
||||||
member<nft_metadata_object, std::string, &nft_metadata_object::symbol>
|
member<nft_metadata_object, std::string, &nft_metadata_object::symbol>
|
||||||
|
>,
|
||||||
|
ordered_non_unique< tag<active_nft_lotteries>,
|
||||||
|
identity< nft_metadata_object >,
|
||||||
|
nft_lottery_comparer
|
||||||
|
>,
|
||||||
|
ordered_unique< tag<by_nft_lottery>,
|
||||||
|
composite_key<
|
||||||
|
nft_metadata_object,
|
||||||
|
const_mem_fun<nft_metadata_object, bool, &nft_metadata_object::is_lottery>,
|
||||||
|
member<object, object_id_type, &object::id>
|
||||||
|
>,
|
||||||
|
composite_key_compare<
|
||||||
|
std::greater< bool >,
|
||||||
|
std::greater< object_id_type >
|
||||||
|
>
|
||||||
|
>,
|
||||||
|
ordered_unique< tag<by_nft_lottery_owner>,
|
||||||
|
composite_key<
|
||||||
|
nft_metadata_object,
|
||||||
|
const_mem_fun<nft_metadata_object, bool, &nft_metadata_object::is_lottery>,
|
||||||
|
const_mem_fun<nft_metadata_object, uint32_t, &nft_metadata_object::get_owner_num>,
|
||||||
|
member<object, object_id_type, &object::id>
|
||||||
|
>,
|
||||||
|
composite_key_compare<
|
||||||
|
std::greater< bool >,
|
||||||
|
std::greater< uint32_t >,
|
||||||
|
std::greater< object_id_type >
|
||||||
|
>
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
@ -86,8 +167,23 @@ namespace graphene { namespace chain {
|
||||||
>;
|
>;
|
||||||
using nft_index = generic_index<nft_object, nft_multi_index_type>;
|
using nft_index = generic_index<nft_object, nft_multi_index_type>;
|
||||||
|
|
||||||
|
using nft_lottery_balance_index_type = multi_index_container<
|
||||||
|
nft_lottery_balance_object,
|
||||||
|
indexed_by<
|
||||||
|
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
using nft_lottery_balance_index = generic_index<nft_lottery_balance_object, nft_lottery_balance_index_type>;
|
||||||
|
|
||||||
} } // graphene::chain
|
} } // graphene::chain
|
||||||
|
|
||||||
|
FC_REFLECT_DERIVED( graphene::chain::nft_lottery_balance_object, (graphene::db::object),
|
||||||
|
(total_progressive_jackpot)
|
||||||
|
(jackpot)
|
||||||
|
(sweeps_tickets_sold) )
|
||||||
|
|
||||||
|
FC_REFLECT( graphene::chain::nft_lottery_data, (lottery_options)(lottery_balance_id) )
|
||||||
|
|
||||||
FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object),
|
FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object),
|
||||||
(owner)
|
(owner)
|
||||||
(name)
|
(name)
|
||||||
|
|
@ -97,7 +193,9 @@ FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object)
|
||||||
(revenue_split)
|
(revenue_split)
|
||||||
(is_transferable)
|
(is_transferable)
|
||||||
(is_sellable)
|
(is_sellable)
|
||||||
(account_role) )
|
(account_role)
|
||||||
|
(max_supply)
|
||||||
|
(lottery_data) )
|
||||||
|
|
||||||
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
|
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
|
||||||
(nft_metadata_id)
|
(nft_metadata_id)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
#pragma once
|
||||||
|
#include <graphene/chain/protocol/base.hpp>
|
||||||
|
#include <graphene/chain/protocol/types.hpp>
|
||||||
|
|
||||||
|
namespace graphene
|
||||||
|
{
|
||||||
|
namespace chain
|
||||||
|
{
|
||||||
|
struct nft_lottery_token_purchase_operation : public base_operation
|
||||||
|
{
|
||||||
|
struct fee_parameters_type
|
||||||
|
{
|
||||||
|
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||||
|
};
|
||||||
|
asset fee;
|
||||||
|
// Lottery NFT Metadata
|
||||||
|
nft_metadata_id_type lottery_id;
|
||||||
|
// Buyer purchasing lottery tickets
|
||||||
|
account_id_type buyer;
|
||||||
|
// count of tickets to buy
|
||||||
|
uint64_t tickets_to_buy;
|
||||||
|
// amount that can spent
|
||||||
|
asset amount;
|
||||||
|
|
||||||
|
extensions_type extensions;
|
||||||
|
|
||||||
|
account_id_type fee_payer() const { return buyer; }
|
||||||
|
void validate() const;
|
||||||
|
share_type calculate_fee(const fee_parameters_type &k) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nft_lottery_reward_operation : public base_operation
|
||||||
|
{
|
||||||
|
struct fee_parameters_type
|
||||||
|
{
|
||||||
|
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||||
|
};
|
||||||
|
|
||||||
|
asset fee;
|
||||||
|
// Lottery NFT Metadata
|
||||||
|
nft_metadata_id_type lottery_id;
|
||||||
|
// winner account
|
||||||
|
account_id_type winner;
|
||||||
|
// amount that won
|
||||||
|
asset amount;
|
||||||
|
// percentage of jackpot that user won
|
||||||
|
uint16_t win_percentage;
|
||||||
|
// true if recieved from benefators section of lottery; false otherwise
|
||||||
|
bool is_benefactor_reward;
|
||||||
|
|
||||||
|
uint64_t winner_ticket_id;
|
||||||
|
|
||||||
|
extensions_type extensions;
|
||||||
|
|
||||||
|
account_id_type fee_payer() const { return account_id_type(); }
|
||||||
|
void validate() const {};
|
||||||
|
share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nft_lottery_end_operation : public base_operation
|
||||||
|
{
|
||||||
|
struct fee_parameters_type
|
||||||
|
{
|
||||||
|
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||||
|
};
|
||||||
|
|
||||||
|
asset fee;
|
||||||
|
// Lottery NFT Metadata
|
||||||
|
nft_metadata_id_type lottery_id;
|
||||||
|
|
||||||
|
extensions_type extensions;
|
||||||
|
|
||||||
|
account_id_type fee_payer() const { return account_id_type(); }
|
||||||
|
void validate() const {}
|
||||||
|
share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chain
|
||||||
|
} // namespace graphene
|
||||||
|
|
||||||
|
FC_REFLECT(graphene::chain::nft_lottery_token_purchase_operation::fee_parameters_type, (fee))
|
||||||
|
FC_REFLECT(graphene::chain::nft_lottery_reward_operation::fee_parameters_type, (fee))
|
||||||
|
FC_REFLECT(graphene::chain::nft_lottery_end_operation::fee_parameters_type, (fee))
|
||||||
|
FC_REFLECT(graphene::chain::nft_lottery_token_purchase_operation, (fee)(lottery_id)(buyer)(tickets_to_buy)(amount)(extensions))
|
||||||
|
FC_REFLECT(graphene::chain::nft_lottery_reward_operation, (fee)(lottery_id)(winner)(amount)(win_percentage)(is_benefactor_reward)(winner_ticket_id)(extensions))
|
||||||
|
FC_REFLECT(graphene::chain::nft_lottery_end_operation, (fee)(lottery_id)(extensions))
|
||||||
|
|
@ -4,6 +4,29 @@
|
||||||
|
|
||||||
namespace graphene { namespace chain {
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
struct nft_lottery_benefactor {
|
||||||
|
account_id_type id;
|
||||||
|
uint16_t share; // percent * GRAPHENE_1_PERCENT
|
||||||
|
nft_lottery_benefactor() = default;
|
||||||
|
nft_lottery_benefactor( const nft_lottery_benefactor & ) = default;
|
||||||
|
nft_lottery_benefactor( account_id_type _id, uint16_t _share ) : id( _id ), share( _share ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nft_lottery_options
|
||||||
|
{
|
||||||
|
std::vector<nft_lottery_benefactor> benefactors;
|
||||||
|
// specifying winning tickets as shares that will be issued
|
||||||
|
std::vector<uint16_t> winning_tickets;
|
||||||
|
asset ticket_price;
|
||||||
|
time_point_sec end_date;
|
||||||
|
bool ending_on_soldout;
|
||||||
|
bool is_active;
|
||||||
|
bool delete_tickets_after_draw = false;
|
||||||
|
std::vector<nft_metadata_id_type> progressive_jackpots;
|
||||||
|
|
||||||
|
void validate() const;
|
||||||
|
};
|
||||||
|
|
||||||
struct nft_metadata_create_operation : public base_operation
|
struct nft_metadata_create_operation : public base_operation
|
||||||
{
|
{
|
||||||
struct fee_parameters_type
|
struct fee_parameters_type
|
||||||
|
|
@ -23,6 +46,10 @@ namespace graphene { namespace chain {
|
||||||
bool is_sellable = true;
|
bool is_sellable = true;
|
||||||
// Accounts Role
|
// Accounts Role
|
||||||
optional<account_role_id_type> account_role;
|
optional<account_role_id_type> account_role;
|
||||||
|
// Max number of NFTs that can be minted from the metadata
|
||||||
|
optional<share_type> max_supply;
|
||||||
|
// Lottery configuration
|
||||||
|
optional<nft_lottery_options> lottery_options;
|
||||||
extensions_type extensions;
|
extensions_type extensions;
|
||||||
|
|
||||||
account_id_type fee_payer()const { return owner; }
|
account_id_type fee_payer()const { return owner; }
|
||||||
|
|
@ -133,6 +160,9 @@ namespace graphene { namespace chain {
|
||||||
|
|
||||||
} } // graphene::chain
|
} } // graphene::chain
|
||||||
|
|
||||||
|
FC_REFLECT( graphene::chain::nft_lottery_benefactor, (id)(share) )
|
||||||
|
FC_REFLECT( graphene::chain::nft_lottery_options, (benefactors)(winning_tickets)(ticket_price)(end_date)(ending_on_soldout)(is_active)(delete_tickets_after_draw)(progressive_jackpots) )
|
||||||
|
|
||||||
FC_REFLECT( graphene::chain::nft_metadata_create_operation::fee_parameters_type, (fee) (price_per_kbyte) )
|
FC_REFLECT( graphene::chain::nft_metadata_create_operation::fee_parameters_type, (fee) (price_per_kbyte) )
|
||||||
FC_REFLECT( graphene::chain::nft_metadata_update_operation::fee_parameters_type, (fee) )
|
FC_REFLECT( graphene::chain::nft_metadata_update_operation::fee_parameters_type, (fee) )
|
||||||
FC_REFLECT( graphene::chain::nft_mint_operation::fee_parameters_type, (fee) (price_per_kbyte) )
|
FC_REFLECT( graphene::chain::nft_mint_operation::fee_parameters_type, (fee) (price_per_kbyte) )
|
||||||
|
|
@ -140,7 +170,7 @@ 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_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_set_approval_for_all_operation::fee_parameters_type, (fee) )
|
||||||
|
|
||||||
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
|
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (max_supply) (lottery_options) (extensions) )
|
||||||
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
|
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
|
||||||
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) )
|
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) )
|
||||||
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) )
|
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) )
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,14 @@
|
||||||
#include <graphene/chain/protocol/offer.hpp>
|
#include <graphene/chain/protocol/offer.hpp>
|
||||||
#include <graphene/chain/protocol/nft_ops.hpp>
|
#include <graphene/chain/protocol/nft_ops.hpp>
|
||||||
#include <graphene/chain/protocol/account_role.hpp>
|
#include <graphene/chain/protocol/account_role.hpp>
|
||||||
|
#include <graphene/chain/protocol/nft_lottery.hpp>
|
||||||
#include <graphene/chain/protocol/son.hpp>
|
#include <graphene/chain/protocol/son.hpp>
|
||||||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||||
#include <graphene/chain/protocol/son_wallet_deposit.hpp>
|
#include <graphene/chain/protocol/son_wallet_deposit.hpp>
|
||||||
#include <graphene/chain/protocol/son_wallet_withdraw.hpp>
|
#include <graphene/chain/protocol/son_wallet_withdraw.hpp>
|
||||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||||
|
#include <graphene/chain/protocol/random_number.hpp>
|
||||||
|
|
||||||
namespace graphene { namespace chain {
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
|
@ -184,7 +186,11 @@ namespace graphene { namespace chain {
|
||||||
sidechain_transaction_create_operation,
|
sidechain_transaction_create_operation,
|
||||||
sidechain_transaction_sign_operation,
|
sidechain_transaction_sign_operation,
|
||||||
sidechain_transaction_send_operation,
|
sidechain_transaction_send_operation,
|
||||||
sidechain_transaction_settle_operation
|
sidechain_transaction_settle_operation,
|
||||||
|
nft_lottery_token_purchase_operation,
|
||||||
|
nft_lottery_reward_operation,
|
||||||
|
nft_lottery_end_operation,
|
||||||
|
random_number_store_operation
|
||||||
> operation;
|
> operation;
|
||||||
|
|
||||||
/// @} // operations group
|
/// @} // operations group
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
struct random_number_store_operation : public base_operation
|
||||||
|
{
|
||||||
|
struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||||
|
|
||||||
|
asset fee;
|
||||||
|
|
||||||
|
account_id_type account;
|
||||||
|
vector<uint64_t> random_number;
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
account_id_type fee_payer()const { return account; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} } // graphene::chain
|
||||||
|
|
||||||
|
FC_REFLECT( graphene::chain::random_number_store_operation::fee_parameters_type, (fee) )
|
||||||
|
FC_REFLECT( graphene::chain::random_number_store_operation, (fee)
|
||||||
|
(account)
|
||||||
|
(random_number)
|
||||||
|
(data) )
|
||||||
|
|
||||||
|
|
@ -183,6 +183,7 @@ namespace graphene { namespace chain {
|
||||||
son_wallet_withdraw_object_type,
|
son_wallet_withdraw_object_type,
|
||||||
sidechain_address_object_type,
|
sidechain_address_object_type,
|
||||||
sidechain_transaction_object_type,
|
sidechain_transaction_object_type,
|
||||||
|
random_number_object_type,
|
||||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -214,7 +215,8 @@ namespace graphene { namespace chain {
|
||||||
impl_sweeps_vesting_balance_object_type,
|
impl_sweeps_vesting_balance_object_type,
|
||||||
impl_offer_history_object_type,
|
impl_offer_history_object_type,
|
||||||
impl_son_statistics_object_type,
|
impl_son_statistics_object_type,
|
||||||
impl_son_schedule_object_type
|
impl_son_schedule_object_type,
|
||||||
|
impl_nft_lottery_balance_object_type
|
||||||
};
|
};
|
||||||
|
|
||||||
//typedef fc::unsigned_int object_id_type;
|
//typedef fc::unsigned_int object_id_type;
|
||||||
|
|
@ -258,6 +260,7 @@ namespace graphene { namespace chain {
|
||||||
class son_wallet_withdraw_object;
|
class son_wallet_withdraw_object;
|
||||||
class sidechain_address_object;
|
class sidechain_address_object;
|
||||||
class sidechain_transaction_object;
|
class sidechain_transaction_object;
|
||||||
|
class random_number_object;
|
||||||
|
|
||||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||||
|
|
@ -297,6 +300,7 @@ namespace graphene { namespace chain {
|
||||||
typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type;
|
typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type;
|
||||||
typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type;
|
typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type;
|
||||||
typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type;
|
typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type;
|
||||||
|
typedef object_id< protocol_ids, random_number_object_type, random_number_object> random_number_id_type;
|
||||||
|
|
||||||
// implementation types
|
// implementation types
|
||||||
class global_property_object;
|
class global_property_object;
|
||||||
|
|
@ -321,6 +325,7 @@ namespace graphene { namespace chain {
|
||||||
class lottery_balance_object;
|
class lottery_balance_object;
|
||||||
class sweeps_vesting_balance_object;
|
class sweeps_vesting_balance_object;
|
||||||
class offer_history_object;
|
class offer_history_object;
|
||||||
|
class nft_lottery_balance_object;
|
||||||
class son_statistics_object;
|
class son_statistics_object;
|
||||||
class son_schedule_object;
|
class son_schedule_object;
|
||||||
|
|
||||||
|
|
@ -352,6 +357,7 @@ namespace graphene { namespace chain {
|
||||||
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
||||||
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
||||||
typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type;
|
typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type;
|
||||||
|
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_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_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
|
||||||
|
|
||||||
|
|
@ -496,6 +502,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
||||||
(son_wallet_withdraw_object_type)
|
(son_wallet_withdraw_object_type)
|
||||||
(sidechain_address_object_type)
|
(sidechain_address_object_type)
|
||||||
(sidechain_transaction_object_type)
|
(sidechain_transaction_object_type)
|
||||||
|
(random_number_object_type)
|
||||||
(OBJECT_TYPE_COUNT)
|
(OBJECT_TYPE_COUNT)
|
||||||
)
|
)
|
||||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||||
|
|
@ -526,6 +533,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||||
(impl_offer_history_object_type)
|
(impl_offer_history_object_type)
|
||||||
(impl_son_statistics_object_type)
|
(impl_son_statistics_object_type)
|
||||||
(impl_son_schedule_object_type)
|
(impl_son_schedule_object_type)
|
||||||
|
(impl_nft_lottery_balance_object_type)
|
||||||
)
|
)
|
||||||
|
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||||
|
|
@ -575,6 +583,7 @@ FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type )
|
||||||
|
FC_REFLECT_TYPENAME( graphene::chain::nft_lottery_balance_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
||||||
|
|
@ -582,7 +591,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
|
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_address_id_type )
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
|
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
|
||||||
|
FC_REFLECT_TYPENAME( graphene::chain::random_number_id_type )
|
||||||
|
|
||||||
FC_REFLECT( graphene::chain::void_t, )
|
FC_REFLECT( graphene::chain::void_t, )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include <graphene/chain/database.hpp>
|
||||||
|
#include <graphene/chain/evaluator.hpp>
|
||||||
|
#include <graphene/chain/protocol/operations.hpp>
|
||||||
|
#include <graphene/chain/protocol/types.hpp>
|
||||||
|
|
||||||
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
class random_number_store_evaluator : public evaluator<random_number_store_evaluator>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef random_number_store_operation operation_type;
|
||||||
|
|
||||||
|
void_result do_evaluate( const random_number_store_operation& o );
|
||||||
|
object_id_type do_apply( const random_number_store_operation& o );
|
||||||
|
};
|
||||||
|
|
||||||
|
} } // graphene::chain
|
||||||
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace graphene { namespace chain {
|
||||||
|
using namespace graphene::db;
|
||||||
|
|
||||||
|
class random_number_object : public abstract_object<random_number_object>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const uint8_t space_id = protocol_ids;
|
||||||
|
static const uint8_t type_id = random_number_object_type;
|
||||||
|
|
||||||
|
account_id_type account; /* account who requested random number */
|
||||||
|
time_point_sec timestamp; /* date and time when the number is read */
|
||||||
|
vector<uint64_t> random_number; /* random number(s) */
|
||||||
|
std::string data; /* custom data in json format */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct by_account;
|
||||||
|
struct by_timestamp;
|
||||||
|
using random_number_multi_index_type = multi_index_container<
|
||||||
|
random_number_object,
|
||||||
|
indexed_by<
|
||||||
|
ordered_unique< tag<by_id>,
|
||||||
|
member<object, object_id_type, &object::id>
|
||||||
|
>,
|
||||||
|
ordered_non_unique< tag<by_account>,
|
||||||
|
member<random_number_object, account_id_type, &random_number_object::account>
|
||||||
|
>,
|
||||||
|
ordered_non_unique< tag<by_timestamp>,
|
||||||
|
member<random_number_object, time_point_sec, &random_number_object::timestamp>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
using random_number_index = generic_index<random_number_object, random_number_multi_index_type>;
|
||||||
|
|
||||||
|
} } // graphene::chain
|
||||||
|
|
||||||
|
FC_REFLECT_DERIVED( graphene::chain::random_number_object, (graphene::db::object),
|
||||||
|
(account) (timestamp)
|
||||||
|
(random_number) (data) )
|
||||||
|
|
||||||
|
|
@ -57,6 +57,9 @@ namespace graphene
|
||||||
case operation::tag<account_role_create_operation>::value:
|
case operation::tag<account_role_create_operation>::value:
|
||||||
case operation::tag<account_role_update_operation>::value:
|
case operation::tag<account_role_update_operation>::value:
|
||||||
case operation::tag<account_role_delete_operation>::value:
|
case operation::tag<account_role_delete_operation>::value:
|
||||||
|
case operation::tag<nft_lottery_token_purchase_operation>::value:
|
||||||
|
case operation::tag<nft_lottery_reward_operation>::value:
|
||||||
|
case operation::tag<nft_lottery_end_operation>::value:
|
||||||
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!");
|
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,26 @@ void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_creat
|
||||||
const auto& ar_obj = (*op.account_role)(db());
|
const auto& ar_obj = (*op.account_role)(db());
|
||||||
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
|
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lottery Related
|
||||||
|
if (!op.lottery_options) {
|
||||||
|
return void_result();
|
||||||
|
}
|
||||||
|
FC_ASSERT((*op.lottery_options).end_date > now || (*op.lottery_options).end_date == time_point_sec());
|
||||||
|
if (op.max_supply) {
|
||||||
|
FC_ASSERT(*op.max_supply >= 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto lottery_id: (*op.lottery_options).progressive_jackpots) {
|
||||||
|
const auto& lottery_obj = lottery_id(db());
|
||||||
|
FC_ASSERT(lottery_obj.owner == op.owner, "Only the Owner can attach progressive jackpots");
|
||||||
|
FC_ASSERT(lottery_obj.is_lottery(), "Only lottery objects can be attached as progressive jackpots");
|
||||||
|
FC_ASSERT(lottery_obj.lottery_data->lottery_options.is_active == false, "Lottery should not be active");
|
||||||
|
FC_ASSERT(lottery_obj.lottery_data->lottery_options.ticket_price.asset_id == (*op.lottery_options).ticket_price.asset_id, "Lottery asset type should be same");
|
||||||
|
const auto& lottery_balance_obj = lottery_obj.lottery_data->lottery_balance_id(db());
|
||||||
|
FC_ASSERT(lottery_balance_obj.jackpot.amount > 0, "Non zero progressive jackpot not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
return void_result();
|
return void_result();
|
||||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||||
|
|
||||||
|
|
@ -39,6 +59,26 @@ object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_creat
|
||||||
obj.is_transferable = op.is_transferable;
|
obj.is_transferable = op.is_transferable;
|
||||||
obj.is_sellable = op.is_sellable;
|
obj.is_sellable = op.is_sellable;
|
||||||
obj.account_role = op.account_role;
|
obj.account_role = op.account_role;
|
||||||
|
if (op.max_supply) {
|
||||||
|
obj.max_supply = *op.max_supply;
|
||||||
|
}
|
||||||
|
if (op.lottery_options) {
|
||||||
|
asset jackpot_sum(0,(*op.lottery_options).ticket_price.asset_id);
|
||||||
|
for(auto lottery_id: (*op.lottery_options).progressive_jackpots) {
|
||||||
|
const auto& lottery_obj = lottery_id(db());
|
||||||
|
const auto& lottery_balance_obj = lottery_obj.lottery_data->lottery_balance_id(db());
|
||||||
|
FC_ASSERT(lottery_balance_obj.jackpot.amount > 0, "Non zero progressive jackpot not allowed");
|
||||||
|
db().modify(lottery_balance_obj, [&] ( nft_lottery_balance_object& nlbo ) {
|
||||||
|
jackpot_sum += nlbo.jackpot;
|
||||||
|
nlbo.jackpot -= nlbo.jackpot;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const auto& new_lottery_balance_obj = db().create<nft_lottery_balance_object>([&](nft_lottery_balance_object& nlbo) {
|
||||||
|
nlbo.total_progressive_jackpot = jackpot_sum;
|
||||||
|
nlbo.jackpot = jackpot_sum;
|
||||||
|
});
|
||||||
|
obj.lottery_data = nft_lottery_data(*op.lottery_options, new_lottery_balance_obj.id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return new_nft_metadata_object.id;
|
return new_nft_metadata_object.id;
|
||||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||||
|
|
@ -110,6 +150,7 @@ void_result nft_mint_evaluator::do_evaluate( const nft_mint_operation& op )
|
||||||
FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" );
|
FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" );
|
||||||
FC_ASSERT( itr_nft_md->owner == op.payer, "Only metadata owner can mint NFT" );
|
FC_ASSERT( itr_nft_md->owner == op.payer, "Only metadata owner can mint NFT" );
|
||||||
|
|
||||||
|
FC_ASSERT(itr_nft_md->get_token_current_supply(db()) < itr_nft_md->max_supply, "NFTs can't be minted more than max_supply");
|
||||||
return void_result();
|
return void_result();
|
||||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||||
|
|
||||||
|
|
|
||||||
145
libraries/chain/nft_lottery_evaluator.cpp
Normal file
145
libraries/chain/nft_lottery_evaluator.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include <graphene/chain/nft_lottery_evaluator.hpp>
|
||||||
|
#include <graphene/chain/nft_object.hpp>
|
||||||
|
#include <graphene/chain/protocol/operations.hpp>
|
||||||
|
#include <graphene/chain/account_role_object.hpp>
|
||||||
|
#include <graphene/chain/hardfork.hpp>
|
||||||
|
|
||||||
|
namespace graphene
|
||||||
|
{
|
||||||
|
namespace chain
|
||||||
|
{
|
||||||
|
void_result nft_lottery_token_purchase_evaluator::do_evaluate(const nft_lottery_token_purchase_operation &op)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const database &d = db();
|
||||||
|
auto now = d.head_block_time();
|
||||||
|
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
|
||||||
|
op.buyer(d);
|
||||||
|
const auto &lottery_md_obj = op.lottery_id(d);
|
||||||
|
FC_ASSERT(lottery_md_obj.is_lottery(), "Not a lottery type");
|
||||||
|
if (lottery_md_obj.account_role)
|
||||||
|
{
|
||||||
|
const auto &ar_idx = d.get_index_type<account_role_index>().indices().get<by_id>();
|
||||||
|
auto ar_itr = ar_idx.find(*lottery_md_obj.account_role);
|
||||||
|
if (ar_itr != ar_idx.end())
|
||||||
|
{
|
||||||
|
FC_ASSERT(d.account_role_valid(*ar_itr, op.buyer, get_type()), "Account role not valid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lottery_options = lottery_md_obj.lottery_data->lottery_options;
|
||||||
|
FC_ASSERT(lottery_options.ticket_price.asset_id == op.amount.asset_id);
|
||||||
|
FC_ASSERT((double)op.amount.amount.value / lottery_options.ticket_price.amount.value == (double)op.tickets_to_buy);
|
||||||
|
return void_result();
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((op))
|
||||||
|
}
|
||||||
|
|
||||||
|
object_id_type nft_lottery_token_purchase_evaluator::do_apply(const nft_lottery_token_purchase_operation &op)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
transaction_evaluation_state nft_mint_context(&db());
|
||||||
|
nft_mint_context.skip_fee_schedule_check = true;
|
||||||
|
const auto &lottery_md_obj = op.lottery_id(db());
|
||||||
|
nft_id_type nft_id;
|
||||||
|
for (size_t i = 0; i < op.tickets_to_buy; i++)
|
||||||
|
{
|
||||||
|
nft_mint_operation mint_op;
|
||||||
|
mint_op.payer = lottery_md_obj.owner;
|
||||||
|
mint_op.nft_metadata_id = lottery_md_obj.id;
|
||||||
|
mint_op.owner = op.buyer;
|
||||||
|
nft_id = db().apply_operation(nft_mint_context, mint_op).get<object_id_type>();
|
||||||
|
}
|
||||||
|
db().adjust_balance(op.buyer, -op.amount);
|
||||||
|
db().modify(lottery_md_obj.lottery_data->lottery_balance_id(db()), [&](nft_lottery_balance_object &obj) {
|
||||||
|
obj.jackpot += op.amount;
|
||||||
|
});
|
||||||
|
return nft_id;
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((op))
|
||||||
|
}
|
||||||
|
|
||||||
|
void_result nft_lottery_reward_evaluator::do_evaluate(const nft_lottery_reward_operation &op)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const database &d = db();
|
||||||
|
auto now = d.head_block_time();
|
||||||
|
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
|
||||||
|
op.winner(d);
|
||||||
|
|
||||||
|
const auto &lottery_md_obj = op.lottery_id(d);
|
||||||
|
FC_ASSERT(lottery_md_obj.is_lottery());
|
||||||
|
|
||||||
|
const auto &lottery_options = lottery_md_obj.lottery_data->lottery_options;
|
||||||
|
FC_ASSERT(lottery_options.is_active);
|
||||||
|
FC_ASSERT(lottery_md_obj.get_lottery_jackpot(d) >= op.amount);
|
||||||
|
return void_result();
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((op))
|
||||||
|
}
|
||||||
|
|
||||||
|
void_result nft_lottery_reward_evaluator::do_apply(const nft_lottery_reward_operation &op)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &lottery_md_obj = op.lottery_id(db());
|
||||||
|
db().adjust_balance(op.winner, op.amount);
|
||||||
|
db().modify(lottery_md_obj.lottery_data->lottery_balance_id(db()), [&](nft_lottery_balance_object &obj) {
|
||||||
|
obj.jackpot -= op.amount;
|
||||||
|
});
|
||||||
|
return void_result();
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((op))
|
||||||
|
}
|
||||||
|
|
||||||
|
void_result nft_lottery_end_evaluator::do_evaluate(const nft_lottery_end_operation &op)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const database &d = db();
|
||||||
|
auto now = d.head_block_time();
|
||||||
|
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
|
||||||
|
const auto &lottery_md_obj = op.lottery_id(d);
|
||||||
|
FC_ASSERT(lottery_md_obj.is_lottery());
|
||||||
|
|
||||||
|
const auto &lottery_options = lottery_md_obj.lottery_data->lottery_options;
|
||||||
|
FC_ASSERT(lottery_options.is_active);
|
||||||
|
FC_ASSERT(lottery_md_obj.get_lottery_jackpot(d).amount == 0);
|
||||||
|
return void_result();
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((op))
|
||||||
|
}
|
||||||
|
|
||||||
|
void_result nft_lottery_end_evaluator::do_apply(const nft_lottery_end_operation &op)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &lottery_md_obj = op.lottery_id(db());
|
||||||
|
db().modify(lottery_md_obj, [&](nft_metadata_object &obj) {
|
||||||
|
obj.lottery_data->lottery_options.is_active = false;
|
||||||
|
});
|
||||||
|
db().modify(lottery_md_obj.lottery_data->lottery_balance_id(db()), [&](nft_lottery_balance_object &obj) {
|
||||||
|
obj.sweeps_tickets_sold = lottery_md_obj.get_token_current_supply(db());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lottery_md_obj.lottery_data->lottery_options.delete_tickets_after_draw)
|
||||||
|
{
|
||||||
|
const auto &nft_index_by_md = db().get_index_type<nft_index>().indices().get<by_metadata>();
|
||||||
|
auto delete_nft_itr = nft_index_by_md.lower_bound(op.lottery_id);
|
||||||
|
while (delete_nft_itr != nft_index_by_md.end() && delete_nft_itr->nft_metadata_id == op.lottery_id)
|
||||||
|
{
|
||||||
|
const nft_object &nft_obj = *delete_nft_itr;
|
||||||
|
++delete_nft_itr;
|
||||||
|
db().remove(nft_obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return void_result();
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((op))
|
||||||
|
}
|
||||||
|
} // namespace chain
|
||||||
|
} // namespace graphene
|
||||||
171
libraries/chain/nft_lottery_object.cpp
Normal file
171
libraries/chain/nft_lottery_object.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
#include <graphene/chain/database.hpp>
|
||||||
|
#include <graphene/chain/nft_object.hpp>
|
||||||
|
|
||||||
|
namespace graphene
|
||||||
|
{
|
||||||
|
namespace chain
|
||||||
|
{
|
||||||
|
time_point_sec nft_metadata_object::get_lottery_expiration() const
|
||||||
|
{
|
||||||
|
if (lottery_data)
|
||||||
|
return lottery_data->lottery_options.end_date;
|
||||||
|
return time_point_sec();
|
||||||
|
}
|
||||||
|
|
||||||
|
asset nft_metadata_object::get_lottery_jackpot(const database &db) const
|
||||||
|
{
|
||||||
|
if (lottery_data)
|
||||||
|
return lottery_data->lottery_balance_id(db).jackpot;
|
||||||
|
return asset();
|
||||||
|
}
|
||||||
|
|
||||||
|
share_type nft_metadata_object::get_token_current_supply(const database &db) const
|
||||||
|
{
|
||||||
|
share_type current_supply;
|
||||||
|
const auto &idx_lottery_by_md = db.get_index_type<nft_index>().indices().get<by_metadata>();
|
||||||
|
auto lottery_range = idx_lottery_by_md.equal_range(id);
|
||||||
|
current_supply = std::distance(lottery_range.first, lottery_range.second);
|
||||||
|
return current_supply;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<account_id_type> nft_metadata_object::get_holders(const database &db) const
|
||||||
|
{
|
||||||
|
const auto &idx_lottery_by_md = db.get_index_type<nft_index>().indices().get<by_metadata>();
|
||||||
|
auto lottery_range = idx_lottery_by_md.equal_range(id);
|
||||||
|
vector<account_id_type> holders;
|
||||||
|
holders.reserve(std::distance(lottery_range.first, lottery_range.second));
|
||||||
|
std::for_each(lottery_range.first, lottery_range.second,
|
||||||
|
[&](const nft_object &ticket) {
|
||||||
|
holders.emplace_back(ticket.owner);
|
||||||
|
});
|
||||||
|
return holders;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uint64_t> nft_metadata_object::get_ticket_ids(const database &db) const
|
||||||
|
{
|
||||||
|
const auto &idx_lottery_by_md = db.get_index_type<nft_index>().indices().get<by_metadata>();
|
||||||
|
auto lottery_range = idx_lottery_by_md.equal_range(id);
|
||||||
|
vector<uint64_t> tickets;
|
||||||
|
tickets.reserve(std::distance(lottery_range.first, lottery_range.second));
|
||||||
|
std::for_each(lottery_range.first, lottery_range.second,
|
||||||
|
[&](const nft_object &ticket) {
|
||||||
|
tickets.emplace_back(ticket.id.instance());
|
||||||
|
});
|
||||||
|
return tickets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nft_metadata_object::distribute_benefactors_part(database &db)
|
||||||
|
{
|
||||||
|
transaction_evaluation_state eval(&db);
|
||||||
|
const auto &lottery_options = lottery_data->lottery_options;
|
||||||
|
share_type jackpot = lottery_options.ticket_price.amount * get_token_current_supply(db) + lottery_data->lottery_balance_id(db).total_progressive_jackpot.amount;
|
||||||
|
|
||||||
|
for (auto benefactor : lottery_options.benefactors)
|
||||||
|
{
|
||||||
|
nft_lottery_reward_operation reward_op;
|
||||||
|
reward_op.lottery_id = id;
|
||||||
|
reward_op.winner = benefactor.id;
|
||||||
|
reward_op.is_benefactor_reward = true;
|
||||||
|
reward_op.win_percentage = benefactor.share;
|
||||||
|
reward_op.amount = asset(jackpot.value * benefactor.share / GRAPHENE_100_PERCENT, lottery_options.ticket_price.asset_id);
|
||||||
|
db.apply_operation(eval, reward_op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map<account_id_type, vector<uint16_t>> nft_metadata_object::distribute_winners_part(database &db)
|
||||||
|
{
|
||||||
|
transaction_evaluation_state eval(&db);
|
||||||
|
auto current_supply = get_token_current_supply(db);
|
||||||
|
auto &lottery_options = lottery_data->lottery_options;
|
||||||
|
|
||||||
|
auto holders = get_holders(db);
|
||||||
|
vector<uint64_t> ticket_ids = get_ticket_ids(db);
|
||||||
|
FC_ASSERT(current_supply.value == (int64_t)holders.size());
|
||||||
|
FC_ASSERT(get_lottery_jackpot(db).amount.value == current_supply.value * lottery_options.ticket_price.amount.value);
|
||||||
|
map<account_id_type, vector<uint16_t>> structurized_participants;
|
||||||
|
for (account_id_type holder : holders)
|
||||||
|
{
|
||||||
|
if (!structurized_participants.count(holder))
|
||||||
|
structurized_participants.emplace(holder, vector<uint16_t>());
|
||||||
|
}
|
||||||
|
uint64_t jackpot = get_lottery_jackpot(db).amount.value;
|
||||||
|
auto selections = lottery_options.winning_tickets.size() <= holders.size() ? lottery_options.winning_tickets.size() : holders.size();
|
||||||
|
auto winner_numbers = db.get_random_numbers(0, holders.size(), selections, false);
|
||||||
|
|
||||||
|
auto &tickets(lottery_options.winning_tickets);
|
||||||
|
|
||||||
|
if (holders.size() < tickets.size())
|
||||||
|
{
|
||||||
|
uint16_t percents_to_distribute = 0;
|
||||||
|
for (auto i = tickets.begin() + holders.size(); i != tickets.end();)
|
||||||
|
{
|
||||||
|
percents_to_distribute += *i;
|
||||||
|
i = tickets.erase(i);
|
||||||
|
}
|
||||||
|
for (auto t = tickets.begin(); t != tickets.begin() + holders.size(); ++t)
|
||||||
|
*t += percents_to_distribute / holders.size();
|
||||||
|
}
|
||||||
|
auto sweeps_distribution_percentage = db.get_global_properties().parameters.sweeps_distribution_percentage();
|
||||||
|
for (size_t c = 0; c < winner_numbers.size(); ++c)
|
||||||
|
{
|
||||||
|
auto winner_num = winner_numbers[c];
|
||||||
|
nft_lottery_reward_operation reward_op;
|
||||||
|
reward_op.lottery_id = id;
|
||||||
|
reward_op.is_benefactor_reward = false;
|
||||||
|
reward_op.winner = holders[winner_num];
|
||||||
|
if (ticket_ids.size() > winner_num)
|
||||||
|
{
|
||||||
|
reward_op.winner_ticket_id = ticket_ids[winner_num];
|
||||||
|
}
|
||||||
|
reward_op.win_percentage = tickets[c];
|
||||||
|
reward_op.amount = asset(jackpot * tickets[c] * (1. - sweeps_distribution_percentage / (double)GRAPHENE_100_PERCENT) / GRAPHENE_100_PERCENT, lottery_options.ticket_price.asset_id);
|
||||||
|
db.apply_operation(eval, reward_op);
|
||||||
|
|
||||||
|
structurized_participants[holders[winner_num]].push_back(tickets[c]);
|
||||||
|
}
|
||||||
|
return structurized_participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nft_metadata_object::distribute_sweeps_holders_part(database &db)
|
||||||
|
{
|
||||||
|
transaction_evaluation_state eval(&db);
|
||||||
|
auto &asset_bal_idx = db.get_index_type<account_balance_index>().indices().get<by_asset_balance>();
|
||||||
|
auto sweeps_params = db.get_global_properties().parameters;
|
||||||
|
uint64_t distribution_asset_supply = sweeps_params.sweeps_distribution_asset()(db).dynamic_data(db).current_supply.value;
|
||||||
|
const auto range = asset_bal_idx.equal_range(boost::make_tuple(sweeps_params.sweeps_distribution_asset()));
|
||||||
|
asset remaining_jackpot = get_lottery_jackpot(db);
|
||||||
|
uint64_t holders_sum = 0;
|
||||||
|
for (const account_balance_object &holder_balance : boost::make_iterator_range(range.first, range.second))
|
||||||
|
{
|
||||||
|
int64_t holder_part = remaining_jackpot.amount.value / (double)distribution_asset_supply * holder_balance.balance.value * SWEEPS_VESTING_BALANCE_MULTIPLIER;
|
||||||
|
db.adjust_sweeps_vesting_balance(holder_balance.owner, holder_part);
|
||||||
|
holders_sum += holder_part;
|
||||||
|
}
|
||||||
|
uint64_t balance_rest = remaining_jackpot.amount.value * SWEEPS_VESTING_BALANCE_MULTIPLIER - holders_sum;
|
||||||
|
db.adjust_sweeps_vesting_balance(sweeps_params.sweeps_vesting_accumulator_account(), balance_rest);
|
||||||
|
db.modify(lottery_data->lottery_balance_id(db), [&](nft_lottery_balance_object &obj) {
|
||||||
|
obj.jackpot -= remaining_jackpot;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void nft_metadata_object::end_lottery(database &db)
|
||||||
|
{
|
||||||
|
transaction_evaluation_state eval(&db);
|
||||||
|
const auto &lottery_options = lottery_data->lottery_options;
|
||||||
|
|
||||||
|
FC_ASSERT(is_lottery());
|
||||||
|
FC_ASSERT(lottery_options.is_active && (lottery_options.end_date <= db.head_block_time() || lottery_options.ending_on_soldout));
|
||||||
|
|
||||||
|
auto participants = distribute_winners_part(db);
|
||||||
|
if (participants.size() > 0)
|
||||||
|
{
|
||||||
|
distribute_benefactors_part(db);
|
||||||
|
distribute_sweeps_holders_part(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
nft_lottery_end_operation end_op;
|
||||||
|
end_op.lottery_id = get_id();
|
||||||
|
db.apply_operation(eval, end_op);
|
||||||
|
}
|
||||||
|
} // namespace chain
|
||||||
|
} // namespace graphene
|
||||||
|
|
@ -219,6 +219,18 @@ struct proposal_operation_hardfork_visitor
|
||||||
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" );
|
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void operator()(const nft_lottery_token_purchase_operation &v) const {
|
||||||
|
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_lottery_token_purchase_operation not allowed yet!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const nft_lottery_reward_operation &v) const {
|
||||||
|
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_lottery_reward_operation not allowed yet!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const nft_lottery_end_operation &v) const {
|
||||||
|
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_lottery_end_operation not allowed yet!" );
|
||||||
|
}
|
||||||
|
|
||||||
void operator()(const son_create_operation &v) const {
|
void operator()(const son_create_operation &v) const {
|
||||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" );
|
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ void nft_metadata_create_operation::validate() const
|
||||||
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||||
FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided");
|
FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided");
|
||||||
FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided");
|
FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided");
|
||||||
|
if (lottery_options)
|
||||||
|
{
|
||||||
|
(*lottery_options).validate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nft_metadata_update_operation::validate() const
|
void nft_metadata_update_operation::validate() const
|
||||||
|
|
|
||||||
38
libraries/chain/protocol/nft_lottery.cpp
Normal file
38
libraries/chain/protocol/nft_lottery.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include <graphene/chain/protocol/nft_ops.hpp>
|
||||||
|
#include <graphene/chain/protocol/nft_lottery.hpp>
|
||||||
|
#include <graphene/chain/protocol/operations.hpp>
|
||||||
|
|
||||||
|
namespace graphene
|
||||||
|
{
|
||||||
|
namespace chain
|
||||||
|
{
|
||||||
|
|
||||||
|
void nft_lottery_options::validate() const
|
||||||
|
{
|
||||||
|
FC_ASSERT(winning_tickets.size() <= 64);
|
||||||
|
FC_ASSERT(ticket_price.amount >= 1);
|
||||||
|
uint16_t total = 0;
|
||||||
|
for (auto benefactor : benefactors)
|
||||||
|
{
|
||||||
|
total += benefactor.share;
|
||||||
|
}
|
||||||
|
for (auto share : winning_tickets)
|
||||||
|
{
|
||||||
|
total += share;
|
||||||
|
}
|
||||||
|
FC_ASSERT(total == GRAPHENE_100_PERCENT, "distribution amount not equals GRAPHENE_100_PERCENT");
|
||||||
|
FC_ASSERT(ending_on_soldout == true || end_date != time_point_sec(), "lottery may not end");
|
||||||
|
}
|
||||||
|
|
||||||
|
share_type nft_lottery_token_purchase_operation::calculate_fee(const fee_parameters_type &k) const
|
||||||
|
{
|
||||||
|
return k.fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nft_lottery_token_purchase_operation::validate() const
|
||||||
|
{
|
||||||
|
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||||
|
FC_ASSERT(tickets_to_buy > 0);
|
||||||
|
}
|
||||||
|
} // namespace chain
|
||||||
|
} // namespace graphene
|
||||||
24
libraries/chain/random_number_evaluator.cpp
Normal file
24
libraries/chain/random_number_evaluator.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include <graphene/chain/random_number_evaluator.hpp>
|
||||||
|
#include <graphene/chain/random_number_object.hpp>
|
||||||
|
|
||||||
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
void_result random_number_store_evaluator::do_evaluate( const random_number_store_operation& op )
|
||||||
|
{ try {
|
||||||
|
|
||||||
|
return void_result();
|
||||||
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||||
|
|
||||||
|
object_id_type random_number_store_evaluator::do_apply( const random_number_store_operation& op )
|
||||||
|
{ try {
|
||||||
|
const auto& new_random_number_object = db().create<random_number_object>( [&]( random_number_object& obj ) {
|
||||||
|
obj.account = op.account;
|
||||||
|
obj.timestamp = db().head_block_time();
|
||||||
|
obj.random_number = op.random_number;
|
||||||
|
obj.data = op.data;
|
||||||
|
});
|
||||||
|
return new_random_number_object.id;
|
||||||
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||||
|
|
||||||
|
} } // graphene::chain
|
||||||
|
|
||||||
|
|
@ -1943,6 +1943,32 @@ class wallet_api
|
||||||
bool broadcast /* = false */
|
bool broadcast /* = false */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Get random numbers
|
||||||
|
* @brief Returns the random number
|
||||||
|
* @param minimum Lower bound of segment containing random number
|
||||||
|
* @param maximum Upper bound of segment containing random number
|
||||||
|
* @param selections Number of random numbers to return
|
||||||
|
* @param duplicates Allow duplicated numbers
|
||||||
|
* @param broadcast true if you wish to broadcast the transaction
|
||||||
|
* @return the signed version of the transaction
|
||||||
|
* @return Vector containing random numbers from segment [minimum, maximum)
|
||||||
|
*/
|
||||||
|
vector<uint64_t> get_random_number_ex(string account,
|
||||||
|
uint64_t minimum,
|
||||||
|
uint64_t maximum,
|
||||||
|
uint64_t selections,
|
||||||
|
bool duplicates,
|
||||||
|
bool broadcast);
|
||||||
|
|
||||||
|
/** Get random number
|
||||||
|
* @brief Returns the random number
|
||||||
|
* @param bound Upper bound of segment containing random number
|
||||||
|
* @return Random number from segment [0, bound)
|
||||||
|
*/
|
||||||
|
uint64_t get_random_number(string account,
|
||||||
|
uint64_t bound,
|
||||||
|
bool broadcast);
|
||||||
|
|
||||||
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50);
|
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50);
|
||||||
|
|
||||||
asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id);
|
asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id);
|
||||||
|
|
@ -2201,6 +2227,7 @@ class wallet_api
|
||||||
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const;
|
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const;
|
||||||
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const;
|
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const;
|
||||||
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const;
|
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const;
|
||||||
|
|
||||||
/////////
|
/////////
|
||||||
// NFT //
|
// NFT //
|
||||||
/////////
|
/////////
|
||||||
|
|
@ -2226,6 +2253,8 @@ class wallet_api
|
||||||
bool is_transferable,
|
bool is_transferable,
|
||||||
bool is_sellable,
|
bool is_sellable,
|
||||||
optional<account_role_id_type> role_id,
|
optional<account_role_id_type> role_id,
|
||||||
|
optional<share_type> max_supply,
|
||||||
|
optional<nft_lottery_options> lottery_options,
|
||||||
bool broadcast);
|
bool broadcast);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2363,6 +2392,7 @@ class wallet_api
|
||||||
* @return Returns vector of NFT objects, empty vector if none
|
* @return Returns vector of NFT objects, empty vector if none
|
||||||
*/
|
*/
|
||||||
vector<nft_object> nft_get_all_tokens() const;
|
vector<nft_object> nft_get_all_tokens() const;
|
||||||
|
signed_transaction nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast );
|
||||||
|
|
||||||
signed_transaction create_offer(set<nft_id_type> item_ids,
|
signed_transaction create_offer(set<nft_id_type> item_ids,
|
||||||
string issuer_accound_id_or_name,
|
string issuer_accound_id_or_name,
|
||||||
|
|
@ -2634,6 +2664,8 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(propose_fee_change)
|
(propose_fee_change)
|
||||||
(propose_dividend_asset_update)
|
(propose_dividend_asset_update)
|
||||||
(approve_proposal)
|
(approve_proposal)
|
||||||
|
(get_random_number_ex)
|
||||||
|
(get_random_number)
|
||||||
(dbg_make_uia)
|
(dbg_make_uia)
|
||||||
(dbg_make_mia)
|
(dbg_make_mia)
|
||||||
(dbg_push_blocks)
|
(dbg_push_blocks)
|
||||||
|
|
@ -2697,6 +2729,7 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(nft_get_approved)
|
(nft_get_approved)
|
||||||
(nft_is_approved_for_all)
|
(nft_is_approved_for_all)
|
||||||
(nft_get_all_tokens)
|
(nft_get_all_tokens)
|
||||||
|
(nft_lottery_buy_ticket)
|
||||||
(create_offer)
|
(create_offer)
|
||||||
(create_bid)
|
(create_bid)
|
||||||
(cancel_offer)
|
(cancel_offer)
|
||||||
|
|
|
||||||
|
|
@ -3819,6 +3819,38 @@ public:
|
||||||
return _remote_db->get_active_custom_account_authorities_by_operation(get_account(owner).id, operation_type);
|
return _remote_db->get_active_custom_account_authorities_by_operation(get_account(owner).id, operation_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<uint64_t> get_random_number_ex(string account,
|
||||||
|
uint64_t minimum,
|
||||||
|
uint64_t maximum,
|
||||||
|
uint64_t selections,
|
||||||
|
bool duplicates,
|
||||||
|
bool broadcast)
|
||||||
|
{
|
||||||
|
|
||||||
|
vector<uint64_t> v = _remote_db->get_random_number_ex(minimum, maximum, selections, duplicates);
|
||||||
|
|
||||||
|
random_number_store_operation op;
|
||||||
|
op.account = get_account(account).id;
|
||||||
|
op.random_number = v;
|
||||||
|
op.data = "";
|
||||||
|
|
||||||
|
signed_transaction trx;
|
||||||
|
trx.operations.push_back(op);
|
||||||
|
set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees );
|
||||||
|
trx.validate();
|
||||||
|
sign_transaction( trx, broadcast );
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t get_random_number(string account,
|
||||||
|
uint64_t bound,
|
||||||
|
bool broadcast)
|
||||||
|
{
|
||||||
|
vector<uint64_t> v = get_random_number_ex(account, 0, bound, 1, false, broadcast);
|
||||||
|
return v.at(0);
|
||||||
|
}
|
||||||
|
|
||||||
void dbg_make_uia(string creator, string symbol)
|
void dbg_make_uia(string creator, string symbol)
|
||||||
{
|
{
|
||||||
asset_options opts;
|
asset_options opts;
|
||||||
|
|
@ -5352,6 +5384,23 @@ vector<authority> wallet_api::get_active_custom_account_authorities_by_operation
|
||||||
return my->get_active_custom_account_authorities_by_operation(owner, operation_type);
|
return my->get_active_custom_account_authorities_by_operation(owner, operation_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<uint64_t> wallet_api::get_random_number_ex(string account,
|
||||||
|
uint64_t minimum,
|
||||||
|
uint64_t maximum,
|
||||||
|
uint64_t selections,
|
||||||
|
bool duplicates,
|
||||||
|
bool broadcast)
|
||||||
|
{
|
||||||
|
return my->get_random_number_ex( account, minimum, maximum, selections, duplicates, broadcast );
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t wallet_api::get_random_number(string account,
|
||||||
|
uint64_t bound,
|
||||||
|
bool broadcast)
|
||||||
|
{
|
||||||
|
return my->get_random_number( account, bound, broadcast );
|
||||||
|
}
|
||||||
|
|
||||||
global_property_object wallet_api::get_global_properties() const
|
global_property_object wallet_api::get_global_properties() const
|
||||||
{
|
{
|
||||||
return my->get_global_properties();
|
return my->get_global_properties();
|
||||||
|
|
@ -6972,6 +7021,8 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
|
||||||
bool is_transferable,
|
bool is_transferable,
|
||||||
bool is_sellable,
|
bool is_sellable,
|
||||||
optional<account_role_id_type> role_id,
|
optional<account_role_id_type> role_id,
|
||||||
|
optional<share_type> max_supply,
|
||||||
|
optional<nft_lottery_options> lottery_options,
|
||||||
bool broadcast)
|
bool broadcast)
|
||||||
{
|
{
|
||||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||||
|
|
@ -6995,6 +7046,8 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
|
||||||
op.is_transferable = is_transferable;
|
op.is_transferable = is_transferable;
|
||||||
op.is_sellable = is_sellable;
|
op.is_sellable = is_sellable;
|
||||||
op.account_role = role_id;
|
op.account_role = role_id;
|
||||||
|
op.max_supply = max_supply;
|
||||||
|
op.lottery_options = lottery_options;
|
||||||
|
|
||||||
signed_transaction trx;
|
signed_transaction trx;
|
||||||
trx.operations.push_back(op);
|
trx.operations.push_back(op);
|
||||||
|
|
@ -7178,6 +7231,21 @@ vector<nft_object> wallet_api::nft_get_all_tokens() const
|
||||||
return my->_remote_db->nft_get_all_tokens();
|
return my->_remote_db->nft_get_all_tokens();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signed_transaction wallet_api::nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast )
|
||||||
|
{
|
||||||
|
nft_lottery_token_purchase_operation op;
|
||||||
|
op.lottery_id = lottery;
|
||||||
|
op.buyer = buyer;
|
||||||
|
op.tickets_to_buy = tickets_to_buy;
|
||||||
|
|
||||||
|
signed_transaction trx;
|
||||||
|
trx.operations.push_back(op);
|
||||||
|
my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||||
|
trx.validate();
|
||||||
|
|
||||||
|
return my->sign_transaction( trx, broadcast );
|
||||||
|
}
|
||||||
|
|
||||||
signed_transaction wallet_api::create_offer(set<nft_id_type> item_ids,
|
signed_transaction wallet_api::create_offer(set<nft_id_type> item_ids,
|
||||||
string issuer_accound_id_or_name,
|
string issuer_accound_id_or_name,
|
||||||
asset minimum_price,
|
asset minimum_price,
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
#include <graphene/chain/event_object.hpp>
|
#include <graphene/chain/event_object.hpp>
|
||||||
#include <graphene/chain/tournament_object.hpp>
|
#include <graphene/chain/tournament_object.hpp>
|
||||||
#include <graphene/chain/offer_object.hpp>
|
#include <graphene/chain/offer_object.hpp>
|
||||||
|
#include <graphene/chain/nft_object.hpp>
|
||||||
|
|
||||||
#include <graphene/utilities/tempdir.hpp>
|
#include <graphene/utilities/tempdir.hpp>
|
||||||
|
|
||||||
|
|
@ -335,6 +336,14 @@ void database_fixture::verify_asset_supplies( const database& db )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const nft_metadata_object &o : db.get_index_type<nft_metadata_index>().indices())
|
||||||
|
{
|
||||||
|
if (o.lottery_data)
|
||||||
|
{
|
||||||
|
total_balances[o.get_lottery_jackpot(db).asset_id] += o.get_lottery_jackpot(db).amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t sweeps_vestings = 0;
|
uint64_t sweeps_vestings = 0;
|
||||||
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )
|
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )
|
||||||
sweeps_vestings += svbo.balance;
|
sweeps_vestings += svbo.balance;
|
||||||
|
|
|
||||||
842
tests/tests/nft_lottery_tests.cpp
Normal file
842
tests/tests/nft_lottery_tests.cpp
Normal file
|
|
@ -0,0 +1,842 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 PBSA, Inc., and contributors.
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <graphene/chain/database.hpp>
|
||||||
|
|
||||||
|
#include <fc/crypto/digest.hpp>
|
||||||
|
#include <fc/crypto/elliptic.hpp>
|
||||||
|
#include <fc/reflect/variant.hpp>
|
||||||
|
|
||||||
|
#include "../common/database_fixture.hpp"
|
||||||
|
|
||||||
|
#include <graphene/chain/hardfork.hpp>
|
||||||
|
#include <graphene/chain/nft_object.hpp>
|
||||||
|
|
||||||
|
using namespace graphene::chain;
|
||||||
|
using namespace graphene::chain::test;
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(nft_lottery_tests, database_fixture)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(create_lottery_nft_md_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generate_blocks(HARDFORK_NFT_TIME);
|
||||||
|
generate_block();
|
||||||
|
generate_block();
|
||||||
|
set_expiration(db, trx);
|
||||||
|
// Lottery Options
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
nft_lottery_options lottery_options;
|
||||||
|
lottery_options.benefactors.push_back(nft_lottery_benefactor(account_id_type(), 25 * GRAPHENE_1_PERCENT));
|
||||||
|
lottery_options.end_date = db.head_block_time() + fc::minutes(5);
|
||||||
|
lottery_options.ticket_price = asset(100);
|
||||||
|
lottery_options.winning_tickets = {5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT};
|
||||||
|
//lottery_options.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
|
||||||
|
lottery_options.is_active = test_nft_md_id.instance.value % 2 ? false : true;
|
||||||
|
lottery_options.ending_on_soldout = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
|
||||||
|
nft_metadata_create_operation op;
|
||||||
|
op.owner = account_id_type();
|
||||||
|
op.symbol = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
|
||||||
|
op.base_uri = "http://nft.example.com";
|
||||||
|
op.is_transferable = true;
|
||||||
|
op.name = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
|
||||||
|
op.max_supply = 200;
|
||||||
|
op.lottery_options = lottery_options;
|
||||||
|
|
||||||
|
trx.operations.push_back(std::move(op));
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
|
||||||
|
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
|
||||||
|
|
||||||
|
const auto &obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(obj.owner == account_id_type());
|
||||||
|
BOOST_CHECK(obj.name == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
|
||||||
|
BOOST_CHECK(obj.symbol == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
|
||||||
|
BOOST_CHECK(obj.base_uri == "http://nft.example.com");
|
||||||
|
BOOST_CHECK(obj.max_supply == share_type(200));
|
||||||
|
BOOST_CHECK(obj.is_lottery());
|
||||||
|
BOOST_CHECK(obj.get_token_current_supply(db) == share_type(0));
|
||||||
|
BOOST_CHECK(obj.get_lottery_jackpot(db) == asset());
|
||||||
|
BOOST_CHECK(obj.lottery_data->lottery_balance_id(db).sweeps_tickets_sold == share_type(0));
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(lottery_idx_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// generate loterries with different end_dates and is_active_flag
|
||||||
|
for (int i = 0; i < 26; ++i)
|
||||||
|
{
|
||||||
|
generate_blocks(30);
|
||||||
|
graphene::chain::test::set_expiration(db, trx);
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &test_nft_md_idx = db.get_index_type<nft_metadata_index>().indices().get<active_nft_lotteries>();
|
||||||
|
auto test_itr = test_nft_md_idx.begin();
|
||||||
|
bool met_not_active = false;
|
||||||
|
// check sorting
|
||||||
|
while (test_itr != test_nft_md_idx.end())
|
||||||
|
{
|
||||||
|
if (!met_not_active && (!test_itr->is_lottery() || !test_itr->lottery_data->lottery_options.is_active))
|
||||||
|
met_not_active = true;
|
||||||
|
FC_ASSERT(!met_not_active || met_not_active && (!test_itr->is_lottery() || !test_itr->lottery_data->lottery_options.is_active), "MET ACTIVE LOTTERY AFTER NOT ACTIVE");
|
||||||
|
++test_itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(tickets_purchase_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
nft_id_type test_nft_id = db.get_index<nft_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto &test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type();
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(100);
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
generate_block();
|
||||||
|
trx.operations.clear();
|
||||||
|
auto &test_nft_ticket = test_nft_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == tpo.tickets_to_buy);
|
||||||
|
BOOST_CHECK(test_nft_ticket.owner == tpo.buyer);
|
||||||
|
BOOST_CHECK(test_nft_ticket.nft_metadata_id == test_nft_md_obj.id);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(tickets_purchase_fail_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto &test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type();
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 2;
|
||||||
|
tpo.amount = asset(100);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); // amount/tickets_to_buy != price
|
||||||
|
trx.operations.clear();
|
||||||
|
|
||||||
|
tpo.amount = asset(205);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); // amount/tickets_to_buy != price
|
||||||
|
|
||||||
|
tpo.amount = asset(200, asset_id_type(1));
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); // trying to buy in other asset
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(lottery_end_by_stage_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
for (int i = 1; i < 17; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4 || i == 1 || i == 16 || i == 15)
|
||||||
|
continue;
|
||||||
|
if (i != 0)
|
||||||
|
transfer(account_id_type(), account_id_type(i), asset(100000));
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = i;
|
||||||
|
tpo.amount = asset(100 * (i));
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
generate_block();
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
uint64_t benefactor_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
|
||||||
|
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
|
||||||
|
uint16_t winners_part = 0;
|
||||||
|
for (uint16_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
|
||||||
|
winners_part += win;
|
||||||
|
|
||||||
|
uint16_t participants_percents_sum = 0;
|
||||||
|
auto participants = test_nft_md_obj.distribute_winners_part(db);
|
||||||
|
for (auto p : participants)
|
||||||
|
{
|
||||||
|
for (auto e : p.second)
|
||||||
|
{
|
||||||
|
participants_percents_sum += e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(participants_percents_sum == winners_part);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT);
|
||||||
|
test_nft_md_obj.distribute_benefactors_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT);
|
||||||
|
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
|
||||||
|
|
||||||
|
uint64_t benefactor_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - benefactor_balance_before_end;
|
||||||
|
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->lottery_options.benefactors[0].share / GRAPHENE_100_PERCENT == benefactor_recieved);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(lottery_end_by_stage_with_fractional_test)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
db.modify(test_nft_md_id(db), [&](nft_metadata_object &obj) {
|
||||||
|
obj.lottery_data->lottery_options.is_active = true;
|
||||||
|
});
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
|
||||||
|
for (int i = 1; i < 17; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4)
|
||||||
|
continue;
|
||||||
|
if (i != 0)
|
||||||
|
transfer(account_id_type(), account_id_type(i), asset(100000));
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = i;
|
||||||
|
tpo.amount = asset(100 * (i));
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
generate_block();
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
uint64_t benefactor_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
|
||||||
|
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
|
||||||
|
uint16_t winners_part = 0;
|
||||||
|
for (uint16_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
|
||||||
|
winners_part += win;
|
||||||
|
|
||||||
|
uint16_t participants_percents_sum = 0;
|
||||||
|
auto participants = test_nft_md_obj.distribute_winners_part(db);
|
||||||
|
for (auto p : participants)
|
||||||
|
for (auto e : p.second)
|
||||||
|
participants_percents_sum += e;
|
||||||
|
|
||||||
|
BOOST_CHECK(participants_percents_sum == winners_part);
|
||||||
|
// balance should be bigger than expected because of rouning during distribution
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value > (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT);
|
||||||
|
test_nft_md_obj.distribute_benefactors_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value > jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT);
|
||||||
|
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
// but at the end is always equals 0
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
|
||||||
|
|
||||||
|
uint64_t benefactor_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - benefactor_balance_before_end;
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->lottery_options.benefactors[0].share / GRAPHENE_100_PERCENT == benefactor_recieved);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(lottery_end_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
for (int i = 1; i < 17; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4 || i == 1 || i == 16 || i == 15)
|
||||||
|
continue;
|
||||||
|
if (i != 0)
|
||||||
|
transfer(account_id_type(), account_id_type(i), asset(100000));
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = i;
|
||||||
|
tpo.amount = asset(100 * (i));
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
uint64_t creator_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
|
||||||
|
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
|
||||||
|
uint16_t winners_part = 0;
|
||||||
|
for (uint8_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
|
||||||
|
winners_part += win;
|
||||||
|
|
||||||
|
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date + fc::seconds(30)))
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
|
||||||
|
uint64_t creator_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - creator_balance_before_end;
|
||||||
|
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->lottery_options.benefactors[0].share / GRAPHENE_100_PERCENT == creator_recieved);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(claim_sweeps_vesting_balance_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(lottery_end_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
account_id_type benefactor = test_nft_md_obj.lottery_data->lottery_options.benefactors[0].id;
|
||||||
|
const auto &svbo_index = db.get_index_type<sweeps_vesting_balance_index>().indices().get<by_owner>();
|
||||||
|
auto benefactor_svbo = svbo_index.find(benefactor);
|
||||||
|
BOOST_CHECK(benefactor_svbo != svbo_index.end());
|
||||||
|
|
||||||
|
auto balance_before_claim = db.get_balance(benefactor, SWEEPS_DEFAULT_DISTRIBUTION_ASSET);
|
||||||
|
auto available_for_claim = benefactor_svbo->available_for_claim();
|
||||||
|
sweeps_vesting_claim_operation claim;
|
||||||
|
claim.account = benefactor;
|
||||||
|
claim.amount_to_claim = available_for_claim;
|
||||||
|
trx.clear();
|
||||||
|
graphene::chain::test::set_expiration(db, trx);
|
||||||
|
trx.operations.push_back(claim);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
generate_block();
|
||||||
|
|
||||||
|
BOOST_CHECK(db.get_balance(benefactor, SWEEPS_DEFAULT_DISTRIBUTION_ASSET) - balance_before_claim == available_for_claim);
|
||||||
|
benefactor_svbo = svbo_index.find(benefactor);
|
||||||
|
BOOST_CHECK(benefactor_svbo->available_for_claim().amount == 0);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(more_winners_then_participants_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
for (int i = 1; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4)
|
||||||
|
continue;
|
||||||
|
if (i != 0)
|
||||||
|
transfer(account_id_type(), account_id_type(i), asset(1000000));
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(100);
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
auto holders = test_nft_md_obj.get_holders(db);
|
||||||
|
auto participants = test_nft_md_obj.distribute_winners_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
test_nft_md_obj.distribute_benefactors_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
generate_block();
|
||||||
|
for (auto p : participants)
|
||||||
|
{
|
||||||
|
idump((get_operation_history(p.first)));
|
||||||
|
}
|
||||||
|
auto benefactor_history = get_operation_history(account_id_type());
|
||||||
|
for (auto h : benefactor_history)
|
||||||
|
{
|
||||||
|
idump((h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ending_by_date_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
for (int i = 1; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4)
|
||||||
|
continue;
|
||||||
|
if (i != 0)
|
||||||
|
transfer(account_id_type(), account_id_type(i), asset(1000000));
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(100);
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
auto holders = test_nft_md_obj.get_holders(db);
|
||||||
|
idump((test_nft_md_obj.get_lottery_jackpot(db)));
|
||||||
|
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date + fc::seconds(30)))
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
idump((test_nft_md_obj.get_lottery_jackpot(db)));
|
||||||
|
vector<account_id_type> participants = {account_id_type(1), account_id_type(2), account_id_type(3)};
|
||||||
|
for (auto p : participants)
|
||||||
|
{
|
||||||
|
idump((get_operation_history(p)));
|
||||||
|
}
|
||||||
|
auto benefactor_history = get_operation_history(account_id_type());
|
||||||
|
for (auto h : benefactor_history)
|
||||||
|
{
|
||||||
|
if (h.op.which() == operation::tag<lottery_reward_operation>::value)
|
||||||
|
{
|
||||||
|
auto reward_op = h.op.get<lottery_reward_operation>();
|
||||||
|
idump((reward_op));
|
||||||
|
BOOST_CHECK(reward_op.is_benefactor_reward);
|
||||||
|
BOOST_CHECK(reward_op.amount.amount.value == 75);
|
||||||
|
BOOST_CHECK(reward_op.amount.asset_id == test_nft_md_obj.lottery_data->lottery_options.ticket_price.asset_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ending_by_participants_count_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
FC_ASSERT(test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
account_id_type buyer(3);
|
||||||
|
transfer(account_id_type(), buyer, asset(10000000));
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = buyer;
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 200;
|
||||||
|
tpo.amount = asset(200 * 100);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
FC_ASSERT(!test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(try_to_end_empty_lottery_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date + fc::seconds(30)))
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(!test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(lottery_winner_ticket_id_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
INVOKE(create_lottery_nft_md_test);
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
for (int i = 1; i < 4; ++i)
|
||||||
|
{
|
||||||
|
transfer(account_id_type(), account_id_type(i), asset(2000000));
|
||||||
|
}
|
||||||
|
for (int i = 1; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4)
|
||||||
|
continue;
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(100);
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (i == 4)
|
||||||
|
continue;
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.buyer = account_id_type(i);
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(100);
|
||||||
|
trx.operations.push_back(std::move(tpo));
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
uint64_t creator_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
|
||||||
|
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
|
||||||
|
uint16_t winners_part = 0;
|
||||||
|
for (uint8_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
|
||||||
|
winners_part += win;
|
||||||
|
|
||||||
|
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date))
|
||||||
|
generate_block();
|
||||||
|
auto op_history = get_operation_history(account_id_type(1)); //Can observe operation 79 to verify winner ticket number
|
||||||
|
for (auto h : op_history)
|
||||||
|
{
|
||||||
|
idump((h));
|
||||||
|
}
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
|
||||||
|
uint64_t creator_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - creator_balance_before_end;
|
||||||
|
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->lottery_options.benefactors[0].share / GRAPHENE_100_PERCENT == creator_recieved);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(create_lottery_nft_md_delete_tickets_on_draw_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generate_blocks(HARDFORK_NFT_TIME);
|
||||||
|
generate_block();
|
||||||
|
generate_block();
|
||||||
|
set_expiration(db, trx);
|
||||||
|
// Lottery Options
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
nft_lottery_options lottery_options;
|
||||||
|
lottery_options.benefactors.push_back(nft_lottery_benefactor(account_id_type(), 25 * GRAPHENE_1_PERCENT));
|
||||||
|
lottery_options.end_date = db.head_block_time() + fc::minutes(5);
|
||||||
|
lottery_options.ticket_price = asset(100);
|
||||||
|
lottery_options.winning_tickets = {5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT};
|
||||||
|
//lottery_options.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
|
||||||
|
lottery_options.is_active = test_nft_md_id.instance.value % 2 ? false : true;
|
||||||
|
lottery_options.ending_on_soldout = true;
|
||||||
|
lottery_options.delete_tickets_after_draw = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
|
||||||
|
nft_metadata_create_operation op;
|
||||||
|
op.owner = account_id_type();
|
||||||
|
op.symbol = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
|
||||||
|
op.base_uri = "http://nft.example.com";
|
||||||
|
op.is_transferable = true;
|
||||||
|
op.name = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
|
||||||
|
op.max_supply = 200;
|
||||||
|
op.lottery_options = lottery_options;
|
||||||
|
|
||||||
|
trx.operations.push_back(std::move(op));
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
|
||||||
|
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
|
||||||
|
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.owner == account_id_type());
|
||||||
|
BOOST_CHECK(test_nft_md_obj.name == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.symbol == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.base_uri == "http://nft.example.com");
|
||||||
|
BOOST_CHECK(test_nft_md_obj.max_supply == share_type(200));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.is_lottery());
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == share_type(0));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db) == asset());
|
||||||
|
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_balance_id(db).sweeps_tickets_sold == share_type(0));
|
||||||
|
|
||||||
|
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
account_id_type buyer(3);
|
||||||
|
transfer(account_id_type(), buyer, asset(10000000));
|
||||||
|
{
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = buyer;
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 199;
|
||||||
|
tpo.amount = asset(199 * 100);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 199);
|
||||||
|
{
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = buyer;
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(1 * 100);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(!test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 0);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(create_lottery_nft_with_permission_test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generate_blocks(HARDFORK_NFT_TIME);
|
||||||
|
generate_block();
|
||||||
|
generate_block();
|
||||||
|
set_expiration(db, trx);
|
||||||
|
// Lottery Options
|
||||||
|
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||||
|
nft_lottery_options lottery_options;
|
||||||
|
lottery_options.benefactors.push_back(nft_lottery_benefactor(account_id_type(), 25 * GRAPHENE_1_PERCENT));
|
||||||
|
lottery_options.end_date = db.head_block_time() + fc::minutes(5);
|
||||||
|
lottery_options.ticket_price = asset(100);
|
||||||
|
lottery_options.winning_tickets = {5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT};
|
||||||
|
//lottery_options.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
|
||||||
|
lottery_options.is_active = test_nft_md_id.instance.value % 2 ? false : true;
|
||||||
|
lottery_options.ending_on_soldout = true;
|
||||||
|
lottery_options.delete_tickets_after_draw = true;
|
||||||
|
|
||||||
|
account_id_type buyer1(3);
|
||||||
|
account_id_type buyer2(6);
|
||||||
|
generate_block();
|
||||||
|
transfer(account_id_type(), buyer1, asset(1000000));
|
||||||
|
transfer(account_id_type(), buyer2, asset(1000000));
|
||||||
|
generate_block();
|
||||||
|
|
||||||
|
{
|
||||||
|
BOOST_TEST_MESSAGE("Send account_role_create_operation");
|
||||||
|
|
||||||
|
account_role_create_operation op;
|
||||||
|
op.owner = account_id_type();
|
||||||
|
op.name = "Test Account Role";
|
||||||
|
op.metadata = "{\"country\": \"earth\", \"race\": \"human\" }";
|
||||||
|
|
||||||
|
int ops[] = {operation::tag<nft_lottery_token_purchase_operation>::value};
|
||||||
|
op.allowed_operations.insert(ops, ops + 1);
|
||||||
|
op.whitelisted_accounts.emplace(buyer1);
|
||||||
|
op.valid_to = db.head_block_time() + 1000;
|
||||||
|
|
||||||
|
trx.operations.push_back(op);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
|
||||||
|
nft_metadata_create_operation op;
|
||||||
|
op.owner = account_id_type();
|
||||||
|
op.symbol = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
|
||||||
|
op.base_uri = "http://nft.example.com";
|
||||||
|
op.is_transferable = true;
|
||||||
|
op.name = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
|
||||||
|
op.max_supply = 200;
|
||||||
|
op.lottery_options = lottery_options;
|
||||||
|
op.account_role = account_role_id_type(0);
|
||||||
|
|
||||||
|
trx.operations.push_back(std::move(op));
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
|
||||||
|
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
|
||||||
|
|
||||||
|
auto test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.owner == account_id_type());
|
||||||
|
BOOST_CHECK(test_nft_md_obj.name == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.symbol == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.base_uri == "http://nft.example.com");
|
||||||
|
BOOST_CHECK(test_nft_md_obj.max_supply == share_type(200));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.is_lottery());
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == share_type(0));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db) == asset());
|
||||||
|
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_balance_id(db).sweeps_tickets_sold == share_type(0));
|
||||||
|
BOOST_CHECK(test_nft_md_obj.account_role == account_role_id_type(0));
|
||||||
|
|
||||||
|
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
|
||||||
|
{
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = buyer1;
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 199;
|
||||||
|
tpo.amount = asset(199 * 100);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
set_expiration(db, trx);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 199);
|
||||||
|
{
|
||||||
|
nft_lottery_token_purchase_operation tpo;
|
||||||
|
tpo.fee = asset();
|
||||||
|
tpo.buyer = buyer2;
|
||||||
|
tpo.lottery_id = test_nft_md_obj.id;
|
||||||
|
tpo.tickets_to_buy = 1;
|
||||||
|
tpo.amount = asset(1 * 100);
|
||||||
|
trx.operations.push_back(tpo);
|
||||||
|
set_expiration(db, trx);
|
||||||
|
BOOST_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception);
|
||||||
|
trx.operations.clear();
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
test_nft_md_obj = test_nft_md_id(db);
|
||||||
|
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 199);
|
||||||
|
}
|
||||||
|
catch (fc::exception &e)
|
||||||
|
{
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
Loading…
Reference in a new issue