Search Apps Documentation Source Content File Folder Download Copy Actions Download

protocol_fee_reward_manager.gno

8.76 Kb · 263 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 positive")
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
174	currentTotal := self.GetTotalStakedAmount()
175	if currentTotal > math.MaxInt64-amount {
176		return errors.New("total staked amount would overflow")
177	}
178	updatedTotalStakedAmount := safeAddInt64(currentTotal, amount)
179
180	err = resolvedState.addStakeWithUpdateRewardDebtX128(amount, self.GetAllAccumulatedProtocolFeeX128PerStake(), currentTimestamp)
181	if err != nil {
182		return err
183	}
184
185	self.setRewardState(address, rewardState)
186	self.SetTotalStakedAmount(updatedTotalStakedAmount)
187
188	return nil
189}
190
191// removeStake removes a stake for an address and updates their protocol fee reward state.
192// This method ensures rewards are properly calculated before the stake change.
193//
194// Parameters:
195//   - address: staker's address
196//   - amount: amount of stake to remove
197//   - currentTimestamp: current timestamp
198func (self *ProtocolFeeRewardManagerResolver) removeStake(address string, amount int64, currentTimestamp int64) error {
199	if amount < 0 {
200		return errors.New("amount must be non-negative")
201	}
202
203	rewardState, ok, err := self.GetRewardState(address)
204	if err != nil {
205		return err
206	}
207	if !ok {
208		rewardState = staker.NewProtocolFeeRewardState(self.GetAllAccumulatedProtocolFeeX128PerStake())
209	}
210
211	resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
212	err = resolvedState.removeStakeWithUpdateRewardDebtX128(amount, self.GetAllAccumulatedProtocolFeeX128PerStake(), currentTimestamp)
213	if err != nil {
214		return err
215	}
216
217	self.setRewardState(address, rewardState)
218
219	updatedTotalStakedAmount := safeSubInt64(self.GetTotalStakedAmount(), amount)
220	if updatedTotalStakedAmount < 0 {
221		updatedTotalStakedAmount = 0
222	}
223	self.SetTotalStakedAmount(updatedTotalStakedAmount)
224
225	return nil
226}
227
228// claimRewards processes protocol fee reward claiming for an address.
229// This method calculates and returns the amounts of rewards claimed for each token.
230//
231// Parameters:
232//   - address: staker's address claiming rewards
233//   - currentTimestamp: current timestamp
234//
235// Returns:
236//   - map[string]int64: map of token path to claimed reward amount
237//   - error: nil on success, error if claiming fails
238func (self *ProtocolFeeRewardManagerResolver) claimRewards(address string, currentTimestamp int64) (map[string]int64, error) {
239	rewardState, ok, err := self.GetRewardState(address)
240	if err != nil {
241		return nil, err
242	}
243	if !ok {
244		return make(map[string]int64), nil
245	}
246
247	resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
248	claimedRewards, err := resolvedState.claimRewardsWithUpdateRewardDebtX128(
249		self.GetAllAccumulatedProtocolFeeX128PerStake(),
250		currentTimestamp,
251	)
252	if err != nil {
253		return nil, err
254	}
255
256	self.setRewardState(address, rewardState)
257
258	return claimedRewards, nil
259}
260
261func (self *ProtocolFeeRewardManagerResolver) setRewardState(address string, rewardState *staker.ProtocolFeeRewardState) {
262	self.SetRewardState(address, rewardState)
263}