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

init.gno

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