Search Apps Documentation Source Content File Folder Download Copy Actions Download

protocol_fee_reward_state.gno

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