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 }