github.com/deso-protocol/core@v1.2.9/lib/supply.go (about)

     1  package lib
     2  
     3  // supply.go defines all of the logic regarding the DeSo supply schedule. It also
     4  // defines the Bitcoin <-> DeSo exchange schedule.
     5  
     6  type MiningSupplyIntervalStart struct {
     7  	StartBlockHeight uint32
     8  	BlockRewardNanos uint64
     9  }
    10  
    11  type PurchaseSupplyIntervalStart struct {
    12  	// How much each unit costs to purchase in Satoshis.
    13  	SatoshisPerUnit uint64
    14  	// The total supply cutoff at which this price applies.
    15  	SupplyStartNanos uint64
    16  }
    17  
    18  const (
    19  	NanosPerUnit  = uint64(1000000000)
    20  	BlocksPerYear = uint32(12 * 24 * 365)
    21  	BlocksPerDay = uint32(12 * 24)
    22  	// Every 1M DeSo we sell causes the price to increase by a factor of 2.
    23  	TrancheSizeNanos = uint64(1000000000000000)
    24  	// When exchanging Bitcoin for DeSo, we don't allow transactions to create
    25  	// less than this amount. This avoids issues around small transactions that
    26  	// exploit floating point errors.
    27  	MinNanosToCreate = 50
    28  
    29  	// The price of DeSo at the beginning.
    30  	StartDeSoPriceUSDCents = 50
    31  	SatoshisPerBitcoin         = 100000000
    32  
    33  	// The minimum and maximum Bitcoin prices, used as a sanity-check.
    34  	MinUSDCentsPerBitcoin = 100 * 100
    35  	MaxUSDCentsPerBitcoin = 1000000 * 100
    36  
    37  	// Used for sanity checks for now. This is not necessarily the actual the max supply.
    38  	MaxNanos = uint64(30000000) * NanosPerUnit
    39  )
    40  
    41  var (
    42  	NaturalLogOfTwo = BigFloatLog(NewFloat().SetUint64(2))
    43  
    44  	DeflationBombBlockRewardAdjustmentBlockHeight = uint32(32060)
    45  
    46  	MiningSupplyIntervals = []*MiningSupplyIntervalStart{
    47  		{
    48  			StartBlockHeight: 0,
    49  			BlockRewardNanos: 1 * NanosPerUnit,
    50  		},
    51  		// Adjust the block reward as part of the deflation bomb to mark the DeSo
    52  		// dev community's commitment to a zero-waste, environmentally-friendly
    53  		// consensus mechanism. Do a smooth ramp in order to minimize issues with
    54  		// block mining times.
    55  		{
    56  			StartBlockHeight: DeflationBombBlockRewardAdjustmentBlockHeight,
    57  			BlockRewardNanos: NanosPerUnit * 3 / 4,
    58  		},
    59  		{
    60  			StartBlockHeight: DeflationBombBlockRewardAdjustmentBlockHeight + BlocksPerDay,
    61  			BlockRewardNanos: NanosPerUnit / 2,
    62  		},
    63  		{
    64  			StartBlockHeight: DeflationBombBlockRewardAdjustmentBlockHeight + 2 * BlocksPerDay,
    65  			BlockRewardNanos: NanosPerUnit / 4,
    66  		},
    67  		{
    68  			StartBlockHeight: DeflationBombBlockRewardAdjustmentBlockHeight + 3 * BlocksPerDay,
    69  			BlockRewardNanos: NanosPerUnit / 8,
    70  		},
    71  		{
    72  			StartBlockHeight: DeflationBombBlockRewardAdjustmentBlockHeight + 4 * BlocksPerDay,
    73  			BlockRewardNanos: NanosPerUnit / 10,
    74  		},
    75  		// Leave the block reward at .1 for the medium-term then tamp it down to zero.
    76  		// Note that the consensus mechanism will likely change to something more
    77  		// more energy-efficient before this point.
    78  		{
    79  			StartBlockHeight: 15 * BlocksPerYear,
    80  			BlockRewardNanos: NanosPerUnit / 20,
    81  		},
    82  		{
    83  			StartBlockHeight: 32 * BlocksPerYear,
    84  			BlockRewardNanos: 0,
    85  		},
    86  	}
    87  
    88  	// This is used for various calculations but can be updated on the fly with a
    89  	// special transaction type in the event that the Bitcoin price fluctuates
    90  	// significantly. We make this a var rather than a const so that tests can
    91  	// change the value.
    92  	InitialUSDCentsPerBitcoinExchangeRate = uint64(3000000)
    93  )
    94  
    95  // CalcBlockRewardNanos computes the block reward for a given block height.
    96  func CalcBlockRewardNanos(blockHeight uint32) uint64 {
    97  	if blockHeight == 0 {
    98  		return MiningSupplyIntervals[0].BlockRewardNanos
    99  	}
   100  
   101  	// Skip the first interval since we know we're past block height zero.
   102  	for intervalIndex, intervalStart := range MiningSupplyIntervals {
   103  		if intervalIndex == 0 {
   104  			// Skip the first iteration.
   105  			continue
   106  		}
   107  		if intervalStart.StartBlockHeight > blockHeight {
   108  			// We found an interval that has a greater block height than what was
   109  			// passed in, so the interval just before it should be the one containing
   110  			// this block height.
   111  			return MiningSupplyIntervals[intervalIndex-1].BlockRewardNanos
   112  		}
   113  	}
   114  
   115  	// If we get here then all of the intervals had a lower block height than
   116  	// the passed-in block height. In this case, the block reward is zero.
   117  	return 0
   118  }
   119  
   120  func GetStartPriceSatoshisPerDeSo(usdCentsPerBitcoinExchangeRate uint64) uint64 {
   121  	return StartDeSoPriceUSDCents * SatoshisPerBitcoin / usdCentsPerBitcoinExchangeRate
   122  }
   123  
   124  func GetSatoshisPerUnitExchangeRate(startNanos uint64, usdCentsPerBitcoinExchangeRate uint64) uint64 {
   125  	startPriceSatoshisPerDeSo := GetStartPriceSatoshisPerDeSo(usdCentsPerBitcoinExchangeRate)
   126  	val, _ := Mul(NewFloat().SetUint64(startPriceSatoshisPerDeSo), BigFloatPow(
   127  		bigTwo, Div(NewFloat().SetUint64(startNanos), NewFloat().SetUint64(TrancheSizeNanos)))).Uint64()
   128  	return val
   129  }
   130  
   131  func CalcNanosToCreate(
   132  	startNanos uint64, satoshisToBurn uint64, usdCentsPerBitcoinExchangeRate uint64) (
   133  	_nanosToCreate uint64) {
   134  
   135  	// Given the amount this user wants to burn, we have a formula that tells us
   136  	// how much DeSo we should have after processing the transaction. The
   137  	// "tranche size nanos" below simply modulates how quickly the price doubles.
   138  	// The smaller it is, the faster the price increases with each DeSo sold.
   139  	//
   140  	// price in satoshis per DeSo
   141  	//   = 2 ^((nanos sold) / tranche size nanos) * SatoshisPerDeSo
   142  	//
   143  	// Taking the integral of this with respect to the nanos sold tells us how
   144  	// many Bitcoin we burn for a given number of nanos.
   145  	//
   146  	// Bitcoin to burn = (SatoshisPerDeSo) * (tranche size in nanos) / (ln(2)) * (
   147  	// 		2^((final DeSo burned) / (tranche size in nanos) -
   148  	//    2^((initial DeSo burned) / (tranche size in nanos)))
   149  	//
   150  	// Solving this equation for "final DeSo burned" yields the formula you see
   151  	// below, which we can then use to figure out how many nanos we should create.
   152  	startPriceSatoshisPerDeSo := GetStartPriceSatoshisPerDeSo(usdCentsPerBitcoinExchangeRate)
   153  	nanosComponent := Div(NewFloat().SetUint64(NanosPerUnit), NewFloat().SetUint64(TrancheSizeNanos))
   154  	bitcoinComponent := Div(NewFloat().SetUint64(satoshisToBurn), NewFloat().SetUint64(startPriceSatoshisPerDeSo))
   155  	bigFloatFinalDeSoNanos := Mul(NewFloat().SetUint64(TrancheSizeNanos), BigFloatLog2(
   156  		Add(Mul(nanosComponent, Mul(bitcoinComponent, NaturalLogOfTwo)),
   157  			BigFloatPow(bigTwo, Div(NewFloat().SetUint64(startNanos), NewFloat().SetUint64(TrancheSizeNanos))))))
   158  
   159  	// If somehow the final amount is less than what we started with then return
   160  	// zero just to be safe.
   161  	finalDeSoNanos, _ := bigFloatFinalDeSoNanos.Uint64()
   162  	if finalDeSoNanos <= startNanos {
   163  		return uint64(0)
   164  	}
   165  	nanosToCreate := finalDeSoNanos - startNanos
   166  
   167  	// Return zero unless we're above a threshold amount. This avoids floating
   168  	// point issues around very small exchanges.
   169  	if nanosToCreate <= MinNanosToCreate {
   170  		return uint64(0)
   171  	}
   172  
   173  	return nanosToCreate
   174  }