PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnoswap/gov/staker/v1

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v1
Namespace
gnoswap / gov / staker
Files
25 (README)(gnomod.toml)
Exported functions
12
Module
gno.land/r/gnoswap/gov/staker/v1
gno
0.9

Files (25)

  • README.mdmarkdown
  • gnomod.tomltoml
  • assert.gnogno
  • consts.gnogno
  • delegation_manager.gnogno
  • delegation_withdraw.gnogno
  • delegation.gnogno
  • doc.gnogno
  • emission_reward_manager.gnogno
  • emission_reward_state.gnogno
  • emits.gnogno
  • errors.gnogno
  • getter_utils.gnogno
  • getter.gnogno
  • init.gnogno
  • instance.gnogno
  • launchpad_project_deposits.gnogno
  • protocol_fee_reward_manager.gnogno
  • protocol_fee_reward_state.gnogno
  • render.gnogno
  • staker_delegate.gnogno
  • staker_delegation_snapshot.gnogno
  • staker_reward.gnogno
  • state.gnogno
  • util.gnogno
  • staker_delegate.gnogno
    1package staker23import (4	"chain"5	"chain/runtime"6	"errors"7	"time"8

    Functions

    • 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.

    Rendered

    RenderedRawgnoweb ↗

    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
    59func (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
    152func (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
    238func (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
    313func (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.
    354func (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.
    394func (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.
    480func (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
    562func (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
    572func (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
    586func (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
    596func (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.
    612func (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