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
  • state.gnogno
    1package staker23import (4	"errors"5	"math"67	gnsmath "gno.land/p/gnoswap/gnsmath/v1"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.

    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
    21func (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
    31func (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
    44func (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
    60func (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
    72func (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
    95func (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
    123func (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
    142func (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
    171func (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
    184func (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
    209func (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.
    229func (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.
    252func (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
    278func (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
    310func (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
    344func (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
    376func (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
    390func (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
    418func (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.
    441func (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
    459func (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.
    509func (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.
    535func (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
    567func (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
    581func (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
    596func (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
    628func (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
    665func (g *govStakerV1) makeLaunchpadRewardID(address string) string {
    666 return "launchpad:" + address
    667}
    668