Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

store.gno

45.04 Kb · 1311 lines
   1package staker
   2
   3import (
   4	"errors"
   5
   6	"gno.land/p/gnoswap/store/v1"
   7	bptree "gno.land/p/nt/bptree/v0"
   8	ufmt "gno.land/p/nt/ufmt/v0"
   9)
  10
  11type StoreKey string
  12
  13// StoreKey.String returns the textual key used by the KV store.
  14//
  15// Returns:
  16//   - string: Underlying storage-key text.
  17func (s StoreKey) String() string {
  18	return string(s)
  19}
  20
  21const (
  22	StoreKeyDepositGnsAmount                 StoreKey = "depositGnsAmount"
  23	StoreKeyMinimumRewardAmount              StoreKey = "minimumRewardAmount"
  24	StoreKeyDeposits                         StoreKey = "deposits"
  25	StoreKeyExternalIncentives               StoreKey = "externalIncentives"
  26	StoreKeyTotalEmissionSent                StoreKey = "totalEmissionSent"
  27	StoreKeyAllowedTokens                    StoreKey = "allowedTokens"
  28	StoreKeyDeniedRewardTokens               StoreKey = "deniedRewardTokens"
  29	StoreKeyIncentiveCounter                 StoreKey = "incentiveCounter"
  30	StoreKeyTokenSpecificMinimumRewards      StoreKey = "tokenSpecificMinimumRewards"
  31	StoreKeyUnstakingFee                     StoreKey = "unstakingFee"
  32	StoreKeyPendingProtocolFees              StoreKey = "pendingProtocolFees"
  33	StoreKeyUnstakedPositions                StoreKey = "unstakedPositions"
  34	StoreKeyUncollectedIncentiveCounts       StoreKey = "uncollectedIncentiveCounts"
  35	StoreKeyPools                            StoreKey = "pools"
  36	StoreKeyPoolTierMemberships              StoreKey = "poolTierMemberships"
  37	StoreKeyPoolTierRatio                    StoreKey = "poolTierRatio"
  38	StoreKeyPoolTierCounts                   StoreKey = "poolTierCounts"
  39	StoreKeyPoolTierLastRewardCacheTimestamp StoreKey = "poolTierLastRewardCacheTimestamp"
  40	StoreKeyPoolTierCurrentEmission          StoreKey = "poolTierCurrentEmission"
  41	StoreKeyPoolTierGetEmission              StoreKey = "poolTierGetEmission"
  42	StoreKeyPoolTierGetHalvingBlocksInRange  StoreKey = "poolTierGetHalvingBlocksInRange"
  43	StoreKeyWarmupTemplate                   StoreKey = "warmupTemplate"
  44	StoreKeyCurrentSwapBatch                 StoreKey = "currentSwapBatch"
  45)
  46
  47type stakerStore struct {
  48	kvStore store.KVStore
  49}
  50
  51// DepositGnsAmount
  52// HasDepositGnsAmountStoreKey reports whether the GNS reserve amount key exists.
  53//
  54// Returns:
  55//   - bool: True when a deposit GNS amount has been initialized in storage.
  56func (s *stakerStore) HasDepositGnsAmountStoreKey() bool {
  57	return s.kvStore.Has(StoreKeyDepositGnsAmount.String())
  58}
  59
  60// GetDepositGnsAmount retrieves the GNS reserve amount deposited for the staker.
  61//
  62// Returns:
  63//   - int64: Stored GNS reserve amount; panics on read or type failure.
  64func (s *stakerStore) GetDepositGnsAmount() int64 {
  65	result, err := s.kvStore.Get(StoreKeyDepositGnsAmount.String())
  66	if err != nil {
  67		panic(err)
  68	}
  69
  70	amount, ok := result.(int64)
  71	if !ok {
  72		panic(ufmt.Sprintf("failed to cast result to int64: %T", result))
  73	}
  74
  75	return amount
  76}
  77
  78// SetDepositGnsAmount stores the GNS amount deposited as the staker reserve.
  79//
  80// Parameters:
  81//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
  82//   - rlm: Current staker realm context; it must be current before storage is written.
  83//   - amount: GNS reserve amount used for staker initialization and accounting.
  84//
  85// Returns:
  86//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
  87func (s *stakerStore) SetDepositGnsAmount(_ int, rlm realm, amount int64) error {
  88	if !rlm.IsCurrent() {
  89		return errors.New(ErrSpoofedRealm)
  90	}
  91
  92	return s.kvStore.Set(0, rlm, StoreKeyDepositGnsAmount.String(), amount)
  93}
  94
  95// MinimumRewardAmount
  96// HasMinimumRewardAmountStoreKey reports whether the default minimum-reward key exists.
  97//
  98// Returns:
  99//   - bool: True when the default minimum reward amount has been initialized in storage.
 100func (s *stakerStore) HasMinimumRewardAmountStoreKey() bool {
 101	return s.kvStore.Has(StoreKeyMinimumRewardAmount.String())
 102}
 103
 104// GetMinimumRewardAmount retrieves the default minimum external reward amount.
 105//
 106// Returns:
 107//   - int64: Stored default minimum reward amount; panics on read or type failure.
 108func (s *stakerStore) GetMinimumRewardAmount() int64 {
 109	result, err := s.kvStore.Get(StoreKeyMinimumRewardAmount.String())
 110	if err != nil {
 111		panic(err)
 112	}
 113
 114	amount, ok := result.(int64)
 115	if !ok {
 116		panic(ufmt.Sprintf("failed to cast result to int64: %T", result))
 117	}
 118
 119	return amount
 120}
 121
 122// SetMinimumRewardAmount stores the default minimum external reward amount.
 123//
 124// Parameters:
 125//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 126//   - rlm: Current staker realm context; it must be current before storage is written.
 127//   - amount: Default minimum reward amount applied when no token-specific override exists.
 128//
 129// Returns:
 130//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 131func (s *stakerStore) SetMinimumRewardAmount(_ int, rlm realm, amount int64) error {
 132	if !rlm.IsCurrent() {
 133		return errors.New(ErrSpoofedRealm)
 134	}
 135
 136	return s.kvStore.Set(0, rlm, StoreKeyMinimumRewardAmount.String(), amount)
 137}
 138
 139// Deposits
 140// HasDepositsStoreKey reports whether the active-deposits key exists.
 141//
 142// Returns:
 143//   - bool: True when active deposits have been initialized in storage.
 144func (s *stakerStore) HasDepositsStoreKey() bool {
 145	return s.kvStore.Has(StoreKeyDeposits.String())
 146}
 147
 148// GetDeposits retrieves the active deposit tree.
 149//
 150// Returns:
 151//   - *bptree.BPTree: Stored position-to-deposit records; panics on read or type failure.
 152func (s *stakerStore) GetDeposits() *bptree.BPTree {
 153	result, err := s.kvStore.Get(StoreKeyDeposits.String())
 154	if err != nil {
 155		panic(err)
 156	}
 157
 158	deposits, ok := result.(*bptree.BPTree)
 159	if !ok {
 160		panic(ufmt.Sprintf("failed to cast result to *bptree.BPTree: %T", result))
 161	}
 162
 163	return deposits
 164}
 165
 166// SetDeposits stores the deposit tree keyed by position identifier.
 167//
 168// Parameters:
 169//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 170//   - rlm: Current staker realm context; it must be current before storage is written.
 171//   - deposits: B+ tree containing active staking deposits.
 172//
 173// Returns:
 174//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 175func (s *stakerStore) SetDeposits(_ int, rlm realm, deposits *bptree.BPTree) error {
 176	if !rlm.IsCurrent() {
 177		return errors.New(ErrSpoofedRealm)
 178	}
 179
 180	return s.kvStore.Set(0, rlm, StoreKeyDeposits.String(), deposits)
 181}
 182
 183// ExternalIncentives
 184// HasExternalIncentivesStoreKey reports whether the external-incentives key exists.
 185//
 186// Returns:
 187//   - bool: True when external incentives have been initialized in storage.
 188func (s *stakerStore) HasExternalIncentivesStoreKey() bool {
 189	return s.kvStore.Has(StoreKeyExternalIncentives.String())
 190}
 191
 192// GetExternalIncentives retrieves the external incentive tree.
 193//
 194// Returns:
 195//   - *bptree.BPTree: Stored external incentive records; panics on read or type failure.
 196func (s *stakerStore) GetExternalIncentives() *bptree.BPTree {
 197	result, err := s.kvStore.Get(StoreKeyExternalIncentives.String())
 198	if err != nil {
 199		panic(err)
 200	}
 201
 202	incentives, ok := result.(*bptree.BPTree)
 203	if !ok {
 204		panic(ufmt.Sprintf("failed to cast result to *bptree.BPTree: %T", result))
 205	}
 206
 207	return incentives
 208}
 209
 210// SetExternalIncentives stores the external incentive tree.
 211//
 212// Parameters:
 213//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 214//   - rlm: Current staker realm context; it must be current before storage is written.
 215//   - incentives: B+ tree mapping external incentive identifiers to incentive records.
 216//
 217// Returns:
 218//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 219func (s *stakerStore) SetExternalIncentives(_ int, rlm realm, incentives *bptree.BPTree) error {
 220	if !rlm.IsCurrent() {
 221		return errors.New(ErrSpoofedRealm)
 222	}
 223
 224	return s.kvStore.Set(0, rlm, StoreKeyExternalIncentives.String(), incentives)
 225}
 226
 227// TotalEmissionSent
 228// HasTotalEmissionSentStoreKey reports whether the total-emission-sent key exists.
 229//
 230// Returns:
 231//   - bool: True when cumulative sent emission has been initialized in storage.
 232func (s *stakerStore) HasTotalEmissionSentStoreKey() bool {
 233	return s.kvStore.Has(StoreKeyTotalEmissionSent.String())
 234}
 235
 236// GetTotalEmissionSent retrieves the cumulative emission amount sent.
 237//
 238// Returns:
 239//   - int64: Stored cumulative emission amount; panics on read or type failure.
 240func (s *stakerStore) GetTotalEmissionSent() int64 {
 241	result, err := s.kvStore.Get(StoreKeyTotalEmissionSent.String())
 242	if err != nil {
 243		panic(err)
 244	}
 245
 246	amount, ok := result.(int64)
 247	if !ok {
 248		panic(ufmt.Sprintf("failed to cast result to int64: %T", result))
 249	}
 250
 251	return amount
 252}
 253
 254// SetTotalEmissionSent stores the cumulative emission amount sent to staker pools.
 255//
 256// Parameters:
 257//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 258//   - rlm: Current staker realm context; it must be current before storage is written.
 259//   - amount: Cumulative emission amount already sent.
 260//
 261// Returns:
 262//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 263func (s *stakerStore) SetTotalEmissionSent(_ int, rlm realm, amount int64) error {
 264	if !rlm.IsCurrent() {
 265		return errors.New(ErrSpoofedRealm)
 266	}
 267
 268	return s.kvStore.Set(0, rlm, StoreKeyTotalEmissionSent.String(), amount)
 269}
 270
 271// AllowedTokens
 272// HasAllowedTokensStoreKey reports whether the allowed-token key exists.
 273//
 274// Returns:
 275//   - bool: True when the allowed external-reward token list has been initialized in storage.
 276func (s *stakerStore) HasAllowedTokensStoreKey() bool {
 277	return s.kvStore.Has(StoreKeyAllowedTokens.String())
 278}
 279
 280// GetAllowedTokens retrieves a clone of the allowed external-reward token list.
 281//
 282// Returns:
 283//   - []string: Allowed token paths copied for the caller; panics on read or type failure.
 284func (s *stakerStore) GetAllowedTokens() []string {
 285	result, err := s.kvStore.Get(StoreKeyAllowedTokens.String())
 286	if err != nil {
 287		panic(err)
 288	}
 289
 290	tokens, ok := result.([]string)
 291	if !ok {
 292		panic(ufmt.Sprintf("failed to cast result to []string: %T", result))
 293	}
 294
 295	return cloneStringSlice(tokens)
 296}
 297
 298// SetAllowedTokens replaces the allowed external-reward token list.
 299//
 300// Parameters:
 301//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 302//   - rlm: Current staker realm context; it must be current before storage is written.
 303//   - tokens: Registered token paths permitted as external reward tokens.
 304//
 305// Returns:
 306//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 307func (s *stakerStore) SetAllowedTokens(_ int, rlm realm, tokens []string) error {
 308	if !rlm.IsCurrent() {
 309		return errors.New(ErrSpoofedRealm)
 310	}
 311
 312	return s.kvStore.Set(0, rlm, StoreKeyAllowedTokens.String(), tokens)
 313}
 314
 315// AddAllowedToken appends tokenPath to the allowed-tokens list when absent.
 316// The store-owned slice is cloned before mutation to avoid readonly-taint panics.
 317//
 318// Parameters:
 319//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 320//   - rlm: Current staker realm context; it must be current before storage is written.
 321//   - tokenPath: Registered token path to add to the allowed-token list.
 322//
 323// Returns:
 324//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 325func (s *stakerStore) AddAllowedToken(_ int, rlm realm, tokenPath string) error {
 326	if !rlm.IsCurrent() {
 327		return errors.New(ErrSpoofedRealm)
 328	}
 329
 330	tokens := s.GetAllowedTokens()
 331	if !contains(tokens, tokenPath) {
 332		tokens = append(tokens, tokenPath)
 333	}
 334
 335	return s.kvStore.Set(0, rlm, StoreKeyAllowedTokens.String(), tokens)
 336}
 337
 338// RemoveAllowedToken removes tokenPath from the allowed-tokens list when present.
 339// The store-owned slice is cloned before mutation to avoid readonly-taint panics.
 340//
 341// Parameters:
 342//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 343//   - rlm: Current staker realm context; it must be current before storage is written.
 344//   - tokenPath: Registered token path to remove from the allowed-token list.
 345//
 346// Returns:
 347//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 348func (s *stakerStore) RemoveAllowedToken(_ int, rlm realm, tokenPath string) error {
 349	if !rlm.IsCurrent() {
 350		return errors.New(ErrSpoofedRealm)
 351	}
 352
 353	tokens := s.GetAllowedTokens()
 354	for i, t := range tokens {
 355		if t == tokenPath {
 356			tokens = append(tokens[:i], tokens[i+1:]...)
 357			break
 358		}
 359	}
 360
 361	return s.kvStore.Set(0, rlm, StoreKeyAllowedTokens.String(), tokens)
 362}
 363
 364// DeniedRewardTokens
 365//
 366// The deny list is the operational stop switch for the pool-pair reward-token
 367// policy: pair tokens qualify as external-incentive reward tokens without going
 368// through the governance allowlist (by design), so without this list there is
 369// no lever to stop NEW incentives in a pair token whose issuer turned hostile.
 370// A missing store key means an empty list, so no initializer seeding and no
 371// upgrade migration are needed.
 372// HasDeniedRewardTokensStoreKey reports whether the denied-reward-token key exists.
 373//
 374// Returns:
 375//   - bool: True when a denied-reward list has been initialized in storage.
 376func (s *stakerStore) HasDeniedRewardTokensStoreKey() bool {
 377	return s.kvStore.Has(StoreKeyDeniedRewardTokens.String())
 378}
 379
 380// GetDeniedRewardTokens retrieves the denied-reward list, treating a missing key as empty.
 381//
 382// Returns:
 383//   - []string: Clone of denied reward-token paths; empty when the key is absent, and panics on read or type failure.
 384func (s *stakerStore) GetDeniedRewardTokens() []string {
 385	if !s.kvStore.Has(StoreKeyDeniedRewardTokens.String()) {
 386		return []string{}
 387	}
 388
 389	result, err := s.kvStore.Get(StoreKeyDeniedRewardTokens.String())
 390	if err != nil {
 391		panic(err)
 392	}
 393
 394	tokens, ok := result.([]string)
 395	if !ok {
 396		panic(ufmt.Sprintf("failed to cast result to []string: %T", result))
 397	}
 398
 399	return cloneStringSlice(tokens)
 400}
 401
 402// AddDeniedRewardToken appends tokenPath to the denied-reward-tokens list when absent.
 403// The store-owned slice is cloned before mutation to avoid readonly-taint panics.
 404//
 405// Parameters:
 406//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 407//   - rlm: Current staker realm context; it must be current before storage is written.
 408//   - tokenPath: Reward-token path to block for new external incentives.
 409//
 410// Returns:
 411//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 412func (s *stakerStore) AddDeniedRewardToken(_ int, rlm realm, tokenPath string) error {
 413	if !rlm.IsCurrent() {
 414		return errors.New(ErrSpoofedRealm)
 415	}
 416
 417	tokens := s.GetDeniedRewardTokens()
 418	if !contains(tokens, tokenPath) {
 419		tokens = append(tokens, tokenPath)
 420	}
 421
 422	return s.kvStore.Set(0, rlm, StoreKeyDeniedRewardTokens.String(), tokens)
 423}
 424
 425// RemoveDeniedRewardToken removes tokenPath from the denied-reward-tokens list when present.
 426// The store-owned slice is cloned before mutation to avoid readonly-taint panics.
 427//
 428// Parameters:
 429//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 430//   - rlm: Current staker realm context; it must be current before storage is written.
 431//   - tokenPath: Reward-token path to remove from the deny list.
 432//
 433// Returns:
 434//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 435func (s *stakerStore) RemoveDeniedRewardToken(_ int, rlm realm, tokenPath string) error {
 436	if !rlm.IsCurrent() {
 437		return errors.New(ErrSpoofedRealm)
 438	}
 439
 440	tokens := s.GetDeniedRewardTokens()
 441	for i, t := range tokens {
 442		if t == tokenPath {
 443			tokens = append(tokens[:i], tokens[i+1:]...)
 444			break
 445		}
 446	}
 447
 448	return s.kvStore.Set(0, rlm, StoreKeyDeniedRewardTokens.String(), tokens)
 449}
 450
 451// IncentiveCounter
 452// HasIncentiveCounterStoreKey reports whether the incentive-counter key exists.
 453//
 454// Returns:
 455//   - bool: True when an incentive counter has been initialized in storage.
 456func (s *stakerStore) HasIncentiveCounterStoreKey() bool {
 457	return s.kvStore.Has(StoreKeyIncentiveCounter.String())
 458}
 459
 460// GetIncentiveCounter retrieves the persistent incentive identifier counter.
 461//
 462// Returns:
 463//   - *Counter: Stored counter state; panics on read or type failure.
 464func (s *stakerStore) GetIncentiveCounter() *Counter {
 465	result, err := s.kvStore.Get(StoreKeyIncentiveCounter.String())
 466	if err != nil {
 467		panic(err)
 468	}
 469
 470	counter, ok := result.(*Counter)
 471	if !ok {
 472		panic(ufmt.Sprintf("failed to cast result to *Counter: %T", result))
 473	}
 474
 475	return counter
 476}
 477
 478// SetIncentiveCounter stores the counter used to allocate incentive identifiers.
 479//
 480// Parameters:
 481//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 482//   - rlm: Current staker realm context; it must be current before storage is written.
 483//   - counter: Counter state whose next value is used by NextIncentiveID.
 484//
 485// Returns:
 486//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 487func (s *stakerStore) SetIncentiveCounter(_ int, rlm realm, counter *Counter) error {
 488	if !rlm.IsCurrent() {
 489		return errors.New(ErrSpoofedRealm)
 490	}
 491
 492	return s.kvStore.Set(0, rlm, StoreKeyIncentiveCounter.String(), counter)
 493}
 494
 495// NextIncentiveID advances the stored counter and builds a unique incentive identifier.
 496//
 497// Parameters:
 498//   - creator: Address that created the incentive.
 499//   - timestamp: Incentive start or creation timestamp included in the identifier.
 500//
 501// Returns:
 502//   - string: Identifier combining creator, timestamp, and the next counter index.
 503func (s *stakerStore) NextIncentiveID(creator address, timestamp int64) string {
 504	counter := s.GetIncentiveCounter()
 505	return makeIncentiveID(creator, timestamp, counter.Next())
 506}
 507
 508// TokenSpecificMinimumRewards
 509// HasTokenSpecificMinimumRewardsStoreKey reports whether the token-specific minimum-rewards key exists.
 510//
 511// Returns:
 512//   - bool: True when token-specific minimum rewards have been initialized in storage.
 513func (s *stakerStore) HasTokenSpecificMinimumRewardsStoreKey() bool {
 514	return s.kvStore.Has(StoreKeyTokenSpecificMinimumRewards.String())
 515}
 516
 517// GetTokenSpecificMinimumRewards retrieves per-token minimum reward amounts.
 518//
 519// Returns:
 520//   - map[string]int64: Stored minimum reward overrides keyed by token path; panics on read or type failure.
 521func (s *stakerStore) GetTokenSpecificMinimumRewards() map[string]int64 {
 522	result, err := s.kvStore.Get(StoreKeyTokenSpecificMinimumRewards.String())
 523	if err != nil {
 524		panic(err)
 525	}
 526
 527	rewards, ok := result.(map[string]int64)
 528	if !ok {
 529		panic(ufmt.Sprintf("failed to cast result to map[string]int64: %T", result))
 530	}
 531
 532	return rewards
 533}
 534
 535// SetTokenSpecificMinimumRewards replaces the token-specific minimum reward map.
 536//
 537// Parameters:
 538//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 539//   - rlm: Current staker realm context; it must be current before storage is written.
 540//   - rewards: Minimum reward amounts keyed by token path.
 541//
 542// Returns:
 543//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 544func (s *stakerStore) SetTokenSpecificMinimumRewards(_ int, rlm realm, rewards map[string]int64) error {
 545	if !rlm.IsCurrent() {
 546		return errors.New(ErrSpoofedRealm)
 547	}
 548
 549	return s.kvStore.Set(0, rlm, StoreKeyTokenSpecificMinimumRewards.String(), rewards)
 550}
 551
 552// SetTokenSpecificMinimumRewardItem sets a single token's minimum reward amount.
 553//
 554// Parameters:
 555//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 556//   - rlm: Current staker realm context; it must be current before storage is written.
 557//   - tokenPath: Token path receiving the minimum reward override.
 558//   - amount: Minimum reward amount required for tokenPath.
 559//
 560// Returns:
 561//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 562func (s *stakerStore) SetTokenSpecificMinimumRewardItem(_ int, rlm realm, tokenPath string, amount int64) error {
 563	if !rlm.IsCurrent() {
 564		return errors.New(ErrSpoofedRealm)
 565	}
 566
 567	rewards := s.GetTokenSpecificMinimumRewards()
 568
 569	owned := make(map[string]int64)
 570	for k, v := range rewards {
 571		owned[k] = v
 572	}
 573
 574	owned[tokenPath] = amount
 575
 576	return s.kvStore.Set(0, rlm, StoreKeyTokenSpecificMinimumRewards.String(), owned)
 577}
 578
 579// RemoveTokenSpecificMinimumRewardItem removes a single token's minimum reward entry.
 580//
 581// Parameters:
 582//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 583//   - rlm: Current staker realm context; it must be current before storage is written.
 584//   - tokenPath: Token path whose override is removed.
 585//
 586// Returns:
 587//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 588func (s *stakerStore) RemoveTokenSpecificMinimumRewardItem(_ int, rlm realm, tokenPath string) error {
 589	if !rlm.IsCurrent() {
 590		return errors.New(ErrSpoofedRealm)
 591	}
 592
 593	rewards := s.GetTokenSpecificMinimumRewards()
 594
 595	owned := make(map[string]int64)
 596	for k, v := range rewards {
 597		if k == tokenPath {
 598			continue
 599		}
 600
 601		owned[k] = v
 602	}
 603
 604	return s.kvStore.Set(0, rlm, StoreKeyTokenSpecificMinimumRewards.String(), owned)
 605}
 606
 607// UnstakingFee
 608// HasUnstakingFeeStoreKey reports whether the unstaking-fee key exists.
 609//
 610// Returns:
 611//   - bool: True when an unstaking fee has been initialized in storage.
 612func (s *stakerStore) HasUnstakingFeeStoreKey() bool {
 613	return s.kvStore.Has(StoreKeyUnstakingFee.String())
 614}
 615
 616// GetUnstakingFee retrieves the configured unstaking fee rate.
 617//
 618// Returns:
 619//   - uint64: Stored unstaking fee rate in basis points; panics on read or type failure.
 620func (s *stakerStore) GetUnstakingFee() uint64 {
 621	result, err := s.kvStore.Get(StoreKeyUnstakingFee.String())
 622	if err != nil {
 623		panic(err)
 624	}
 625
 626	fee, ok := result.(uint64)
 627	if !ok {
 628		panic(ufmt.Sprintf("failed to cast result to uint64: %T", result))
 629	}
 630
 631	return fee
 632}
 633
 634// SetUnstakingFee stores the fee charged when a position is unstaked.
 635//
 636// Parameters:
 637//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 638//   - rlm: Current staker realm context; it must be current before storage is written.
 639//   - fee: Unstaking fee rate in basis points.
 640//
 641// Returns:
 642//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 643func (s *stakerStore) SetUnstakingFee(_ int, rlm realm, fee uint64) error {
 644	if !rlm.IsCurrent() {
 645		return errors.New(ErrSpoofedRealm)
 646	}
 647
 648	return s.kvStore.Set(0, rlm, StoreKeyUnstakingFee.String(), fee)
 649}
 650
 651// HasPendingProtocolFeesStoreKey reports whether the pending protocol-fees key exists.
 652//
 653// Returns:
 654//   - bool: True when pending protocol fees have been initialized in storage.
 655func (s *stakerStore) HasPendingProtocolFeesStoreKey() bool {
 656	return s.kvStore.Has(StoreKeyPendingProtocolFees.String())
 657}
 658
 659// GetPendingProtocolFees retrieves pending protocol fee amounts keyed by token path.
 660//
 661// Returns:
 662//   - map[string]int64: Stored token-to-fee map; panics on read or type failure.
 663func (s *stakerStore) GetPendingProtocolFees() map[string]int64 {
 664	result, err := s.kvStore.Get(StoreKeyPendingProtocolFees.String())
 665	if err != nil {
 666		panic(err)
 667	}
 668
 669	fees, ok := result.(map[string]int64)
 670	if !ok {
 671		panic(ufmt.Sprintf("failed to cast result to map[string]int64: %T", result))
 672	}
 673
 674	return fees
 675}
 676
 677// SetPendingProtocolFees replaces the pending protocol-fee map with a store-owned copy.
 678//
 679// Parameters:
 680//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 681//   - rlm: Current staker realm context; it must be current before storage is written.
 682//   - fees: Pending protocol fee amounts keyed by token path.
 683//
 684// Returns:
 685//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 686func (s *stakerStore) SetPendingProtocolFees(_ int, rlm realm, fees map[string]int64) error {
 687	if !rlm.IsCurrent() {
 688		return errors.New(ErrSpoofedRealm)
 689	}
 690
 691	// The map is copied here so it is allocated by, and therefore mutable from, this realm.
 692	owned := make(map[string]int64, len(fees))
 693	for tokenPath, amount := range fees {
 694		owned[tokenPath] = amount
 695	}
 696
 697	return s.kvStore.Set(0, rlm, StoreKeyPendingProtocolFees.String(), owned)
 698}
 699
 700// GetPendingProtocolFee returns one token's pending protocol fee amount.
 701//
 702// Parameters:
 703//   - tokenPath: Token path whose pending fee is read.
 704//
 705// Returns:
 706//   - int64: Stored pending fee amount, or zero when tokenPath has no map entry.
 707func (s *stakerStore) GetPendingProtocolFee(tokenPath string) int64 {
 708	return s.GetPendingProtocolFees()[tokenPath]
 709}
 710
 711// SetPendingProtocolFee updates one token's pending protocol fee in the stored fee map.
 712//
 713// Parameters:
 714//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 715//   - rlm: Current staker realm context and write-authorizing realm.
 716//   - tokenPath: Token path whose pending fee amount is updated.
 717//   - amount: Pending protocol fee amount for tokenPath.
 718//
 719// Returns:
 720//   - error: ErrSpoofedRealm or write-permission error when the realm cannot mutate this store; nil on success.
 721func (s *stakerStore) SetPendingProtocolFee(_ int, rlm realm, tokenPath string, amount int64) error {
 722	if !rlm.IsCurrent() {
 723		return errors.New(ErrSpoofedRealm)
 724	}
 725
 726	if rlm.IsCode() && !s.kvStore.IsWriteAuthorized(rlm.Address()) {
 727		return errors.New(store.ErrWritePermissionDenied)
 728	}
 729
 730	s.GetPendingProtocolFees()[tokenPath] = amount
 731
 732	return nil
 733}
 734
 735// RemovePendingProtocolFee removes one token's pending protocol fee entry.
 736//
 737// Parameters:
 738//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 739//   - rlm: Current staker realm context and write-authorizing realm.
 740//   - tokenPath: Token path whose pending fee entry is deleted.
 741//
 742// Returns:
 743//   - error: ErrSpoofedRealm or write-permission error when the realm cannot mutate this store; nil on success.
 744func (s *stakerStore) RemovePendingProtocolFee(_ int, rlm realm, tokenPath string) error {
 745	if !rlm.IsCurrent() {
 746		return errors.New(ErrSpoofedRealm)
 747	}
 748
 749	if rlm.IsCode() && !s.kvStore.IsWriteAuthorized(rlm.Address()) {
 750		return errors.New(store.ErrWritePermissionDenied)
 751	}
 752
 753	delete(s.GetPendingProtocolFees(), tokenPath)
 754
 755	return nil
 756}
 757
 758// UnstakedPositions
 759// HasUnstakedPositionsStoreKey reports whether the unstaked-positions key exists.
 760//
 761// Returns:
 762//   - bool: True when unstaked positions have been initialized in storage.
 763func (s *stakerStore) HasUnstakedPositionsStoreKey() bool {
 764	return s.kvStore.Has(StoreKeyUnstakedPositions.String())
 765}
 766
 767// GetUnstakedPositions retrieves the unstaked-position tree.
 768//
 769// Returns:
 770//   - *bptree.BPTree: Stored unstaked-position records; panics on read or type failure.
 771func (s *stakerStore) GetUnstakedPositions() *bptree.BPTree {
 772	result, err := s.kvStore.Get(StoreKeyUnstakedPositions.String())
 773	if err != nil {
 774		panic(err)
 775	}
 776
 777	positions, ok := result.(*bptree.BPTree)
 778	if !ok {
 779		panic(ufmt.Sprintf("failed to cast result to *bptree.BPTree: %T", result))
 780	}
 781
 782	return positions
 783}
 784
 785// SetUnstakedPositions stores positions that have been unstaked but retain reward checkpoints.
 786//
 787// Parameters:
 788//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 789//   - rlm: Current staker realm context; it must be current before storage is written.
 790//   - positions: B+ tree of unstaked position records and their exit checkpoints.
 791//
 792// Returns:
 793//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 794func (s *stakerStore) SetUnstakedPositions(_ int, rlm realm, positions *bptree.BPTree) error {
 795	if !rlm.IsCurrent() {
 796		return errors.New(ErrSpoofedRealm)
 797	}
 798
 799	return s.kvStore.Set(0, rlm, StoreKeyUnstakedPositions.String(), positions)
 800}
 801
 802// UncollectedIncentiveCounts
 803// HasUncollectedIncentiveCountsStoreKey reports whether the uncollected-counts key exists.
 804//
 805// Returns:
 806//   - bool: True when uncollected incentive counts have been initialized in storage.
 807func (s *stakerStore) HasUncollectedIncentiveCountsStoreKey() bool {
 808	return s.kvStore.Has(StoreKeyUncollectedIncentiveCounts.String())
 809}
 810
 811// GetUncollectedIncentiveCounts retrieves outstanding-position counts by incentive.
 812//
 813// Returns:
 814//   - *bptree.BPTree: Stored incentive-to-count tree; panics on read or type failure.
 815func (s *stakerStore) GetUncollectedIncentiveCounts() *bptree.BPTree {
 816	result, err := s.kvStore.Get(StoreKeyUncollectedIncentiveCounts.String())
 817	if err != nil {
 818		panic(err)
 819	}
 820
 821	counts, ok := result.(*bptree.BPTree)
 822	if !ok {
 823		panic(ufmt.Sprintf("failed to cast result to *bptree.BPTree: %T", result))
 824	}
 825
 826	return counts
 827}
 828
 829// SetUncollectedIncentiveCounts stores counts of unstaked positions owing each incentive.
 830//
 831// Parameters:
 832//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 833//   - rlm: Current staker realm context; it must be current before storage is written.
 834//   - counts: B+ tree mapping incentive identifiers to outstanding position counts.
 835//
 836// Returns:
 837//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 838func (s *stakerStore) SetUncollectedIncentiveCounts(_ int, rlm realm, counts *bptree.BPTree) error {
 839	if !rlm.IsCurrent() {
 840		return errors.New(ErrSpoofedRealm)
 841	}
 842
 843	return s.kvStore.Set(0, rlm, StoreKeyUncollectedIncentiveCounts.String(), counts)
 844}
 845
 846// Pools
 847// HasPoolsStoreKey reports whether the pools key exists.
 848//
 849// Returns:
 850//   - bool: True when the pool collection has been initialized in storage.
 851func (s *stakerStore) HasPoolsStoreKey() bool {
 852	return s.kvStore.Has(StoreKeyPools.String())
 853}
 854
 855// GetPools retrieves the staker's pool collection.
 856//
 857// Returns:
 858//   - *bptree.BPTree: Stored pool tree; panics on read or type failure.
 859func (s *stakerStore) GetPools() *bptree.BPTree {
 860	result, err := s.kvStore.Get(StoreKeyPools.String())
 861	if err != nil {
 862		panic(err)
 863	}
 864
 865	pools, ok := result.(*bptree.BPTree)
 866	if !ok {
 867		panic(ufmt.Sprintf("failed to cast result to *bptree.BPTree: %T", result))
 868	}
 869
 870	return pools
 871}
 872
 873// SetPools stores the pool collection used by the staker.
 874//
 875// Parameters:
 876//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 877//   - rlm: Current staker realm context; it must be current before storage is written.
 878//   - pools: Pool tree containing registered pools and their reward state.
 879//
 880// Returns:
 881//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 882func (s *stakerStore) SetPools(_ int, rlm realm, pools *bptree.BPTree) error {
 883	if !rlm.IsCurrent() {
 884		return errors.New(ErrSpoofedRealm)
 885	}
 886
 887	return s.kvStore.Set(0, rlm, StoreKeyPools.String(), pools)
 888}
 889
 890// PoolTierMemberships
 891// HasPoolTierMembershipsStoreKey reports whether the pool-tier membership key exists.
 892//
 893// Returns:
 894//   - bool: True when pool-tier memberships have been initialized in storage.
 895func (s *stakerStore) HasPoolTierMembershipsStoreKey() bool {
 896	return s.kvStore.Has(StoreKeyPoolTierMemberships.String())
 897}
 898
 899// GetPoolTierMemberships retrieves the pool-to-tier membership tree.
 900//
 901// Returns:
 902//   - *bptree.BPTree: Stored mapping from pool paths to tier numbers; panics on read or type failure.
 903func (s *stakerStore) GetPoolTierMemberships() *bptree.BPTree {
 904	result, err := s.kvStore.Get(StoreKeyPoolTierMemberships.String())
 905	if err != nil {
 906		panic(err)
 907	}
 908
 909	memberships, ok := result.(*bptree.BPTree)
 910	if !ok {
 911		panic(ufmt.Sprintf("failed to cast result to *bptree.BPTree: %T", result))
 912	}
 913
 914	return memberships
 915}
 916
 917// SetPoolTierMemberships stores the pool-to-tier membership tree.
 918//
 919// Parameters:
 920//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 921//   - rlm: Current staker realm context; it must be current before storage is written.
 922//   - memberships: Mapping from pool paths to their assigned tier numbers.
 923//
 924// Returns:
 925//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 926func (s *stakerStore) SetPoolTierMemberships(_ int, rlm realm, memberships *bptree.BPTree) error {
 927	if !rlm.IsCurrent() {
 928		return errors.New(ErrSpoofedRealm)
 929	}
 930
 931	return s.kvStore.Set(0, rlm, StoreKeyPoolTierMemberships.String(), memberships)
 932}
 933
 934// PoolTierRatio
 935// HasPoolTierRatioStoreKey reports whether the pool-tier ratio key exists.
 936//
 937// Returns:
 938//   - bool: True when a pool-tier ratio has been initialized in storage.
 939func (s *stakerStore) HasPoolTierRatioStoreKey() bool {
 940	return s.kvStore.Has(StoreKeyPoolTierRatio.String())
 941}
 942
 943// GetPoolTierRatio retrieves the configured pool-tier reward-share ratio.
 944//
 945// Returns:
 946//   - TierRatio: Stored ratio distribution across tiers; panics on read or type failure.
 947func (s *stakerStore) GetPoolTierRatio() TierRatio {
 948	result, err := s.kvStore.Get(StoreKeyPoolTierRatio.String())
 949	if err != nil {
 950		panic(err)
 951	}
 952
 953	ratio, ok := result.(TierRatio)
 954	if !ok {
 955		panic(ufmt.Sprintf("failed to cast result to TierRatio: %T", result))
 956	}
 957
 958	return ratio
 959}
 960
 961// SetPoolTierRatio stores the reward-share ratio for each pool tier.
 962//
 963// Parameters:
 964//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
 965//   - rlm: Current staker realm context; it must be current before storage is written.
 966//   - ratio: Tier reward-share ratio used to divide current emission.
 967//
 968// Returns:
 969//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
 970func (s *stakerStore) SetPoolTierRatio(_ int, rlm realm, ratio TierRatio) error {
 971	if !rlm.IsCurrent() {
 972		return errors.New(ErrSpoofedRealm)
 973	}
 974
 975	return s.kvStore.Set(0, rlm, StoreKeyPoolTierRatio.String(), ratio)
 976}
 977
 978// PoolTierCounts
 979// HasPoolTierCountsStoreKey reports whether the pool-tier counts key exists.
 980//
 981// Returns:
 982//   - bool: True when pool counts have been initialized in storage.
 983func (s *stakerStore) HasPoolTierCountsStoreKey() bool {
 984	return s.kvStore.Has(StoreKeyPoolTierCounts.String())
 985}
 986
 987// GetPoolTierCounts retrieves the pool count for every tier index.
 988//
 989// Returns:
 990//   - [AllTierCount]uint64: Stored pool counts indexed by tier; panics on read or type failure.
 991func (s *stakerStore) GetPoolTierCounts() [AllTierCount]uint64 {
 992	result, err := s.kvStore.Get(StoreKeyPoolTierCounts.String())
 993	if err != nil {
 994		panic(err)
 995	}
 996
 997	counts, ok := result.([AllTierCount]uint64)
 998	if !ok {
 999		panic(ufmt.Sprintf("failed to cast result to [AllTierCount]uint64: %T", result))
1000	}
1001
1002	return counts
1003}
1004
1005// SetPoolTierCounts stores the number of pools assigned to each tier index.
1006//
1007// Parameters:
1008//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1009//   - rlm: Current staker realm context; it must be current before storage is written.
1010//   - counts: Pool counts indexed by tier, including the reserved zero tier.
1011//
1012// Returns:
1013//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1014func (s *stakerStore) SetPoolTierCounts(_ int, rlm realm, counts [AllTierCount]uint64) error {
1015	if !rlm.IsCurrent() {
1016		return errors.New(ErrSpoofedRealm)
1017	}
1018
1019	return s.kvStore.Set(0, rlm, StoreKeyPoolTierCounts.String(), counts)
1020}
1021
1022// PoolTierLastRewardCacheTimestamp
1023// HasPoolTierLastRewardCacheTimestampStoreKey reports whether the tier reward-cache timestamp key exists.
1024//
1025// Returns:
1026//   - bool: True when a tier reward-cache timestamp has been initialized in storage.
1027func (s *stakerStore) HasPoolTierLastRewardCacheTimestampStoreKey() bool {
1028	return s.kvStore.Has(StoreKeyPoolTierLastRewardCacheTimestamp.String())
1029}
1030
1031// GetPoolTierLastRewardCacheTimestamp retrieves the tier reward-cache timestamp.
1032//
1033// Returns:
1034//   - int64: Stored Unix timestamp through which tier rewards are materialized; panics on read or type failure.
1035func (s *stakerStore) GetPoolTierLastRewardCacheTimestamp() int64 {
1036	result, err := s.kvStore.Get(StoreKeyPoolTierLastRewardCacheTimestamp.String())
1037	if err != nil {
1038		panic(err)
1039	}
1040
1041	timestamp, ok := result.(int64)
1042	if !ok {
1043		panic(ufmt.Sprintf("failed to cast result to int64: %T", result))
1044	}
1045
1046	return timestamp
1047}
1048
1049// SetPoolTierLastRewardCacheTimestamp stores the timestamp through which tier rewards are cached.
1050//
1051// Parameters:
1052//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1053//   - rlm: Current staker realm context; it must be current before storage is written.
1054//   - timestamp: Unix timestamp through which pool-tier reward caches are materialized.
1055//
1056// Returns:
1057//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1058func (s *stakerStore) SetPoolTierLastRewardCacheTimestamp(_ int, rlm realm, timestamp int64) error {
1059	if !rlm.IsCurrent() {
1060		return errors.New(ErrSpoofedRealm)
1061	}
1062
1063	return s.kvStore.Set(0, rlm, StoreKeyPoolTierLastRewardCacheTimestamp.String(), timestamp)
1064}
1065
1066// PoolTierCurrentEmission
1067// HasPoolTierCurrentEmissionStoreKey reports whether the current pool-tier emission key exists.
1068//
1069// Returns:
1070//   - bool: True when the current pool-tier emission has been initialized in storage.
1071func (s *stakerStore) HasPoolTierCurrentEmissionStoreKey() bool {
1072	return s.kvStore.Has(StoreKeyPoolTierCurrentEmission.String())
1073}
1074
1075// GetPoolTierCurrentEmission retrieves the current pool-tier emission amount.
1076//
1077// Returns:
1078//   - int64: Stored emission amount used for tier reward accrual; panics on read or type failure.
1079func (s *stakerStore) GetPoolTierCurrentEmission() int64 {
1080	result, err := s.kvStore.Get(StoreKeyPoolTierCurrentEmission.String())
1081	if err != nil {
1082		panic(err)
1083	}
1084
1085	emission, ok := result.(int64)
1086	if !ok {
1087		panic(ufmt.Sprintf("failed to cast result to int64: %T", result))
1088	}
1089
1090	return emission
1091}
1092
1093// SetPoolTierCurrentEmission stores the current pool-tier emission amount.
1094//
1095// Parameters:
1096//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1097//   - rlm: Current staker realm context; it must be current before storage is written.
1098//   - emission: Current emission amount used for tier reward accrual.
1099//
1100// Returns:
1101//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1102func (s *stakerStore) SetPoolTierCurrentEmission(_ int, rlm realm, emission int64) error {
1103	if !rlm.IsCurrent() {
1104		return errors.New(ErrSpoofedRealm)
1105	}
1106
1107	return s.kvStore.Set(0, rlm, StoreKeyPoolTierCurrentEmission.String(), emission)
1108}
1109
1110// PoolTierGetEmission
1111// HasPoolTierGetEmissionStoreKey reports whether the pool-tier emission callback key exists.
1112//
1113// Returns:
1114//   - bool: True when the current-emission callback has been initialized in storage.
1115func (s *stakerStore) HasPoolTierGetEmissionStoreKey() bool {
1116	return s.kvStore.Has(StoreKeyPoolTierGetEmission.String())
1117}
1118
1119// GetPoolTierGetEmission retrieves the current-emission callback.
1120//
1121// Returns:
1122//   - func() (int64, error): Callback returning the current emission amount per second and an error; panics on read or type failure.
1123func (s *stakerStore) GetPoolTierGetEmission() func() (int64, error) {
1124	result, err := s.kvStore.Get(StoreKeyPoolTierGetEmission.String())
1125	if err != nil {
1126		panic(err)
1127	}
1128
1129	fn, ok := result.(func() (int64, error))
1130	if !ok {
1131		panic(ufmt.Sprintf("failed to cast result to func() (int64, error): %T", result))
1132	}
1133
1134	return fn
1135}
1136
1137// SetPoolTierGetEmission stores the callback used to read current staker emission.
1138//
1139// Parameters:
1140//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1141//   - rlm: Current staker realm context; it must be current before storage is written.
1142//   - fn: Callback returning the current emission amount per second and an error.
1143//
1144// Returns:
1145//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1146func (s *stakerStore) SetPoolTierGetEmission(_ int, rlm realm, fn func() (int64, error)) error {
1147	if !rlm.IsCurrent() {
1148		return errors.New(ErrSpoofedRealm)
1149	}
1150
1151	return s.kvStore.Set(0, rlm, StoreKeyPoolTierGetEmission.String(), fn)
1152}
1153
1154// PoolTierGetHalvingBlocksInRange
1155// HasPoolTierGetHalvingBlocksInRangeStoreKey reports whether the pool-tier halving callback key exists.
1156//
1157// Returns:
1158//   - bool: True when the halving-range callback has been initialized in storage.
1159func (s *stakerStore) HasPoolTierGetHalvingBlocksInRangeStoreKey() bool {
1160	return s.kvStore.Has(StoreKeyPoolTierGetHalvingBlocksInRange.String())
1161}
1162
1163// GetPoolTierGetHalvingBlocksInRange retrieves the stored halving-range callback.
1164//
1165// Returns:
1166//   - func(start, end int64) ([]int64, []int64, error): Callback returning halving timestamps and matching emissions for [start, end); panics on read or type failure.
1167func (s *stakerStore) GetPoolTierGetHalvingBlocksInRange() func(start, end int64) ([]int64, []int64, error) {
1168	result, err := s.kvStore.Get(StoreKeyPoolTierGetHalvingBlocksInRange.String())
1169	if err != nil {
1170		panic(err)
1171	}
1172
1173	fn, ok := result.(func(start, end int64) ([]int64, []int64, error))
1174	if !ok {
1175		panic(ufmt.Sprintf("failed to cast result to func(start, end int64) ([]int64, []int64, error): %T", result))
1176	}
1177
1178	return fn
1179}
1180
1181// SetPoolTierGetHalvingBlocksInRange stores the halving-range callback used by pool-tier reward caching.
1182//
1183// Parameters:
1184//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1185//   - rlm: Current staker realm context; it must be current before storage is written.
1186//   - fn: Callback accepting [start, end) timestamps and returning halving timestamps, emissions, and an error.
1187//
1188// Returns:
1189//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1190func (s *stakerStore) SetPoolTierGetHalvingBlocksInRange(_ int, rlm realm, fn func(start, end int64) ([]int64, []int64, error)) error {
1191	if !rlm.IsCurrent() {
1192		return errors.New(ErrSpoofedRealm)
1193	}
1194
1195	return s.kvStore.Set(0, rlm, StoreKeyPoolTierGetHalvingBlocksInRange.String(), fn)
1196}
1197
1198// HasWarmupTemplateStoreKey reports whether the warmup-template key exists.
1199//
1200// Returns:
1201//   - bool: True when a warmup schedule has been initialized in storage.
1202func (s *stakerStore) HasWarmupTemplateStoreKey() bool {
1203	return s.kvStore.Has(StoreKeyWarmupTemplate.String())
1204}
1205
1206// GetWarmupTemplate retrieves a clone of the stored warmup schedule.
1207//
1208// Returns:
1209//   - []Warmup: Warmup entries copied into the caller's realm; panics on read or type failure.
1210func (s *stakerStore) GetWarmupTemplate() []Warmup {
1211	result, err := s.kvStore.Get(StoreKeyWarmupTemplate.String())
1212	if err != nil {
1213		panic(err)
1214	}
1215
1216	warmups, ok := result.([]Warmup)
1217	if !ok {
1218		panic(ufmt.Sprintf("failed to cast result to []Warmup: %T", result))
1219	}
1220
1221	return cloneWarmups(warmups)
1222}
1223
1224// SetWarmupTemplate stores the warmup schedule used for external incentives.
1225//
1226// Parameters:
1227//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1228//   - rlm: Current staker realm context; it must be current before storage is written.
1229//   - warmups: Warmup schedule entries to persist.
1230//
1231// Returns:
1232//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1233func (s *stakerStore) SetWarmupTemplate(_ int, rlm realm, warmups []Warmup) error {
1234	if !rlm.IsCurrent() {
1235		return errors.New(ErrSpoofedRealm)
1236	}
1237
1238	return s.kvStore.Set(0, rlm, StoreKeyWarmupTemplate.String(), warmups)
1239}
1240
1241// CurrentSwapBatch
1242// HasCurrentSwapBatchStoreKey reports whether the current-swap-batch key exists.
1243//
1244// Returns:
1245//   - bool: True when the current swap batch has been initialized in storage.
1246func (s *stakerStore) HasCurrentSwapBatchStoreKey() bool {
1247	return s.kvStore.Has(StoreKeyCurrentSwapBatch.String())
1248}
1249
1250// GetCurrentSwapBatch retrieves the processor for the currently active swap.
1251//
1252// Returns:
1253//   - *SwapBatchProcessor: Stored swap batch, or nil when initialization has recorded no active batch; panics on read or type failure.
1254func (s *stakerStore) GetCurrentSwapBatch() *SwapBatchProcessor {
1255	result, err := s.kvStore.Get(StoreKeyCurrentSwapBatch.String())
1256	if err != nil {
1257		panic(err)
1258	}
1259
1260	batch, ok := result.(*SwapBatchProcessor)
1261	if !ok {
1262		panic(ufmt.Sprintf("failed to cast result to *SwapBatchProcessor: %T", result))
1263	}
1264
1265	return batch
1266}
1267
1268// SetCurrentSwapBatch persists the processor for the currently active swap.
1269//
1270// Parameters:
1271//   - _: Leading realm-call discriminator for the forwarded store call; callers pass 0.
1272//   - rlm: Current staker realm context; it must be current before storage is written.
1273//   - batch: Swap-batch processor to persist, or nil when the swap has ended.
1274//
1275// Returns:
1276//   - error: ErrSpoofedRealm when rlm is not current, or the underlying KV-store write error; nil on success.
1277func (s *stakerStore) SetCurrentSwapBatch(_ int, rlm realm, batch *SwapBatchProcessor) error {
1278	if !rlm.IsCurrent() {
1279		return errors.New(ErrSpoofedRealm)
1280	}
1281
1282	return s.kvStore.Set(0, rlm, StoreKeyCurrentSwapBatch.String(), batch)
1283}
1284
1285// NewStakerStore creates a new staker store instance with the provided KV store.
1286// This function is used by the upgrade system to create storage instances for each implementation.
1287//
1288// Parameters:
1289//   - kvStore: Key-value store used for all staker state.
1290//
1291// Returns:
1292//   - IStakerStore: Store interface backed by the supplied KV store.
1293func NewStakerStore(kvStore store.KVStore) IStakerStore {
1294	return &stakerStore{
1295		kvStore: kvStore,
1296	}
1297}
1298
1299func makeIncentiveID(creator address, timestamp int64, index int64) string {
1300	return ufmt.Sprintf("%s:%d:%d", creator.String(), timestamp, index)
1301}
1302
1303func contains(items []string, item string) bool {
1304	for _, i := range items {
1305		if i == item {
1306			return true
1307		}
1308	}
1309
1310	return false
1311}