Skip to content

Commit

Permalink
Merge pull request #150 from eosnetworkfoundation/tokenomics-2
Browse files Browse the repository at this point in the history
REX/Tokenomics 2.0
  • Loading branch information
nsjames authored Jun 19, 2024
2 parents 0b17275 + 04350f5 commit 1117b73
Show file tree
Hide file tree
Showing 17 changed files with 551 additions and 59 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ project(eosio_contracts)
set(VERSION_MAJOR 3)
set(VERSION_MINOR 5)
set(VERSION_PATCH 0)
set(VERSION_SUFFIX dev)
set(VERSION_SUFFIX rc1)

if(VERSION_SUFFIX)
set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_SUFFIX}")
Expand Down
1 change: 1 addition & 0 deletions contracts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ add_subdirectory(eosio.system)
add_subdirectory(eosio.token)
add_subdirectory(eosio.wrap)
add_subdirectory(eosio.fees)
add_subdirectory(eosio.bpay)

add_subdirectory(test_contracts)
14 changes: 14 additions & 0 deletions contracts/eosio.bpay/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
add_contract(eosio.bpay eosio.bpay ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.bpay.cpp)

target_include_directories(eosio.bpay PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/../eosio.system/include
${CMAKE_CURRENT_SOURCE_DIR}/../eosio.token/include)

set_target_properties(eosio.bpay
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")

configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.bpay.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.bpay.contracts.md @ONLY )

target_compile_options( eosio.bpay PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian )
55 changes: 55 additions & 0 deletions contracts/eosio.bpay/include/eosio.bpay/eosio.bpay.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#pragma once

#include <eosio/eosio.hpp>
#include <eosio.system/eosio.system.hpp>
#include <eosio.token/eosio.token.hpp>

using namespace std;

namespace eosio {
/**
* The `eosio.bpay` contract handles system bpay distribution.
*/
class [[eosio::contract("eosio.bpay")]] bpay : public contract {
public:
using contract::contract;

/**
* ## TABLE `rewards`
*
* @param owner - block producer owner account
* @param quantity - reward quantity in EOS
*
* ### example
*
* ```json
* [
* {
* "owner": "alice",
* "quantity": "8.800 EOS"
* }
* ]
* ```
*/
struct [[eosio::table("rewards")]] rewards_row {
name owner;
asset quantity;

uint64_t primary_key() const { return owner.value; }
};
typedef eosio::multi_index< "rewards"_n, rewards_row > rewards_table;

/**
* Claim rewards for a block producer.
*
* @param owner - block producer owner account
*/
[[eosio::action]]
void claimrewards( const name owner);

[[eosio::on_notify("eosio.token::transfer")]]
void on_transfer( const name from, const name to, const asset quantity, const string memo );

private:
};
} /// namespace eosio
10 changes: 10 additions & 0 deletions contracts/eosio.bpay/ricardian/eosio.bpay.contracts.md.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<h1 class="contract">claimrewards</h1>

---
spec_version: "0.2.0"
title: Claim Rewards
summary: '{{nowrap owner}} claims block production rewards'
icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@
---

{{owner}} claims block production rewards accumulated through network fees.
73 changes: 73 additions & 0 deletions contracts/eosio.bpay/src/eosio.bpay.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <eosio.bpay/eosio.bpay.hpp>

namespace eosio {

void bpay::claimrewards( const name owner ) {
require_auth( owner );

rewards_table _rewards( get_self(), get_self().value );

const auto& row = _rewards.get( owner.value, "no rewards to claim" );

eosio::token::transfer_action transfer( "eosio.token"_n, { get_self(), "active"_n });
transfer.send( get_self(), owner, row.quantity, "producer block pay" );

_rewards.erase(row);
}

void bpay::on_transfer( const name from, const name to, const asset quantity, const string memo ) {
if (from == get_self() || to != get_self()) {
return;
}

// ignore eosio system incoming transfers (caused by bpay income transfers eosio => eosio.bpay => producer)
if ( from == "eosio"_n) return;

symbol system_symbol = eosiosystem::system_contract::get_core_symbol();

check( quantity.symbol == system_symbol, "only core token allowed" );

rewards_table _rewards( get_self(), get_self().value );
eosiosystem::producers_table _producers( "eosio"_n, "eosio"_n.value );

eosiosystem::global_state_singleton _global("eosio"_n, "eosio"_n.value);
check( _global.exists(), "global state does not exist");
uint16_t producer_count = _global.get().last_producer_schedule_size;

asset reward = quantity / producer_count;

// get producer with the most votes
// using `by_votes` secondary index
auto idx = _producers.get_index<"prototalvote"_n>();
auto prod = idx.begin();

// get top n producers by vote, excluding inactive
std::vector<name> top_producers;
while (true) {
if (prod == idx.end()) break;
if (prod->is_active == false) continue;

top_producers.push_back(prod->owner);

if (top_producers.size() == producer_count) break;

prod++;
}

// distribute rewards to top producers
for (auto producer : top_producers) {
auto row = _rewards.find( producer.value );
if (row == _rewards.end()) {
_rewards.emplace( get_self(), [&](auto& row) {
row.owner = producer;
row.quantity = reward;
});
} else {
_rewards.modify(row, get_self(), [&](auto& row) {
row.quantity += reward;
});
}
}
}

} /// namespace eosio
46 changes: 31 additions & 15 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 Expand Up @@ -1765,4 +1781,4 @@ namespace eosiosystem {
void add_to_blockinfo_table(const eosio::checksum256& previous_block_id, const eosio::block_timestamp timestamp) const;
};

}
}
55 changes: 52 additions & 3 deletions contracts/eosio.system/ricardian/eosio.system.contracts.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ icon: @ICON_BASE_URL@/@REX_ICON_URI@

A sell order of the purchased amount can only be initiated after waiting for the maturity period of 4 to 5 days to pass. Even then, depending on the market conditions, the initiated sell order may not be executed immediately.

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

---
spec_version: "0.2.0"
title: Donate system tokens to REX
summary: '{{nowrap payer}} donates {{nowrap quantity}} tokens to REX'
icon: @ICON_BASE_URL@/@REX_ICON_URI@
---

{{quantity}} is taken out of {{payer}}’s token balance and given to REX with the included memo: "{{memo}}".

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

---
Expand Down Expand Up @@ -402,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 @@ -478,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 Expand Up @@ -817,4 +855,15 @@ summary: 'Execute next annual rate schedule'
icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@
---

{{$action.account}} to execute the next upcoming annual rate schedule.
{{$action.account}} to execute the next upcoming annual rate schedule.

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

---
spec_version: "0.2.0"
title: Unvest Tokens
summary: 'Reclaim and retire unvested tokens'
icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@
---

Reclaim and retire {{$action.unvest_net_quantity}} and {{$action.unvest_cpu_quantity}} worth of unvested tokens from the account {{$action.account}}.
Loading

0 comments on commit 1117b73

Please sign in to comment.