Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

protocol_fee_reward_manager.gno

8.83 Kb · 264 lines
  1package staker
  2
  3import (
  4	"errors"
  5	"math"
  6
  7	gnsmath "gno.land/p/gnoswap/gnsmath"
  8	u256 "gno.land/p/gnoswap/uint256"
  9	"gno.land/r/gnoswap/gov/staker"
 10)
 11
 12type ProtocolFeeRewardManagerResolver struct {
 13	*staker.ProtocolFeeRewardManager
 14}
 15
 16func NewProtocolFeeRewardManagerResolver(manager *staker.ProtocolFeeRewardManager) *ProtocolFeeRewardManagerResolver {
 17	return &ProtocolFeeRewardManagerResolver{manager}
 18}
 19
 20// GetClaimableRewardAmounts calculates the claimable reward amounts for all tokens for a specific address.
 21// This method computes rewards based on current protocol fee distribution state and staking history.
 22//
 23// Parameters:
 24//   - protocolFeeAmounts: current protocol fee amounts for all tokens
 25//   - address: staker's address to calculate rewards for
 26//   - currentTimestamp: current timestamp
 27//
 28// Returns:
 29//   - map[string]int64: map of token path to claimable reward amount
 30func (self *ProtocolFeeRewardManagerResolver) GetClaimableRewardAmounts(
 31	protocolFeeAmounts map[string]int64,
 32	address string,
 33	currentTimestamp int64,
 34) (map[string]int64, error) {
 35	rewardState, ok, err := self.GetRewardState(address)
 36	if err != nil {
 37		return nil, err
 38	}
 39	if !ok {
 40		return make(map[string]int64), nil
 41	}
 42
 43	accumulatedRewardX128PerStake, _, err := self.calculateAccumulatedRewardX128PerStake(
 44		protocolFeeAmounts,
 45		currentTimestamp,
 46	)
 47	if err != nil {
 48		return nil, err
 49	}
 50
 51	resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
 52
 53	return resolvedState.GetClaimableRewardAmounts(accumulatedRewardX128PerStake, currentTimestamp)
 54}
 55
 56// calculateAccumulatedRewardX128PerStake calculates the updated accumulated reward per stake for all tokens.
 57// This method computes new accumulated reward rates based on newly distributed protocol fees.
 58//
 59// Parameters:
 60//   - protocolFeeAmounts: current protocol fee amounts for all tokens
 61//   - currentTimestamp: current timestamp
 62//
 63// Returns:
 64//   - map[string]*u256.Uint: updated accumulated reward per stake for each token
 65//   - map[string]int64: updated protocol fee amounts for each token
 66func (self *ProtocolFeeRewardManagerResolver) calculateAccumulatedRewardX128PerStake(
 67	protocolFeeAmounts map[string]int64,
 68	currentTimestamp int64,
 69) (map[string]*u256.Uint, map[string]int64, error) {
 70	// If we're looking at a past timestamp, return current state
 71	if self.GetAccumulatedTimestamp() > currentTimestamp {
 72		return self.GetAllAccumulatedProtocolFeeX128PerStake(), self.GetProtocolFeeAmounts(), nil
 73	}
 74
 75	accumulatedProtocolFeesX128PerStake := make(map[string]*u256.Uint)
 76	changedProtocolFeeAmounts := make(map[string]int64)
 77
 78	// Process each token's protocol fees
 79	for token, protocolFeeAmount := range protocolFeeAmounts {
 80		previousProtocolFeeAmount := self.GetProtocolFeeAmount(token)
 81
 82		protocolFeeDelta := gnsmath.SafeSubInt64(protocolFeeAmount, previousProtocolFeeAmount)
 83
 84		// If no new fees for this token, keep existing rate
 85		if protocolFeeDelta <= 0 {
 86			accumulatedProtocolFeesX128PerStake[token] = self.GetAccumulatedProtocolFeeX128PerStake(token)
 87			if accumulatedProtocolFeesX128PerStake[token] == nil {
 88				accumulatedProtocolFeesX128PerStake[token] = u256.NewUint(0)
 89			}
 90			changedProtocolFeeAmounts[token] = protocolFeeAmount
 91			continue
 92		}
 93
 94		// Scale the fee delta by 2^128 for precision
 95		protocolFeeDeltaX128 := u256.NewUintFromInt64(protocolFeeDelta)
 96		protocolFeeDeltaX128 = u256.Zero().Lsh(protocolFeeDeltaX128, 128)
 97
 98		protocolFeeDeltaX128PerStake := u256.Zero()
 99
100		// Calculate fee per stake if there are staked tokens
101		if self.GetTotalStakedAmount() > 0 {
102			feePerStake := u256.Zero().Div(protocolFeeDeltaX128, u256.NewUintFromInt64(self.GetTotalStakedAmount()))
103			protocolFeeDeltaX128PerStake = feePerStake
104		}
105
106		// Get current accumulated fee per stake for this token
107		accumulatedProtocolFeeX128PerStake := u256.Zero()
108		existingAccumulatedFee := self.GetAccumulatedProtocolFeeX128PerStake(token)
109		if existingAccumulatedFee != nil {
110			accumulatedProtocolFeeX128PerStake = existingAccumulatedFee
111		}
112
113		// Add the new fee per stake to the accumulated amount
114		accumulatedProtocolFeeX128PerStake = u256.Zero().Add(accumulatedProtocolFeeX128PerStake, protocolFeeDeltaX128PerStake)
115		accumulatedProtocolFeesX128PerStake[token] = accumulatedProtocolFeeX128PerStake.Clone()
116
117		changedProtocolFeeAmounts[token] = protocolFeeAmount
118	}
119
120	return accumulatedProtocolFeesX128PerStake, changedProtocolFeeAmounts, nil
121}
122
123// updateAccumulatedProtocolFeeX128PerStake updates the internal accumulated protocol fee state.
124// This method should be called before any stake changes to ensure accurate reward calculations.
125//
126// Parameters:
127//   - protocolFeeAmounts: current protocol fee amounts for all tokens
128//   - currentTimestamp: current timestamp
129func (self *ProtocolFeeRewardManagerResolver) updateAccumulatedProtocolFeeX128PerStake(
130	protocolFeeAmounts map[string]int64,
131	currentTimestamp int64,
132) error {
133	// Don't update if we're looking at a past timestamp
134	if self.GetAccumulatedTimestamp() > currentTimestamp {
135		return nil
136	}
137
138	accumulatedProtocolFeeX128PerStake, changedProtocolFeeAmounts, err := self.calculateAccumulatedRewardX128PerStake(
139		protocolFeeAmounts,
140		currentTimestamp,
141	)
142	if err != nil {
143		return err
144	}
145
146	self.SetAccumulatedProtocolFeeX128PerStake(accumulatedProtocolFeeX128PerStake)
147	self.SetProtocolFeeAmounts(changedProtocolFeeAmounts)
148	self.SetAccumulatedTimestamp(currentTimestamp)
149
150	return nil
151}
152
153// addStake adds a stake for an address and updates their protocol fee reward state.
154// This method ensures rewards are properly calculated before the stake change.
155//
156// Parameters:
157//   - address: staker's address
158//   - amount: amount of stake to add
159//   - currentTimestamp: current timestamp
160func (self *ProtocolFeeRewardManagerResolver) addStake(address string, amount int64, currentTimestamp int64) error {
161	if amount <= 0 {
162		return errors.New("amount must be positive")
163	}
164
165	rewardState, ok, err := self.GetRewardState(address)
166	if err != nil {
167		return err
168	}
169	if !ok {
170		rewardState = staker.NewProtocolFeeRewardState(self.GetAllAccumulatedProtocolFeeX128PerStake())
171	}
172
173	resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
174
175	currentTotal := self.GetTotalStakedAmount()
176	if currentTotal > math.MaxInt64-amount {
177		return errors.New("total staked amount would overflow")
178	}
179	updatedTotalStakedAmount := gnsmath.SafeAddInt64(currentTotal, amount)
180
181	err = resolvedState.addStakeWithUpdateRewardDebtX128(amount, self.GetAllAccumulatedProtocolFeeX128PerStake(), currentTimestamp)
182	if err != nil {
183		return err
184	}
185
186	self.setRewardState(address, rewardState)
187	self.SetTotalStakedAmount(updatedTotalStakedAmount)
188
189	return nil
190}
191
192// removeStake removes a stake for an address and updates their protocol fee reward state.
193// This method ensures rewards are properly calculated before the stake change.
194//
195// Parameters:
196//   - address: staker's address
197//   - amount: amount of stake to remove
198//   - currentTimestamp: current timestamp
199func (self *ProtocolFeeRewardManagerResolver) removeStake(address string, amount int64, currentTimestamp int64) error {
200	if amount < 0 {
201		return errors.New("amount must be non-negative")
202	}
203
204	rewardState, ok, err := self.GetRewardState(address)
205	if err != nil {
206		return err
207	}
208	if !ok {
209		rewardState = staker.NewProtocolFeeRewardState(self.GetAllAccumulatedProtocolFeeX128PerStake())
210	}
211
212	resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
213	err = resolvedState.removeStakeWithUpdateRewardDebtX128(amount, self.GetAllAccumulatedProtocolFeeX128PerStake(), currentTimestamp)
214	if err != nil {
215		return err
216	}
217
218	self.setRewardState(address, rewardState)
219
220	updatedTotalStakedAmount := gnsmath.SafeSubInt64(self.GetTotalStakedAmount(), amount)
221	if updatedTotalStakedAmount < 0 {
222		updatedTotalStakedAmount = 0
223	}
224	self.SetTotalStakedAmount(updatedTotalStakedAmount)
225
226	return nil
227}
228
229// claimRewards processes protocol fee reward claiming for an address.
230// This method calculates and returns the amounts of rewards claimed for each token.
231//
232// Parameters:
233//   - address: staker's address claiming rewards
234//   - currentTimestamp: current timestamp
235//
236// Returns:
237//   - map[string]int64: map of token path to claimed reward amount
238//   - error: nil on success, error if claiming fails
239func (self *ProtocolFeeRewardManagerResolver) claimRewards(address string, currentTimestamp int64) (map[string]int64, error) {
240	rewardState, ok, err := self.GetRewardState(address)
241	if err != nil {
242		return nil, err
243	}
244	if !ok {
245		return make(map[string]int64), nil
246	}
247
248	resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
249	claimedRewards, err := resolvedState.claimRewardsWithUpdateRewardDebtX128(
250		self.GetAllAccumulatedProtocolFeeX128PerStake(),
251		currentTimestamp,
252	)
253	if err != nil {
254		return nil, err
255	}
256
257	self.setRewardState(address, rewardState)
258
259	return claimedRewards, nil
260}
261
262func (self *ProtocolFeeRewardManagerResolver) setRewardState(address string, rewardState *staker.ProtocolFeeRewardState) {
263	self.SetRewardState(address, rewardState)
264}