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

init.gno

7.03 Kb · 258 lines
  1package staker
  2
  3import (
  4	"time"
  5
  6	"gno.land/r/gnoswap/access/v1"
  7
  8	"gno.land/r/gnoswap/emission"
  9
 10	sr "gno.land/r/gnoswap/staker"
 11)
 12
 13const (
 14	defaultDepositGnsAmount    = int64(100_000_000_000)
 15	defaultMinimumRewardAmount = int64(1_000_000_000)
 16
 17	// unstakingFee is the fee charged when unstaking positions.
 18	// This parameter can be modified through governance.
 19	defaultUnstakingFee = uint64(100) // 1%
 20)
 21
 22func init(cur realm) {
 23	registerStakerV1(cur)
 24}
 25
 26func registerStakerV1(cur realm) {
 27	sr.RegisterInitializer(cross(cur), func(_ int, rlm realm, stakerStore sr.IStakerStore, poolAccessor sr.PoolAccessor, emissionAccessor sr.EmissionAccessor, nftAccessor sr.NFTAccessor) sr.IStaker {
 28		access.AssertIsRlmCurrent(0, rlm)
 29
 30		err := initStoreData(0, rlm, stakerStore, emissionAccessor)
 31		if err != nil {
 32			panic(err)
 33		}
 34
 35		instance := NewStakerV1(stakerStore, poolAccessor, emissionAccessor, nftAccessor)
 36		instance.setupSwapHooks(0, rlm)
 37		emissionAccessor.SetOnDistributionPctChangeCallback(0, rlm, instance.emissionCacheUpdateHook)
 38
 39		return instance
 40	})
 41}
 42
 43func initStoreData(_ int, rlm realm, stakerStore sr.IStakerStore, emissionAccessor sr.EmissionAccessor) error {
 44	if !stakerStore.HasDepositGnsAmountStoreKey() {
 45		err := stakerStore.SetDepositGnsAmount(0, rlm, defaultDepositGnsAmount)
 46		if err != nil {
 47			return err
 48		}
 49	}
 50
 51	if !stakerStore.HasMinimumRewardAmountStoreKey() {
 52		err := stakerStore.SetMinimumRewardAmount(0, rlm, defaultMinimumRewardAmount)
 53		if err != nil {
 54			return err
 55		}
 56	}
 57
 58	if !stakerStore.HasDepositsStoreKey() {
 59		err := stakerStore.SetDeposits(0, rlm, sr.NewBPTreeN(16))
 60		if err != nil {
 61			return err
 62		}
 63	}
 64
 65	if !stakerStore.HasExternalIncentivesStoreKey() {
 66		err := stakerStore.SetExternalIncentives(0, rlm, sr.NewBPTreeN(16))
 67		if err != nil {
 68			return err
 69		}
 70	}
 71
 72	if !stakerStore.HasTotalEmissionSentStoreKey() {
 73		err := stakerStore.SetTotalEmissionSent(0, rlm, 0)
 74		if err != nil {
 75			return err
 76		}
 77	}
 78
 79	if !stakerStore.HasAllowedTokensStoreKey() {
 80		// Copy the package-level default slice so each store owns its own
 81		// allowed-tokens slice. Storing sr.DefaultAllowedTokens directly would
 82		// alias the shared (sr-owned) slice across every store, leaking
 83		// mutations and tripping the readonly-taint gate on later writes.
 84		err := stakerStore.SetAllowedTokens(0, rlm, sr.DefaultAllowedTokens())
 85		if err != nil {
 86			return err
 87		}
 88	}
 89
 90	if !stakerStore.HasIncentiveCounterStoreKey() {
 91		err := stakerStore.SetIncentiveCounter(0, rlm, sr.NewCounter())
 92		if err != nil {
 93			return err
 94		}
 95	}
 96
 97	if !stakerStore.HasTokenSpecificMinimumRewardsStoreKey() {
 98		err := stakerStore.SetTokenSpecificMinimumRewards(0, rlm, make(map[string]int64))
 99		if err != nil {
100			return err
101		}
102	}
103
104	if !stakerStore.HasUnstakingFeeStoreKey() {
105		err := stakerStore.SetUnstakingFee(0, rlm, defaultUnstakingFee)
106		if err != nil {
107			return err
108		}
109	}
110
111	pendingProtocolFees := make(map[string]int64)
112	if stakerStore.HasPendingProtocolFeesStoreKey() {
113		pendingProtocolFees = stakerStore.GetPendingProtocolFees()
114	}
115	if err := stakerStore.SetPendingProtocolFees(0, rlm, pendingProtocolFees); err != nil {
116		return err
117	}
118
119	if !stakerStore.HasUnstakedPositionsStoreKey() {
120		err := stakerStore.SetUnstakedPositions(0, rlm, sr.NewBPTreeN(16))
121		if err != nil {
122			return err
123		}
124	}
125
126	if !stakerStore.HasUncollectedIncentiveCountsStoreKey() {
127		err := stakerStore.SetUncollectedIncentiveCounts(0, rlm, sr.NewBPTreeN(16))
128		if err != nil {
129			return err
130		}
131	}
132
133	if !stakerStore.HasWarmupTemplateStoreKey() {
134		err := stakerStore.SetWarmupTemplate(0, rlm, sr.DefaultWarmupTemplate())
135		if err != nil {
136			return err
137		}
138	}
139
140	initializedPoolTier, initializedPools, initialPoolTierTime, initialTierRewards := initializePoolTier(stakerStore)
141
142	previousRealm := rlm.Previous()
143	emitSetPoolTier(
144		previousRealm.Address().String(),
145		previousRealm.PkgPath(),
146		emission.DefaultInitialPoolTierPath,
147		uint64(Tier1),
148		initializedPoolTier.currentEmission,
149		initialTierRewards,
150		initializedPoolTier.counts,
151		initialPoolTierTime,
152	)
153
154	if !stakerStore.HasPoolsStoreKey() {
155		err := stakerStore.SetPools(0, rlm, initializedPools.tree)
156		if err != nil {
157			return err
158		}
159	}
160
161	if !stakerStore.HasPoolTierMembershipsStoreKey() {
162		err := stakerStore.SetPoolTierMemberships(0, rlm, initializedPoolTier.membership)
163		if err != nil {
164			return err
165		}
166	}
167
168	if !stakerStore.HasPoolTierRatioStoreKey() {
169		err := stakerStore.SetPoolTierRatio(0, rlm, initializedPoolTier.tierRatio)
170		if err != nil {
171			return err
172		}
173	}
174
175	if !stakerStore.HasPoolTierCountsStoreKey() {
176		err := stakerStore.SetPoolTierCounts(0, rlm, initializedPoolTier.counts)
177		if err != nil {
178			return err
179		}
180	}
181
182	if !stakerStore.HasPoolTierLastRewardCacheTimestampStoreKey() {
183		err := stakerStore.SetPoolTierLastRewardCacheTimestamp(0, rlm, initializedPoolTier.lastRewardCacheTimestamp)
184		if err != nil {
185			return err
186		}
187	}
188
189	if !stakerStore.HasPoolTierCurrentEmissionStoreKey() {
190		err := stakerStore.SetPoolTierCurrentEmission(0, rlm, initializedPoolTier.currentEmission)
191		if err != nil {
192			return err
193		}
194	}
195
196	if !stakerStore.HasPoolTierGetEmissionStoreKey() {
197		getEmissionFn := func() (int64, error) {
198			return emissionAccessor.GetStakerEmissionAmountPerSecond()
199		}
200
201		err := stakerStore.SetPoolTierGetEmission(0, rlm, getEmissionFn)
202		if err != nil {
203			return err
204		}
205	}
206
207	if !stakerStore.HasPoolTierGetHalvingBlocksInRangeStoreKey() {
208		getHalvingBlocksInRangeFn := func(start, end int64) ([]int64, []int64, error) {
209			return emissionAccessor.GetStakerEmissionAmountPerSecondInRange(start, end)
210		}
211
212		err := stakerStore.SetPoolTierGetHalvingBlocksInRange(0, rlm, getHalvingBlocksInRangeFn)
213		if err != nil {
214			return err
215		}
216	}
217
218	if !stakerStore.HasCurrentSwapBatchStoreKey() {
219		err := stakerStore.SetCurrentSwapBatch(0, rlm, nil)
220		if err != nil {
221			return err
222		}
223	}
224
225	return nil
226}
227
228// initializePoolTier seeds the staker's pool tiering state on first init.
229//
230// The default pool is the canonical WUGNOT:GNS:3000 pool, sourced from the
231// emission realm's DefaultInitialPoolTierPath constant so that the staker's
232// tier-1 base pool is guaranteed to match the pool emission distributes to.
233//
234// When SetDistributionStartTime executes, emission distributes to this default
235// pool and the staker uses the same path as the base tier-1 pool for reward
236// accrual. Keeping both sides on emission's single constant structurally
237// enforces that invariant and removes any risk of the two realms drifting
238// apart on the canonical default pool identity.
239func initializePoolTier(stakerStore sr.IStakerStore) (*PoolTier, *Pools, int64, map[uint64]int64) {
240	pools := NewPools()
241	currentTime := time.Now().Unix()
242
243	pl := NewPools().GetPoolOrNil(emission.DefaultInitialPoolTierPath)
244	if pl == nil {
245		pl = sr.NewPool(emission.DefaultInitialPoolTierPath, currentTime)
246		pools.set(emission.DefaultInitialPoolTierPath, pl)
247	}
248
249	poolTier := NewPoolTier(
250		pools,
251		currentTime,
252		emission.DefaultInitialPoolTierPath,
253		emission.GetStakerEmissionAmountPerSecond,
254		emission.GetStakerEmissionAmountPerSecondInRange,
255	)
256
257	return poolTier, pools, currentTime, poolTier.computeTierRewards(poolTier.currentEmission)
258}