Monitoring tokenomics health

In this post, I’m going to focus on how we can monitor the health of emissions and inflation in real time. The idea is to make sure that the system is working as intended—basically, ensuring everything is running smoothly and we’re not seeing any unintended behavior.

To do this, I’m going to look at two kinds of metrics. First, empirical metrics that will compare actual emissions to the values we expect or desire. These will help us keep track of how closely the system is following the plan.
Second, I’ll introduce a more fundamental metric, which is meant to act as a check for errors in the emission calculation itself. If something goes wrong with the underlying math or the system isn’t behaving as expected, this metric should alert us early on.

Here I discuss three initial metrics for evaluating the health of the tokenomics in our network model: Normalized Treasury Percent Decrease, Normalized Emission per Unit Staked, and Normalized Volatility.

  1. Normalized Treasury Percent Decrease

This measures the fraction of the treasury’s total value that is emitted or distributed over a given period. According to the white paper, we have a nominal value of 0.025 per month. So I calculate the percentage decrease and normalise by 0.025. Monitoring the actual yield against this target provides an immediate indication of whether the system is behaving as expected. From my testing this metric always seems to be a clear indicator that something has gone wrong. This is also the most interpretable and easiest to compute. The same can be done with the staking APY of 12%.

  1. Normalized Emission per Unit Staked

The Normalized Emission per Unit Staked metric compares the actual emission per unit staked against the target emission rate, which is derived from the white paper’s model.

3. Normalized Inflation Rate

The Normalized Inflation Rate measures the actual inflation rate against the target inflation rate. Here I am referring to inflation as the fraction of change in the circulating supply.

We need the absolute value in the last expression if we take a log.

For all three of these metrics, if they are above 1 this means have higher X than our target/nominal value. In some instances I will take a log of these values.

To evaluate these metrics, I simulated the following scenarios:

  1. Base Scenario: Reflects the standard network parameters. (Defaults in the simulator)
  2. Double Emission: Doubles the emission rate to observe its impact on inflation and treasury health. This simulates if someone were to make an error and we accidentally double all emissions.
  3. More Validators and topics: I upped the frequency to 10 per month for each
  4. No Staking: Assumes minimal staking behavior, testing the network’s ability to function with low participation. (1% staking fraction for team, participants, and investors)
  5. Complete Staking: Assumes all tokens are staked, testing the network’s limits under maximum security. (99% staking fraction for team, participants, and investors)
  6. No fee revenue
  7. Volatile staking - I made it so the staking fractions for team, participants, and investors are randomly drawn between .2 and .8 each epoch.

The values for scenarios 1-6 are here:

scenarios = [

    {"name": "Base Scenario"},

    {"name": "Double Emission", "f_emission": 0.05},

    {"name": "More Validators and Topics", "monthly_new_validators": 10, "monthly_new_topics": 10},

    {"name": "No Staking", "participants_stake_fraction": 0.01, "team_stake_fraction": 0.01, "investors_seed_stake_fraction": 0.01, "investors_preseed_stake_fraction": 0.01},

    {"name": "Complete Staking", "participants_stake_fraction": 0.99, "team_stake_fraction": 0.99, "investors_seed_stake_fraction": 0.99, "investors_preseed_stake_fraction": 0.99},

    {"name": "No Fees", "f_fees": 0.0},

]

The results are below:

  1. Base Scenario:
  2. Double Emission:
  3. More Validators and topics:
  4. No staking
  5. Complete Staking:
  6. No fee revenue
  7. Volatile staking

Discussion:
I think really any of these metrics could work. The most interpretable and simple is the normalised rate of treasury decrease. It doesn’t point to exactly what is wrong but it is an indicator that something is going wrong. The normalised emission per unit staked I consider does not seem to give much information at all. The log of the normalised inflation rate appears useful, it seems that is positive after all token unlocks in all problematic cases, and is negative after 36 weeks in the base scenario. The volatility I introduced in the last example highlights the need to filter/EMA these metrics, but the normalised inflation rate appears the most robust to this volatility.

Thanks much once again! Some brief thoughts below:

  • I think the interpretation of f_emission = 0.025 might not be expressed that well in words in the whitepaper. Therefore, I’m not certain if its use in your first metric is correct. In the whitepaper, there is an additional factor (next to the staked fraction normalisation) of N_circ/N_total. If you account for that factor, are you closer? Because being 10^4 off as suggested by your figures clearly isn’t right.
  • For your second and third metrics, I presume you’re conscious of the absence of N_epochs, i.e. you’ve implicitly normalised this out by integrating over the past month?
  • In the first experiment, I’m confused why the 2nd metric seems to indicate too high emission, and the 3rd metric indicates too low emission. I would expect the metrics to be mutually consistent. Or is there a missing log somewhere?
  • Oh wait, only now I notice that the order of the metrics is reversed in the plots relative to the description… sorry I was slow :smiley:
  • Summary: I think metrics 1 and 3 might still need some iteration. Thankfully this statement is true irrespectively of the order!

I see. I have now accounted for N_circ/N_total and that metric now makes more sense. That 10^4 jump in inflation was because I was calculating it including token unlocks which was not included in the “nominal” inflation rate I was normalizing it to. I now only consider the reward emissions over the last month and that seems to help. Correct. This assumes measurements over a month period. In practice, say we measure a day’s data, we would then need to convert. Sorry for the ordering mismatch.

In the correct order:

  1. Normalized Treasury Percent Decrease
  2. Normalized Emission per Unit Staked
  3. Normalized Inflation Rate (this is the only one with a log)

Normal emissions:


Double emissions:

No staking:

These results look better. The metrics tend to agree more (are positive or larger than 1 when they should be). But the first metric, the treasury decrease, seems suspicious. It goes below one (first and second plot) often when the others don’t (or positive for the third metric). I am still investigating.

Yeah, I think this is great as a start. Would it make sense to calculate all three?

For treasury decrease, I agree this is kind of strange. I’m also a bit surprised it goes negative in the no staking case. This metric still feels a bit off…

I have cleaned things up:

  • The first metric behaves much better now
  • Introduce a fundamental metric as discussed with diederik

I was making an error in the way I was normalising my first metric. As a reminder the three metrics are:


The sim results are as follows (this still has fee revenue):




We talked offline about running with no fee revenue, results are here:




I think this is behaving correctly now. When we have no fees and no staking, the first metric sits at 1 after vesting is complete. The first metric is no longer doing bizarre things when compared to the other metrics.

For the fundamental metric discussed in the research sync, I developed a method to check the emission calculations, including the EMA. The details are described here:


The code is here:

def fundamental_metric( E_i, E_i_prev, N_staked_i, N_staked_i_prev, T_total_i, N_circ_i, N_total_i,

 N_epochs, alpha_e, f_e, xi_max, f_stakers_i):

  e_measured_i = (E_i * N_epochs) / N_staked_i
  
  e_target_i = (f_e * T_total_i * N_circ_i) / (N_staked_i * N_total_i)
  
  e_max_i = xi_max / f_stakers_i
  
  e_calculated_i = (alpha_e * min(e_target_i, e_max_i) +
  
   (1 - alpha_e) * (E_i_prev * N_epochs) / N_staked_i_prev)
  
  
  fundamental_metric_value = e_measured_i / e_calculated_i
  
  return fundamental_metric_value

Then plotting the log of this metric on simulated data:


This is essentially 1 +/- numerical errors I believe.

The smallest value is 0.99628. I’m not exactly sure what is causing the fluctuations around 1.

1 Like

Nice! Yeah, it’s not exactly 1, but probably close enough. When implementing this, we’ll need to be aware that the chain sometimes has multiple updates per cycle. So we might need to reverse-engineer some part of the inputs. But yes, this looks great!