Kind Realm (renderable)
Name v1
Namespace gnoswap / gov / staker
Exported functions 12
Module gno.land/r/gnoswap/gov/staker/v1
gno 0.9 state.gno gno
⧉
1 package staker 2 3 import ( 4 "errors" 5 "math" 6 7 gnsmath "gno.land/p/gnoswap/gnsmath/v1" 8 NewDelegation (id int64, delegateFrom string, delegateTo string, delegateAmount int64, createdHeight int64, createdAt int64) *gno.land/r/gnoswap/gov/staker.Delegation
NewDelegationManagerResolver (delegationManager *gno.land/r/gnoswap/gov/staker.DelegationManager) *gno.land/r/gnoswap/gov/staker/v1.DelegationManagerResolver
NewDelegationResolver (delegation *gno.land/r/gnoswap/gov/staker.Delegation) *gno.land/r/gnoswap/gov/staker/v1.DelegationResolver
NewDelegationWithdraw (delegationID int64, unDelegateAmount int64, createdHeight int64, createdAt int64, unDelegationLockupPeriod int64) struct{delegationID int64; unDelegateAmount int64; unDelegatedHeight int64; unDelegatedAt int64; collectedAmount int64; collectableTime int64; collectedAt int64; collected bool}
NewDelegationWithdrawResolver (withdraw *gno.land/r/gnoswap/gov/staker.DelegationWithdraw) *gno.land/r/gnoswap/gov/staker/v1.DelegationWithdrawResolver
NewDelegationWithdrawWithoutLockup (delegationID int64, unDelegateAmount int64, createdHeight int64, createdAt int64) struct{delegationID int64; unDelegateAmount int64; unDelegatedHeight int64; unDelegatedAt int64; collectedAmount int64; collectableTime int64; collectedAt int64; collected bool}
NewEmissionRewardManagerResolver (emissionRewardManager *gno.land/r/gnoswap/gov/staker.EmissionRewardManager) *gno.land/r/gnoswap/gov/staker/v1.EmissionRewardManagerResolver
NewEmissionRewardStateResolver (emissionRewardState *gno.land/r/gnoswap/gov/staker.EmissionRewardState) *gno.land/r/gnoswap/gov/staker/v1.EmissionRewardStateResolver
NewGovStakerV1 (store interface {GetAllDelegations func() *gno.land/p/nt/bptree/v0.BPTree; GetDelegation func(int64) (*gno.land/r/gnoswap/gov/staker.Delegation, bool); GetDelegationCounter func() *gno.land/r/gnoswap/gov/staker.Counter; GetDelegationManager func() *gno.land/r/gnoswap/gov/staker.DelegationManager; GetEmissionRewardManager func() *gno.land/r/gnoswap/gov/staker.EmissionRewardManager; GetLaunchpadProjectDeposits func() *gno.land/r/gnoswap/gov/staker.LaunchpadProjectDeposits; GetProtocolFeeRewardManager func() *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardManager; GetTotalDelegatedAmount func() int64; GetTotalDelegationHistory func() *gno.land/r/gnoswap/gov/staker.UintTree; GetTotalLockedAmount func() int64; GetUnDelegationLockupPeriod func() int64; GetUserDelegationHistory func() *gno.land/p/nt/bptree/v0.BPTree; HasDelegation func(int64) bool; HasDelegationCounterStoreKey func() bool; HasDelegationManagerStoreKey func() bool; HasDelegationsStoreKey func() bool; HasEmissionRewardManagerStoreKey func() bool; HasLaunchpadProjectDepositsStoreKey func() bool; HasProtocolFeeRewardManagerStoreKey func() bool; HasTotalDelegatedAmountStoreKey func() bool; HasTotalDelegationHistoryStoreKey func() bool; HasTotalLockedAmountStoreKey func() bool; HasUnDelegationLockupPeriodStoreKey func() bool; HasUserDelegationHistoryStoreKey func() bool; RemoveDelegation func(int, .uverse.realm, int64) .uverse.error; SetDelegation func(int, .uverse.realm, int64, *gno.land/r/gnoswap/gov/staker.Delegation) .uverse.error; SetDelegationCounter func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.Counter) .uverse.error; SetDelegationManager func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.DelegationManager) .uverse.error; SetDelegations func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetEmissionRewardManager func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.EmissionRewardManager) .uverse.error; SetLaunchpadProjectDeposits func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.LaunchpadProjectDeposits) .uverse.error; SetProtocolFeeRewardManager func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardManager) .uverse.error; SetTotalDelegatedAmount func(int, .uverse.realm, int64) .uverse.error; SetTotalDelegationHistory func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.UintTree) .uverse.error; SetTotalLockedAmount func(int, .uverse.realm, int64) .uverse.error; SetUnDelegationLockupPeriod func(int, .uverse.realm, int64) .uverse.error; SetUserDelegationHistory func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error}) interface {CleanStakerDelegationSnapshotByAdmin func(int, .uverse.realm, int64, .uverse.address); CollectEmissionReward func(int, .uverse.realm); CollectEmissionRewardFromLaunchPad func(int, .uverse.realm, .uverse.address); CollectProtocolFeeReward func(int, .uverse.realm, string); CollectProtocolFeeRewardFromLaunchPad func(int, .uverse.realm, .uverse.address, string); CollectReward func(int, .uverse.realm); CollectRewardFromLaunchPad func(int, .uverse.realm, .uverse.address); CollectUndelegatedGns func(int, .uverse.realm) int64; Delegate func(int, .uverse.realm, .uverse.address, int64, string) int64; ExistsDelegation func(int64) bool; GetClaimableRewardByAddress func(.uverse.address) (int64, map[string]int64, .uverse.error); GetClaimableRewardByLaunchpad func(.uverse.address) (int64, map[string]int64, .uverse.error); GetClaimableRewardByRewardID func(string) (int64, map[string]int64, .uverse.error); GetCollectableWithdrawAmount func(int64) int64; GetDelegationWithdrawCount func(int64) int; GetDelegationWithdraws func(int64, int, int) ([]gno.land/r/gnoswap/gov/staker.DelegationWithdraw, .uverse.error); GetDelegations func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetDelegatorDelegations func(.uverse.address) *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetEmissionAccumulatedTimestamp func() int64; GetEmissionAccumulatedX128PerStake func() *gno.land/p/gnoswap/uint256/v1.Uint; GetEmissionDistributedAmount func() int64; GetLaunchpadProjectDeposit func(string) (int64, bool); GetProtocolFeeAccumulatedTimestamp func() int64; GetProtocolFeeAccumulatedX128PerStake func(string) *gno.land/p/gnoswap/uint256/v1.Uint; GetProtocolFeeAmount func(string) int64; GetTotalDelegated func() int64; GetTotalDelegationAmountAtSnapshot func(int64) (int64, bool); GetTotalLockedAmount func() int64; GetTotalxGnsSupply func() int64; GetUnDelegationLockupPeriod func() int64; GetUserDelegationAmountAtSnapshot func(.uverse.address, int64) (int64, bool); GetUserDelegationIDs func(.uverse.address, .uverse.address) []int64; HasDelegationSnapshotsKey func() bool; Redelegate func(int, .uverse.realm, .uverse.address, .uverse.address, int64) int64; Render func(string) string; SetAmountByProjectWallet func(int, .uverse.realm, .uverse.address, int64, bool); SetUnDelegationLockupPeriodByAdmin func(int, .uverse.realm, int64); Undelegate func(int, .uverse.realm, .uverse.address, int64) int64}
NewLaunchpadProjectDepositsResolver (launchpadProjectDeposits *gno.land/r/gnoswap/gov/staker.LaunchpadProjectDeposits) *gno.land/r/gnoswap/gov/staker/v1.LaunchpadProjectDepositsResolver
NewProtocolFeeRewardManagerResolver (manager *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardManager) *gno.land/r/gnoswap/gov/staker/v1.ProtocolFeeRewardManagerResolver
NewProtocolFeeRewardStateResolver (protocolFeeRewardState *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardState) *gno.land/r/gnoswap/gov/staker/v1.ProtocolFeeRewardStateResolver
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
Render unavailable
rate limit exceeded — the render query is served by the Gnoland archive RPC.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
bptree "gno.land/p/nt/bptree/v0"
9 ufmt "gno.land/p/nt/ufmt/v0"
10
11 "gno.land/r/gnoswap/emission"
12 "gno.land/r/gnoswap/gov/staker"
13 pf "gno.land/r/gnoswap/protocol_fee"
14 )
15
16 // setUnDelegationLockupPeriod updates the undelegation lockup period.
17 // This affects all future undelegation operations.
18 //
19 // Parameters:
20 // - period: new lockup period in seconds
21 func (g *govStakerV1) setUnDelegationLockupPeriod(_ int , rlm realm, period int64 ) {
22 if err := g.store.SetUnDelegationLockupPeriod( 0 , rlm, period); err != nil {
23 panic(err)
24 }
25 }
26
27 // nextDelegationID generates and returns the next unique delegation ID.
28 //
29 // Returns:
30 // - int64: next available delegation ID
31 func (g *govStakerV1) nextDelegationID() int64 {
32 counter := g.store.GetDelegationCounter()
33
34 return counter.Next()
35 }
36
37 // getDelegation retrieves a delegation by its ID.
38 //
39 // Parameters:
40 // - delegationID: unique identifier of the delegation
41 //
42 // Returns:
43 // - *Delegation: delegation instance or nil if not found
44 func (g *govStakerV1) getDelegation(delegationID int64 ) *staker.Delegation {
45 delegation, exists := g.store.GetDelegation(delegationID)
46 if !exists {
47 return nil
48 }
49 return delegation
50 }
51
52 // setDelegation stores or updates a delegation in the storage tree.
53 //
54 // Parameters:
55 // - delegationID: unique identifier of the delegation
56 // - delegation: delegation instance to store
57 //
58 // Returns:
59 // - error: nil on success, otherwise the storage write error
60 func (g *govStakerV1) setDelegation(_ int , rlm realm, delegationID int64 , delegation *staker.Delegation) error {
61 return g.store.SetDelegation( 0 , rlm, delegationID, delegation)
62 }
63
64 // addDelegation adds a new delegation to storage and updates the delegation manager.
65 //
66 // Parameters:
67 // - delegationID: unique identifier of the delegation to add
68 // - delegation: delegation instance to add
69 //
70 // Returns:
71 // - error: nil on success, otherwise the delegation or manager storage error
72 func (g *govStakerV1) addDelegation(_ int , rlm realm, delegationID int64 , delegation *staker.Delegation) error {
73 if err := g.setDelegation( 0 , rlm, delegationID, delegation); err != nil {
74 return err
75 }
76
77 delegationManager := g.store.GetDelegationManager()
78 resolvedManager := NewDelegationManagerResolver(delegationManager)
79 resolvedManager.addDelegation(
80 delegation.DelegateFrom(),
81 delegation.DelegateTo(),
82 delegationID,
83 )
84
85 return g.store.SetDelegationManager( 0 , rlm, delegationManager)
86 }
87
88 // removeDelegation removes a delegation from storage and updates the delegation manager.
89 //
90 // Parameters:
91 // - delegationID: unique identifier of the delegation to remove
92 //
93 // Returns:
94 // - error: nil on success, otherwise an error when the delegation is absent or storage fails
95 func (g *govStakerV1) removeDelegation(_ int , rlm realm, delegationID int64 ) error {
96 delegation := g.getDelegation(delegationID)
97 if delegation == nil {
98 return errors.New(errDelegationNotFound)
99 }
100
101 if err := g.store.RemoveDelegation( 0 , rlm, delegationID); err != nil {
102 return err
103 }
104
105 delegationManager := g.store.GetDelegationManager()
106 resolvedManager := NewDelegationManagerResolver(delegationManager)
107 resolvedManager.removeDelegation(
108 delegation.DelegateFrom(),
109 delegation.DelegateTo(),
110 delegationID,
111 )
112
113 return g.store.SetDelegationManager( 0 , rlm, delegationManager)
114 }
115
116 // getUserDelegations retrieves all delegations for a specific user.
117 //
118 // Parameters:
119 // - user: user's address
120 //
121 // Returns:
122 // - *bptree.BPTree: tree of user's delegations
123 func (g *govStakerV1) getUserDelegations(user address) *bptree.BPTree {
124 delegationManager := g.store.GetDelegationManager()
125
126 userDelegations, exists := delegationManager.GetDelegatorDelegations(user.String())
127 if !exists {
128 return staker.NewUserDelegationTree()
129 }
130
131 return userDelegations
132 }
133
134 // getUserDelegationsWithDelegatee retrieves all delegations from a user to a specific delegatee.
135 //
136 // Parameters:
137 // - user: user's address
138 // - delegatee: delegatee's address
139 //
140 // Returns:
141 // - []int64: list of user's delegation IDs to the delegatee
142 func (g *govStakerV1) getUserDelegationIDsWithDelegatee(user address, delegatee address) [] int64 {
143 delegationManager := g.store.GetDelegationManager()
144
145 userDelegations, exists := delegationManager.GetDelegatorDelegations(user.String())
146 if !exists {
147 return nil
148 }
149
150 delegateeStr := delegatee.String()
151 delegationIDs := userDelegations.Get(delegateeStr)
152 if delegationIDs == nil {
153 return nil
154 }
155
156 ids, ok := delegationIDs.([] int64 )
157 if !ok {
158 return nil
159 }
160
161 return ids
162 }
163
164 // addDelegationRecord records a delegation change in the history.
165 // Updates both total and user delegation histories with cumulative values.
166 //
167 // Parameters:
168 // - delegateeAddr: address of the delegatee
169 // - amount: amount change (positive for delegate, negative for undelegate)
170 // - timestamp: timestamp of the delegation change
171 func (g *govStakerV1) addDelegationRecord(_ int , rlm realm, delegateeAddr address, amount int64 , timestamp int64 ) {
172 // Update total delegation history
173 g.updateTotalDelegationHistory( 0 , rlm, amount, timestamp)
174
175 // Update user delegation history
176 g.updateUserDelegationHistory( 0 , rlm, delegateeAddr, amount, timestamp)
177 }
178
179 // updateTotalDelegationHistory updates the total delegation history with cumulative value.
180 //
181 // Parameters:
182 // - amount: amount change (positive for delegate, negative for undelegate)
183 // - timestamp: timestamp of the change
184 func (g *govStakerV1) updateTotalDelegationHistory(_ int , rlm realm, amount int64 , timestamp int64 ) {
185 history := g.store.GetTotalDelegationHistory()
186
187 // Get current total from the most recent entry
188 currentTotal := g.getLatestTotalDelegation(history)
189 newTotal := gnsmath.SafeAddInt64(currentTotal, amount)
190
191 if newTotal < 0 {
192 newTotal = 0
193 }
194
195 history.Set(timestamp, newTotal)
196
197 if err := g.store.SetTotalDelegationHistory( 0 , rlm, history); err != nil {
198 panic(err)
199 }
200 }
201
202 // updateUserDelegationHistory updates the user delegation history with cumulative values.
203 // Structure: single BPTree keyed by composite key "addrStr|paddedTimestamp" -> int64
204 //
205 // Parameters:
206 // - delegateeAddr: address of the delegatee
207 // - amount: amount change (positive for delegate, negative for undelegate)
208 // - timestamp: timestamp of the change
209 func (g *govStakerV1) updateUserDelegationHistory(_ int , rlm realm, delegateeAddr address, amount int64 , timestamp int64 ) {
210 history := g.store.GetUserDelegationHistory()
211 addrStr := delegateeAddr.String()
212
213 // Get current amount from the most recent entry for this user
214 currentAmount := g.getLatestUserDelegationByAddress(history, addrStr)
215 newAmount := gnsmath.SafeAddInt64(currentAmount, amount)
216 if newAmount < 0 {
217 newAmount = 0
218 }
219
220 // Store new cumulative value under composite key
221 history.Set(makeUserHistoryKey(addrStr, timestamp), newAmount)
222
223 if err := g.store.SetUserDelegationHistory( 0 , rlm, history); err != nil {
224 panic(err)
225 }
226 }
227
228 // getLatestTotalDelegation gets the latest total delegation amount from history.
229 func (g *govStakerV1) getLatestTotalDelegation(history *staker.UintTree) int64 {
230 if history.Size() == 0 {
231 return 0
232 }
233
234 latestTotal := int64 ( 0 )
235
236 history.ReverseIterate( 0 , math.MaxInt64, func (key int64 , value any ) bool {
237 totalInt, ok := value.( int64 )
238 if !ok {
239 panic(ufmt.Sprintf( "invalid total type: %T" , value))
240 }
241
242 latestTotal = totalInt
243
244 return true // stop after first (most recent) entry
245 })
246
247 return latestTotal
248 }
249
250 // getLatestUserDelegationByAddress gets the latest delegation amount for a user
251 // from the composite-keyed user delegation history.
252 func (g *govStakerV1) getLatestUserDelegationByAddress(history *bptree.BPTree, addrStr string ) int64 {
253 lo, hi := userHistoryKeyRange(addrStr)
254
255 latestAmount := int64 ( 0 )
256
257 history.ReverseIterate(lo, hi, func (_ string , value any ) bool {
258 amountInt, ok := value.( int64 )
259 if !ok {
260 panic(ufmt.Sprintf( "invalid amount type: %T" , value))
261 }
262
263 latestAmount = amountInt
264
265 return true // stop after first (most recent) entry
266 })
267
268 return latestAmount
269 }
270
271 // addStakeEmissionReward adds stake to emission reward tracking for an address.
272 // This method updates the emission reward distribution state and adds stake for the specified address.
273 //
274 // Parameters:
275 // - address: staker's address
276 // - amount: amount of stake to add
277 // - currentTimestamp: current timestamp
278 func (g *govStakerV1) addStakeEmissionReward(_ int , rlm realm, address string , amount int64 , currentTimestamp int64 ) error {
279 distributedAmount := emission.GetAccuDistributedToGovStaker()
280
281 emissionRewardManager := g.store.GetEmissionRewardManager()
282 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
283
284 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
285 return err
286 }
287
288 if err := resolvedManager.addStake(address, amount, currentTimestamp); err != nil {
289 return err
290 }
291
292 err := g.store.SetEmissionRewardManager( 0 , rlm, emissionRewardManager)
293 if err != nil {
294 return err
295 }
296
297 // Emit the emission reward accumulation
298 emitUpdateEmissionRewardAccumulation(emissionRewardManager)
299
300 return nil
301 }
302
303 // removeStakeEmissionReward removes stake from emission reward tracking for an address.
304 // This method updates the emission reward distribution state and removes stake for the specified address.
305 //
306 // Parameters:
307 // - address: staker's address
308 // - amount: amount of stake to remove
309 // - currentTimestamp: current timestamp
310 func (g *govStakerV1) removeStakeEmissionReward(_ int , rlm realm, address string , amount int64 , currentTimestamp int64 ) error {
311 distributedAmount := emission.GetAccuDistributedToGovStaker()
312
313 emissionRewardManager := g.store.GetEmissionRewardManager()
314 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
315
316 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
317 return err
318 }
319
320 if err := resolvedManager.removeStake(address, amount, currentTimestamp); err != nil {
321 return err
322 }
323
324 if err := g.store.SetEmissionRewardManager( 0 , rlm, emissionRewardManager); err != nil {
325 return err
326 }
327
328 // Emit the emission reward accumulation
329 emitUpdateEmissionRewardAccumulation(emissionRewardManager)
330
331 return nil
332 }
333
334 // claimRewardsEmissionReward claims emission rewards for an address.
335 // This method updates the emission reward distribution state and processes reward claiming.
336 //
337 // Parameters:
338 // - address: staker's address claiming rewards
339 // - currentTimestamp: current timestamp
340 //
341 // Returns:
342 // - int64: amount of emission rewards claimed
343 // - error: nil on success, error if claiming fails
344 func (g *govStakerV1) claimRewardsEmissionReward(_ int , rlm realm, address string , currentTimestamp int64 ) ( int64 , error ) {
345 distributedAmount := emission.GetAccuDistributedToGovStaker()
346
347 emissionRewardManager := g.store.GetEmissionRewardManager()
348 resolvedManager := NewEmissionRewardManagerResolver(emissionRewardManager)
349
350 if err := resolvedManager.updateAccumulatedRewardX128PerStake(distributedAmount, currentTimestamp); err != nil {
351 return 0 , err
352 }
353
354 amount, err := resolvedManager.claimRewards(address, currentTimestamp)
355 if err != nil {
356 return 0 , err
357 }
358
359 if err := g.store.SetEmissionRewardManager( 0 , rlm, emissionRewardManager); err != nil {
360 return 0 , err
361 }
362
363 // Emit the emission reward accumulation
364 emitUpdateEmissionRewardAccumulation(emissionRewardManager)
365
366 return amount, nil
367 }
368
369 // removeLaunchpadProjectDeposit removes a launchpad project deposit record.
370 //
371 // Parameters:
372 // - ownerAddress: project owner's address identifier
373 //
374 // Returns:
375 // - bool: true if successfully removed
376 func (g *govStakerV1) removeLaunchpadProjectDeposit(ownerAddress string ) bool {
377 launchpadProjectDeposits := g.store.GetLaunchpadProjectDeposits()
378 return launchpadProjectDeposits.RemoveDeposit(ownerAddress)
379 }
380
381 // addStakeProtocolFeeReward records a stake increase for protocol fee rewards.
382 //
383 // The change only opens a new accrual epoch and appends one stake event, so its cost
384 // does not depend on how many tokens collected fees.
385 //
386 // Parameters:
387 // - address: staker's address
388 // - amount: amount of stake to add
389 // - currentTimestamp: current timestamp
390 func (g *govStakerV1) addStakeProtocolFeeReward(_ int , rlm realm, address string , amount int64 , currentTimestamp int64 ) error {
391 epoch := pf.AdvanceAccrualEpoch(cross(rlm))
392
393 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
394 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
395
396 if err := resolvedManager.addStake(address, amount, epoch); err != nil {
397 return err
398 }
399 resolvedManager.SetAccumulatedTimestamp(currentTimestamp)
400
401 if err := g.store.SetProtocolFeeRewardManager( 0 , rlm, protocolFeeRewardManager); err != nil {
402 return err
403 }
404
405 emitUpdateProtocolFeeStakeAccrual(protocolFeeRewardManager)
406
407 return nil
408 }
409
410 // removeStakeProtocolFeeReward records a stake decrease for protocol fee rewards.
411 // Like addStakeProtocolFeeReward it never folds fees, so a withdrawal never pays for
412 // the reward calculation; that happens on collect, one token at a time if need be.
413 //
414 // Parameters:
415 // - address: staker's address
416 // - amount: amount of stake to remove
417 // - currentTimestamp: current timestamp
418 func (g *govStakerV1) removeStakeProtocolFeeReward(_ int , rlm realm, address string , amount int64 , currentTimestamp int64 ) error {
419 epoch := pf.AdvanceAccrualEpoch(cross(rlm))
420
421 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
422 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
423
424 if err := resolvedManager.removeStake(address, amount, epoch); err != nil {
425 return err
426 }
427 resolvedManager.SetAccumulatedTimestamp(currentTimestamp)
428
429 if err := g.store.SetProtocolFeeRewardManager( 0 , rlm, protocolFeeRewardManager); err != nil {
430 return err
431 }
432
433 emitUpdateProtocolFeeStakeAccrual(protocolFeeRewardManager)
434
435 return nil
436 }
437
438 // foldProtocolFeeAccrual consumes up to limit pending accrual buckets of tokenPath from
439 // the protocol fee realm and folds them into the token's accumulator. A limit of zero
440 // or less consumes every pending bucket.
441 func (g *govStakerV1) foldProtocolFeeAccrual(_ int , rlm realm, resolvedManager *ProtocolFeeRewardManagerResolver, tokenPath string , limit int , currentTimestamp int64 ) error {
442 epochs, amounts := pf.ConsumeAccrualBuckets(cross(rlm), tokenPath, limit)
443 exhausted := limit <= 0 || len(epochs) < limit
444
445 return resolvedManager.applyAccrualBuckets(tokenPath, epochs, amounts, exhausted, currentTimestamp)
446 }
447
448 // claimRewardsProtocolFeeReward claims protocol fee rewards for an address in every token.
449 // It folds every pending token first, so its cost grows with the number of tokens
450 // that collected a fee. claimRewardProtocolFeeRewardByTokenPath is the bounded path.
451 //
452 // Parameters:
453 // - address: staker's address claiming rewards
454 // - currentTimestamp: current timestamp
455 //
456 // Returns:
457 // - map[string]int64: protocol fee rewards claimed by token
458 // - error: nil on success, error if claiming fails
459 func (g *govStakerV1) claimRewardsProtocolFeeReward(_ int , rlm realm, address string , currentTimestamp int64 ) ( map [ string ] int64 , error ) {
460 // Claiming pays out of gov/staker's own balance, so the reserved fees are pulled in
461 // from the protocol fee realm first.
462 pf.DistributeProtocolFee(cross(rlm))
463
464 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
465 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
466
467 // Every pending token is consumed from the protocol fee realm. A known token with
468 // nothing pending has no bucket in any epoch since its last fold, so an empty
469 // exhaustive fold is enough to mark every closed epoch as settled for it.
470 pendingTokenPaths := pf.GetAccrualPendingTokens()
471 pending := make( map [ string ] bool , len(pendingTokenPaths))
472 for _, tokenPath := range pendingTokenPaths {
473 pending[tokenPath] = true
474 if err := g.foldProtocolFeeAccrual( 0 , rlm, resolvedManager, tokenPath, 0 , currentTimestamp); err != nil {
475 return nil , err
476 }
477 }
478
479 foldedTokenPaths := pendingTokenPaths
480 for _, tokenPath := range protocolFeeRewardManager.GetTokenPaths() {
481 if pending[tokenPath] {
482 continue
483 }
484 if err := resolvedManager.applyAccrualBuckets(tokenPath, [] int64 {}, [] int64 {}, true , currentTimestamp); err != nil {
485 return nil , err
486 }
487 foldedTokenPaths = append(foldedTokenPaths, tokenPath)
488 }
489
490 rewards, err := resolvedManager.claimRewards(address, currentTimestamp)
491 if err != nil {
492 return nil , err
493 }
494
495 if err := g.store.SetProtocolFeeRewardManager( 0 , rlm, protocolFeeRewardManager); err != nil {
496 return nil , err
497 }
498
499 emitUpdateProtocolFeeRewardAccumulation(protocolFeeRewardManager, foldedTokenPaths)
500
501 return rewards, nil
502 }
503
504 // claimRewardProtocolFeeRewardByTokenPath claims one protocol fee reward token for an address.
505 //
506 // Both the fold and the settlement are bounded per call (maxAccrualBucketsPerCollect,
507 // maxStakeEventsPerCollect); a token with more pending work pays what is settled and
508 // keeps the rest for a later collect.
509 func (g *govStakerV1) claimRewardProtocolFeeRewardByTokenPath(_ int , rlm realm, address string , tokenPath string , currentTimestamp int64 ) ( int64 , error ) {
510 pf.DistributeProtocolFeeByTokenPath(cross(rlm), tokenPath)
511
512 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
513 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
514
515 if err := g.foldProtocolFeeAccrual( 0 , rlm, resolvedManager, tokenPath, maxAccrualBucketsPerCollect, currentTimestamp); err != nil {
516 return 0 , err
517 }
518
519 reward, err := resolvedManager.claimTokenReward(address, tokenPath, maxStakeEventsPerCollect)
520 if err != nil {
521 return 0 , err
522 }
523
524 if err := g.store.SetProtocolFeeRewardManager( 0 , rlm, protocolFeeRewardManager); err != nil {
525 return 0 , err
526 }
527
528 emitUpdateProtocolFeeRewardAccumulation(protocolFeeRewardManager, [] string {tokenPath})
529
530 return reward, nil
531 }
532
533 // getClaimableProtocolFeeRewards reports what collecting every token would pay for a
534 // reward ID right now, pending accrual buckets included, without changing any state.
535 func (g *govStakerV1) getClaimableProtocolFeeRewards(rewardID string ) ( map [ string ] int64 , error ) {
536 protocolFeeRewardManager := g.store.GetProtocolFeeRewardManager()
537 resolvedManager := NewProtocolFeeRewardManagerResolver(protocolFeeRewardManager)
538
539 rewards := make( map [ string ] int64 )
540 tokenPaths := protocolFeeRewardManager.GetTokenPaths()
541 for _, tokenPath := range pf.GetAccrualPendingTokens() {
542 if _, ok := protocolFeeRewardManager.GetTokenAccumulator(tokenPath); !ok {
543 tokenPaths = append(tokenPaths, tokenPath)
544 }
545 }
546
547 for _, tokenPath := range tokenPaths {
548 pendingEpochs, pendingAmounts := pf.GetAccrualBuckets(tokenPath, 0 )
549 reward, err := resolvedManager.GetClaimableRewardAmount(rewardID, tokenPath, pendingEpochs, pendingAmounts)
550 if err != nil {
551 return nil , err
552 }
553 rewards[tokenPath] = reward
554 }
555
556 return rewards, nil
557 }
558
559 // getLaunchpadProjectDeposit retrieves the deposit amount for a launchpad project.
560 //
561 // Parameters:
562 // - ownerAddress: project owner's address identifier
563 //
564 // Returns:
565 // - int64: deposit amount
566 // - bool: true if project exists, false otherwise
567 func (g *govStakerV1) getLaunchpadProjectDeposit(ownerAddress string ) ( int64 , bool ) {
568 launchpadDeposits := g.store.GetLaunchpadProjectDeposits()
569 resolvedDeposits := NewLaunchpadProjectDepositsResolver(launchpadDeposits)
570 return resolvedDeposits.getLaunchpadProjectDeposit(ownerAddress)
571 }
572
573 // setLaunchpadProjectDeposit sets the deposit amount for a launchpad project.
574 //
575 // Parameters:
576 // - ownerAddress: project owner's address identifier
577 // - deposit: deposit amount to set
578 //
579 // Returns:
580 // - error: nil on success, otherwise the storage write error
581 func (g *govStakerV1) setLaunchpadProjectDeposit(_ int , rlm realm, ownerAddress string , deposit int64 ) error {
582 launchpadDeposits := g.store.GetLaunchpadProjectDeposits()
583 resolvedDeposits := NewLaunchpadProjectDepositsResolver(launchpadDeposits)
584 resolvedDeposits.setLaunchpadProjectDeposit(ownerAddress, deposit)
585
586 return g.store.SetLaunchpadProjectDeposits( 0 , rlm, launchpadDeposits)
587 }
588
589 // addStakeFromLaunchpad adds stake for a launchpad project and updates reward tracking.
590 // This method creates a special reward ID for launchpad projects and manages their deposit tracking.
591 //
592 // Parameters:
593 // - address: project wallet address
594 // - amount: amount of stake to add
595 // - currentTimestamp: current timestamp
596 func (g *govStakerV1) addStakeFromLaunchpad(_ int , rlm realm, address string , amount int64 , currentTimestamp int64 ) error {
597 launchpadRewardID := g.makeLaunchpadRewardID(address)
598 err := g.addStakeEmissionReward( 0 , rlm, launchpadRewardID, amount, currentTimestamp)
599 if err != nil {
600 return err
601 }
602
603 err = g.addStakeProtocolFeeReward( 0 , rlm, launchpadRewardID, amount, currentTimestamp)
604 if err != nil {
605 return err
606 }
607
608 deposit, exists := g.getLaunchpadProjectDeposit(launchpadRewardID)
609 if !exists {
610 deposit = 0
611 }
612
613 deposit = gnsmath.SafeAddInt64(deposit, amount)
614 if err := g.setLaunchpadProjectDeposit( 0 , rlm, launchpadRewardID, deposit); err != nil {
615 return err
616 }
617
618 return nil
619 }
620
621 // removeStakeFromLaunchpad removes stake for a launchpad project and updates reward tracking.
622 // This method manages launchpad project deposit tracking and ensures non-negative deposits.
623 //
624 // Parameters:
625 // - address: project wallet address
626 // - amount: amount of stake to remove
627 // - currentTimestamp: current timestamp
628 func (g *govStakerV1) removeStakeFromLaunchpad(_ int , rlm realm, address string , amount int64 , currentTimestamp int64 ) error {
629 launchpadRewardID := g.makeLaunchpadRewardID(address)
630 err := g.removeStakeEmissionReward( 0 , rlm, launchpadRewardID, amount, currentTimestamp)
631 if err != nil {
632 return err
633 }
634
635 err = g.removeStakeProtocolFeeReward( 0 , rlm, launchpadRewardID, amount, currentTimestamp)
636 if err != nil {
637 return err
638 }
639
640 deposit, exists := g.getLaunchpadProjectDeposit(launchpadRewardID)
641 if !exists {
642 deposit = 0
643 }
644
645 deposit = gnsmath.SafeSubInt64(deposit, amount)
646 if deposit < 0 {
647 deposit = 0
648 }
649
650 if err := g.setLaunchpadProjectDeposit( 0 , rlm, launchpadRewardID, deposit); err != nil {
651 return err
652 }
653
654 return nil
655 }
656
657 // makeLaunchpadRewardID creates a special reward identifier for launchpad projects.
658 // This ensures launchpad project rewards are tracked separately from regular user stakes.
659 //
660 // Parameters:
661 // - address: project wallet address
662 //
663 // Returns:
664 // - string: formatted launchpad reward ID
665 func (g *govStakerV1) makeLaunchpadRewardID(address string ) string {
666 return "launchpad:" + address
667 }
668