Skip to content

Commit

Permalink
Merge pull request #80 from AntelopeIO/tokenomics-rex-upgrade
Browse files Browse the repository at this point in the history
Tokenomics - REX Upgrade
  • Loading branch information
nsjames authored Jun 14, 2024
2 parents 47599f0 + 126d5f8 commit 1a603bb
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 51 deletions.
44 changes: 30 additions & 14 deletions contracts/eosio.system/include/eosio.system/eosio.system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,14 @@ namespace eosiosystem {
typedef eosio::multi_index< "rexqueue"_n, rex_order,
indexed_by<"bytime"_n, const_mem_fun<rex_order, uint64_t, &rex_order::by_time>>> rex_order_table;

struct [[eosio::table("rexmaturity"),eosio::contract("eosio.system")]] rex_maturity {
uint32_t num_of_maturity_buckets = 5;
bool sell_matured_rex = false;
bool buy_rex_to_savings = false;
};

typedef eosio::singleton<"rexmaturity"_n, rex_maturity> rex_maturity_singleton;

struct rex_order_outcome {
bool success;
asset proceeds;
Expand Down Expand Up @@ -735,6 +743,7 @@ namespace eosiosystem {
rex_fund_table _rexfunds;
rex_balance_table _rexbalance;
rex_order_table _rexorders;
rex_maturity_singleton _rexmaturity;

public:
static constexpr eosio::name active_permission{"active"_n};
Expand Down Expand Up @@ -922,12 +931,9 @@ namespace eosiosystem {
* @param from - owner account name,
* @param amount - amount of tokens taken out of 'from' REX fund.
*
* @pre A voting requirement must be satisfied before action can be executed.
* @pre User must vote for at least 21 producers or delegate vote to proxy before buying REX.
*
* @post User votes are updated following this action.
* @post Tokens used in purchase are added to user's voting power.
* @post Bought REX cannot be sold before 4 days counting from end of day of purchase.
* @post Bought REX cannot be sold before {num_of_maturity_buckets} days counting from end of day of purchase.
*/
[[eosio::action]]
void buyrex( const name& from, const asset& amount );
Expand All @@ -941,12 +947,9 @@ namespace eosiosystem {
* @param from_net - amount of tokens to be unstaked from NET bandwidth and used for REX purchase,
* @param from_cpu - amount of tokens to be unstaked from CPU bandwidth and used for REX purchase.
*
* @pre A voting requirement must be satisfied before action can be executed.
* @pre User must vote for at least 21 producers or delegate vote to proxy before buying REX.
*
* @post User votes are updated following this action.
* @post Tokens used in purchase are added to user's voting power.
* @post Bought REX cannot be sold before 4 days counting from end of day of purchase.
* @post Bought REX cannot be sold before {num_of_maturity_buckets} days counting from end of day of purchase.
*/
[[eosio::action]]
void unstaketorex( const name& owner, const name& receiver, const asset& from_net, const asset& from_cpu );
Expand Down Expand Up @@ -1075,7 +1078,7 @@ namespace eosiosystem {
void rexexec( const name& user, uint16_t max );

/**
* Consolidate action, consolidates REX maturity buckets into one bucket that can be sold after 4 days
* Consolidate action, consolidates REX maturity buckets into one bucket that can be sold after {num_of_maturity_buckets} days
* starting from the end of the day.
*
* @param owner - REX owner account name.
Expand All @@ -1087,7 +1090,7 @@ namespace eosiosystem {
* Mvtosavings action, moves a specified amount of REX into savings bucket. REX savings bucket
* never matures. In order for it to be sold, it has to be moved explicitly
* out of that bucket. Then the moved amount will have the regular maturity
* period of 4 days starting from the end of the day.
* period of {num_of_maturity_buckets} days starting from the end of the day.
*
* @param owner - REX owner account name.
* @param rex - amount of REX to be moved.
Expand All @@ -1097,7 +1100,7 @@ namespace eosiosystem {

/**
* Mvfrsavings action, moves a specified amount of REX out of savings bucket. The moved amount
* will have the regular REX maturity period of 4 days.
* will have the regular REX maturity period of {num_of_maturity_buckets} days.
*
* @param owner - REX owner account name.
* @param rex - amount of REX to be moved.
Expand All @@ -1119,6 +1122,18 @@ namespace eosiosystem {
[[eosio::action]]
void closerex( const name& owner );

/**
* Facilitates the modification of REX maturity buckets
*
* @param num_of_maturity_buckets - used to calculate maturity time of purchase REX tokens from end of the day UTC.
* @param sell_matured_rex - if true, matured REX is sold immediately.
* https://github.com/eosnetworkfoundation/eos-system-contracts/issues/134
* @param buy_rex_to_savings - if true, buying REX is moved immediately to REX savings.
* https://github.com/eosnetworkfoundation/eos-system-contracts/issues/135
*/
[[eosio::action]]
void setrexmature(const std::optional<uint32_t> num_of_maturity_buckets, const std::optional<bool> sell_matured_rex, const std::optional<bool> buy_rex_to_savings );

/**
* Donatetorex action, donates funds to REX, increases REX pool return buckets
* Executes inline transfer from payer to system contract of tokens will be executed.
Expand Down Expand Up @@ -1672,8 +1687,6 @@ namespace eosiosystem {
void runrex( uint16_t max );
void update_rex_pool();
void update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu );
void check_voting_requirement( const name& owner,
const char* error_msg = "must vote for at least 21 producers or for a proxy before buying REX" )const;
rex_order_outcome fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex );
asset update_rex_account( const name& owner, const asset& proceeds, const asset& unstake_quant, bool force_vote_update = false );
template <typename T>
Expand All @@ -1685,16 +1698,19 @@ namespace eosiosystem {
void transfer_from_fund( const name& owner, const asset& amount );
void transfer_to_fund( const name& owner, const asset& amount );
bool rex_loans_available()const;
static time_point_sec get_rex_maturity();
static time_point_sec get_rex_maturity(const name& system_account_name = "eosio"_n );
asset add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received );
asset add_to_rex_pool( const asset& payment );
void add_to_rex_return_pool( const asset& fee );
void process_rex_maturities( const rex_balance_table::const_iterator& bitr );
void process_sell_matured_rex( const name owner );
void process_buy_rex_to_savings( const name owner, const asset rex );
void consolidate_rex_balance( const rex_balance_table::const_iterator& bitr,
const asset& rex_in_sell_order );
int64_t read_rex_savings( const rex_balance_table::const_iterator& bitr );
void put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex );
void update_rex_stake( const name& voter );
void sell_rex( const name& from, const asset& rex );

void add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan );
void remove_loan_from_rex_pool( const rex_loan& loan );
Expand Down
31 changes: 29 additions & 2 deletions contracts/eosio.system/ricardian/eosio.system.contracts.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,27 @@ icon: @ICON_BASE_URL@/@REX_ICON_URI@

Performs REX maintenance by processing a maximum of {{max}} REX sell orders and expired loans. Any account can execute this action.

<h1 class="contract">setrexmature</h1>

---
spec_version: "0.2.0"
title: Set REX Maturity Settings
summary: 'Sets the options for REX maturity buckets'
icon: @ICON_BASE_URL@/@REX_ICON_URI@
---

{{#if num_of_maturity_buckets}}
Sets the numbers of maturity buckets to '{{num_of_maturity_buckets}}'
{{/if}}

{{#if sell_matured_rex}}
Sets whether or not to immediately sell matured REX to '{{sell_matured_rex}}'
{{/if}}

{{#if buy_rex_to_savings}}
Sets whether or not to immediately move purchased REX to savings to '{{buy_rex_to_savings}}'
{{/if}}

<h1 class="contract">rmvproducer</h1>

---
Expand Down Expand Up @@ -489,9 +510,15 @@ summary: '{{nowrap from}} sells {{nowrap rex}} tokens'
icon: @ICON_BASE_URL@/@REX_ICON_URI@
---

{{from}} initiates a sell order to sell {{rex}} tokens at the market exchange rate during the time at which the order is ultimately executed. If {{from}} already has an open sell order in the sell queue, {{rex}} will be added to the amount of the sell order without change the position of the sell order within the queue. Once the sell order is executed, proceeds are added to {{from}}’s REX fund, the value of sold REX tokens is deducted from {{from}}’s vote stake, and votes are updated accordingly.
The 'rex' parameter no longer has an effect.

{{from}} initiates a sell order to sell all of their matured REX tokens at the market exchange rate during the time at which the order is ultimately executed.
If {{from}} already has an open sell order in the sell queue, {{rex}} will be added to the amount of the sell order without change the position of the sell order within the queue.
Once the sell order is executed, proceeds are added to {{from}}’s REX fund, the value of sold REX tokens is deducted from {{from}}’s vote stake, and votes are updated accordingly.

Depending on the market conditions, it may not be possible to fill the entire sell order immediately. In such a case, the sell order is added to the back of a sell queue. A sell order at the front of the sell queue will automatically be executed when the market conditions allow for the entire order to be filled. Regardless of the market conditions, the system is designed to execute this sell order within 30 days. {{from}} can cancel the order at any time before it is filled using the cnclrexorder action.
Depending on the market conditions, it may not be possible to fill the entire sell order immediately. In such a case, the sell order is added to the back of a sell queue.
A sell order at the front of the sell queue will automatically be executed when the market conditions allow for the entire order to be filled. Regardless of the market conditions,
the system is designed to execute this sell order within 30 days. {{from}} can cancel the order at any time before it is filled using the cnclrexorder action.

<h1 class="contract">setabi</h1>

Expand Down
3 changes: 2 additions & 1 deletion contracts/eosio.system/src/eosio.system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ namespace eosiosystem {
_rexretbuckets(get_self(), get_self().value),
_rexfunds(get_self(), get_self().value),
_rexbalance(get_self(), get_self().value),
_rexorders(get_self(), get_self().value)
_rexorders(get_self(), get_self().value),
_rexmaturity(get_self(), get_self().value)
{
_gstate = _global.exists() ? _global.get() : get_default_parameters();
_gstate2 = _global2.exists() ? _global2.get() : eosio_global_state2{};
Expand Down
82 changes: 64 additions & 18 deletions contracts/eosio.system/src/rex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ namespace eosiosystem {
using eosio::token;
using eosio::seconds;

void system_contract::setrexmature(const std::optional<uint32_t> num_of_maturity_buckets, const std::optional<bool> sell_matured_rex, const std::optional<bool> buy_rex_to_savings )
{
require_auth(get_self());

auto state = _rexmaturity.get_or_default();

check(*num_of_maturity_buckets > 0, "num_of_maturity_buckets must be positive");
check(*num_of_maturity_buckets <= 30, "num_of_maturity_buckets must be less than or equal to 30");

if ( num_of_maturity_buckets ) state.num_of_maturity_buckets = *num_of_maturity_buckets;
if ( sell_matured_rex ) state.sell_matured_rex = *sell_matured_rex;
if ( buy_rex_to_savings ) state.buy_rex_to_savings = *buy_rex_to_savings;
_rexmaturity.set(state, get_self());
}

void system_contract::deposit( const name& owner, const asset& amount )
{
require_auth( owner );
Expand Down Expand Up @@ -43,12 +58,15 @@ namespace eosiosystem {

check( amount.symbol == core_symbol(), "asset must be core token" );
check( 0 < amount.amount, "must use positive amount" );
check_voting_requirement( from );
transfer_from_fund( from, amount );
const asset rex_received = add_to_rex_pool( amount );
const asset delta_rex_stake = add_to_rex_balance( from, amount, rex_received );
runrex(2);
update_rex_account( from, asset( 0, core_symbol() ), delta_rex_stake );

process_buy_rex_to_savings( from, rex_received );
process_sell_matured_rex( from );

// dummy action added so that amount of REX tokens purchased shows up in action trace
rex_results::buyresult_action buyrex_act( rex_account, std::vector<eosio::permission_level>{ } );
buyrex_act.send( rex_received );
Expand All @@ -61,7 +79,6 @@ namespace eosiosystem {
check( from_net.symbol == core_symbol() && from_cpu.symbol == core_symbol(), "asset must be core token" );
check( (0 <= from_net.amount) && (0 <= from_cpu.amount) && (0 < from_net.amount || 0 < from_cpu.amount),
"must unstake a positive amount to buy rex" );
check_voting_requirement( owner );

{
del_bandwidth_table dbw_table( get_self(), owner.value );
Expand Down Expand Up @@ -89,6 +106,10 @@ namespace eosiosystem {
auto rex_stake_delta = add_to_rex_balance( owner, payment, rex_received );
runrex(2);
update_rex_account( owner, asset( 0, core_symbol() ), rex_stake_delta - payment, true );

process_buy_rex_to_savings( owner, rex_received );
process_sell_matured_rex( owner );

// dummy action added so that amount of REX tokens purchased shows up in action trace
rex_results::buyresult_action buyrex_act( rex_account, std::vector<eosio::permission_level>{ } );
buyrex_act.send( rex_received );
Expand All @@ -97,7 +118,12 @@ namespace eosiosystem {
void system_contract::sellrex( const name& from, const asset& rex )
{
require_auth( from );
sell_rex( from, rex );
process_sell_matured_rex( from );
}

void system_contract::sell_rex( const name& from, const asset& rex )
{
runrex(2);

auto bitr = _rexbalance.require_find( from.value, "user must first buyrex" );
Expand Down Expand Up @@ -431,19 +457,6 @@ namespace eosiosystem {
}
}

/**
* @brief Checks if account satisfies voting requirement (voting for a proxy or 21 producers)
* for buying REX
*
* @param owner - account buying or already holding REX tokens
* @err_msg - error message
*/
void system_contract::check_voting_requirement( const name& owner, const char* error_msg )const
{
auto vitr = _voters.find( owner.value );
check( vitr != _voters.end() && ( vitr->proxy || 21 <= vitr->producers.size() ), error_msg );
}

/**
* @brief Checks if CPU and Network loans are available
*
Expand Down Expand Up @@ -932,14 +945,15 @@ namespace eosiosystem {
}

/**
* @brief Calculates maturity time of purchased REX tokens which is 4 days from end
* @brief Calculates maturity time of purchased REX tokens which is {num_of_maturity_buckets} days from end
* of the day UTC
*
* @return time_point_sec
*/
time_point_sec system_contract::get_rex_maturity()
time_point_sec system_contract::get_rex_maturity( const name& system_account_name )
{
const uint32_t num_of_maturity_buckets = 5;
rex_maturity_singleton _rexmaturity(system_account_name, system_account_name.value);
const uint32_t num_of_maturity_buckets = _rexmaturity.get_or_default().num_of_maturity_buckets; // default 5
static const uint32_t now = current_time_point().sec_since_epoch();
static const uint32_t r = now % seconds_per_day;
static const time_point_sec rms{ now - r + num_of_maturity_buckets * seconds_per_day };
Expand All @@ -962,6 +976,38 @@ namespace eosiosystem {
});
}

/**
* @brief Sells matured REX tokens
* https://github.com/eosnetworkfoundation/eos-system-contracts/issues/134
*
* @param owner - owner account name
*/
void system_contract::process_sell_matured_rex( const name owner )
{
const auto rex_maturity_state = _rexmaturity.get_or_default();
if ( rex_maturity_state.sell_matured_rex == false ) return; // skip selling matured REX

const auto itr = _rexbalance.find( owner.value );
if ( itr->matured_rex > 0 ) {
sell_rex(owner, asset(itr->matured_rex, rex_symbol));
}
}

/**
* @brief Move new REX tokens to savings
* https://github.com/eosnetworkfoundation/eos-system-contracts/issues/135
*
* @param owner - owner account name
* @param rex - amount of REX tokens to be moved to savings
*/
void system_contract::process_buy_rex_to_savings( const name owner, const asset rex )
{
const auto rex_maturity_state = _rexmaturity.get_or_default();
if ( rex_maturity_state.buy_rex_to_savings && rex.amount > 0 ) {
mvtosavings( owner, rex );
}
}

/**
* @brief Consolidates REX maturity buckets into one
*
Expand Down
4 changes: 0 additions & 4 deletions contracts/eosio.system/src/voting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,6 @@ namespace eosiosystem {

vote_stake_updater( voter_name );
update_votes( voter_name, proxy, producers, true );
auto rex_itr = _rexbalance.find( voter_name.value );
if( rex_itr != _rexbalance.end() && rex_itr->rex_balance.amount > 0 ) {
check_voting_requirement( voter_name, "voter holding REX tokens must vote for at least 21 producers or for a proxy" );
}
}

void system_contract::voteupdate( const name& voter_name ) {
Expand Down
Loading

0 comments on commit 1a603bb

Please sign in to comment.