Skip to content

Commit

Permalink
Implement eosio.fees core logic
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisCarriere committed Apr 7, 2024
1 parent c9f2714 commit a993d99
Show file tree
Hide file tree
Showing 13 changed files with 579 additions and 89 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

The `eosio.fees` contract handles system fee distribution.

## Strategies

The `eosio.fees` contract is designed to distribute fees from any outstanding EOS balance to a set of strategies. Each strategy is responsible for distributing the fees to a specific pool or contract based on a predefined logic. Each strategy has an associated weight which determines the percentage of fees that will be distributed to that strategy.

| Strategy | Description |
| ------------- | --- |
| `donatetorex` | Donate to REX - Distributes fees to REX pool which is distributed to REX holders over a 30 day period |
| `buyramburn` | Buy RAM & Burn - locks up additional EOS in RAM pool while reducing the total circulating supply of RAM

## Development and Testing

### Build Instructions
Expand Down
30 changes: 29 additions & 1 deletion eosio.fees.contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,32 @@ summary: 'distribute'
icon: https://gateway.pinata.cloud/ipfs/QmZ4HSZDuSrZ4BHawtZRhVfwyYJ4DepNJqVDzxY59KveiM#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19
---

Distribute the system fee to the corresponding account.
Distribute the system fee to the corresponding account.

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

---
spec_version: "0.2.0"
title: init
summary: 'init'
icon: https://gateway.pinata.cloud/ipfs/QmZ4HSZDuSrZ4BHawtZRhVfwyYJ4DepNJqVDzxY59KveiM#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19
---

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

---
spec_version: "0.2.0"
title: setstrategy
summary: 'setstrategy'
icon: https://gateway.pinata.cloud/ipfs/QmZ4HSZDuSrZ4BHawtZRhVfwyYJ4DepNJqVDzxY59KveiM#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19
---


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

---
spec_version: "0.2.0"
title: delstrategy
summary: 'delstrategy'
icon: https://gateway.pinata.cloud/ipfs/QmZ4HSZDuSrZ4BHawtZRhVfwyYJ4DepNJqVDzxY59KveiM#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19
---
99 changes: 93 additions & 6 deletions eosio.fees.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,104 @@

namespace eosio {

// @system or @user
[[eosio::on_notify("*::transfer")]]
void fees::on_transfer( const name from, const name to, const asset quantity, const string memo )
[[eosio::action]]
void fees::init( const uint32_t epoch_time_interval ) {
require_auth( get_self() );

settings_table _settings( get_self(), get_self().value );
auto settings = _settings.get_or_default();
settings.epoch_time_interval = epoch_time_interval;
_settings.set(settings, get_self());
}

[[eosio::action]]
void fees::setstrategy( const name strategy, const uint16_t weight ) {
require_auth( get_self() );

strategies_table _strategies( get_self(), get_self().value );

// validate input
check(weight > 0, "weight must be greater than 0");
check(STRATEGIES.find(strategy) != STRATEGIES.end(), "strategy not defined");

// update weights
auto itr = _strategies.find(strategy.value);
if (itr == _strategies.end()) {
_strategies.emplace(get_self(), [&](auto& row) {
row.strategy = strategy;
row.weight = weight;
});
} else {
_strategies.modify(itr, get_self(), [&](auto& row) {
row.weight = weight;
});
}
}

[[eosio::action]]
void fees::delstrategy( const name strategy )
{
// ignore transfers not sent to this contract
if (to != get_self()) { return; }
require_auth( get_self() );

strategies_table _strategies( get_self(), get_self().value );
auto &itr = _strategies.get(strategy.value, "strategy not found");
_strategies.erase(itr);
}

[[eosio::action]]
void fees::distribute()
{
require_auth( get_self() );
// any authority is allowed to call this action
update_next_epoch();

strategies_table _strategies( get_self(), get_self().value );
const uint16_t total_weight = get_total_weight();

// distributing fees in EOS
const asset balance = eosio::token::get_balance( "eosio.token"_n, get_self(), symbol_code("EOS") );

for ( auto& row : _strategies ) {
const asset fee_to_distribute = balance * row.weight / total_weight;
if (fee_to_distribute.amount <= 0) continue; // skip if no fee to distribute

// Donate to REX
// Distributes fees to REX pool which is distributed to REX holders over a 30 day period
if ( row.strategy == "donatetorex"_n) {
eosiosystem::system_contract::donatetorex_action donatetorex( "eosio"_n, { get_self(), "active"_n });
donatetorex.send( get_self(), fee_to_distribute, "system fees" );
// Buy RAM & burn bytes
// locks up additional EOS in RAM pool while reducing the total circulating supply of RAM
} else if ( row.strategy == "buyramburn"_n) {
eosiosystem::system_contract::buyramburn_action buyramburn( "eosio"_n, { get_self(), "active"_n });
buyramburn.send( get_self(), fee_to_distribute, "system fees" );
}
}
}

uint16_t fees::get_total_weight()
{
strategies_table _strategies( get_self(), get_self().value );

uint16_t total_weight = 0;
for (auto& row : _strategies) {
total_weight += row.weight;
}
return total_weight;
}

void fees::update_next_epoch()
{
fees::settings_table _settings( get_self(), get_self().value );
auto settings = _settings.get();

// handle epoch
const uint32_t now = current_time_point().sec_since_epoch();
const uint32_t interval = settings.epoch_time_interval;
check( settings.next_epoch_timestamp.sec_since_epoch() <= now, "epoch not finished");

// update next epoch (round to the next interval)
settings.next_epoch_timestamp = time_point_sec( (now / interval) * interval + interval );
_settings.set( settings, get_self() );
}

} /// namespace eosio
118 changes: 100 additions & 18 deletions eosio.fees.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,109 @@

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

using namespace std;

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

/**
* Disallow sending tokens to this contract.
*/
[[eosio::on_notify("*::transfer")]]
void on_transfer(const name from, const name to, const asset quantity, const string memo);

[[eosio::action]]
void distribute();

using distribute_action = eosio::action_wrapper<"distribute"_n, &fees::distribute>;
};

const set<name> STRATEGIES = {
"buyramburn"_n,
"donatetorex"_n
};
/**
* The `eosio.fees` contract handles system fees distribution.
*/
class [[eosio::contract("eosio.fees")]] fees : public contract {
public:
using contract::contract;

/**
* ## TABLE `strategies`
*
* - `{name} strategy` - strategy name
* - `{uint16_t} weight` - strategy weight (proportional to the total weight of all strategies)
*
* ### example
*
* ```json
* [
* {
* "strategy": "buyramburn",
* "weight": 60
* },
* {
* "strategy": "donatetorex",
* "weight": 30
* }
* ]
* ```
*/
struct [[eosio::table("strategies")]] strategies_row {
name strategy;
uint16_t weight;

uint64_t primary_key() const { return strategy.value; }
};
typedef eosio::multi_index< "strategies"_n, strategies_row > strategies_table;

/**
* ## TABLE `settings`
*
* - `{uint32} epoch_time_interval` - epoch time interval in seconds (time between epoch distribution events)
* - `{time_point_sec} next_epoch_timestamp` - next epoch timestamp event to trigger strategy distribution
*
* ### example
*
* ```json
* {
* "epoch_time_interval": 600,
* "next_epoch_timestamp": "2024-04-07T00:00:00"
* }
* ```
*/
struct [[eosio::table("settings")]] settings_row {
uint32_t epoch_time_interval = 600; // 10 minutes
time_point_sec next_epoch_timestamp;
};
typedef eosio::singleton< "settings"_n, settings_row > settings_table;

/**
* Initialize the contract with the epoch period.
*
* @param epoch_period - epoch period in seconds
*/
[[eosio::action]]
void init( const uint32_t epoch_period );

/**
* Set a strategy with a weight.
*
* @param strategy - strategy name
* @param weight - strategy weight
*/
[[eosio::action]]
void setstrategy( const name strategy, const uint16_t weight );

/**
* Delete a strategy.
*
* @param strategy - strategy name
*/
[[eosio::action]]
void delstrategy( const name strategy );

/**
* Distribute fees to all defined strategies.
*/
[[eosio::action]]
void distribute();

using distribute_action = eosio::action_wrapper<"distribute"_n, &fees::distribute>;

private:
void update_next_epoch();
uint16_t get_total_weight();
};
} /// namespace eosio
Loading

0 comments on commit a993d99

Please sign in to comment.