reward_calculation_incentives.gno
4.78 Kb ยท 180 lines
1package v1
2
3import (
4 "gno.land/p/nt/avl"
5
6 sr "gno.land/r/gnoswap/staker"
7)
8
9type IncentivesResolver struct {
10 *sr.Incentives
11}
12
13func NewIncentivesResolver(incentives *sr.Incentives) *IncentivesResolver {
14 return &IncentivesResolver{
15 Incentives: incentives,
16 }
17}
18
19// Get incentive by incentiveId
20func (self *IncentivesResolver) Get(incentiveId string) (*sr.ExternalIncentive, bool) {
21 return retrieveIncentive(self.IncentiveTrees(), incentiveId)
22}
23
24func (self *IncentivesResolver) GetIncentiveResolver(incentiveId string) (*ExternalIncentiveResolver, bool) {
25 if incentive, ok := self.Get(incentiveId); ok {
26 return NewExternalIncentiveResolver(incentive), true
27 }
28 return nil, false
29}
30
31func retrieveIncentive(tree *avl.Tree, id string) (*sr.ExternalIncentive, bool) {
32 value, ok := tree.Get(id)
33 if !ok {
34 return nil, false
35 }
36 v, ok := value.(*sr.ExternalIncentive)
37 if !ok {
38 panic("failed to cast value to *sr.ExternalIncentive")
39 }
40 return v, true
41}
42
43// Create a new external incentive
44// Panics if the incentive already exists.
45func (self *IncentivesResolver) create(
46 creator address,
47 incentive *sr.ExternalIncentive,
48) {
49 self.IncentiveTrees().Set(incentive.IncentiveId(), incentive)
50}
51
52// update updates an existing incentive with new information
53func (self *IncentivesResolver) update(
54 creator address,
55 incentive *sr.ExternalIncentive,
56) {
57 self.IncentiveTrees().Set(incentive.IncentiveId(), incentive)
58}
59
60// starts incentive unclaimable period for this pool
61func (self *IncentivesResolver) startUnclaimablePeriod(startTimestamp int64) {
62 self.UnclaimablePeriods().Set(startTimestamp, int64(0))
63}
64
65// ends incentive unclaimable period for this pool
66// ignores if currently not in unclaimable period
67func (self *IncentivesResolver) endUnclaimablePeriod(endTimestamp int64) {
68 startTimestamp := int64(0)
69 self.UnclaimablePeriods().ReverseIterate(0, endTimestamp, func(key int64, value any) bool {
70 v, ok := value.(int64)
71 if !ok {
72 panic("failed to cast value to int64")
73 }
74 if v != 0 {
75 // Already ended, no need to update
76 // keeping startTimestamp as 0 to indicate this
77 return true
78 }
79 startTimestamp = key
80 return true
81 })
82
83 if startTimestamp == 0 {
84 // No ongoing unclaimable period found
85 return
86 }
87
88 if startTimestamp == endTimestamp {
89 self.UnclaimablePeriods().Remove(startTimestamp)
90 } else {
91 self.UnclaimablePeriods().Set(startTimestamp, endTimestamp)
92 }
93}
94
95// calculate unclaimable reward by checking unclaimable periods
96func (self *IncentivesResolver) calculateUnclaimableReward(incentiveId string) int64 {
97 incentive, ok := self.Get(incentiveId)
98 if !ok {
99 return 0
100 }
101
102 timeDiff := int64(0)
103
104 // Find unclaimable periods that end before or at incentive start
105 self.UnclaimablePeriods().ReverseIterate(0, incentive.StartTimestamp(), func(startTimestamp int64, value any) bool {
106 endTimestamp, ok := value.(int64)
107 if !ok {
108 panic("failed to cast value to int64")
109 }
110
111 if endTimestamp == 0 {
112 endTimestamp = incentive.EndTimestamp()
113 }
114
115 if endTimestamp <= incentive.StartTimestamp() {
116 return true
117 }
118
119 // Calculate duration of unclaimable period that overlaps with incentive period
120 duration := calculateUnClaimableDuration(
121 startTimestamp,
122 endTimestamp,
123 incentive.StartTimestamp(),
124 incentive.EndTimestamp(),
125 )
126
127 timeDiff = safeAddInt64(timeDiff, duration)
128
129 return true
130 })
131
132 // Find unclaimable periods that start within incentive period
133 self.UnclaimablePeriods().Iterate(incentive.StartTimestamp(), incentive.EndTimestamp(), func(startTimestamp int64, value any) bool {
134 endTimestamp, ok := value.(int64)
135 if !ok {
136 panic("failed to cast value to int64")
137 }
138
139 if endTimestamp == 0 {
140 endTimestamp = incentive.EndTimestamp()
141 }
142
143 // Calculate duration of unclaimable period that overlaps with incentive period
144 duration := calculateUnClaimableDuration(
145 startTimestamp,
146 endTimestamp,
147 incentive.StartTimestamp(),
148 incentive.EndTimestamp(),
149 )
150 timeDiff = safeAddInt64(timeDiff, duration)
151
152 // ensures continue iterating through all unclaimable periods
153 return false
154 })
155
156 return safeMulInt64(timeDiff, incentive.RewardPerSecond())
157}
158
159// calculateUnClaimableDuration calculates the duration of overlap between an unclaimable period and incentive period
160func calculateUnClaimableDuration(unclaimableStart, unclaimableEnd, incentiveStartTimestamp, incentiveEndTimestamp int64) int64 {
161 // Use later timestamp between unclaimable start and incentive start
162 startTime := unclaimableStart
163 if startTime < incentiveStartTimestamp {
164 startTime = incentiveStartTimestamp
165 }
166
167 // Use earlier timestamp between unclaimable end and incentive end
168 endTime := unclaimableEnd
169 if endTime > incentiveEndTimestamp {
170 endTime = incentiveEndTimestamp
171 }
172
173 // Return 0 if no overlap
174 if endTime < startTime {
175 return 0
176 }
177
178 // Calculate overlap duration
179 return safeSubInt64(endTime, startTime)
180}