Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}