Kind Realm (renderable)
Name v1
Namespace gnoswap / gov / staker
Exported functions 12
Module gno.land/r/gnoswap/gov/staker/v1
gno 0.9 staker_delegate.gno gno
⧉
1 package staker 2 3 import ( 4 "chain" 5 "chain/runtime" 6 "errors" 7 "time" 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.
9 gnsmath "gno.land/p/gnoswap/gnsmath/v1"
10 "gno.land/p/gnoswap/utils/v1"
11 "gno.land/r/gnoswap/access/v1"
12 "gno.land/r/gnoswap/emission"
13 "gno.land/r/gnoswap/gns"
14
15 "gno.land/r/gnoswap/gov/staker"
16 "gno.land/r/gnoswap/gov/xgns"
17 "gno.land/r/gnoswap/halt/v1"
18 "gno.land/r/gnoswap/referral/v1"
19 )
20
21 // Spoofed-realm guards on entry points reject any caller that fabricates a
22 // realm value distinct from the current crossing frame. The proxy in
23 // gno.land/r/gnoswap/gov/staker always forwards its own `cur`, so a mismatch
24 // here means somebody bypassed the proxy and threaded a fake realm directly
25 // into the implementation.
26
27 // Delegate delegates GNS tokens to an address.
28 //
29 // Converts GNS to xGNS and assigns voting power.
30 // Primary mechanism for participating in governance.
31 // Can delegate to self or any other address.
32 //
33 // Parameters:
34 //
35 // - _: Noncrossing implementation-call discriminator; pass 0.
36 //
37 // - rlm: Current realm context forwarded unchanged by the governance-staker proxy.
38 //
39 // - to: Address to receive voting power (can be self)
40 //
41 // - amount: Amount of GNS to stake and delegate
42 //
43 // - referrer: Optional referral address for tracking
44 //
45 // Process:
46 // 1. Transfers GNS from caller
47 // 2. Mints equivalent xGNS (1:1 ratio)
48 // 3. Assigns voting power to target address
49 // 4. Creates delegation snapshot for voting
50 //
51 // Requirements:
52 // - Minimum 1 GNS delegation
53 // - Valid target address
54 // - Sufficient GNS balance
55 // - Approval for GNS transfer
56 //
57 // Returns:
58 // - delegatedAmount: amount of GNS delegated and mirrored as xGNS
59 func (gs *govStakerV1) Delegate(
60 _ int ,
61 rlm realm,
62 to address,
63 amount int64 ,
64 referrer string ,
65 ) int64 {
66 access.AssertIsRlmCurrent( 0 , rlm)
67
68 halt.AssertIsNotHaltedGovStaker()
69
70 prev := rlm.Previous()
71 access.AssertIsValidAddress(to)
72
73 assertIsValidDelegateAmount(amount)
74
75 caller := prev.Address()
76 from := caller
77 currentHeight := runtime.ChainHeight()
78 currentTimestamp := time.Now().Unix()
79
80 emission.MintAndDistributeGns(cross(rlm))
81
82 delegation, err := gs.delegate(
83 0 ,
84 rlm,
85 from,
86 to,
87 amount,
88 currentHeight,
89 currentTimestamp,
90 )
91 if err != nil {
92 panic(err)
93 }
94
95 if err := gs.increaseTotalDelegatedAmount( 0 , rlm, amount); err != nil {
96 panic(err)
97 }
98 if err := gs.increaseTotalLockedAmount( 0 , rlm, amount); err != nil {
99 panic(err)
100 }
101
102 gns.TransferFrom(cross(rlm), from, rlm.Address(), amount)
103 xgns.Mint(cross(rlm), from, amount)
104
105 registeredReferrer := referral.TryRegister(cross(rlm), caller, referrer)
106
107 resolver := NewDelegationResolver(delegation)
108
109 chain.Emit(
110 "Delegate" ,
111 "prevAddr" , prev.Address().String(),
112 "prevRealm" , prev.PkgPath(),
113 "from" , resolver.delegation.DelegateFrom().String(),
114 "to" , resolver.delegation.DelegateTo().String(),
115 "amount" , utils.FormatInt(resolver.DelegatedAmount()),
116 "totalDelegatedAmount" , utils.FormatInt(gs.store.GetTotalDelegatedAmount()),
117 "referrer" , registeredReferrer,
118 )
119
120 return amount
121 }
122
123 // Undelegate undelegates xGNS from the existing delegate.
124 //
125 // Initiates withdrawal of staked GNS with lockup period.
126 // Voting power removed immediately, tokens locked for configurable period.
127 // Prevents governance attacks through time delay.
128 //
129 // Parameters:
130 //
131 // - _: Noncrossing implementation-call discriminator; pass 0.
132 //
133 // - rlm: Current realm context forwarded unchanged by the governance-staker proxy.
134 //
135 // - from: Address currently delegated to
136 //
137 // - amount: Amount of xGNS to undelegate
138 //
139 // Process:
140 // 1. Removes voting power immediately
141 // 2. Creates withdrawal request with timestamp
142 // 3. Locks GNS for configurable cooldown period
143 //
144 // Requirements:
145 // - Must have delegated to target address
146 // - Sufficient delegated amount
147 //
148 // After lockup period ends, use CollectUndelegatedGns() to claim GNS.
149 //
150 // Returns:
151 // - undelegatedAmount: amount moved out of active delegation into undelegation lockup
152 func (gs *govStakerV1) Undelegate(
153 _ int ,
154 rlm realm,
155 from address,
156 amount int64 ,
157 ) int64 {
158 access.AssertIsRlmCurrent( 0 , rlm)
159
160 halt.AssertIsNotHaltedWithdraw()
161
162 prev := rlm.Previous()
163 caller := prev.Address()
164 access.AssertIsValidAddress(from)
165
166 assertIsValidDelegateAmount(amount)
167
168 currentHeight := runtime.ChainHeight()
169 currentTimestamp := time.Now().Unix()
170
171 emission.MintAndDistributeGns(cross(rlm))
172
173 unDelegationAmount, err := gs.unDelegate(
174 0 ,
175 rlm,
176 caller,
177 from,
178 amount,
179 currentHeight,
180 currentTimestamp,
181 )
182 if err != nil {
183 panic(err)
184 }
185
186 if err := gs.decreaseTotalDelegatedAmount( 0 , rlm, unDelegationAmount); err != nil {
187 panic(err)
188 }
189
190 chain.Emit(
191 "Undelegate" ,
192 "prevAddr" , prev.Address().String(),
193 "prevRealm" , prev.PkgPath(),
194 "from" , caller.String(),
195 "to" , from.String(),
196 "amount" , utils.FormatInt(unDelegationAmount),
197 "totalDelegatedAmount" , utils.FormatInt(gs.store.GetTotalDelegatedAmount()),
198 )
199
200 return unDelegationAmount
201 }
202
203 // Redelegate redelegates xGNS from an existing delegate to another.
204 //
205 // Atomic operation to change delegation target.
206 // Maintains voting power continuity without unstaking.
207 // Useful for vote delegation services and DAO coordination.
208 //
209 // Parameters:
210 //
211 // - _: Noncrossing implementation-call discriminator; pass 0.
212 //
213 // - rlm: Current realm context forwarded unchanged by the governance-staker proxy.
214 //
215 // - delegatee: Current address delegated to
216 //
217 // - newDelegatee: New address to delegate to
218 //
219 // - amount: Amount of xGNS to redelegate
220 //
221 // Process:
222 // 1. Validates current delegation exists
223 // 2. Removes voting power from the old delegatee without a lockup
224 // 3. Assigns the same amount to the new delegatee
225 // 4. Updates delegation history and reward stake events
226 //
227 // Requirements:
228 // - Must have active delegation to current delegatee
229 // - Both addresses must be valid
230 // - Amount must not exceed current delegation
231 // - Cannot redelegate to same address
232 //
233 // No user-facing lockup period; reward accounting records one removal and one
234 // addition across the corresponding accrual epochs.
235 //
236 // Returns:
237 // - redelegatedAmount: amount moved from the current delegatee to the new delegatee
238 func (gs *govStakerV1) Redelegate(
239 _ int ,
240 rlm realm,
241 delegatee,
242 newDelegatee address,
243 amount int64 ,
244 ) int64 {
245 access.AssertIsRlmCurrent( 0 , rlm)
246
247 halt.AssertIsNotHaltedGovStaker()
248
249 prev := rlm.Previous()
250 caller := prev.Address()
251 access.AssertIsValidAddress(delegatee)
252 access.AssertIsValidAddress(newDelegatee)
253
254 assertIsValidDelegateAmount(amount)
255 assertNoSameDelegatee(delegatee, newDelegatee)
256
257 currentHeight := runtime.ChainHeight()
258 currentTimestamp := time.Now().Unix()
259 delegator := caller
260
261 emission.MintAndDistributeGns(cross(rlm))
262
263 unDelegationAmount, err := gs.unDelegateWithoutLockup(
264 0 ,
265 rlm,
266 delegator,
267 delegatee,
268 amount,
269 currentHeight,
270 currentTimestamp,
271 )
272 if err != nil {
273 panic(err)
274 }
275
276 delegation, err := gs.delegate(
277 0 ,
278 rlm,
279 delegator,
280 newDelegatee,
281 unDelegationAmount,
282 currentHeight,
283 currentTimestamp,
284 )
285 if err != nil {
286 panic(err)
287 }
288
289 resolver := NewDelegationResolver(delegation)
290 chain.Emit(
291 "Redelegate" ,
292 "prevAddr" , prev.Address().String(),
293 "prevRealm" , prev.PkgPath(),
294 "from" , delegator.String(),
295 "previousDelegatee" , delegatee.String(),
296 "newDelegatee" , newDelegatee.String(),
297 "amount" , utils.FormatInt(resolver.DelegatedAmount()),
298 )
299
300 return amount
301 }
302
303 // CollectUndelegatedGns collects undelegated GNS tokens.
304 // Allows users to collect GNS tokens that completed undelegation lockup period.
305 // Burns xGNS and returns GNS tokens.
306 //
307 // Parameters:
308 // - _: Noncrossing implementation-call discriminator; pass 0.
309 // - rlm: Current realm context forwarded unchanged by the governance-staker proxy.
310 //
311 // Returns:
312 // - collectedAmount: amount of undelegated GNS released to the caller after lockup expiry
313 func (gs *govStakerV1) CollectUndelegatedGns(_ int , rlm realm) int64 {
314 access.AssertIsRlmCurrent( 0 , rlm)
315
316 halt.AssertIsNotHaltedWithdraw()
317
318 prev := rlm.Previous()
319 caller := prev.Address()
320 currentTime := time.Now().Unix()
321
322 emission.MintAndDistributeGns(cross(rlm))
323
324 collectedAmount, err := gs.collectDelegations( 0 , rlm, caller, currentTime)
325 if err != nil {
326 panic(err)
327 }
328
329 if collectedAmount == 0 {
330 return 0
331 }
332
333 if err := gs.decreaseTotalLockedAmount( 0 , rlm, collectedAmount); err != nil {
334 panic(err)
335 }
336
337 xgns.Burn(cross(rlm), caller, collectedAmount)
338 gns.Transfer(cross(rlm), caller, collectedAmount)
339
340 chain.Emit(
341 "CollectUndelegatedGns" ,
342 "prevAddr" , prev.Address().String(),
343 "prevRealm" , prev.PkgPath(),
344 "from" , prev.Address().String(),
345 "to" , caller.String(),
346 "collectedAmount" , utils.FormatInt(collectedAmount),
347 )
348
349 return collectedAmount
350 }
351
352 // delegate processes delegation operations.
353 // Validates delegation amount, creates delegation records, and updates reward tracking.
354 func (gs *govStakerV1) delegate(
355 _ int ,
356 rlm realm,
357 from address,
358 to address,
359 amount,
360 currentHeight,
361 currentTimestamp int64 ,
362 ) (*staker.Delegation, error ) {
363 delegationID := gs.nextDelegationID()
364 delegation := staker.NewDelegation(
365 delegationID,
366 from,
367 to,
368 amount,
369 currentHeight,
370 currentTimestamp,
371 )
372 delegationResolver := NewDelegationResolver(delegation)
373 delegatedAmount := delegationResolver.DelegatedAmount()
374 if delegatedAmount < 0 {
375 return nil , errors.New(errDelegatedAmountNegative)
376 }
377
378 if err := gs.addDelegation( 0 , rlm, delegationID, delegation); err != nil {
379 return nil , err
380 }
381 gs.addDelegationRecord( 0 , rlm, to, delegatedAmount, currentTimestamp)
382 if err := gs.addStakeEmissionReward( 0 , rlm, from.String(), amount, currentTimestamp); err != nil {
383 return nil , err
384 }
385 if err := gs.addStakeProtocolFeeReward( 0 , rlm, from.String(), amount, currentTimestamp); err != nil {
386 return nil , err
387 }
388
389 return delegation, nil
390 }
391
392 // unDelegate processes undelegation operations with lockup.
393 // Validates undelegation amount, processes withdrawals, and updates reward tracking.
394 func (gs *govStakerV1) unDelegate(
395 _ int ,
396 rlm realm,
397 delegator,
398 delegatee address,
399 amount,
400 currentHeight,
401 currentTimestamp int64 ,
402 ) ( int64 , error ) {
403 delegationIDs := gs.getUserDelegationIDsWithDelegatee(delegator, delegatee)
404 if len(delegationIDs) == 0 {
405 return 0 , nil
406 }
407
408 unDelegationAmount := amount
409 lockupPeriod := gs.store.GetUnDelegationLockupPeriod()
410 totalDelegated := int64 ( 0 )
411 delegations := make([]*staker.Delegation, 0 , len(delegationIDs))
412
413 for _, id := range delegationIDs {
414 delegation, exists := gs.store.GetDelegation(id)
415 if !exists {
416 continue
417 }
418
419 totalDelegated = gnsmath.SafeAddInt64(totalDelegated, NewDelegationResolver(delegation).DelegatedAmount())
420 delegations = append(delegations, delegation)
421 }
422
423 if amount > totalDelegated {
424 return 0 , errors.New(errNotEnoughDelegated)
425 }
426
427 // Process undelegation across multiple delegation records if necessary
428 for _, delegation := range delegations {
429 resolver := NewDelegationResolver(delegation)
430 if resolver.IsEmpty() {
431 if err := gs.removeDelegation( 0 , rlm, delegation.ID()); err != nil {
432 return 0 , err
433 }
434 continue
435 }
436
437 currentUnDelegationAmount := unDelegationAmount
438
439 if currentUnDelegationAmount > resolver.DelegatedAmount() {
440 currentUnDelegationAmount = resolver.DelegatedAmount()
441 }
442
443 if currentUnDelegationAmount < 0 {
444 return 0 , errors.New(errUndelegationAmountNegative)
445 }
446
447 if currentUnDelegationAmount == 0 {
448 continue
449 }
450
451 resolver.UnDelegate(
452 currentUnDelegationAmount,
453 currentHeight,
454 currentTimestamp,
455 lockupPeriod,
456 )
457
458 if err := gs.setDelegation( 0 , rlm, delegation.ID(), delegation); err != nil {
459 return 0 , err
460 }
461 gs.addDelegationRecord( 0 , rlm, delegatee, -currentUnDelegationAmount, currentTimestamp)
462 if err := gs.removeStakeEmissionReward( 0 , rlm, delegator.String(), currentUnDelegationAmount, currentTimestamp); err != nil {
463 return 0 , err
464 }
465 if err := gs.removeStakeProtocolFeeReward( 0 , rlm, delegator.String(), currentUnDelegationAmount, currentTimestamp); err != nil {
466 return 0 , err
467 }
468
469 unDelegationAmount = gnsmath.SafeSubInt64(unDelegationAmount, currentUnDelegationAmount)
470 if unDelegationAmount <= 0 {
471 break
472 }
473 }
474
475 return amount, nil
476 }
477
478 // unDelegateWithoutLockup processes undelegation without lockup.
479 // Used for redelegation where tokens are immediately available.
480 func (gs *govStakerV1) unDelegateWithoutLockup(
481 _ int ,
482 rlm realm,
483 delegator,
484 delegatee address,
485 amount,
486 currentHeight,
487 currentTime int64 ,
488 ) ( int64 , error ) {
489 delegationIDs := gs.getUserDelegationIDsWithDelegatee(delegator, delegatee)
490 if len(delegationIDs) == 0 {
491 return 0 , errors.New(errNotEnoughDelegated)
492 }
493
494 unDelegationAmount := amount
495 totalDelegated := int64 ( 0 )
496 delegations := make([]*staker.Delegation, 0 , len(delegationIDs))
497
498 for _, id := range delegationIDs {
499 delegation, exists := gs.store.GetDelegation(id)
500 if !exists {
501 continue
502 }
503
504 totalDelegated = gnsmath.SafeAddInt64(totalDelegated, NewDelegationResolver(delegation).DelegatedAmount())
505 delegations = append(delegations, delegation)
506 }
507
508 if amount > totalDelegated {
509 return 0 , errors.New(errNotEnoughDelegated)
510 }
511
512 // Process undelegation across multiple delegation records if necessary
513 for _, delegation := range delegations {
514 resolver := NewDelegationResolver(delegation)
515 if resolver.IsEmpty() {
516 if err := gs.removeDelegation( 0 , rlm, delegation.ID()); err != nil {
517 return 0 , err
518 }
519 continue
520 }
521
522 currentUnDelegationAmount := unDelegationAmount
523
524 if currentUnDelegationAmount > resolver.DelegatedAmount() {
525 currentUnDelegationAmount = resolver.DelegatedAmount()
526 }
527
528 if currentUnDelegationAmount == 0 {
529 continue
530 }
531
532 resolver.UnDelegateWithoutLockup(
533 currentUnDelegationAmount,
534 currentHeight,
535 currentTime,
536 )
537
538 if resolver.IsEmpty() {
539 if err := gs.removeDelegation( 0 , rlm, delegation.ID()); err != nil {
540 return 0 , err
541 }
542 } else if err := gs.setDelegation( 0 , rlm, delegation.ID(), delegation); err != nil {
543 return 0 , err
544 }
545 gs.addDelegationRecord( 0 , rlm, delegatee, -currentUnDelegationAmount, currentTime)
546 if err := gs.removeStakeEmissionReward( 0 , rlm, delegator.String(), currentUnDelegationAmount, currentTime); err != nil {
547 return 0 , err
548 }
549 if err := gs.removeStakeProtocolFeeReward( 0 , rlm, delegator.String(), currentUnDelegationAmount, currentTime); err != nil {
550 return 0 , err
551 }
552
553 unDelegationAmount = gnsmath.SafeSubInt64(unDelegationAmount, currentUnDelegationAmount)
554 if unDelegationAmount <= 0 {
555 break
556 }
557 }
558
559 return amount, nil
560 }
561
562 func (gs *govStakerV1) increaseTotalDelegatedAmount(_ int , rlm realm, amount int64 ) error {
563 currentDelegated := gs.store.GetTotalDelegatedAmount()
564
565 if err := gs.store.SetTotalDelegatedAmount( 0 , rlm, gnsmath.SafeAddInt64(currentDelegated, amount)); err != nil {
566 return err
567 }
568
569 return nil
570 }
571
572 func (gs *govStakerV1) decreaseTotalDelegatedAmount(_ int , rlm realm, amount int64 ) error {
573 currentDelegated := gs.store.GetTotalDelegatedAmount()
574
575 newDelegated := gnsmath.SafeSubInt64(currentDelegated, amount)
576 if newDelegated < 0 {
577 newDelegated = 0
578 }
579 if err := gs.store.SetTotalDelegatedAmount( 0 , rlm, newDelegated); err != nil {
580 return err
581 }
582
583 return nil
584 }
585
586 func (gs *govStakerV1) increaseTotalLockedAmount(_ int , rlm realm, amount int64 ) error {
587 currentLocked := gs.store.GetTotalLockedAmount()
588
589 if err := gs.store.SetTotalLockedAmount( 0 , rlm, gnsmath.SafeAddInt64(currentLocked, amount)); err != nil {
590 return err
591 }
592
593 return nil
594 }
595
596 func (gs *govStakerV1) decreaseTotalLockedAmount(_ int , rlm realm, amount int64 ) error {
597 currentLocked := gs.store.GetTotalLockedAmount()
598
599 newLocked := gnsmath.SafeSubInt64(currentLocked, amount)
600 if newLocked < 0 {
601 newLocked = 0
602 }
603 if err := gs.store.SetTotalLockedAmount( 0 , rlm, newLocked); err != nil {
604 return err
605 }
606
607 return nil
608 }
609
610 // collectDelegations processes collection of undelegated tokens.
611 // Iterates through user delegations and collects available amounts.
612 func (gs *govStakerV1) collectDelegations(_ int , rlm realm, user address, currentTime int64 ) ( int64 , error ) {
613 totalCollectedAmount := int64 ( 0 )
614
615 delegationTree := gs.getUserDelegations(user)
616
617 var err error
618 var idsToRemove [] int64
619 allDelegations := gs.store.GetAllDelegations()
620
621 // Collect from all available delegations
622 delegationTree.Iterate( "" , "" , func (delegatee string , value any ) bool {
623 delegationIDs, ok := value.([] int64 )
624 if !ok {
625 return false
626 }
627
628 if len(delegationIDs) == 0 {
629 return false
630 }
631 for _, id := range delegationIDs {
632 delegationRaw := allDelegations.Get(utils.FormatInt(id))
633 if delegationRaw == nil {
634 continue
635 }
636 delegation, ok := delegationRaw.(*staker.Delegation)
637 if !ok {
638 continue
639 }
640
641 resolver := NewDelegationResolver(delegation)
642
643 collectedAmount, iErr := resolver.processCollection(currentTime)
644 if iErr != nil {
645 err = iErr
646 return true
647 }
648
649 // Simple addition since addToCollectedAmount was removed
650 totalCollectedAmount = gnsmath.SafeAddInt64(totalCollectedAmount, collectedAmount)
651
652 // Save updated delegation state after collection
653 if resolver.IsEmpty() {
654 idsToRemove = append(idsToRemove, delegation.ID())
655 } else {
656 if iErr := gs.setDelegation( 0 , rlm, delegation.ID(), delegation); iErr != nil {
657 err = iErr
658 return true
659 }
660 }
661 }
662
663 return false
664 })
665
666 if err != nil {
667 return totalCollectedAmount, makeErrorWithDetails(errInvalidAmount, err.Error())
668 }
669
670 for _, id := range idsToRemove {
671 if err := gs.removeDelegation( 0 , rlm, id); err != nil {
672 return totalCollectedAmount, err
673 }
674 }
675
676 return totalCollectedAmount, nil
677 }
678