Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}