Skip to content

Commit

Permalink
Walkthrough & initial simplification for Shannon (#3)
Browse files Browse the repository at this point in the history
This was a collaboration between @RawthiL and @Olshansk to go through the notebooks and understand how the repo can be simplified into an initial version that can go to production.

See pokt-network/poktroll#708 for reference.

---------

Co-authored-by: Ramiro Rodriguez Colmeiro <[email protected]>
  • Loading branch information
Olshansk and RawthiL authored Aug 28, 2024
1 parent 6d343af commit 2797337
Show file tree
Hide file tree
Showing 17 changed files with 5,548 additions and 3,252 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ untracked
.vscode
__pycache__
venv
.DS_store
.venv
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ py_format: check-env ## Format the python code
black .
isort .

########################
### Marimo notebooks ###
########################

.PHONY: marimo_edit_tokenomics_compare
marimo_edit_tokenomics_compare: check-env ## Edit the tokenomics compare notebook
cd notebooks && marimo edit tokenomics_simplified_marimo.py

####################
### Your stuff ###
####################
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ This repository contains preliminary tests for the Shannon tokenomics.
make pip_freeze
```

###

## Source Data

The data used to run this models can be gathered from [POKTscan](https://poktscan.com/).
Expand All @@ -33,3 +31,17 @@ by downloading the `Performance` table. A sample of this data is provided in the

- **Tokenomics_Compare** : This notebook makes a simple comparison (a single scenario) of two proposed models.
- **Random_Scenarios** : This notebook tests many random network scenarios (services, relays and compute costs) and relays/nodes distributions over services on all the proposed models and then compares them head to head using several graphics.

## [WIP] How to make TLM's avoid being gameable?

1. Distribute rewards evenly.

1. How? All the POKT to be minted in a session should be evenly distributed
evenly amongst all the nodes in that session.
2. Why? To avoid gaming the system and pointing at your own node
3. Issues? Free loader nodes.
4. How do we punish free loader nodes? Will require implicit QoS.

2. Delaying CUTTM (discuss more later)
1. How? The delay mechanism for updating RTTM for each service.
2. Why?
Binary file added assets/TLMs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions lib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Tokenomics Models <!-- omit in toc -->

This folder contains three different tokenomic models:

- [Shane](#shane)
- [MINT-V2](#mint-v2)
- [Token Logic Modules](#token-logic-modules)
- [Core Model](#core-model)
- [Boosts](#boosts)

## Shane

A python version of the final [spreadsheet model from Shane](https://docs.google.com/spreadsheets/d/1-_G8VCQ7WbytNmps_N6LhQJatPN3CvV6z2_e0bN9cC0/edit).

The model implements the initial concept of Token Logic Modules (TLMs).

## MINT-V2

This is an upgrade to the [MINT model](https://forum.pokt.network/t/mint-incentivizing-network-transformation-mint/4522) featuring non-linear parameter modulation and a normalization mechanic based on the entropy concept [described here](https://forum.pokt.network/t/v1-how-to-enforce-fairness-and-decentralization-a-first-approach/3992).

## Token Logic Modules

_tl;dr Independent decoupled tokenomic modules._

This is an composed version of the two models above. While more complex, it enables the implementation of arbitrary politics that can be applied or removed without cross-talk. For example, if we want to increase the DAO income by means of over-minting, we don't need to change the base minting, increase the DAO take and reduce the Validators, Sources and Servicers take, instead, we would only increase the DAO boost module (or add a new one).

The concept of the TLM is described in the following figure:
![TLMs Flow Diagram](../assets/TLMs.png)
The TLM model is composed of a single `Core Model` and multiple `Boosts`.

### Core Model

The `Core Model` represents the ultimate tokenomics of the network, meaning, self-sustained economy with `mint == burn` (or very little supply growth/attrition). This module will mint proportional to the burn and divide the rewards among actors following a share table similar to Morse.

### Boosts

The `Boosts` (DAO, Validator, Supplier, Source) are modules that provide extra minting to a given actor. A module has the following characteristics:

- _Recipient_ : The `network actor` that will receive the boost. A TLM can only affect a single actor.
- _Condition_ : A set of thresholds over a given network parameter that determine whether to apply or not the TLM.
- _Minting_ : The logic required to produce the minting. This function can be arbitrary, the only condition is that it should provide a value for each service in the network as return value.
- _Budget_ : The maximum amount of coins that a module can produce. This is potentially optional, but is useful to control overall minting. For example, a given module can only mint up to a given percentage of the total supply (annualized) or a fixed amount of POKT coins.

After all the TLMs are executed, the proposed minting values are processed by the `global module`. This module applies base minting values (for example a base minting of 15000 USD to all suppliers) and controls the total supply growth (a safe mechanisms to keep supply growth controlled).

Finally the minting budget is processed by the normalization and penalties module. This module takes the potential minting and strictly reduces it, this means that this module will never output more coins (in total) than the previous one. Here the service imbalance correction and under-provided minting limits are implemented.
227 changes: 227 additions & 0 deletions lib/boost.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
from dataclasses import dataclass
from typing import Callable, List

import math_utils
import pandas as pd


@dataclass
class Condition:
# TODO: Make ENUM for metrics
metric: str # total_cus, sum_emas_relays, etc...
low_threshold: float
high_threshold: float


@dataclass
class Point:
x: float
y: float


@dataclass
class Variables:
x: str
y: str


@dataclass
class Budget:
type: str
value: float


@dataclass
class Parameters:
start: Point
end: Point
variables: Variables
budget: Budget


@dataclass
class TLMBoost:
name: str
actor: str # make this an enum too
conditions: List[Condition]
minting_func: Callable
parameters: Parameters


# TODO_FUTURE:
# 1. report card how well you did
# 2. punch card: how much you did -> EMA of # relays
# -> Low -> shitty job based on off chain (implicit QoS)
# -> high -> good job based on off chain (implicit QoS)


def boost_cuttm_f_CUs_nonlinear(tlm_per_service_df: pd.DataFrame, network_macro: dict, params: dict) -> pd.Series:
"""
This is a basic non-linear boost functions that modifies the CUTTM as a
function of the CUs:
CUTTM = f(CU)
This is the result of separating the MINT-V2 mechanisms into stand-alone
modules.
Goal of this function: Trying to achieve a boost value for the CUTTM
Boost should be near zero when in equilibrium.
"""

# Assert that the TLM config is correct
assert params.variables.x == "total_cus"
assert params.variables.y == "CUTTM"

# Calculate the non-linear parameters of this boost
a_param, b_param = math_utils.get_non_linear_params(
[params.start.x, params.start.y],
[params.end.x, params.end.y],
)
# Calculate the parameter cap
param_cap = None
if params.budget.type == "annual_supply_growth":
# Calculate annualized growth
param_cap = [
((params.budget.value / 100.0) * network_macro["total_supply"]) / (network_macro["total_cus"] * 365.2)
]
else:
raise ValueError('Budget type "%s" not supported' % params.budget.type)
# Calculate the parameter to use
param_use = math_utils.calc_non_linear_param(
[network_macro[params.variables.x]],
a_param,
b_param,
params.end.x,
bootstrap_start=params.start.x,
max_param=param_cap,
)

# Calculate (maximum) total minted in each service
# The same as the core TLM (or any TLM) this value will be potentially reduced
return param_use * tlm_per_service_df["cu_per_node"] * tlm_per_service_df["active_nodes"]


dao_boost = TLMBoost(
name="DAO Boost",
actor="DAO",
conditions=[Condition(metric="total_cus", low_threshold=0, high_threshold=2500 * 1e9)],
minting_func=boost_cuttm_f_CUs_nonlinear,
parameters=Parameters(
start=Point(x=250 * 1e9, y=5e-9),
end=Point(x=2500 * 1e9, y=0),
variables=Variables(x="total_cus", y="CUTTM"),
budget=Budget(
type="annual_supply_growth",
value=0.5,
),
),
)

proposer_boost = TLMBoost(
name="Proposer Boost",
actor="Validator",
conditions=[Condition(metric="total_cus", low_threshold=0, high_threshold=2500 * 1e9)],
minting_func=boost_cuttm_f_CUs_nonlinear,
parameters=Parameters(
start=Point(x=250 * 1e9, y=2.5e-9),
end=Point(x=2500 * 1e9, y=0),
variables=Variables(x="total_cus", y="CUTTM"),
budget=Budget(
type="annual_supply_growth",
value=0.25,
),
),
)

supplier_boost = TLMBoost(
name="Supplier Boost",
actor="Supplier",
conditions=[Condition(metric="total_cus", low_threshold=0, high_threshold=2500 * 1e9)],
minting_func=boost_cuttm_f_CUs_nonlinear,
parameters=Parameters(
start=Point(x=250 * 1e9, y=3.5e-8),
end=Point(x=2500 * 1e9, y=0),
variables=Variables(x="total_cus", y="CUTTM"),
budget=Budget(
type="annual_supply_growth",
value=3.5,
),
),
)

source_boost_1 = TLMBoost(
name="Sources Boost 1 - CU Based",
actor="Source",
conditions=[Condition(metric="total_cus", low_threshold=0, high_threshold=2500 * 1e9)],
minting_func=boost_cuttm_f_CUs_nonlinear,
parameters=Parameters(
start=Point(x=250 * 1e9, y=7.5e-9),
end=Point(x=2500 * 1e9, y=0),
variables=Variables(x="total_cus", y="CUTTM"),
budget=Budget(
type="annual_supply_growth",
value=0.75,
),
),
)


# def boost_prop_f_CUs_sources_custom(tlm_per_service_df: pd.DataFrame, network_macro: dict, params: dict) -> pd.Series:
# """
# This boost is a proportional cuttm boost on top of sources boost.
# It is intended to reflect the additional per-service boost that is applied
# in the spreadsheet as the "sources boost" made by Shane.
# """

# assert params.variables.x == "total_cus"

# # The modulation of the parameter is linear
# a_param, b_param = math_utils.get_linear_params(
# [params.start.x, params.start.y],
# [params.end.x, params.end.y],
# )
# max_mint = -1
# if params.budget.type == "annual_supply_growth":
# # Calculate annualized growth
# max_mint = ((params.budget.value / 100.0) * network_macro["total_supply"]) / (365.2)
# elif params.budget.type == "POKT":
# max_mint = params.budget.value
# else:
# raise ValueError('Budget type "%s" not supported' % params.budget.type)
# param_use = math_utils.calc_linear_param(
# [network_macro[params.variables.x]],
# a_param,
# b_param,
# params.end.x,
# bootstrap_start=params.start.x,
# )

# # Calculate (maximum) total minted in each service
# per_service_max = (
# param_use * network_macro["CUTTM"] * tlm_per_service_df["cu_per_node"] * tlm_per_service_df["active_nodes"]
# )
# # Apply budget
# if max_mint > 0:
# if max_mint < per_service_max.sum():
# # Scale values
# per_service_max *= max_mint / per_service_max.sum()
# # Return amount to mint in each service by this boost
# return per_service_max


# source_boost_2 = TLMBoost(
# name="Sources Boost 2 - Shane's",
# recipient="Source",
# conditions=[Condition(metric="total_cus", low_threshold=0, high_threshold=1500 * 1e9)],
# minting_func=boost_prop_f_CUs_sources_custom,
# parameters=Parameters(
# start=Point(x=5 * 1e9, y=0.9 * 0.7),
# end=Point(x=1500 * 1e9, y=0.1 * 0.7),
# variables=Variables(x="total_cus", y="CUTTM"),
# budget=Budget(
# type="POKT",
# value=40e3,
# ),
# ),
# )
Loading

0 comments on commit 2797337

Please sign in to comment.