Search Apps Documentation Source Content File Folder Download Copy Actions Download

protocol_fee_reward_manager.gno

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