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}