Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}