state.gno
18.27 Kb · 574 lines
1package v1
2
3import (
4 "math"
5
6 bptree "gno.land/p/nt/bptree/v0"
7 ufmt "gno.land/p/nt/ufmt/v0"
8
9 "gno.land/r/gnoswap/emission"
10 "gno.land/r/gnoswap/gov/staker"
11 pf "gno.land/r/gnoswap/protocol_fee"
12)
13
14// setUnDelegationLockupPeriod updates the undelegation lockup period.
15// This affects all future undelegation operations.
16//
17// Parameters:
18// - period: new lockup period in seconds
19func (g *govStakerV1) setUnDelegationLockupPeriod(_ int, rlm realm, period int64) {
20 if err := g.store.SetUnDelegationLockupPeriod(0, rlm, period); err != nil {
21 panic(err)
22 }
23}
24
25// nextDelegationID generates and returns the next unique delegation ID.
26//
27// Returns:
28// - int64: next available delegation ID
29func (g *govStakerV1) nextDelegationID() int64 {
30 counter := g.store.GetDelegationCounter()
31
32 return counter.Next()
33}
34
35// getDelegation retrieves a delegation by its ID.
36//
37// Parameters:
38// - delegationID: unique identifier of the delegation
39//
40// Returns:
41// - *Delegation: delegation instance or nil if not found
42func (g *govStakerV1) getDelegation(delegationID int64) *staker.Delegation {
43 delegation, exists := g.store.GetDelegation(delegationID)
44 if !exists {
45 return nil
46 }
47 return delegation
48}
49
50// setDelegation stores or updates a delegation in the storage tree.
51//
52// Parameters:
53// - delegationID: unique identifier of the delegation
54// - delegation: delegation instance to store
55//
56// Returns:
57// - bool: true if successfully stored
58func (g *govStakerV1) setDelegation(_ int, rlm realm, delegationID int64, delegation *staker.Delegation) bool {
59 if err := g.store.SetDelegation(0, rlm, delegationID, delegation); err != nil {
60 return false
61 }
62 return true
63}
64
65// addDelegation adds a new delegation to storage and updates the delegation manager.
66//
67// Parameters:
68// - delegationID: unique identifier of the delegation
69// - delegation: delegation instance to add
70//
71// Returns:
72// - bool: true if successfully added
73func (g *govStakerV1) addDelegation(_ int, rlm realm, delegationID int64, delegation *staker.Delegation) bool {
74 if ok := g.setDelegation(0, rlm, delegationID, delegation); !ok {
75 return false
76 }
77
78 // Update delegation manager
79 delegationManager := g.store.GetDelegationManager()
80 resolvedManager := NewDelegationManagerResolver(delegationManager)
81 resolvedManager.addDelegation(
82 delegation.DelegateFrom(),
83 delegation.DelegateTo(),
84 delegationID,
85 )
86 if err := g.store.SetDelegationManager(0, rlm, delegationManager); err != nil {
87 return false
88 }
89
90 return true
91}
92
93// removeDelegation removes a delegation from storage and updates the delegation manager.
94//
95// Parameters:
96// - delegationID: unique identifier of the delegation to remove
97//
98// Returns:
99// - bool: true if successfully removed
100func (g *govStakerV1) removeDelegation(_ int, rlm realm, delegationID int64) bool {
101 delegation := g.getDelegation(delegationID)
102 if delegation == nil {
103 return false
104 }
105
106 // Remove from store
107 if err := g.store.RemoveDelegation(0, rlm, delegationID); err != nil {
108 return false
109 }
110
111 // Update delegation manager
112 delegationManager := g.store.GetDelegationManager()
113 resolvedManager := NewDelegationManagerResolver(delegationManager)
114 resolvedManager.removeDelegation(
115 delegation.DelegateFrom(),
116 delegation.DelegateTo(),
117 delegationID,
118 )
119 if err := g.store.SetDelegationManager(0, rlm, delegationManager); err != nil {
120 return false
121 }
122
123 return true
124}
125
126// getUserDelegations retrieves all delegations for a specific user.
127//
128// Parameters:
129// - user: user's address
130//
131// Returns:
132// - *bptree.BPTree: tree of user's delegations
133func (g *govStakerV1) getUserDelegations(user address) *bptree.BPTree {
134 delegationManager := g.store.GetDelegationManager()
135
136 userDelegations, exists := delegationManager.GetDelegatorDelegations(user.String())
137 if !exists {
138 return staker.NewUserDelegationTree()
139 }
140
141 return userDelegations
142}
143
144// getUserDelegationsWithDelegatee retrieves all delegations from a user to a specific delegatee.
145//
146// Parameters:
147// - user: user's address
148// - delegatee: delegatee's address
149//
150// Returns:
151// - []int64: list of user's delegation IDs to the delegatee
152func (g *govStakerV1) getUserDelegationIDsWithDelegatee(user address, delegatee address) []int64 {
153 delegationManager := g.store.GetDelegationManager()
154
155 userDelegations, exists := delegationManager.GetDelegatorDelegations(user.String())
156 if !exists {
157 return nil
158 }
159
160 delegateeStr := delegatee.String()
161 delegationIDs, exists := userDelegations.Get(delegateeStr)
162 if !exists {
163 return nil
164 }
165
166 ids, ok := delegationIDs.([]int64)
167 if !ok {
168 return nil
169 }
170
171 return ids
172}
173
174// addDelegationRecord records a delegation change in the history.
175// Updates both total and user delegation histories with cumulative values.
176//
177// Parameters:
178// - delegateeAddr: address of the delegatee
179// - amount: amount change (positive for delegate, negative for undelegate)
180// - timestamp: timestamp of the delegation change
181func (g *govStakerV1) addDelegationRecord(_ int, rlm realm, delegateeAddr address, amount int64, timestamp int64) {
182 // Update total delegation history
183 g.updateTotalDelegationHistory(0, rlm, amount, timestamp)
184
185 // Update user delegation history
186 g.updateUserDelegationHistory(0, rlm, delegateeAddr, amount, timestamp)
187}
188
189// updateTotalDelegationHistory updates the total delegation history with cumulative value.
190//
191// Parameters:
192// - amount: amount change (positive for delegate, negative for undelegate)
193// - timestamp: timestamp of the change
194func (g *govStakerV1) updateTotalDelegationHistory(_ int, rlm realm, amount int64, timestamp int64) {
195 history := g.store.GetTotalDelegationHistory()
196
197 // Get current total from the most recent entry
198 currentTotal := g.getLatestTotalDelegation(history)
199 newTotal := safeAddInt64(currentTotal, amount)
200
201 if newTotal < 0 {
202 newTotal = 0
203 }
204
205 history.Set(timestamp, newTotal)
206
207 if err := g.store.SetTotalDelegationHistory(0, rlm, history); err != nil {
208 panic(err)
209 }
210}
211
212// updateUserDelegationHistory updates the user delegation history with cumulative values.
213// Structure: single BPTree keyed by composite key "addrStr|paddedTimestamp" -> int64
214//
215// Parameters:
216// - delegateeAddr: address of the delegatee
217// - amount: amount change (positive for delegate, negative for undelegate)
218// - timestamp: timestamp of the change
219func (g *govStakerV1) updateUserDelegationHistory(_ int, rlm realm, delegateeAddr address, amount int64, timestamp int64) {
220 history := g.store.GetUserDelegationHistory()
221 addrStr := delegateeAddr.String()
222
223 // Get current amount from the most recent entry for this user
224 currentAmount := g.getLatestUserDelegationByAddress(history, addrStr)
225 newAmount := safeAddInt64(currentAmount, amount)
226 if newAmount < 0 {
227 newAmount = 0
228 }
229
230 // Store new cumulative value under composite key
231 history.Set(makeUserHistoryKey(addrStr, timestamp), newAmount)
232
233 if err := g.store.SetUserDelegationHistory(0, rlm, history); err != nil {
234 panic(err)
235 }
236}
237
238// getLatestTotalDelegation gets the latest total delegation amount from history.
239func (g *govStakerV1) getLatestTotalDelegation(history *staker.UintTree) int64 {
240 if history.Size() == 0 {
241 return 0
242 }
243
244 latestTotal := int64(0)
245
246 history.ReverseIterate(0, math.MaxInt64, func(key int64, value any) bool {
247 totalInt, ok := value.(int64)
248 if !ok {
249 panic(ufmt.Sprintf("invalid total type: %T", value))
250 }
251
252 latestTotal = totalInt
253
254 return true // stop after first (most recent) entry
255 })
256
257 return latestTotal
258}
259
260// getLatestUserDelegationByAddress gets the latest delegation amount for a user
261// from the composite-keyed user delegation history.
262func (g *govStakerV1) getLatestUserDelegationByAddress(history *bptree.BPTree, addrStr string) int64 {
263 lo, hi := userHistoryKeyRange(addrStr)
264
265 latestAmount := int64(0)
266
267 history.ReverseIterate(lo, hi, func(_ string, value any) bool {
268 amountInt, ok := value.(int64)
269 if !ok {
270 panic(ufmt.Sprintf("invalid amount type: %T", value))
271 }
272
273 latestAmount = amountInt
274
275 return true // stop after first (most recent) entry
276 })
277
278 return latestAmount
279}
280
281// addStakeEmissionReward adds stake to emission reward tracking for an address.
282// This method updates the emission reward distribution state and adds stake for the specified address.
283//
284// Parameters:
285// - address: staker's address
286// - amount: amount of stake to add
287// - currentTimestamp: current timestamp
288func (g *govStakerV1) addStakeEmissionReward(_ int, rlm realm, address string, amount int64, currentTimestamp int64) error {
289 distributedAmount := emission.GetAccuDistributedToGovStaker()
290
291 emissionRewardManager := g.store.GetEmissionRewardManager()
292 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
293
294 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
295 return err
296 }
297
298 if err := resolvedManager.addStake(address, amount, currentTimestamp); err != nil {
299 return err
300 }
301
302 return g.store.SetEmissionRewardManager(0, rlm, emissionRewardManager)
303}
304
305// removeStakeEmissionReward removes stake from emission reward tracking for an address.
306// This method updates the emission reward distribution state and removes stake for the specified address.
307//
308// Parameters:
309// - address: staker's address
310// - amount: amount of stake to remove
311// - currentTimestamp: current timestamp
312func (g *govStakerV1) removeStakeEmissionReward(_ int, rlm realm, address string, amount int64, currentTimestamp int64) error {
313 distributedAmount := emission.GetAccuDistributedToGovStaker()
314
315 emissionRewardManager := g.store.GetEmissionRewardManager()
316 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
317
318 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
319 return err
320 }
321
322 if err := resolvedManager.removeStake(address, amount, currentTimestamp); err != nil {
323 return err
324 }
325
326 return g.store.SetEmissionRewardManager(0, rlm, emissionRewardManager)
327}
328
329// claimRewardsEmissionReward claims emission rewards for an address.
330// This method updates the emission reward distribution state and processes reward claiming.
331//
332// Parameters:
333// - address: staker's address claiming rewards
334// - currentTimestamp: current timestamp
335//
336// Returns:
337// - int64: amount of emission rewards claimed
338// - error: nil on success, error if claiming fails
339func (g *govStakerV1) claimRewardsEmissionReward(_ int, rlm realm, address string, currentTimestamp int64) (int64, error) {
340 distributedAmount := emission.GetAccuDistributedToGovStaker()
341
342 emissionRewardManager := g.store.GetEmissionRewardManager()
343 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
344
345 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
346 return 0, err
347 }
348
349 amount, err := resolvedManager.claimRewards(address, currentTimestamp)
350 if err != nil {
351 return 0, err
352 }
353
354 if err := g.store.SetEmissionRewardManager(0, rlm, emissionRewardManager); err != nil {
355 return 0, err
356 }
357
358 return amount, nil
359}
360
361// removeLaunchpadProjectDeposit removes a launchpad project deposit record.
362//
363// Parameters:
364// - ownerAddress: project owner's address identifier
365//
366// Returns:
367// - bool: true if successfully removed
368func (g *govStakerV1) removeLaunchpadProjectDeposit(ownerAddress string) bool {
369 launchpadProjectDeposits := g.store.GetLaunchpadProjectDeposits()
370 return launchpadProjectDeposits.RemoveDeposit(ownerAddress)
371}
372
373// addStakeProtocolFeeReward adds stake to protocol fee reward tracking for an address.
374// This method distributes protocol fees and updates the protocol fee reward state.
375//
376// Parameters:
377// - address: staker's address
378// - amount: amount of stake to add
379// - currentTimestamp: current timestamp
380func (g *govStakerV1) addStakeProtocolFeeReward(_ int, rlm realm, address string, amount int64, currentTimestamp int64) error {
381 pf.DistributeProtocolFee(cross(rlm))
382
383 distributedAmounts := g.getDistributedProtocolFees()
384
385 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
386 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
387
388 if err := resolvedManager.updateAccumulatedProtocolFeeX128PerStake(distributedAmounts, currentTimestamp); err != nil {
389 return err
390 }
391
392 if err := resolvedManager.addStake(address, amount, currentTimestamp); err != nil {
393 return err
394 }
395
396 return g.store.SetProtocolFeeRewardManager(0, rlm, protocolFeeRewardManager)
397}
398
399// removeStakeProtocolFeeReward removes stake from protocol fee reward tracking for an address.
400// This method distributes protocol fees and updates the protocol fee reward state.
401//
402// Parameters:
403// - address: staker's address
404// - amount: amount of stake to remove
405// - currentTimestamp: current timestamp
406func (g *govStakerV1) removeStakeProtocolFeeReward(_ int, rlm realm, address string, amount int64, currentTimestamp int64) error {
407 pf.DistributeProtocolFee(cross(rlm))
408
409 distributedAmounts := g.getDistributedProtocolFees()
410
411 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
412 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
413
414 if err := resolvedManager.updateAccumulatedProtocolFeeX128PerStake(distributedAmounts, currentTimestamp); err != nil {
415 return err
416 }
417
418 if err := resolvedManager.removeStake(address, amount, currentTimestamp); err != nil {
419 return err
420 }
421
422 return g.store.SetProtocolFeeRewardManager(0, rlm, protocolFeeRewardManager)
423}
424
425// claimRewardsProtocolFeeReward claims protocol fee rewards for an address.
426// This method distributes protocol fees and processes reward claiming for all token types.
427//
428// Parameters:
429// - address: staker's address claiming rewards
430// - currentTimestamp: current timestamp
431//
432// Returns:
433// - map[string]int64: protocol fee rewards claimed by token
434// - error: nil on success, error if claiming fails
435func (g *govStakerV1) claimRewardsProtocolFeeReward(_ int, rlm realm, address string, currentTimestamp int64) (map[string]int64, error) {
436 pf.DistributeProtocolFee(cross(rlm))
437
438 distributedAmounts := g.getDistributedProtocolFees()
439
440 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
441 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
442
443 if err := resolvedManager.updateAccumulatedProtocolFeeX128PerStake(distributedAmounts, currentTimestamp); err != nil {
444 return nil, err
445 }
446
447 rewards, err := resolvedManager.claimRewards(address, currentTimestamp)
448 if err != nil {
449 return nil, err
450 }
451
452 if err := g.store.SetProtocolFeeRewardManager(0, rlm, protocolFeeRewardManager); err != nil {
453 return nil, err
454 }
455
456 return rewards, nil
457}
458
459// getDistributedProtocolFees retrieves the current distributed protocol fee amounts for all tokens.
460// This method queries the protocol fee contract for accumulated distributions.
461//
462// Returns:
463// - map[string]int64: distributed amounts by token path
464func (g *govStakerV1) getDistributedProtocolFees() map[string]int64 {
465 return pf.GetActualDistributedToGovStaker()
466}
467
468// getLaunchpadProjectDeposit retrieves the deposit amount for a launchpad project.
469//
470// Parameters:
471// - ownerAddress: project owner's address identifier
472//
473// Returns:
474// - int64: deposit amount
475// - bool: true if project exists, false otherwise
476func (g *govStakerV1) getLaunchpadProjectDeposit(ownerAddress string) (int64, bool) {
477 launchpadDeposits := g.store.GetLaunchpadProjectDeposits()
478 resolvedDeposits := NewLaunchpadProjectDepositsResolver(launchpadDeposits)
479 return resolvedDeposits.getLaunchpadProjectDeposit(ownerAddress)
480}
481
482// setLaunchpadProjectDeposit sets the deposit amount for a launchpad project.
483//
484// Parameters:
485// - ownerAddress: project owner's address identifier
486// - deposit: deposit amount to set
487//
488// Returns:
489// - bool: true if successfully set
490func (g *govStakerV1) setLaunchpadProjectDeposit(_ int, rlm realm, ownerAddress string, deposit int64) bool {
491 launchpadDeposits := g.store.GetLaunchpadProjectDeposits()
492 resolvedDeposits := NewLaunchpadProjectDepositsResolver(launchpadDeposits)
493 resolvedDeposits.setLaunchpadProjectDeposit(ownerAddress, deposit)
494 if err := g.store.SetLaunchpadProjectDeposits(0, rlm, launchpadDeposits); err != nil {
495 return false
496 }
497 return true
498}
499
500// addStakeFromLaunchpad adds stake for a launchpad project and updates reward tracking.
501// This method creates a special reward ID for launchpad projects and manages their deposit tracking.
502//
503// Parameters:
504// - address: project wallet address
505// - amount: amount of stake to add
506// - currentTimestamp: current timestamp
507func (g *govStakerV1) addStakeFromLaunchpad(_ int, rlm realm, address string, amount int64, currentTimestamp int64) error {
508 launchpadRewardID := g.makeLaunchpadRewardID(address)
509 err := g.addStakeEmissionReward(0, rlm, launchpadRewardID, amount, currentTimestamp)
510 if err != nil {
511 return err
512 }
513
514 err = g.addStakeProtocolFeeReward(0, rlm, launchpadRewardID, amount, currentTimestamp)
515 if err != nil {
516 return err
517 }
518
519 deposit, exists := g.getLaunchpadProjectDeposit(launchpadRewardID)
520 if !exists {
521 deposit = 0
522 }
523
524 deposit = safeAddInt64(deposit, amount)
525 g.setLaunchpadProjectDeposit(0, rlm, launchpadRewardID, deposit)
526
527 return nil
528}
529
530// removeStakeFromLaunchpad removes stake for a launchpad project and updates reward tracking.
531// This method manages launchpad project deposit tracking and ensures non-negative deposits.
532//
533// Parameters:
534// - address: project wallet address
535// - amount: amount of stake to remove
536// - currentTimestamp: current timestamp
537func (g *govStakerV1) removeStakeFromLaunchpad(_ int, rlm realm, address string, amount int64, currentTimestamp int64) error {
538 launchpadRewardID := g.makeLaunchpadRewardID(address)
539 err := g.removeStakeEmissionReward(0, rlm, launchpadRewardID, amount, currentTimestamp)
540 if err != nil {
541 return err
542 }
543
544 err = g.removeStakeProtocolFeeReward(0, rlm, launchpadRewardID, amount, currentTimestamp)
545 if err != nil {
546 return err
547 }
548
549 deposit, exists := g.getLaunchpadProjectDeposit(launchpadRewardID)
550 if !exists {
551 deposit = 0
552 }
553
554 deposit = safeSubInt64(deposit, amount)
555 if deposit < 0 {
556 deposit = 0
557 }
558
559 g.setLaunchpadProjectDeposit(0, rlm, launchpadRewardID, deposit)
560
561 return nil
562}
563
564// makeLaunchpadRewardID creates a special reward identifier for launchpad projects.
565// This ensures launchpad project rewards are tracked separately from regular user stakes.
566//
567// Parameters:
568// - address: project wallet address
569//
570// Returns:
571// - string: formatted launchpad reward ID
572func (g *govStakerV1) makeLaunchpadRewardID(address string) string {
573 return "launchpad:" + address
574}