Kind Realm (renderable)
Name v1
Namespace gnoswap / protocol_fee
Exported functions 2
Module gno.land/r/gnoswap/protocol_fee/v1
gno 0.9 protocol_fee.gno gno
⧉
1 package protocol_fee 2 3 import ( 4 "chain" 5 "errors" 6 "strconv" 7 8 NewProtocolFeeStateBy (protocolFeeStore interface {AddAccrualBucket func(int, .uverse.realm, string, int64, int64) .uverse.error; AddAccrualPendingToken func(int, .uverse.realm, string) .uverse.error; AddReservedToken func(int, .uverse.realm, string) .uverse.error; GetAccrualBuckets func(string, int) ([]int64, []int64); GetAccrualEpoch func() int64; GetAccrualPendingTokens func() []string; GetAccuToDevOps func() *gno.land/p/nt/bptree/v0.BPTree; GetAccuToDevOpsItem func(string) (int64, bool); GetAccuToGovStaker func() *gno.land/p/nt/bptree/v0.BPTree; GetAccuToGovStakerItem func(string) (int64, bool); GetDevOpsPct func() int64; GetDistributedToDevOpsHistory func() *gno.land/p/nt/bptree/v0.BPTree; GetDistributedToDevOpsHistoryItem func(string) (int64, bool); GetDistributedToGovStakerHistory func() *gno.land/p/nt/bptree/v0.BPTree; GetDistributedToGovStakerHistoryItem func(string) (int64, bool); GetReservedTokens func() []string; HasAccrualBucketsStoreKey func() bool; HasAccrualEpochStoreKey func() bool; HasAccrualPendingTokensStoreKey func() bool; HasAccuToDevOpsStoreKey func() bool; HasAccuToGovStakerStoreKey func() bool; HasDevOpsPctStoreKey func() bool; HasDistributedToDevOpsHistoryStoreKey func() bool; HasDistributedToGovStakerHistoryStoreKey func() bool; HasReservedToken func(string) bool; HasReservedTokensStoreKey func() bool; InitializeAccrualBuckets func(int, .uverse.realm) .uverse.error; InitializeAccrualEpoch func(int, .uverse.realm) .uverse.error; InitializeAccrualPendingTokens func(int, .uverse.realm) .uverse.error; InitializeAccuToDevOps func(int, .uverse.realm) .uverse.error; InitializeAccuToGovStaker func(int, .uverse.realm) .uverse.error; InitializeDevOpsPct func(int, .uverse.realm) .uverse.error; InitializeDistributedToDevOpsHistory func(int, .uverse.realm) .uverse.error; InitializeDistributedToGovStakerHistory func(int, .uverse.realm) .uverse.error; InitializeReservedTokens func(int, .uverse.realm) .uverse.error; RemoveAccrualBuckets func(int, .uverse.realm, string, []int64) .uverse.error; RemoveAccrualPendingToken func(int, .uverse.realm, string) .uverse.error; RemoveReservedToken func(int, .uverse.realm, string) .uverse.error; SetAccrualEpoch func(int, .uverse.realm, int64) .uverse.error; SetAccuToDevOpsItem func(int, .uverse.realm, string, int64) .uverse.error; SetAccuToGovStakerItem func(int, .uverse.realm, string, int64) .uverse.error; SetDevOpsPct func(int, .uverse.realm, int64) .uverse.error; SetDistributedToDevOpsHistoryItem func(int, .uverse.realm, string, int64) .uverse.error; SetDistributedToGovStakerHistoryItem func(int, .uverse.realm, string, int64) .uverse.error}) *gno.land/r/gnoswap/protocol_fee/v1.protocolFeeState
NewProtocolFeeV1 (protocolFeeStore interface {AddAccrualBucket func(int, .uverse.realm, string, int64, int64) .uverse.error; AddAccrualPendingToken func(int, .uverse.realm, string) .uverse.error; AddReservedToken func(int, .uverse.realm, string) .uverse.error; GetAccrualBuckets func(string, int) ([]int64, []int64); GetAccrualEpoch func() int64; GetAccrualPendingTokens func() []string; GetAccuToDevOps func() *gno.land/p/nt/bptree/v0.BPTree; GetAccuToDevOpsItem func(string) (int64, bool); GetAccuToGovStaker func() *gno.land/p/nt/bptree/v0.BPTree; GetAccuToGovStakerItem func(string) (int64, bool); GetDevOpsPct func() int64; GetDistributedToDevOpsHistory func() *gno.land/p/nt/bptree/v0.BPTree; GetDistributedToDevOpsHistoryItem func(string) (int64, bool); GetDistributedToGovStakerHistory func() *gno.land/p/nt/bptree/v0.BPTree; GetDistributedToGovStakerHistoryItem func(string) (int64, bool); GetReservedTokens func() []string; HasAccrualBucketsStoreKey func() bool; HasAccrualEpochStoreKey func() bool; HasAccrualPendingTokensStoreKey func() bool; HasAccuToDevOpsStoreKey func() bool; HasAccuToGovStakerStoreKey func() bool; HasDevOpsPctStoreKey func() bool; HasDistributedToDevOpsHistoryStoreKey func() bool; HasDistributedToGovStakerHistoryStoreKey func() bool; HasReservedToken func(string) bool; HasReservedTokensStoreKey func() bool; InitializeAccrualBuckets func(int, .uverse.realm) .uverse.error; InitializeAccrualEpoch func(int, .uverse.realm) .uverse.error; InitializeAccrualPendingTokens func(int, .uverse.realm) .uverse.error; InitializeAccuToDevOps func(int, .uverse.realm) .uverse.error; InitializeAccuToGovStaker func(int, .uverse.realm) .uverse.error; InitializeDevOpsPct func(int, .uverse.realm) .uverse.error; InitializeDistributedToDevOpsHistory func(int, .uverse.realm) .uverse.error; InitializeDistributedToGovStakerHistory func(int, .uverse.realm) .uverse.error; InitializeReservedTokens func(int, .uverse.realm) .uverse.error; RemoveAccrualBuckets func(int, .uverse.realm, string, []int64) .uverse.error; RemoveAccrualPendingToken func(int, .uverse.realm, string) .uverse.error; RemoveReservedToken func(int, .uverse.realm, string) .uverse.error; SetAccrualEpoch func(int, .uverse.realm, int64) .uverse.error; SetAccuToDevOpsItem func(int, .uverse.realm, string, int64) .uverse.error; SetAccuToGovStakerItem func(int, .uverse.realm, string, int64) .uverse.error; SetDevOpsPct func(int, .uverse.realm, int64) .uverse.error; SetDistributedToDevOpsHistoryItem func(int, .uverse.realm, string, int64) .uverse.error; SetDistributedToGovStakerHistoryItem func(int, .uverse.realm, string, int64) .uverse.error}) interface {AddToProtocolFee func(int, .uverse.realm, string, int64) .uverse.error; AdvanceAccrualEpoch func(int, .uverse.realm) int64; ConsumeAccrualBuckets func(int, .uverse.realm, string, int) ([]int64, []int64); DistributeProtocolFee func(int, .uverse.realm); DistributeProtocolFeeByTokenPath func(int, .uverse.realm, string); GetAccrualBuckets func(string, int) ([]int64, []int64); GetAccrualEpoch func() int64; GetAccrualPendingTokens func() []string; GetAccuTransferToDevOpsByTokenPath func(string) int64; GetAccuTransferToGovStakerByTokenPath func(string) int64; GetAccuTransfersToDevOps func() map[string]int64; GetAccuTransfersToGovStaker func() map[string]int64; GetActualDistributedToDevOps func() map[string]int64; GetActualDistributedToDevOpsByTokenPath func(string) int64; GetActualDistributedToGovStaker func() map[string]int64; GetActualDistributedToGovStakerByTokenPath func(string) int64; GetDevOpsPct func() int64; GetGovStakerPct func() int64; GetReservedTokens func() []string; Render func(string) string; SetDevOpsPct func(int, .uverse.realm, int64); SetGovStakerPct func(int, .uverse.realm, int64)}
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
This realm does not provide renderable output (vm/qrender → NoRenderDeclError).
View on gnoweb ↗ vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
ufmt "gno.land/p/nt/ufmt/v0"
9
10 gnsmath "gno.land/p/gnoswap/gnsmath/v1"
11 prabc "gno.land/p/gnoswap/rbac/v1"
12 "gno.land/r/gnoswap/access/v1"
13 "gno.land/r/gnoswap/common"
14 "gno.land/r/gnoswap/halt/v1"
15 )
16
17 // DistributeProtocolFee distributes collected protocol fees.
18 //
19 // Splits fees between devOps and gov/staker based on configured percentages.
20 // This function processes all accumulated fees since last distribution.
21 //
22 // Only callable by admin or gov/staker contract.
23 // Note: Default split is 0% devOps, 100% gov/staker.
24 //
25 // Parameters:
26 // - _: cross-call discriminator; callers pass 0
27 // - rlm: propagated protocol_fee realm context; it must match the current crossing frame
28 func (pf *protocolFeeV1) DistributeProtocolFee(_ int , rlm realm) {
29 access.AssertIsRlmCurrent( 0 , rlm)
30
31 prev := rlm.Previous()
32 assertIsAdminOrGovStaker(prev.Address())
33
34 if halt.IsHaltedWithdraw() {
35 return
36 }
37
38 protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
39 pfs := pf.getProtocolFeeState()
40
41 reservedTokens := pfs.ReservedTokens()
42
43 for _, token := range reservedTokens {
44 pf.distributeProtocolFeeForToken(
45 0 ,
46 rlm,
47 pfs,
48 protocolFeeAddr,
49 token,
50 )
51 }
52 }
53
54 // DistributeProtocolFeeByTokenPath distributes collected protocol fees for one token path.
55 //
56 // Parameters:
57 // - _: cross-call discriminator; callers pass 0
58 // - rlm: propagated protocol_fee realm context; it must match the current crossing frame
59 // - tokenPath: token contract path whose reserved fee is eligible for distribution
60 func (pf *protocolFeeV1) DistributeProtocolFeeByTokenPath(_ int , rlm realm, tokenPath string ) {
61 access.AssertIsRlmCurrent( 0 , rlm)
62
63 prev := rlm.Previous()
64 assertIsAdminOrGovStaker(prev.Address())
65
66 if halt.IsHaltedWithdraw() {
67 return
68 }
69
70 protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
71 pfs := pf.getProtocolFeeState()
72
73 if !pfs.store.HasReservedToken(tokenPath) {
74 return
75 }
76
77 pf.distributeProtocolFeeForToken(
78 0 ,
79 rlm,
80 pfs,
81 protocolFeeAddr,
82 tokenPath,
83 )
84 }
85
86 // distributeProtocolFeeForToken validates and distributes one reserved token's pending fees.
87 func (pf *protocolFeeV1) distributeProtocolFeeForToken(_ int , rlm realm, pfs *protocolFeeState, protocolFeeAddr address, tokenPath string ) {
88 toDevOpsAmount := gnsmath.SafeSubInt64(
89 pfs.GetAccuTransferToDevOpsByTokenPath(tokenPath),
90 pfs.GetActualDistributedToDevOpsByTokenPath(tokenPath),
91 )
92 toGovStakerAmount := gnsmath.SafeSubInt64(
93 pfs.GetAccuTransferToGovStakerByTokenPath(tokenPath),
94 pfs.GetActualDistributedToGovStakerByTokenPath(tokenPath),
95 )
96
97 amount := gnsmath.SafeAddInt64(toDevOpsAmount, toGovStakerAmount)
98 balance := common.BalanceOf(tokenPath, protocolFeeAddr)
99
100 // amount should be less than or equal to balance
101 if amount > balance {
102 panic(makeErrorWithDetail(
103 errInvalidAmount,
104 ufmt.Sprintf( "amount: %d should be less than or equal to balance: %d" , amount, balance),
105 ))
106 }
107
108 if err := pfs.removeReservedToken( 0 , rlm, tokenPath); err != nil {
109 panic(err)
110 }
111
112 if amount <= 0 {
113 return
114 }
115
116 // distributeToDevOps and distributeToGovStaker record history before transferring.
117 if err := pfs.distributeToDevOps( 0 , rlm, tokenPath, toDevOpsAmount); err != nil {
118 panic(err)
119 }
120 if err := pfs.distributeToGovStaker( 0 , rlm, tokenPath, toGovStakerAmount); err != nil {
121 panic(err)
122 }
123
124 prev := rlm.Previous()
125
126 chain.Emit(
127 "TransferProtocolFee" ,
128 "prevAddr" , prev.Address().String(),
129 "prevRealm" , prev.PkgPath(),
130 "tokenPath" , tokenPath,
131 "toDevOpsAmount" , strconv.FormatInt(toDevOpsAmount, 10 ),
132 "toGovStakerAmount" , strconv.FormatInt(toGovStakerAmount, 10 ),
133 "amount" , strconv.FormatInt(amount, 10 ),
134 )
135 }
136
137 // SetDevOpsPct sets the devOpsPct.
138 //
139 // Parameters:
140 // - _: cross-call discriminator; callers pass 0
141 // - rlm: propagated protocol_fee realm context; it must match the current crossing frame
142 // - pct: percentage for devOps (0-10000, where 10000 = 100%)
143 //
144 // Only callable by admin or governance.
145 // Note: GovStaker percentage is automatically adjusted to (10000 - devOpsPct).
146 func (pf *protocolFeeV1) SetDevOpsPct(_ int , rlm realm, pct int64 ) {
147 access.AssertIsRlmCurrent( 0 , rlm)
148
149 halt.AssertIsNotHaltedProtocolFee()
150
151 prev := rlm.Previous()
152 access.AssertIsAdminOrGovernance(prev.Address())
153
154 assertIsValidPercent(pct)
155
156 prevDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
157 prevGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
158
159 newDevOpsPct, err := pf.getProtocolFeeState().setDevOpsPct( 0 , rlm, pct)
160 if err != nil {
161 panic(err)
162 }
163 newGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
164
165 chain.Emit(
166 "SetDevOpsPct" ,
167 "prevAddr" , prev.Address().String(),
168 "prevRealm" , prev.PkgPath(),
169 "newDevOpsPct" , strconv.FormatInt(newDevOpsPct, 10 ),
170 "prevDevOpsPct" , strconv.FormatInt(prevDevOpsPct, 10 ),
171 "newGovStakerPct" , strconv.FormatInt(newGovStakerPct, 10 ),
172 "prevGovStakerPct" , strconv.FormatInt(prevGovStakerPct, 10 ),
173 )
174 }
175
176 // SetGovStakerPct sets the stakerPct.
177 //
178 // Parameters:
179 // - _: cross-call discriminator; callers pass 0
180 // - rlm: propagated protocol_fee realm context; it must match the current crossing frame
181 // - pct: percentage for gov/staker (0-10000, where 10000 = 100%)
182 //
183 // Only callable by admin or governance.
184 // Note: DevOps percentage is automatically adjusted to (10000 - govStakerPct).
185 func (pf *protocolFeeV1) SetGovStakerPct(_ int , rlm realm, pct int64 ) {
186 access.AssertIsRlmCurrent( 0 , rlm)
187
188 halt.AssertIsNotHaltedProtocolFee()
189
190 prev := rlm.Previous()
191 access.AssertIsAdminOrGovernance(prev.Address())
192
193 assertIsValidPercent(pct)
194
195 prevDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
196 prevGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
197
198 newGovStakerPct, err := pf.getProtocolFeeState().setGovStakerPct( 0 , rlm, pct)
199 if err != nil {
200 panic(err)
201 }
202 newDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
203
204 chain.Emit(
205 "SetGovStakerPct" ,
206 "prevAddr" , prev.Address().String(),
207 "prevRealm" , prev.PkgPath(),
208 "newDevOpsPct" , strconv.FormatInt(newDevOpsPct, 10 ),
209 "prevDevOpsPct" , strconv.FormatInt(prevDevOpsPct, 10 ),
210 "newGovStakerPct" , strconv.FormatInt(newGovStakerPct, 10 ),
211 "prevGovStakerPct" , strconv.FormatInt(prevGovStakerPct, 10 ),
212 )
213 }
214
215 // AddToProtocolFee pulls the approved amount into protocol fee accounting.
216 //
217 // Parameters:
218 // - _: cross-call discriminator; callers pass 0
219 // - rlm: propagated protocol_fee realm context; a non-current value returns errSpoofedRealm
220 // - tokenPath: token contract path from which the approved fee is pulled
221 // - amount: non-negative fee amount in token base units; zero returns nil without transferring
222 //
223 // Returns:
224 // - err: nil on success; errSpoofedRealm for a spoofed-realm call; errProtocolFeeHalted when protocol-fee collection is halted
225 //
226 // Only callable by pool, position, router or staker contracts.
227 // Caller must approve the protocol fee realm for at least amount before calling.
228 // Note: Accumulated fees are distributed when DistributeProtocolFee is called.
229 func (pf *protocolFeeV1) AddToProtocolFee(_ int , rlm realm, tokenPath string , amount int64 ) error {
230 if !rlm.IsCurrent() {
231 return errors.New(errSpoofedRealm)
232 }
233
234 if halt.IsHaltedProtocolFee() {
235 return errors.New(errProtocolFeeHalted)
236 }
237
238 prev := rlm.Previous()
239 caller := prev.Address()
240 assertIsPoolOrPositionOrRouterOrStaker(caller)
241
242 if amount < 0 {
243 panic(makeErrorWithDetail(
244 errInvalidAmount,
245 ufmt.Sprintf( "amount(%d) should not be negative" , amount),
246 ))
247 }
248
249 if amount == 0 {
250 return nil
251 }
252
253 pf.reserveCollectedProtocolFee( 0 , rlm, tokenPath, amount)
254 protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
255 common.SafeGRC20TransferFrom( 0 , rlm, tokenPath, caller, protocolFeeAddr, amount)
256
257 chain.Emit(
258 "AddToProtocolFee" ,
259 "prevAddr" , caller.String(),
260 "prevRealm" , prev.PkgPath(),
261 "tokenPath" , tokenPath,
262 "amount" , strconv.FormatInt(amount, 10 ),
263 )
264
265 return nil
266 }
267
268 func (pf *protocolFeeV1) reserveCollectedProtocolFee(_ int , rlm realm, tokenPath string , amount int64 ) {
269 pfs := pf.getProtocolFeeState()
270
271 toDevOpsAmount := gnsmath.SafeMulDivInt64(amount, pfs.DevOpsPct(), 10000 )
272 toGovStakerAmount := gnsmath.SafeSubInt64(amount, toDevOpsAmount)
273
274 if err := pfs.addAccuToDevOps( 0 , rlm, tokenPath, toDevOpsAmount); err != nil {
275 panic(err)
276 }
277 if err := pfs.addAccuToGovStaker( 0 , rlm, tokenPath, toGovStakerAmount); err != nil {
278 panic(err)
279 }
280
281 if err := pfs.store.AddReservedToken( 0 , rlm, tokenPath); err != nil {
282 panic(err)
283 }
284
285 // Bucket the gov/staker share under the accrual epoch in force so that gov/staker
286 // can later fold it against the stake distribution that was live when it arrived.
287 // A split that leaves gov/staker nothing moves no accumulator, so it is not bucketed.
288 if toGovStakerAmount > 0 {
289 epoch := pfs.store.GetAccrualEpoch()
290 if err := pfs.store.AddAccrualBucket( 0 , rlm, tokenPath, epoch, toGovStakerAmount); err != nil {
291 panic(err)
292 }
293 if err := pfs.store.AddAccrualPendingToken( 0 , rlm, tokenPath); err != nil {
294 panic(err)
295 }
296 }
297 }
298
299 // AdvanceAccrualEpoch closes the accrual epoch in force and returns the new one.
300 //
301 // gov/staker calls it on every stake change, before it records the new stake
302 // distribution, so every fee bucketed from now on is attributed to that distribution.
303 // Restricted to gov/staker: an epoch advanced by anyone else would fragment the buckets
304 // without a matching stake record on the gov/staker side.
305 //
306 // Parameters:
307 // - _: cross-call discriminator; callers pass 0
308 // - rlm: propagated protocol_fee realm context; it must be current and be called by GovStaker
309 //
310 // Returns:
311 // - epoch: newly stored non-negative accrual epoch for fees collected after the stake change
312 func (pf *protocolFeeV1) AdvanceAccrualEpoch(_ int , rlm realm) int64 {
313 if !rlm.IsCurrent() {
314 panic(errors.New(errSpoofedRealm))
315 }
316
317 prev := rlm.Previous()
318 access.AssertIsGovStaker(prev.Address())
319
320 pfs := pf.getProtocolFeeState()
321 epoch := gnsmath.SafeAddInt64(pfs.store.GetAccrualEpoch(), 1 )
322 if err := pfs.store.SetAccrualEpoch( 0 , rlm, epoch); err != nil {
323 panic(err)
324 }
325
326 return epoch
327 }
328
329 // ConsumeAccrualBuckets returns and clears up to limit of the oldest pending buckets of
330 // tokenPath, as parallel epoch and amount slices in epoch order. A limit of zero or
331 // less consumes every pending bucket. Once no bucket remains the token leaves the
332 // pending set.
333 //
334 // Restricted to gov/staker: clearing a bucket without folding it into an accumulator
335 // would strand that fee share.
336 //
337 // Parameters:
338 // - _: cross-call discriminator; callers pass 0
339 // - rlm: propagated protocol_fee realm context; it must be current and be called by GovStaker
340 // - tokenPath: token contract path whose pending accrual buckets are consumed
341 // - limit: maximum number of oldest buckets to consume; zero or negative consumes all pending buckets
342 //
343 // Returns:
344 // - epochs: consumed accrual epoch numbers in ascending epoch order
345 // - amounts: fee amounts corresponding positionally to epochs, in token base units
346 func (pf *protocolFeeV1) ConsumeAccrualBuckets(_ int , rlm realm, tokenPath string , limit int ) ([] int64 , [] int64 ) {
347 if !rlm.IsCurrent() {
348 panic(errors.New(errSpoofedRealm))
349 }
350
351 prev := rlm.Previous()
352 access.AssertIsGovStaker(prev.Address())
353
354 pfs := pf.getProtocolFeeState()
355 epochs, amounts := pfs.store.GetAccrualBuckets(tokenPath, limit)
356 if len(epochs) == 0 {
357 return epochs, amounts
358 }
359
360 if err := pfs.store.RemoveAccrualBuckets( 0 , rlm, tokenPath, epochs); err != nil {
361 panic(err)
362 }
363
364 remainingEpochs, _ := pfs.store.GetAccrualBuckets(tokenPath, 1 )
365 if len(remainingEpochs) == 0 {
366 if err := pfs.store.RemoveAccrualPendingToken( 0 , rlm, tokenPath); err != nil {
367 panic(err)
368 }
369 }
370
371 return epochs, amounts
372 }
373