Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

state.gno

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