state.gno
18.07 Kb ยท 592 lines
1package v1
2
3import (
4 "math"
5
6 "gno.land/p/nt/avl"
7 "gno.land/p/nt/ufmt"
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(period int64) {
20 if err := g.store.SetUnDelegationLockupPeriod(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(delegationID int64, delegation *staker.Delegation) bool {
59 if err := g.store.SetDelegation(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(delegationID int64, delegation *staker.Delegation) bool {
74 if ok := g.setDelegation(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(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(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(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(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// - *avl.Tree: tree of user's delegations
133func (g *govStakerV1) getUserDelegations(user address) *avl.Tree {
134 delegationManager := g.store.GetDelegationManager()
135
136 userDelegations, exists := delegationManager.GetDelegatorDelegations(user.String())
137 if !exists {
138 return avl.NewTree()
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(delegateeAddr address, amount int64, timestamp int64) {
182 // Update total delegation history
183 g.updateTotalDelegationHistory(amount, timestamp)
184
185 // Update user delegation history
186 g.updateUserDelegationHistory(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(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(history); err != nil {
208 panic(err)
209 }
210}
211
212// updateUserDelegationHistory updates the user delegation history with cumulative values.
213// Structure: address -> *UintTree[timestamp -> 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(delegateeAddr address, amount int64, timestamp int64) {
220 history := g.store.GetUserDelegationHistory()
221 addrStr := delegateeAddr.String()
222
223 // Get or create user's timestamp tree
224 var userHistory *staker.UintTree
225
226 if existingHistory, exists := history.Get(addrStr); exists {
227 userHistoryInt, ok := existingHistory.(*staker.UintTree)
228 if !ok {
229 panic(ufmt.Sprintf("invalid user history type: %T", existingHistory))
230 }
231
232 userHistory = userHistoryInt
233 } else {
234 userHistory = staker.NewUintTree()
235 }
236
237 // Get current amount from the most recent entry
238 currentAmount := g.getLatestUserDelegation(userHistory)
239 newAmount := safeAddInt64(currentAmount, amount)
240 if newAmount < 0 {
241 newAmount = 0
242 }
243
244 // Store new cumulative value at this timestamp
245 userHistory.Set(timestamp, newAmount)
246 history.Set(addrStr, userHistory)
247
248 if err := g.store.SetUserDelegationHistory(history); err != nil {
249 panic(err)
250 }
251}
252
253// getLatestTotalDelegation gets the latest total delegation amount from history.
254func (g *govStakerV1) getLatestTotalDelegation(history *staker.UintTree) int64 {
255 if history.Size() == 0 {
256 return 0
257 }
258
259 latestTotal := int64(0)
260
261 history.ReverseIterate(0, math.MaxInt64, func(key int64, value any) bool {
262 totalInt, ok := value.(int64)
263 if !ok {
264 panic(ufmt.Sprintf("invalid total type: %T", value))
265 }
266
267 latestTotal = totalInt
268
269 return true // stop after first (most recent) entry
270 })
271
272 return latestTotal
273}
274
275// getLatestUserDelegation gets the latest delegation amount for a user from their history.
276func (g *govStakerV1) getLatestUserDelegation(userHistory *staker.UintTree) int64 {
277 if userHistory.Size() == 0 {
278 return 0
279 }
280
281 latestAmount := int64(0)
282
283 userHistory.ReverseIterate(0, math.MaxInt64, func(key int64, value any) bool {
284 amountInt, ok := value.(int64)
285 if !ok {
286 panic(ufmt.Sprintf("invalid amount type: %T", value))
287 }
288
289 latestAmount = amountInt
290
291 return true // stop after first (most recent) entry
292 })
293
294 return latestAmount
295}
296
297// addStakeEmissionReward adds stake to emission reward tracking for an address.
298// This method updates the emission reward distribution state and adds stake for the specified address.
299//
300// Parameters:
301// - address: staker's address
302// - amount: amount of stake to add
303// - currentTimestamp: current timestamp
304func (g *govStakerV1) addStakeEmissionReward(address string, amount int64, currentTimestamp int64) error {
305 distributedAmount := emission.GetAccuDistributedToGovStaker()
306
307 emissionRewardManager := g.store.GetEmissionRewardManager()
308 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
309
310 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
311 return err
312 }
313
314 if err := resolvedManager.addStake(address, amount, currentTimestamp); err != nil {
315 return err
316 }
317
318 return g.store.SetEmissionRewardManager(emissionRewardManager)
319}
320
321// removeStakeEmissionReward removes stake from emission reward tracking for an address.
322// This method updates the emission reward distribution state and removes stake for the specified address.
323//
324// Parameters:
325// - address: staker's address
326// - amount: amount of stake to remove
327// - currentTimestamp: current timestamp
328func (g *govStakerV1) removeStakeEmissionReward(address string, amount int64, currentTimestamp int64) error {
329 distributedAmount := emission.GetAccuDistributedToGovStaker()
330
331 emissionRewardManager := g.store.GetEmissionRewardManager()
332 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
333
334 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
335 return err
336 }
337
338 if err := resolvedManager.removeStake(address, amount, currentTimestamp); err != nil {
339 return err
340 }
341
342 return g.store.SetEmissionRewardManager(emissionRewardManager)
343}
344
345// claimRewardsEmissionReward claims emission rewards for an address.
346// This method updates the emission reward distribution state and processes reward claiming.
347//
348// Parameters:
349// - address: staker's address claiming rewards
350// - currentTimestamp: current timestamp
351//
352// Returns:
353// - int64: amount of emission rewards claimed
354// - error: nil on success, error if claiming fails
355func (g *govStakerV1) claimRewardsEmissionReward(address string, currentTimestamp int64) (int64, error) {
356 distributedAmount := emission.GetAccuDistributedToGovStaker()
357
358 emissionRewardManager := g.store.GetEmissionRewardManager()
359 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
360
361 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
362 return 0, err
363 }
364
365 amount, err := resolvedManager.claimRewards(address, currentTimestamp)
366 if err != nil {
367 return 0, err
368 }
369
370 if err := g.store.SetEmissionRewardManager(emissionRewardManager); err != nil {
371 return 0, err
372 }
373
374 return amount, nil
375}
376
377// removeLaunchpadProjectDeposit removes a launchpad project deposit record.
378//
379// Parameters:
380// - ownerAddress: project owner's address identifier
381//
382// Returns:
383// - bool: true if successfully removed
384func (g *govStakerV1) removeLaunchpadProjectDeposit(ownerAddress string) bool {
385 launchpadProjectDeposits := g.store.GetLaunchpadProjectDeposits()
386
387 resolver := NewLaunchpadProjectDepositsResolver(launchpadProjectDeposits)
388 return resolver.RemoveDeposit(ownerAddress)
389}
390
391// addStakeProtocolFeeReward adds stake to protocol fee reward tracking for an address.
392// This method distributes protocol fees and updates the protocol fee reward state.
393//
394// Parameters:
395// - address: staker's address
396// - amount: amount of stake to add
397// - currentTimestamp: current timestamp
398func (g *govStakerV1) addStakeProtocolFeeReward(address string, amount int64, currentTimestamp int64) error {
399 pf.DistributeProtocolFee(cross)
400
401 distributedAmounts := g.getDistributedProtocolFees()
402
403 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
404 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
405
406 if err := resolvedManager.updateAccumulatedProtocolFeeX128PerStake(distributedAmounts, currentTimestamp); err != nil {
407 return err
408 }
409
410 if err := resolvedManager.addStake(address, amount, currentTimestamp); err != nil {
411 return err
412 }
413
414 return g.store.SetProtocolFeeRewardManager(protocolFeeRewardManager)
415}
416
417// removeStakeProtocolFeeReward removes stake from protocol fee reward tracking for an address.
418// This method distributes protocol fees and updates the protocol fee reward state.
419//
420// Parameters:
421// - address: staker's address
422// - amount: amount of stake to remove
423// - currentTimestamp: current timestamp
424func (g *govStakerV1) removeStakeProtocolFeeReward(address string, amount int64, currentTimestamp int64) error {
425 pf.DistributeProtocolFee(cross)
426
427 distributedAmounts := g.getDistributedProtocolFees()
428
429 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
430 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
431
432 if err := resolvedManager.updateAccumulatedProtocolFeeX128PerStake(distributedAmounts, currentTimestamp); err != nil {
433 return err
434 }
435
436 if err := resolvedManager.removeStake(address, amount, currentTimestamp); err != nil {
437 return err
438 }
439
440 return g.store.SetProtocolFeeRewardManager(protocolFeeRewardManager)
441}
442
443// claimRewardsProtocolFeeReward claims protocol fee rewards for an address.
444// This method distributes protocol fees and processes reward claiming for all token types.
445//
446// Parameters:
447// - address: staker's address claiming rewards
448// - currentTimestamp: current timestamp
449//
450// Returns:
451// - map[string]int64: protocol fee rewards claimed by token
452// - error: nil on success, error if claiming fails
453func (g *govStakerV1) claimRewardsProtocolFeeReward(address string, currentTimestamp int64) (map[string]int64, error) {
454 pf.DistributeProtocolFee(cross)
455
456 distributedAmounts := g.getDistributedProtocolFees()
457
458 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
459 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
460
461 if err := resolvedManager.updateAccumulatedProtocolFeeX128PerStake(distributedAmounts, currentTimestamp); err != nil {
462 return nil, err
463 }
464
465 rewards, err := resolvedManager.claimRewards(address, currentTimestamp)
466 if err != nil {
467 return nil, err
468 }
469
470 if err := g.store.SetProtocolFeeRewardManager(protocolFeeRewardManager); err != nil {
471 return nil, err
472 }
473
474 return rewards, nil
475}
476
477// getDistributedProtocolFees retrieves the current distributed protocol fee amounts for all tokens.
478// This method queries the protocol fee contract for accumulated distributions.
479//
480// Returns:
481// - map[string]int64: distributed amounts by token path
482func (g *govStakerV1) getDistributedProtocolFees() map[string]int64 {
483 return pf.GetAccuTransfersToGovStaker()
484}
485
486// getLaunchpadProjectDeposit retrieves the deposit amount for a launchpad project.
487//
488// Parameters:
489// - ownerAddress: project owner's address identifier
490//
491// Returns:
492// - int64: deposit amount
493// - bool: true if project exists, false otherwise
494func (g *govStakerV1) getLaunchpadProjectDeposit(ownerAddress string) (int64, bool) {
495 launchpadDeposits := g.store.GetLaunchpadProjectDeposits()
496 resolvedDeposits := NewLaunchpadProjectDepositsResolver(launchpadDeposits)
497 return resolvedDeposits.getLaunchpadProjectDeposit(ownerAddress)
498}
499
500// setLaunchpadProjectDeposit sets the deposit amount for a launchpad project.
501//
502// Parameters:
503// - ownerAddress: project owner's address identifier
504// - deposit: deposit amount to set
505//
506// Returns:
507// - bool: true if successfully set
508func (g *govStakerV1) setLaunchpadProjectDeposit(ownerAddress string, deposit int64) bool {
509 launchpadDeposits := g.store.GetLaunchpadProjectDeposits()
510 resolvedDeposits := NewLaunchpadProjectDepositsResolver(launchpadDeposits)
511 resolvedDeposits.setLaunchpadProjectDeposit(ownerAddress, deposit)
512 if err := g.store.SetLaunchpadProjectDeposits(launchpadDeposits); err != nil {
513 return false
514 }
515 return true
516}
517
518// addStakeFromLaunchpad adds stake for a launchpad project and updates reward tracking.
519// This method creates a special reward ID for launchpad projects and manages their deposit tracking.
520//
521// Parameters:
522// - address: project wallet address
523// - amount: amount of stake to add
524// - currentTimestamp: current timestamp
525func (g *govStakerV1) addStakeFromLaunchpad(address string, amount int64, currentTimestamp int64) error {
526 launchpadRewardID := g.makeLaunchpadRewardID(address)
527 err := g.addStakeEmissionReward(launchpadRewardID, amount, currentTimestamp)
528 if err != nil {
529 return err
530 }
531
532 err = g.addStakeProtocolFeeReward(launchpadRewardID, amount, currentTimestamp)
533 if err != nil {
534 return err
535 }
536
537 deposit, exists := g.getLaunchpadProjectDeposit(launchpadRewardID)
538 if !exists {
539 deposit = 0
540 }
541
542 deposit = safeAddInt64(deposit, amount)
543 g.setLaunchpadProjectDeposit(launchpadRewardID, deposit)
544
545 return nil
546}
547
548// removeStakeFromLaunchpad removes stake for a launchpad project and updates reward tracking.
549// This method manages launchpad project deposit tracking and ensures non-negative deposits.
550//
551// Parameters:
552// - address: project wallet address
553// - amount: amount of stake to remove
554// - currentTimestamp: current timestamp
555func (g *govStakerV1) removeStakeFromLaunchpad(address string, amount int64, currentTimestamp int64) error {
556 launchpadRewardID := g.makeLaunchpadRewardID(address)
557 err := g.removeStakeEmissionReward(launchpadRewardID, amount, currentTimestamp)
558 if err != nil {
559 return err
560 }
561
562 err = g.removeStakeProtocolFeeReward(launchpadRewardID, amount, currentTimestamp)
563 if err != nil {
564 return err
565 }
566
567 deposit, exists := g.getLaunchpadProjectDeposit(launchpadRewardID)
568 if !exists {
569 deposit = 0
570 }
571
572 deposit = safeSubInt64(deposit, amount)
573 if deposit < 0 {
574 deposit = 0
575 }
576
577 g.setLaunchpadProjectDeposit(launchpadRewardID, deposit)
578
579 return nil
580}
581
582// makeLaunchpadRewardID creates a special reward identifier for launchpad projects.
583// This ensures launchpad project rewards are tracked separately from regular user stakes.
584//
585// Parameters:
586// - address: project wallet address
587//
588// Returns:
589// - string: formatted launchpad reward ID
590func (g *govStakerV1) makeLaunchpadRewardID(address string) string {
591 return "launchpad:" + address
592}