Last active
April 21, 2020 13:42
-
-
Save gagbo/990028fd5b02740f59c5076689e11ae5 to your computer and use it in GitHub Desktop.
Compute estimation of reward rates for ATOM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from typing import NewType | |
Atom = NewType("Atom", float) | |
Seconds = NewType("Seconds", float) | |
# Network parameters that affect the formula | |
# Target bonded ratio of ATOMs | |
TARGET_BONDED_RATIO = 0.67 | |
# Community pool commission (Fixed by network) | |
POOL_COMMISSION = 0.02 | |
# Assumed average time per block (from the blocks_per_year parameter) | |
ASSUMED_TIME_PER_BLOCK = Seconds(6.5) | |
# Inflation rate change (yearly) | |
INFLATION_RATE_CHANGE = 0.13 | |
# Maximum yearly inflation | |
INFLATION_MAX_VALUE = 0.20 | |
# Minimum yearly inflation | |
INFLATION_MIN_VALUE = 0.07 | |
def compute_avg_yearly_inflation( | |
initial_yearly_inflation: float, bonded_ratio: float | |
) -> float: | |
""" | |
Return the average yearly inflation if we start from initial_yearly_inflation and keep | |
a constant bonded_ratio. | |
This add the effect of capping the inflation to the maximum value or the minimum value | |
Some constants defined in the file help implementing the network rules regarding inflation evolution. | |
""" | |
if ( | |
initial_yearly_inflation < INFLATION_MIN_VALUE | |
or initial_yearly_inflation > INFLATION_MAX_VALUE | |
): | |
raise ValueError( | |
f"Initial inflation ({initial_yearly_inflation}) is not between {INFLATION_MIN_VALUE} and {INFLATION_MAX_VALUE}." | |
) | |
inflation_slope = (1 - bonded_ratio / TARGET_BONDED_RATIO) * INFLATION_RATE_CHANGE | |
unrestricted_end_of_year_inflation = initial_yearly_inflation * ( | |
1 + inflation_slope | |
) | |
if ( | |
unrestricted_end_of_year_inflation <= INFLATION_MAX_VALUE | |
and unrestricted_end_of_year_inflation >= INFLATION_MIN_VALUE | |
): | |
return (initial_yearly_inflation + unrestricted_end_of_year_inflation) / 2 | |
if unrestricted_end_of_year_inflation > INFLATION_MAX_VALUE: | |
diff_to_max = INFLATION_MAX_VALUE - initial_yearly_inflation | |
max_point = diff_to_max / inflation_slope | |
avg_inflation = ( | |
1 - max_point / 2 | |
) * INFLATION_MAX_VALUE + max_point / 2 * initial_yearly_inflation | |
return avg_inflation | |
if unrestricted_end_of_year_inflation < INFLATION_MIN_VALUE: | |
diff_to_min = initial_yearly_inflation - INFLATION_MIN_VALUE | |
min_point = diff_to_min / inflation_slope | |
avg_inflation = ( | |
1 - min_point / 2 | |
) * INFLATION_MIN_VALUE + min_point / 2 * initial_yearly_inflation | |
return avg_inflation | |
raise RuntimeError("Unreachable code") | |
def rate( | |
*, | |
validator_commission: float, | |
bonded_ratio: float, | |
initial_inflation: float, | |
time_per_block: Seconds, | |
daily_fees: Atom, | |
total_supply: Atom, | |
) -> float: | |
""" | |
Compute the yearly rewards rate for a given validator at a given state of the reward chain. | |
Parameters : | |
- validator_commission : the commission the validator advertises | |
- bonded_ratio : the percentage of bonded tokens | |
- initial_inflation : the inflation at the beginning of the computation | |
- time_per_block : the actual average time per block on the blockchain | |
- daily_fees : the average amount of daily fees on the network | |
- total_supply : the total supply of Atom on the chain | |
Main assumptions / error-inducing hypotheses : | |
- The total_supply doesn't move during the year (at least daily_fees/total_supply ratio doesn't move) | |
- The bonded_ratio stays the same during the year | |
- The network parameters (CAPITALIZED_VARS) do not change during the year | |
- 1 uatom is exactly 1 unit of "voting power" (in reality, slashing changes that, but we ignore the difference it makes.) | |
""" | |
# This correction changes how inflation is computed vs. what the network advertises | |
inexact_block_time_correction = ASSUMED_TIME_PER_BLOCK / time_per_block | |
# This correction assumes a constant bonded_ratio, which gives a different yearly inflation as advertised | |
yearly_inflation = compute_avg_yearly_inflation(initial_inflation, bonded_ratio) | |
# This computation adds the fees to the rate computation | |
yearly_fee_rate = daily_fees * 365.24 / total_supply | |
return ( | |
inexact_block_time_correction | |
* (yearly_inflation + yearly_fee_rate) | |
* 1 | |
/ bonded_ratio | |
* (1 - POOL_COMMISSION) | |
* (1 - validator_commission) | |
) | |
def main(*_args, **kwargs): | |
""" | |
Main entrypoint to yearly rate computation | |
""" | |
# Dynamic parameters that affect the formula | |
commission = kwargs.get("validator_commission", 0) | |
bonded_ratio = kwargs.get("bonded_ratio", TARGET_BONDED_RATIO) | |
initial_inflation = kwargs.get("initial_inflation", 0.07) | |
time_per_block = kwargs.get("time_per_block", ASSUMED_TIME_PER_BLOCK) | |
daily_fees = kwargs.get("daily_fees", Atom(150)) | |
total_supply = kwargs.get("total_supply", Atom(254000000)) | |
yearly_rate = rate( | |
validator_commission=commission, | |
bonded_ratio=bonded_ratio, | |
initial_inflation=initial_inflation, | |
time_per_block=time_per_block, | |
daily_fees=daily_fees, | |
total_supply=total_supply, | |
) | |
print( | |
f"The yearly rate for a validator with {commission * 100}% commission is {round(yearly_rate * 100, 2)}%" | |
) | |
if __name__ == "__main__": | |
main( | |
validator_commission=0.04, # The higher the commission, the lower the rewards rate | |
bonded_ratio=0.7204, # The higher the bonded ratio, the lower the rewards rate | |
initial_inflation=0.07, # The higher the inflation, the higher the rewards rate | |
time_per_block=Seconds( | |
7 | |
), # The higher the time per block, the lower the rewards rate | |
daily_fees=Atom(150), # The higher the daily fees, the higher the rewards rate | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment