reward_calculation_warmup.gno
3.09 Kb · 101 lines
1package staker
2
3import (
4 "errors"
5 "math"
6
7 "gno.land/p/gnoswap/gnsmath"
8 u256 "gno.land/p/gnoswap/uint256"
9 ufmt "gno.land/p/nt/ufmt/v0"
10 sr "gno.land/r/gnoswap/staker"
11)
12
13const maxDurationOneYear = int64(365 * 86400) // 31,536,000 seconds
14
15// expected to be called by governance
16func modifyWarmup(warmupTemplate []sr.Warmup, index int, timeDuration int64) []sr.Warmup {
17 if index >= len(warmupTemplate) {
18 panic(ufmt.Sprintf("index(%d) is out of range", index))
19 }
20
21 // Handle negative duration - panic with error
22 if timeDuration < 0 {
23 panic(ufmt.Sprintf("warmup duration cannot be negative, got %d seconds", timeDuration))
24 }
25
26 // Early return for last tier - must always be math.MaxInt64
27 if index == len(warmupTemplate)-1 {
28 if timeDuration != math.MaxInt64 {
29 panic(ufmt.Sprintf("last warmup tier must have duration of math.MaxInt64, got %d", timeDuration))
30 }
31 // No modification needed as it's already math.MaxInt64
32 return warmupTemplate
33 }
34
35 // Limit non-final tier durations to 1 year (365 days)
36 if timeDuration > maxDurationOneYear {
37 panic(ufmt.Sprintf("warmup duration cannot exceed 1 year (365 days), got %d seconds", timeDuration))
38 }
39
40 warmupTemplate[index].SetTimeDuration(timeDuration)
41
42 return warmupTemplate
43}
44
45func instantiateWarmup(warmupTemplate []sr.Warmup, currentTime int64) []sr.Warmup {
46 warmups := make([]sr.Warmup, 0, len(warmupTemplate))
47 for i, warmup := range warmupTemplate {
48 nextWarmupTime := safeAddTime(currentTime, warmup.TimeDuration)
49
50 warmups = append(warmups, sr.NewWarmup(warmup.TimeDuration, nextWarmupTime, warmup.WarmupRatio))
51
52 // Only update currentTime if not the last tier
53 if i < len(warmupTemplate)-1 {
54 currentTime = safeAddTime(currentTime, warmup.TimeDuration)
55 }
56 }
57 return warmups
58}
59
60func applyWarmup(warmup sr.Warmup, poolReward int64, positionLiquidity, stakedLiquidity *u256.Uint) (int64, int64) {
61 if stakedLiquidity.IsZero() {
62 return 0, 0
63 }
64
65 divisor := u256.NewUint(100)
66 poolRewardUint := u256.NewUintFromInt64(poolReward)
67 perPositionReward, overflow := u256.Zero().MulOverflow(poolRewardUint, positionLiquidity)
68 if overflow {
69 panic(errors.New(errOverflow))
70 }
71 perPositionReward = u256.Zero().Div(perPositionReward, stakedLiquidity)
72
73 penaltyRatio := u256.NewUint(100 - warmup.WarmupRatio)
74 rewardRatio := u256.NewUint(warmup.WarmupRatio)
75 totalReward, overflow := u256.Zero().MulOverflow(perPositionReward, rewardRatio)
76 if overflow {
77 panic(errors.New(errOverflow))
78 }
79 totalReward = u256.Zero().Div(totalReward, divisor)
80
81 totalPenalty, overflow := u256.Zero().MulOverflow(perPositionReward, penaltyRatio)
82 if overflow {
83 panic(errors.New(errOverflow))
84 }
85 totalPenalty = u256.Zero().Div(totalPenalty, divisor)
86 return gnsmath.SafeConvertToInt64(totalReward), gnsmath.SafeConvertToInt64(totalPenalty)
87}
88
89// safeAddTime performs safe addition with overflow protection
90func safeAddTime(currentTime, duration int64) int64 {
91 if duration == math.MaxInt64 {
92 return math.MaxInt64
93 }
94
95 // Check for overflow before addition
96 if currentTime > 0 && duration > math.MaxInt64-currentTime {
97 return math.MaxInt64
98 }
99
100 return gnsmath.SafeAddInt64(currentTime, duration)
101}