manage_pool_tier_and_warmup.gno
8.02 Kb · 254 lines
1package staker
2
3import (
4 "chain"
5 "chain/runtime"
6 "time"
7
8 "gno.land/p/gnoswap/utils/v1"
9
10 "gno.land/r/gnoswap/access/v1"
11 "gno.land/r/gnoswap/halt/v1"
12 sr "gno.land/r/gnoswap/staker"
13)
14
15const (
16 NOT_EMISSION_TARGET_TIER uint64 = 0
17)
18
19// SetPoolTier assigns a tier level to a pool for internal GNS emission rewards.
20// Only admin or governance can call this function.
21//
22// Parameters:
23// - _: Noncrossing implementation-call discriminator; pass 0.
24// - rlm: Current realm context forwarded unchanged by the staker proxy.
25// - poolPath: Existing pool path whose internal GNS emission tier is being assigned.
26// - tier: Emission tier to assign; valid values are below AllTierCount, including 0 for no emission target.
27func (s *stakerV1) SetPoolTier(_ int, rlm realm, poolPath string, tier uint64) {
28 access.AssertIsRlmCurrent(0, rlm)
29
30 halt.AssertIsNotHaltedStaker()
31
32 caller := rlm.Previous().Address()
33 access.AssertIsAdminOrGovernance(caller)
34
35 assertIsPoolExists(s, poolPath)
36 assertIsValidPoolTier(tier)
37
38 // If the tier is already set, do nothing
39 poolTier := s.getPoolTier()
40 previousTier := poolTier.CurrentTier(poolPath)
41 if previousTier == tier {
42 return
43 }
44
45 currentTime := time.Now().Unix()
46 rewardPerSecond, tierRewards := s.setPoolTier(0, rlm, poolPath, tier, currentTime)
47
48 previousRealm := rlm.Previous()
49 emitSetPoolTier(
50 previousRealm.Address().String(),
51 previousRealm.PkgPath(),
52 poolPath,
53 tier,
54 rewardPerSecond,
55 tierRewards,
56 poolTier.counts,
57 currentTime,
58 )
59}
60
61// ChangePoolTier modifies the tier level of an existing pool.
62// Only admin or governance can call this function.
63//
64// Parameters:
65// - _: Noncrossing implementation-call discriminator; pass 0.
66// - rlm: Current realm context forwarded unchanged by the staker proxy.
67// - poolPath: Existing pool path whose emission tier is being changed.
68// - tier: Replacement emission tier; values at or above AllTierCount are rejected.
69func (s *stakerV1) ChangePoolTier(_ int, rlm realm, poolPath string, tier uint64) {
70 access.AssertIsRlmCurrent(0, rlm)
71
72 halt.AssertIsNotHaltedStaker()
73
74 previousRealm := rlm.Previous()
75 caller := previousRealm.Address()
76 access.AssertIsAdminOrGovernance(caller)
77
78 assertIsPoolExists(s, poolPath)
79 assertIsValidPoolTier(tier)
80
81 poolTier := s.getPoolTier()
82
83 // If the tier is already set, do nothing
84 previousTier := poolTier.CurrentTier(poolPath)
85 if previousTier == tier {
86 return
87 }
88
89 currentTime := time.Now().Unix()
90 previousTier, newTier, rewardPerSecond, tierRewards := s.changePoolTier(0, rlm, poolPath, tier, currentTime)
91
92 chain.Emit(
93 "ChangePoolTier",
94 "prevAddr", caller.String(),
95 "prevRealm", previousRealm.PkgPath(),
96 "poolPath", poolPath,
97 "prevTier", utils.FormatUint(previousTier),
98 "newTier", utils.FormatUint(newTier),
99 "rewardPerSecond", utils.FormatInt(rewardPerSecond),
100 "tier1RewardPerSecond", utils.FormatInt(tierRewards[Tier1]),
101 "tier2RewardPerSecond", utils.FormatInt(tierRewards[Tier2]),
102 "tier3RewardPerSecond", utils.FormatInt(tierRewards[Tier3]),
103 "tier1Count", utils.FormatUint(poolTier.counts[Tier1]),
104 "tier2Count", utils.FormatUint(poolTier.counts[Tier2]),
105 "tier3Count", utils.FormatUint(poolTier.counts[Tier3]),
106 "currentTime", utils.FormatInt(currentTime),
107 "currentHeight", utils.FormatInt(runtime.ChainHeight()),
108 )
109}
110
111// RemovePoolTier removes a pool from internal GNS emission rewards.
112// Only admin or governance can call this function.
113//
114// Parameters:
115// - _: Noncrossing implementation-call discriminator; pass 0.
116// - rlm: Current realm context forwarded unchanged by the staker proxy.
117// - poolPath: Existing pool path to remove from the emission-tier accounting.
118func (s *stakerV1) RemovePoolTier(_ int, rlm realm, poolPath string) {
119 access.AssertIsRlmCurrent(0, rlm)
120
121 halt.AssertIsNotHaltedStaker()
122
123 previousRealm := rlm.Previous()
124 caller := previousRealm.Address()
125 access.AssertIsAdminOrGovernance(caller)
126
127 assertIsPoolExists(s, poolPath)
128
129 poolTier := s.getPoolTier()
130
131 // If the tier is already set, do nothing
132 previousTier := poolTier.CurrentTier(poolPath)
133 if previousTier == NOT_EMISSION_TARGET_TIER {
134 return
135 }
136
137 currentTime := time.Now().Unix()
138 rewardPerSecond, tierRewards := s.removePoolTier(0, rlm, poolPath, currentTime)
139
140 chain.Emit(
141 "RemovePoolTier",
142 "prevAddr", caller.String(),
143 "prevRealm", previousRealm.PkgPath(),
144 "poolPath", poolPath,
145 "rewardPerSecond", utils.FormatInt(rewardPerSecond),
146 "tier1RewardPerSecond", utils.FormatInt(tierRewards[Tier1]),
147 "tier2RewardPerSecond", utils.FormatInt(tierRewards[Tier2]),
148 "tier3RewardPerSecond", utils.FormatInt(tierRewards[Tier3]),
149 "tier1Count", utils.FormatUint(poolTier.counts[Tier1]),
150 "tier2Count", utils.FormatUint(poolTier.counts[Tier2]),
151 "tier3Count", utils.FormatUint(poolTier.counts[Tier3]),
152 "currentTime", utils.FormatInt(currentTime),
153 "currentHeight", utils.FormatInt(runtime.ChainHeight()),
154 )
155}
156
157// SetWarmUp configures the duration for the warm-up tier selected by its fixed ratio.
158// Finite tiers are capped at 365 days; the final 100% tier must remain math.MaxInt64.
159// Only admin or governance can call this function.
160//
161// Parameters:
162// - _: Noncrossing implementation-call discriminator; pass 0.
163// - rlm: Current realm context forwarded unchanged by the staker proxy.
164// - pct: Warm-up tier percentage selecting one of the configured 30%, 50%, 70%, or 100% templates.
165// - timeDuration: Warm-up duration in seconds; finite tiers are capped at 365 days while the 100% tier uses the maximum int64 duration.
166func (s *stakerV1) SetWarmUp(_ int, rlm realm, pct, timeDuration int64) {
167 access.AssertIsRlmCurrent(0, rlm)
168
169 halt.AssertIsNotHaltedStaker()
170
171 previousRealm := rlm.Previous()
172 caller := previousRealm.Address()
173 access.AssertIsAdminOrGovernance(caller)
174
175 s.setWarmUp(0, rlm, pct, timeDuration)
176
177 chain.Emit(
178 "SetWarmUp",
179 "prevAddr", caller.String(),
180 "prevRealm", previousRealm.PkgPath(),
181 "pct", utils.FormatInt(pct),
182 "timeDuration", utils.FormatInt(timeDuration),
183 )
184}
185
186// setPoolTier internally sets the pool tier.
187func (s *stakerV1) setPoolTier(_ int, rlm realm, poolPath string, tier uint64, currentTime int64) (int64, map[uint64]int64) {
188 s.emissionAccessor.MintAndDistributeGns(0, rlm)
189
190 pool := s.getPools().GetPoolOrNil(poolPath)
191 if pool == nil {
192 pool = sr.NewPool(poolPath, currentTime)
193 s.getPools().set(poolPath, pool)
194 }
195 poolTier := s.getPoolTier()
196 tierRewards := poolTier.changeTier(currentTime, s.getPools(), poolPath, tier)
197
198 s.updatePoolTier(0, rlm, poolTier)
199
200 return poolTier.currentEmission, tierRewards
201}
202
203// changePoolTier internally changes the pool tier and returns old and new tiers.
204func (s *stakerV1) changePoolTier(_ int, rlm realm, poolPath string, tier uint64, currentTime int64) (uint64, uint64, int64, map[uint64]int64) {
205 s.emissionAccessor.MintAndDistributeGns(0, rlm)
206 poolTier := s.getPoolTier()
207 previousTier := poolTier.CurrentTier(poolPath)
208
209 tierRewards := poolTier.changeTier(currentTime, s.getPools(), poolPath, tier)
210
211 s.updatePoolTier(0, rlm, poolTier)
212
213 return previousTier, tier, poolTier.currentEmission, tierRewards
214}
215
216// removePoolTier internally removes the pool from tier system.
217func (s *stakerV1) removePoolTier(_ int, rlm realm, poolPath string, currentTime int64) (int64, map[uint64]int64) {
218 s.emissionAccessor.MintAndDistributeGns(0, rlm)
219
220 poolTier := s.getPoolTier()
221 tierRewards := poolTier.changeTier(currentTime, s.getPools(), poolPath, NOT_EMISSION_TARGET_TIER)
222 s.updatePoolTier(0, rlm, poolTier)
223
224 return poolTier.currentEmission, tierRewards
225}
226
227// setWarmUp internally sets the warm-up parameters.
228func (s *stakerV1) setWarmUp(_ int, rlm realm, pct, timeDuration int64) {
229 s.emissionAccessor.MintAndDistributeGns(0, rlm)
230
231 warmupTemplate := s.store.GetWarmupTemplate()
232 warmupTemplate = modifyWarmup(warmupTemplate, pctToIndex(pct), timeDuration)
233
234 err := s.store.SetWarmupTemplate(0, rlm, warmupTemplate)
235 if err != nil {
236 panic(err)
237 }
238}
239
240// pctToIndex converts percentage to warmup index.
241func pctToIndex(pct int64) int {
242 switch pct {
243 case 30:
244 return 0
245 case 50:
246 return 1
247 case 70:
248 return 2
249 case 100:
250 return 3
251 default:
252 panic("staker.gno__pctToIndex() || pct is not valid")
253 }
254}