Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}