github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/reward/calculator.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package reward 5 6 import ( 7 "math/big" 8 "time" 9 10 "github.com/MetalBlockchain/metalgo/utils/math" 11 ) 12 13 var _ Calculator = (*calculator)(nil) 14 15 type Calculator interface { 16 Calculate(stakedDuration time.Duration, stakedAmount, currentSupply uint64) uint64 17 } 18 19 type calculator struct { 20 maxSubMinConsumptionRate *big.Int 21 minConsumptionRate *big.Int 22 mintingPeriod *big.Int 23 supplyCap uint64 24 } 25 26 func NewCalculator(c Config) Calculator { 27 return &calculator{ 28 maxSubMinConsumptionRate: new(big.Int).SetUint64(c.MaxConsumptionRate - c.MinConsumptionRate), 29 minConsumptionRate: new(big.Int).SetUint64(c.MinConsumptionRate), 30 mintingPeriod: new(big.Int).SetUint64(uint64(c.MintingPeriod)), 31 supplyCap: c.SupplyCap, 32 } 33 } 34 35 // Reward returns the amount of tokens to reward the staker with. 36 // 37 // RemainingSupply = SupplyCap - ExistingSupply 38 // PortionOfExistingSupply = StakedAmount / ExistingSupply 39 // PortionOfStakingDuration = StakingDuration / MaximumStakingDuration 40 // MintingRate = MinMintingRate + MaxSubMinMintingRate * PortionOfStakingDuration 41 // Reward = RemainingSupply * PortionOfExistingSupply * MintingRate * PortionOfStakingDuration 42 func (c *calculator) Calculate(stakedDuration time.Duration, stakedAmount, currentSupply uint64) uint64 { 43 bigStakedDuration := new(big.Int).SetUint64(uint64(stakedDuration)) 44 bigStakedAmount := new(big.Int).SetUint64(stakedAmount) 45 bigCurrentSupply := new(big.Int).SetUint64(currentSupply) 46 47 adjustedConsumptionRateNumerator := new(big.Int).Mul(c.maxSubMinConsumptionRate, bigStakedDuration) 48 adjustedMinConsumptionRateNumerator := new(big.Int).Mul(c.minConsumptionRate, c.mintingPeriod) 49 adjustedConsumptionRateNumerator.Add(adjustedConsumptionRateNumerator, adjustedMinConsumptionRateNumerator) 50 adjustedConsumptionRateDenominator := new(big.Int).Mul(c.mintingPeriod, consumptionRateDenominator) 51 52 remainingSupply := c.supplyCap - currentSupply 53 reward := new(big.Int).SetUint64(remainingSupply) 54 reward.Mul(reward, adjustedConsumptionRateNumerator) 55 reward.Mul(reward, bigStakedAmount) 56 reward.Mul(reward, bigStakedDuration) 57 reward.Div(reward, adjustedConsumptionRateDenominator) 58 reward.Div(reward, bigCurrentSupply) 59 reward.Div(reward, c.mintingPeriod) 60 61 if !reward.IsUint64() { 62 return remainingSupply 63 } 64 65 finalReward := reward.Uint64() 66 if finalReward > remainingSupply { 67 return remainingSupply 68 } 69 70 return finalReward 71 } 72 73 // Split [totalAmount] into [totalAmount * shares percentage] and the remainder. 74 // 75 // Invariant: [shares] <= [PercentDenominator] 76 func Split(totalAmount uint64, shares uint32) (uint64, uint64) { 77 remainderShares := PercentDenominator - uint64(shares) 78 remainderAmount := remainderShares * (totalAmount / PercentDenominator) 79 80 // Delay rounding as long as possible for small numbers 81 if optimisticReward, err := math.Mul64(remainderShares, totalAmount); err == nil { 82 remainderAmount = optimisticReward / PercentDenominator 83 } 84 85 amountFromShares := totalAmount - remainderAmount 86 return amountFromShares, remainderAmount 87 }