# Tokenomics Simulator
This work is a simulator SaaS for Tokenomics applications that is based upon the research conducted in [this paper](https://arxiv.org/abs/2210.12881#). We help model various economic paradigms for systems that rely on underlying token rewards and incentives.
- [Here](https://utexas.app.box.com/s/vtz1lksutqt8cb4ag1p4r6ydz49q1hyp) [Slides 12-21] is further information regarding Tokenomics.
BME (Burn-Mint-Equilibrium) is an economic policy that we simulate wherein the overall influx of value (minting) is completely used up reward the suppliers (burning) at any given time step.
- [This](https://messari.io/report/burn-and-mint-equilibrium) is a useful article that entails the explanation of BME in some detail.
- [Here](https://medium.com/mvp-workshop/burn-and-mint-equilibrium-pros-and-con-s-c27d83748cf5) is another resource that gets into BME in more detail.
To support such economic policies, we define **Controllers** that enforce the system to behave according to them. These are user-specified and can be used to simulate various scenarios.
# Usage
Run the simulation using
```sh
python3 run.py --profile_path <PATH_TO_PROFILE> --output_dir <OUTPUT_DIR>
```
We have provided some example profiles to use inside of the `sample_profiles/` directory
# Run Vanilla BME
Take a look at the sample [BME profile](https://github.com/YasasviPeruvemba/tokenomics_simulator/blob/main/sample_profiles/sample_bme_profile_multiconsumer.json), and run the following command -
```
python3 run.py --profile_path sample_profiles/sample_bme_profile_multiconsumer.json --output_dir sim_plots/bme_plots
```
This should generate an image similar as shown below at - *sim_plots/bme_plots/sim.jpg*

Insights into the plots above
- The Token Reserve as well as Dollar Reserve remain unchanged throughout the simulation which is expected in a Burn-Mint Equilibrium case.
- We are buying back from the Investor, Advisor and Team Pool of tokens to maintain the equillibrium - while the tokens held by the supplier increase gradually based on their demand.
- Since there are these external vested tokens that come into the system, the overall token price gradually dives.
# Code Structure
- constants.py : This file contains all of the constant dependencies regarding this project, like the type of system dynamics supported, controller types, node types, etc.
- controller.py : This file represents the various controllers that can be used to simulate various use cases. These range from simple BME to more complex PID controller implementations.
- state.py : This file contains the various classes used to hold imoprtant information pertaining to the simulation like the ControlSignal, State, ReferenceState, Forecast, etc.
- system_dynamics.py : This file contains the logic related to how the dynamics of the system go, i.e how the supply and token price change corresponding to the control signal.
- time_series.py : This file contains the classes that contain and help generate the time series data for the simulations, ranging from demand, number of consumers, etc.
- sim_profile.py : This file contains the logic to interpret and parse the profile JSON that the user provides and places the information into useful classes (like the Controller, Node, TimeSeries and SystemDynamics) to aid in the simulation.
- simulator.py : This is the overall wrapper that contains how to parse the profile, and run the simulations, and finally generate the plots corresponding to the run that was simulated.
- run.py : This is a simple wrapper over the simulator, that takes the input profile from the user, runs the simulation and dumps the plots to the required directory.
# System Architecture

The entire simulation hinges on the profile that is provided. The profile contains important aspects like the Customers, Suppliers, Dynamics and the Controller.
## Customers
The user can provide the classes of customers within the profile as explained in the section below. For each class of customers, the user is also required to provide the time series data that corresponds to the number of customers of that class in the system. The user is also required to specify the **split of the revenue** that each supplier class will get. All of this information is extracted from the profile and read into corresponding timeseries classes, from which consumer demand per each supplier is formulated.
## Suppliers
The user simply provides information pertaining to the number of unique suppliers that are present in the system. This is set within the profile and interpreted to derive per supplier stats.
## System Dynamics
The user is required to provide a dynamics that the system adheres to, this is further specified in detail within the original [tokenomics paper](https://arxiv.org/abs/2210.12881#). This simulator accepts default dynamics that is provided in the paper, as well as external dynamics that takeas into account capital investment, external demand on an exchange and and investor / advisor / team vesting schedules.
## Controller
The user is required to provide a controller within the profile. This is used to govern the simulations by determining the amount of payments we make to the suppliers as well as how many tokens are bought back from the circulating supply of tokens. More information can be found in detail explained in the [tokenomics paper](https://arxiv.org/abs/2210.12881#). The simulator currently supports 2 types of control, namely BME (Burn-Mint-Equillibrium), and PID (Proportional, Integral and Differential).
## Overall Workflow
The profile is interpreted into the customer side revenue and supplier side revenue as a time series, along with the system dynamics and controller. Depending upon the type of simuation and information provided, the external demand on an exchange and the investor / advisor / team schedules are also read into the system. Once these aspects are set, the simulation starts off with the initial state as provided by the user within the profile.
For each time step, the main loop does the following :
- Derive the **forecast vector** for this timestamp which contains information like demand in the next timestamp, external demand on the exchange, etc.
- Derive the **reference vector** for the price in case the control used is PID.
- Maintain the aggregate **state** of the system by accumulating the token supplies among the suppliers and other external influences (like investors, advisors, etc), the dollar and token reserves and the token price in the current time step.
- Pass the **forecast vector**, **reference vector** and the **state** into the controller to determine the **control signal**, which represents the amount of payments and buybacks we issue.
- Pass the current **state** and the **control signal** into the **dynamics** function to determine the **next state**. Correspondingly update the token supply among supplier classes as well as external influences (advisors, investors, etc) to keep track of per-supplier statistics.
# Running Custom Simulations (Modifying/Creating your own Profile)
In order to run custom simulations, one can start off with the example profile seen [here](https://github.com/YasasviPeruvemba/tokenomics_simulator/blob/main/sample_profiles/sample_bme_profile_multiconsumer.json).
## Time series specification within the Profile
- time_series_type : This can be one of `LINEAR, EXPONENTIAL, FLIPPED_EXPONENTIAL, SIGMOID, FILE, STEP`.
- LINEAR : The linear function is defined as -
```
{0 : t < start_time
t / (stop_time - start_time) : start_time < t < stop_time
1 : t > stop_time}
```
- SIGMOID : The sigmoid function over time t is defined as -
```
1 / ( 1 + exp(-(t + shift * total_time)/(total_time * 2 * spread)))
```
- EXPONENTIAL : The exponential function over time t is defined as -
```
alpha ^ (t / (2 * total_time))
```
- FLIPPED_EXPONENTIAL : Same as above but flipped about the time axis
- STEP : The step function is defined as -
```
lower_lim + (t * step) (until upper_lim is reached)
```
- time_series_attributes : These contain the information required to build timeseries data.
- start : Represents the start timestamp.
- end : Represents the end timestamp.
- duration : Corresponds to the duration of the timeseries : [0, duration).
- upper_lim : Upper limit of data produced.
- lower_lim : Lower limit of data produced.
- spread : Only used with `SIGMOID`, represents spreading of the data.
- shift : Only used with `SIGMOID`, represents the shift in data.
- interval : Only used with `STEP`, represents the increase interval.
- guassian_noise : True/False.
- noise_coef : Only used if `guassian_noise` is True, represents amount of noise.
## Important parts of the Profile
Most of the keys are self explanatory, but here is an exhaustive list of the parameters seen :
- profile_type : Set this as `DEFAULT`
- node_types : This contains the information about the consumer classes, i.e, it contains a list of the dictionaries that correspond to each consumer class, that has the following properties :
- node_type : Set this as `CUSTOMER`
- node_attributes : This contains a dictionary of `consumption_rate` (dollars spent per consumer) and the `supplier_matrix` (a list of revenue split fractions for each supplier).
- time_series_type, time_series_attributes : As per the timeseries specification, indicating the number of customers.
- supplier : This contains information regarding the suppliers in the following way -
- num_suppliers : The number of different supplier classes.
- bad_actors : A list of fractions that correspond to the %age of bad actors for each supplier class.
- controller : This contains information regarding the controller to use
- control_type : Can be one of `BME, PID`.
- Ki, Kp, Kd, H : Required when `control_type` is `PID`, where Ki, Kp and Kd stand for Integral, Proportional and Differential gain. H is the amount of history to maintain.
- dynamics_type : Can be one of `DEFAULT_DYNAMICS, EXTERNAL_DYNAMICS`
- initial_state : This contains information about -
- circulating_supply : The amount of tokens in circulation.
- dollar_reserve : The amount of dollars in the reserve.
- token_reserve : The amount of tokens in the reserve.
- token_price : The price of the token.
- reference : This is only required if `control_type` is `PID`. This contains the time series specification for the reference price.
- capital : This follows the timeseries specification for the capital investment.
- external : This follows the timeseries specification for the external demand on an exchange.
- advisor : This follows the timeseries specification for advisor vesting.
- investor : This follows the timeseries specification for investor vesting.
- team : This follows the timeseries specification for team vesting.
A detailed guide regarding each moving part of the base code can be seen [here](https://docs.google.com/document/d/1Rh8MmyMfrMkE36xYJ-kYxMAIIvAyXCLrSEI6ZGt6FAI/edit?usp=sharing).
## Example Configuration (with comments)
```js
{
"profile_type": "DEFAULT",
"node_types": [
{ // Consumer Class #1
"node_type": "CUSTOMER",
"node_attributes": {
// The amount that a single consumer will spend in dollars
"consumption_rate": 25,
// A vector of how much this consumer will spend to each supplier
// In this example supplier 1 gets 40%, supplier 2 gets 10%, ...
"supplier_matrix": [0.4, 0.1, 0.5]
},
"time_series_type": "LINEAR", // Choosing a simple linear timeseries
"time_series_attributes": {
"start": 5, // Start timestamp of timeseries
"end": 45, // End timestamp of timeseries
"duration": 50, // How long the timeseries lasts
"upper_lim": 250, // Upper limit of data
"lower_lim": 100, // Lower limit of data
"guassian_noise": true, // Whether or not to use noise Default: false
"noise_coef": 0.1, // Noise coeficeint for guassian_noise option
}
},
{ // Consumer Class #2
"node_type": "CUSTOMER",
"node_attributes": {
"consumption_rate": 10,
"supplier_matrix": [0.2, 0.4, 0.4]
},
"time_series_type": "LINEAR",
"time_series_attributes": {
"start": 5,
"end": 45,
"duration": 50,
"upper_lim": 1000,
"lower_lim": 500,
"guassian_noise": true,
"noise_coef": 0.1,
}
}
],
"supplier": {
"num_suppliers": 3, // Total number of suppliers in the system
"bad_actors": [0.1, 0.1, 0.1] // Percentage of bad actors per supplier class
},
"controller": {
"control_type": "BME_CONTROL" // Choosing PID Control
},
"dynamics_type": "EXTERNAL_DYNAMICS", // Choosing external dynamics
"initial_state": {
"circulating_supply": 2000, // The Token supply in circulation
"dollar_reserve": 1000, // The Dollar reserve in the treasury
"token_reserve": 1000, // How many tokens are held in the treasury
"token_price": 1 // The Token price relative to dollars
},
"reference": {
// A time series of how the reference supply should change
// not used with BME_CONTROL
"time_series_type": "LINEAR",
"time_series_attributes": {
"start": 5,
"end": 35,
"duration": 50,
"upper_lim": 20,
"lower_lim": 1.1
}
},
"capital": {
// A time series for how external capital changes
"time_series_type": "LINEAR",
"time_series_attributes": {
"start": 5,
"end": 35,
"duration": 50,
"upper_lim": 500,
"lower_lim": 100
}
},
"external": {
// A time series for external actors
"time_series_type": "SIGMOID", // Choosing sigmoidal timeseries
"time_series_attributes": {
"start": 5,
"end": 35,
"duration": 50,
"upper_lim": 500,
"lower_lim": 100,
"spread": 0.1, // Spread for the sigmoidal curve
"shift": 0.2 // Shift for the sigmoidal curve
}
},
"investor": {
// A time series for external investors, on a vesting scheldue
"time_series_type": "STEP",
"time_series_attributes": {
"duration": 50,
"interval": 2, // The interval after which data is incremented
"lower_lim": 0,
"upper_lim": 15000
}
},
"advisor": {
// A time series for external advisors, on a vesting scheldue
"time_series_type": "STEP",
"time_series_attributes": {
"duration": 50,
"interval": 5,
"lower_lim": 0,
"upper_lim": 5000
}
},
"team": {
// A time series for team, on a vesting scheldue
"time_series_type": "STEP",
"time_series_attributes": {
"duration": 50,
"interval": 10,
"lower_lim": 0,
"upper_lim": 1000
}
}
}
```
This code is the proprietary property of Chainhub Consulting. LLC, and it is subject to a previously executed end user agreement and contract between Chainhub Consulting, LLC, INC. Any unauthorized use, reproduction, or distribution of this code, in whole or in part, is strictly prohibited.