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_state.gno

9.08 Kb · 267 lines
  1package staker
  2
  3import (
  4	"errors"
  5
  6	"gno.land/p/gnoswap/consts"
  7	gnsmath "gno.land/p/gnoswap/gnsmath"
  8	u256 "gno.land/p/gnoswap/uint256"
  9	"gno.land/r/gnoswap/gov/staker"
 10)
 11
 12type ProtocolFeeRewardStateResolver struct {
 13	*staker.ProtocolFeeRewardState
 14}
 15
 16func NewProtocolFeeRewardStateResolver(protocolFeeRewardState *staker.ProtocolFeeRewardState) *ProtocolFeeRewardStateResolver {
 17	return &ProtocolFeeRewardStateResolver{protocolFeeRewardState}
 18}
 19
 20// IsClaimable checks if rewards can be claimed at the given timestamp.
 21// Rewards are claimable if the current timestamp is greater than the last claimed timestamp.
 22//
 23// Parameters:
 24//   - currentTimestamp: current timestamp to check against
 25//
 26// Returns:
 27//   - bool: true if rewards can be claimed, false otherwise
 28func (p *ProtocolFeeRewardStateResolver) IsClaimable(currentTimestamp int64) bool {
 29	return p.GetClaimedTimestamp() < currentTimestamp
 30}
 31
 32// GetClaimableRewardAmounts calculates the claimable reward amounts for all tokens.
 33// This includes both accumulated rewards and newly earned rewards based on current state.
 34//
 35// Parameters:
 36//   - accumulatedRewardsX128PerStake: current system-wide accumulated rewards per stake for all tokens
 37//   - currentTimestamp: current timestamp
 38//
 39// Returns:
 40//   - map[string]int64: map of token path to claimable reward amount
 41//   - error: nil on success, error if claiming is not allowed
 42func (p *ProtocolFeeRewardStateResolver) GetClaimableRewardAmounts(
 43	accumulatedRewardsX128PerStake map[string]*u256.Uint,
 44	currentTimestamp int64,
 45) (map[string]int64, error) {
 46	newlyEarnedRewards, err := p.calculateClaimableRewards(accumulatedRewardsX128PerStake, currentTimestamp)
 47	if err != nil {
 48		return nil, err
 49	}
 50
 51	claimableRewards := make(map[string]int64)
 52	accumulatedRewards := p.GetAccumulatedRewards()
 53	claimedRewards := p.GetClaimedRewards()
 54
 55	for token, accumulatedReward := range accumulatedRewards {
 56		claimableRewards[token] = gnsmath.SafeSubInt64(accumulatedReward, claimedRewards[token])
 57	}
 58
 59	for token, newlyEarnedReward := range newlyEarnedRewards {
 60		claimableRewards[token] = gnsmath.SafeAddInt64(claimableRewards[token], newlyEarnedReward)
 61	}
 62
 63	return claimableRewards, nil
 64}
 65
 66// calculateClaimableRewards calculates newly earned rewards for all tokens since the last update.
 67// This method uses the difference between current and stored reward debt to calculate earnings.
 68//
 69// Parameters:
 70//   - accumulatedRewardsX128PerStake: current system-wide accumulated rewards per stake for all tokens
 71//   - currentTimestamp: current timestamp
 72//
 73// Returns:
 74//   - map[string]int64: map of token path to newly earned reward amount
 75func (p *ProtocolFeeRewardStateResolver) calculateClaimableRewards(
 76	accumulatedRewardsX128PerStake map[string]*u256.Uint,
 77	currentTimestamp int64,
 78) (map[string]int64, error) {
 79	// Don't calculate rewards for past timestamps
 80	if p.GetAccumulatedTimestamp() >= currentTimestamp {
 81		return make(map[string]int64), nil
 82	}
 83
 84	rewardAmounts := make(map[string]int64)
 85	stakedAmount := p.GetStakedAmount()
 86
 87	// Calculate rewards for each token type
 88	for token, accumulatedRewardX128PerStake := range accumulatedRewardsX128PerStake {
 89		// Get reward debt for this token
 90		rewardDebtX128 := p.GetRewardDebtX128ForToken(token)
 91		if rewardDebtX128 == nil {
 92			rewardDebtX128 = u256.Zero()
 93		}
 94
 95		// Calculate the difference in accumulated rewards per stake since last update
 96		// Using modular arithmetic for accumulator values - underflow is allowed and handled correctly
 97		rewardDebtDeltaX128 := u256.Zero().Sub(
 98			accumulatedRewardX128PerStake,
 99			rewardDebtX128,
100		)
101
102		// Multiply by staked amount to get total reward for this staker and token
103		rewardAmount := u256.MulDiv(
104			rewardDebtDeltaX128,
105			u256.NewUintFromInt64(stakedAmount),
106			consts.Q128(),
107		)
108
109		rewardAmounts[token] = gnsmath.SafeConvertToInt64(rewardAmount)
110	}
111
112	return rewardAmounts, nil
113}
114
115// addStake increases the staked amount for this address.
116// This method should be called when a user increases their stake.
117//
118// Parameters:
119//   - amount: amount of stake to add
120func (p *ProtocolFeeRewardStateResolver) addStake(amount int64) {
121	p.SetStakedAmount(gnsmath.SafeAddInt64(p.GetStakedAmount(), amount))
122}
123
124// removeStake decreases the staked amount for this address.
125// This method should be called when a user decreases their stake.
126//
127// Parameters:
128//   - amount: amount of stake to remove
129func (p *ProtocolFeeRewardStateResolver) removeStake(amount int64) {
130	newAmount := gnsmath.SafeSubInt64(p.GetStakedAmount(), amount)
131	if newAmount < 0 {
132		newAmount = 0
133	}
134	p.SetStakedAmount(newAmount)
135}
136
137// claimRewards processes reward claiming for all tokens and updates the claim state.
138// This method validates claimability and transfers accumulated rewards to claimed status.
139//
140// Parameters:
141//   - currentTimestamp: current timestamp
142//
143// Returns:
144//   - map[string]int64: map of token path to claimed reward amount
145//   - error: nil on success, error if reward debt is stale
146func (p *ProtocolFeeRewardStateResolver) claimRewards(currentTimestamp int64) (map[string]int64, error) {
147	if !p.IsClaimable(currentTimestamp) {
148		return make(map[string]int64), nil
149	}
150
151	if p.GetAccumulatedTimestamp() < currentTimestamp {
152		return nil, errors.New("must update reward debt before claiming rewards")
153	}
154
155	currentClaimedRewards := make(map[string]int64)
156	accumulatedRewards := p.GetAccumulatedRewards()
157	claimedRewards := p.GetClaimedRewards()
158
159	// Calculate and update claimed amounts for each token
160	for token, rewardAmount := range accumulatedRewards {
161		claimedAmount := claimedRewards[token]
162		currentClaimedRewards[token] = gnsmath.SafeSubInt64(rewardAmount, claimedAmount)
163		p.SetClaimedRewardForToken(token, rewardAmount)
164	}
165
166	p.SetClaimedTimestamp(currentTimestamp)
167
168	return currentClaimedRewards, nil
169}
170
171// updateRewardDebtX128 updates the reward debt and accumulates new rewards for all tokens.
172// This method should be called before any stake changes to ensure accurate reward tracking.
173//
174// Parameters:
175//   - accumulatedProtocolFeeX128PerStake: current system-wide accumulated protocol fees per stake for all tokens
176//   - currentTimestamp: current timestamp
177func (p *ProtocolFeeRewardStateResolver) updateRewardDebtX128(
178	accumulatedProtocolFeeX128PerStake map[string]*u256.Uint,
179	currentTimestamp int64,
180) error {
181	// Don't update if we're looking at a past timestamp
182	if p.GetAccumulatedTimestamp() >= currentTimestamp {
183		return nil
184	}
185
186	// Calculate and accumulate new rewards for all tokens
187	rewardAmounts, err := p.calculateClaimableRewards(accumulatedProtocolFeeX128PerStake, currentTimestamp)
188	if err != nil {
189		return err
190	}
191
192	// Update reward debt for all tokens
193	p.SetRewardDebtX128(accumulatedProtocolFeeX128PerStake)
194
195	// Add newly calculated rewards to accumulated amounts
196	accumulatedRewards := p.GetAccumulatedRewards()
197	for token, rewardAmount := range rewardAmounts {
198		p.SetAccumulatedRewardForToken(token, gnsmath.SafeAddInt64(accumulatedRewards[token], rewardAmount))
199	}
200
201	p.SetAccumulatedTimestamp(currentTimestamp)
202
203	return nil
204}
205
206// addStakeWithUpdateRewardDebtX128 adds stake and updates reward debt in one operation.
207// This ensures rewards are properly calculated before the stake change takes effect.
208//
209// Parameters:
210//   - amount: amount of stake to add
211//   - accumulatedProtocolFeeX128PerStake: current system-wide accumulated protocol fees per stake
212//   - currentTimestamp: current timestamp
213func (p *ProtocolFeeRewardStateResolver) addStakeWithUpdateRewardDebtX128(
214	amount int64,
215	accumulatedProtocolFeeX128PerStake map[string]*u256.Uint,
216	currentTimestamp int64,
217) error {
218	err := p.updateRewardDebtX128(accumulatedProtocolFeeX128PerStake, currentTimestamp)
219	if err != nil {
220		return err
221	}
222
223	p.addStake(amount)
224
225	return nil
226}
227
228// removeStakeWithUpdateRewardDebtX128 removes stake and updates reward debt in one operation.
229// This ensures rewards are properly calculated before the stake change takes effect.
230//
231// Parameters:
232//   - amount: amount of stake to remove
233//   - accumulatedProtocolFeeX128PerStake: current system-wide accumulated protocol fees per stake
234//   - currentTimestamp: current timestamp
235func (p *ProtocolFeeRewardStateResolver) removeStakeWithUpdateRewardDebtX128(
236	amount int64,
237	accumulatedProtocolFeeX128PerStake map[string]*u256.Uint,
238	currentTimestamp int64,
239) error {
240	err := p.updateRewardDebtX128(accumulatedProtocolFeeX128PerStake, currentTimestamp)
241	if err != nil {
242		return err
243	}
244
245	p.removeStake(amount)
246
247	return nil
248}
249
250// claimRewardsWithUpdateRewardDebtX128 claims rewards and updates reward debt in one operation.
251// This ensures all rewards are properly calculated before claiming.
252//
253// Parameters:
254//   - accumulatedProtocolFeeX128PerStake: current system-wide accumulated protocol fees per stake
255//   - currentTimestamp: current timestamp
256//
257// Returns:
258//   - map[string]int64: map of token path to claimed reward amount
259//   - error: nil on success, error if claiming fails
260func (p *ProtocolFeeRewardStateResolver) claimRewardsWithUpdateRewardDebtX128(
261	accumulatedProtocolFeeX128PerStake map[string]*u256.Uint,
262	currentTimestamp int64,
263) (map[string]int64, error) {
264	p.updateRewardDebtX128(accumulatedProtocolFeeX128PerStake, currentTimestamp)
265
266	return p.claimRewards(currentTimestamp)
267}