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