store.gno
21.09 Kb · 606 lines
1package pool
2
3import (
4 "errors"
5
6 "gno.land/p/gnoswap/store/v1"
7 bptree "gno.land/p/nt/bptree/v0"
8 ufmt "gno.land/p/nt/ufmt/v0"
9)
10
11// StoreKey defines the keys used for storing pool data in the KV store.
12// These keys are prefixed with the domain address to ensure namespace isolation.
13type StoreKey string
14
15// Returns:
16// - keyText: the textual store key represented by s.
17func (s StoreKey) String() string {
18 return string(s)
19}
20
21const (
22 // Pool data storage keys
23 StoreKeyPools StoreKey = "pools" // Map containing all pools
24 StoreKeyObservations StoreKey = "observations" // poolPath -> observation B+tree
25 StoreKeyFeeAmountTickSpacing StoreKey = "feeAmountTickSpacing" // Fee tier to tick spacing mapping
26 StoreKeySlot0FeeProtocol StoreKey = "slot0FeeProtocol" // Protocol fee denominator(s)
27
28 // Protocol fee storage keys
29 StoreKeyPoolCreationFee StoreKey = "poolCreationFee" // Pool creation fee amount
30 StoreKeyPendingProtocolFees StoreKey = "pendingProtocolFees" // tokenPath -> amount held locally for protocol_fee
31 StoreKeyWithdrawalFeeBPS StoreKey = "withdrawalFeeBPS" // Withdrawal fee in basis points
32 StoreKeyUnlocked StoreKey = "unlocked" // Global pool reentrancy lock
33
34 // Swap hook storage keys
35 StoreKeySwapStartHook StoreKey = "swapStartHook" // Swap start hook function
36 StoreKeySwapEndHook StoreKey = "swapEndHook" // Swap end hook function
37 StoreKeyTickCrossHook StoreKey = "tickCrossHook" // Tick cross hook function
38)
39
40// poolStore implements the IPoolStore interface for pool domain storage.
41// It provides type-safe access to pool data stored in the underlying KV store.
42type poolStore struct {
43 kvStore store.KVStore
44}
45
46// Returns:
47// - exists: true when the pool collection key is present in the KV store.
48func (s *poolStore) HasPools() bool {
49 return s.kvStore.Has(StoreKeyPools.String())
50}
51
52// GetPools retrieves the map containing all pool data.
53// This is the main data structure that stores all pool instances.
54// Returns:
55// - pools: the B+tree containing all persisted pool instances; panics if the
56// KV store cannot read it or the stored value is nil.
57func (s *poolStore) GetPools() *bptree.BPTree {
58 pools, err := s.kvStore.GetBPTree(StoreKeyPools.String())
59 if err != nil {
60 panic(err)
61 }
62
63 if pools == nil {
64 panic("pools is nil")
65 }
66
67 return pools
68}
69
70// SetPools stores the map containing all pool data.
71// Parameters:
72// - _: leading realm-call discriminator; callers pass 0.
73// - rlm: propagated realm context; it must be the current realm or the method
74// returns ErrSpoofedRealm before writing.
75// - pools: non-nil B+tree containing the pool instances to persist.
76//
77// Returns:
78// - err: nil when the pool tree is stored; ErrSpoofedRealm for a non-current
79// realm, or the underlying KV-store write error.
80func (s *poolStore) SetPools(_ int, rlm realm, pools *bptree.BPTree) error {
81 if !rlm.IsCurrent() {
82 return errors.New(ErrSpoofedRealm)
83 }
84
85 if pools == nil {
86 panic("pools is nil")
87 }
88
89 return s.kvStore.Set(0, rlm, StoreKeyPools.String(), pools)
90}
91
92// Returns:
93// - exists: true when the observations collection key is present in the KV store.
94func (s *poolStore) HasObservations() bool {
95 return s.kvStore.Has(StoreKeyObservations.String())
96}
97
98// Returns:
99// - observations: the B+tree containing persisted observation trees; panics if
100// the KV store cannot read it or the stored value is nil.
101func (s *poolStore) GetObservations() *bptree.BPTree {
102 observations, err := s.kvStore.GetBPTree(StoreKeyObservations.String())
103 if err != nil {
104 panic(err)
105 }
106 if observations == nil {
107 panic("observations is nil")
108 }
109 return observations
110}
111
112// Parameters:
113// - _: leading realm-call discriminator; callers pass 0.
114// - rlm: propagated realm context; it must be the current realm or the method
115// returns ErrSpoofedRealm before writing.
116// - observations: non-nil B+tree containing the observation data to persist.
117//
118// Returns:
119// - err: nil when the observation tree is stored; ErrSpoofedRealm for a
120// non-current realm, or the underlying KV-store write error.
121func (s *poolStore) SetObservations(_ int, rlm realm, observations *bptree.BPTree) error {
122 if !rlm.IsCurrent() {
123 return errors.New(ErrSpoofedRealm)
124 }
125 if observations == nil {
126 panic("observations is nil")
127 }
128 return s.kvStore.Set(0, rlm, StoreKeyObservations.String(), observations)
129}
130
131// Returns:
132// - exists: true when the fee-tier/tick-spacing mapping key is present in the
133// KV store.
134func (s *poolStore) HasFeeAmountTickSpacing() bool {
135 return s.kvStore.Has(StoreKeyFeeAmountTickSpacing.String())
136}
137
138// GetFeeAmountTickSpacing retrieves the mapping between fee amounts and tick spacing.
139// This mapping determines the tick spacing for each supported fee tier.
140// Returns:
141// - feeAmountTickSpacing: a copy of the fee amount to tick-spacing mapping;
142// panics if the KV value cannot be read, has the wrong type, or is nil.
143func (s *poolStore) GetFeeAmountTickSpacing() map[uint32]int32 {
144 result, err := s.kvStore.Get(StoreKeyFeeAmountTickSpacing.String())
145 if err != nil {
146 panic(err)
147 }
148
149 feeAmountTickSpacing, ok := result.(map[uint32]int32)
150 if !ok {
151 panic(ufmt.Sprintf("failed to cast result to map[uint32]int32: %T", result))
152 }
153
154 if feeAmountTickSpacing == nil {
155 panic("feeAmountTickSpacing is nil")
156 }
157
158 return cloneFeeAmountTickSpacings(feeAmountTickSpacing)
159}
160
161// SetFeeAmountTickSpacing stores the mapping between fee amounts and tick spacing.
162// Parameters:
163// - _: leading realm-call discriminator; callers pass 0.
164// - rlm: propagated realm context; it must be the current realm or the method
165// returns ErrSpoofedRealm before writing.
166// - feeAmountTickSpacing: non-nil mapping from fee tiers to their required
167// int32 tick spacing values.
168//
169// Returns:
170// - err: nil when the mapping is stored; ErrSpoofedRealm for a non-current
171// realm, or the underlying KV-store write error.
172func (s *poolStore) SetFeeAmountTickSpacing(_ int, rlm realm, feeAmountTickSpacing map[uint32]int32) error {
173 if !rlm.IsCurrent() {
174 return errors.New(ErrSpoofedRealm)
175 }
176
177 if feeAmountTickSpacing == nil {
178 panic("feeAmountTickSpacing is nil")
179 }
180
181 return s.kvStore.Set(0, rlm, StoreKeyFeeAmountTickSpacing.String(), feeAmountTickSpacing)
182}
183
184// Returns:
185// - exists: true when the slot0 protocol-fee configuration key is present in
186// the KV store.
187func (s *poolStore) HasSlot0FeeProtocol() bool {
188 return s.kvStore.Has(StoreKeySlot0FeeProtocol.String())
189}
190
191// GetSlot0FeeProtocol retrieves the protocol fee denominator(s) for slot0.
192// Returns:
193// - slot0FeeProtocol: packed protocol-fee denominator configuration for
194// slot0; panics if the KV value cannot be read or has the wrong type.
195func (s *poolStore) GetSlot0FeeProtocol() uint8 {
196 result, err := s.kvStore.Get(StoreKeySlot0FeeProtocol.String())
197 if err != nil {
198 panic(err)
199 }
200
201 slot0FeeProtocol, ok := result.(uint8)
202 if !ok {
203 panic(ufmt.Sprintf("failed to cast result to uint8: %T", result))
204 }
205
206 return slot0FeeProtocol
207}
208
209// SetSlot0FeeProtocol stores the protocol fee denominator(s) for slot0.
210// Parameters:
211// - _: leading realm-call discriminator; callers pass 0.
212// - rlm: propagated realm context; it must be the current realm or the method
213// returns ErrSpoofedRealm before writing.
214// - slot0FeeProtocol: packed uint8 protocol-fee denominators for token0 and
215// token1.
216//
217// Returns:
218// - err: nil when the protocol-fee configuration is stored; ErrSpoofedRealm
219// for a non-current realm, or the underlying KV-store write error.
220func (s *poolStore) SetSlot0FeeProtocol(_ int, rlm realm, slot0FeeProtocol uint8) error {
221 if !rlm.IsCurrent() {
222 return errors.New(ErrSpoofedRealm)
223 }
224
225 return s.kvStore.Set(0, rlm, StoreKeySlot0FeeProtocol.String(), slot0FeeProtocol)
226}
227
228// Returns:
229// - exists: true when the pool-creation-fee key is present in the KV store.
230func (s *poolStore) HasPoolCreationFee() bool {
231 return s.kvStore.Has(StoreKeyPoolCreationFee.String())
232}
233
234// GetPoolCreationFee retrieves the pool creation fee amount.
235// Returns:
236// - poolCreationFee: configured pool-creation charge in the chain's smallest
237// currency unit; panics if the KV value cannot be read or has the wrong type.
238func (s *poolStore) GetPoolCreationFee() int64 {
239 result, err := s.kvStore.Get(StoreKeyPoolCreationFee.String())
240 if err != nil {
241 panic(err)
242 }
243
244 poolCreationFee, ok := result.(int64)
245 if !ok {
246 panic(ufmt.Sprintf("failed to cast result to int64: %T", result))
247 }
248
249 return poolCreationFee
250}
251
252// SetPoolCreationFee stores the pool creation fee amount.
253// Parameters:
254// - _: leading realm-call discriminator; callers pass 0.
255// - rlm: propagated realm context; it must be the current realm or the method
256// returns ErrSpoofedRealm before writing.
257// - poolCreationFee: pool-creation charge in the chain's smallest currency
258// unit.
259//
260// Returns:
261// - err: nil when the fee is stored; ErrSpoofedRealm for a non-current realm,
262// or the underlying KV-store write error.
263func (s *poolStore) SetPoolCreationFee(_ int, rlm realm, poolCreationFee int64) error {
264 if !rlm.IsCurrent() {
265 return errors.New(ErrSpoofedRealm)
266 }
267
268 return s.kvStore.Set(0, rlm, StoreKeyPoolCreationFee.String(), poolCreationFee)
269}
270
271// Returns:
272// - exists: true when the pending protocol-fees key is present in the KV store.
273func (s *poolStore) HasPendingProtocolFees() bool {
274 return s.kvStore.Has(StoreKeyPendingProtocolFees.String())
275}
276
277// Returns:
278// - pendingProtocolFees: mapping from token path to pending protocol-fee
279// amount in the chain's smallest currency unit; panics if the KV value
280// cannot be read or has the wrong type.
281func (s *poolStore) GetPendingProtocolFees() map[string]int64 {
282 result, err := s.kvStore.Get(StoreKeyPendingProtocolFees.String())
283 if err != nil {
284 panic(err)
285 }
286
287 pendingProtocolFees, ok := result.(map[string]int64)
288 if !ok {
289 panic(ufmt.Sprintf("failed to cast result to map[string]int64: %T", result))
290 }
291
292 return pendingProtocolFees
293}
294
295// Parameters:
296// - _: leading realm-call discriminator; callers pass 0.
297// - rlm: propagated realm context; it must be the current realm or the method
298// returns ErrSpoofedRealm before writing.
299// - pendingProtocolFees: token-path-to-amount mapping; amounts are in the
300// chain's smallest currency unit and are copied before storage.
301//
302// Returns:
303// - err: nil when the copied mapping is stored; ErrSpoofedRealm for a
304// non-current realm, or the underlying KV-store write error.
305func (s *poolStore) SetPendingProtocolFees(_ int, rlm realm, pendingProtocolFees map[string]int64) error {
306 if !rlm.IsCurrent() {
307 return errors.New(ErrSpoofedRealm)
308 }
309
310 // The map is copied here so it is allocated by, and therefore mutable from, this realm.
311 owned := make(map[string]int64, len(pendingProtocolFees))
312 for tokenPath, amount := range pendingProtocolFees {
313 owned[tokenPath] = amount
314 }
315
316 return s.kvStore.Set(0, rlm, StoreKeyPendingProtocolFees.String(), owned)
317}
318
319// Parameters:
320// - tokenPath: token contract path used as the pending-fee map key.
321//
322// Returns:
323// - amount: pending protocol-fee amount for tokenPath in the chain's smallest
324// currency unit, or zero when no entry exists.
325func (s *poolStore) GetPendingProtocolFee(tokenPath string) int64 {
326 return s.GetPendingProtocolFees()[tokenPath]
327}
328
329// Parameters:
330// - _: leading realm-call discriminator; callers pass 0.
331// - rlm: propagated realm context; it must be the current realm or the method
332// returns ErrSpoofedRealm before writing.
333// - tokenPath: token contract path identifying the pending-fee entry.
334// - amount: pending protocol-fee amount to assign for tokenPath, in the
335// chain's smallest currency unit.
336//
337// Returns:
338// - err: nil after the map entry is updated; ErrSpoofedRealm for a non-current
339// realm, or store.ErrWritePermissionDenied for an unauthorized code realm.
340func (s *poolStore) SetPendingProtocolFee(_ int, rlm realm, tokenPath string, amount int64) error {
341 if !rlm.IsCurrent() {
342 return errors.New(ErrSpoofedRealm)
343 }
344
345 if rlm.IsCode() && !s.kvStore.IsWriteAuthorized(rlm.Address()) {
346 return errors.New(store.ErrWritePermissionDenied)
347 }
348
349 s.GetPendingProtocolFees()[tokenPath] = amount
350
351 return nil
352}
353
354// Parameters:
355// - _: leading realm-call discriminator; callers pass 0.
356// - rlm: propagated realm context; it must be the current realm or the method
357// returns ErrSpoofedRealm before writing.
358// - tokenPath: token contract path identifying the pending-fee entry to delete.
359//
360// Returns:
361// - err: nil after the map entry is removed; ErrSpoofedRealm for a non-current
362// realm, or store.ErrWritePermissionDenied for an unauthorized code realm.
363func (s *poolStore) RemovePendingProtocolFee(_ int, rlm realm, tokenPath string) error {
364 if !rlm.IsCurrent() {
365 return errors.New(ErrSpoofedRealm)
366 }
367
368 if rlm.IsCode() && !s.kvStore.IsWriteAuthorized(rlm.Address()) {
369 return errors.New(store.ErrWritePermissionDenied)
370 }
371
372 delete(s.GetPendingProtocolFees(), tokenPath)
373
374 return nil
375}
376
377// Returns:
378// - exists: true when the withdrawal-fee key is present in the KV store.
379func (s *poolStore) HasWithdrawalFeeBPS() bool {
380 return s.kvStore.Has(StoreKeyWithdrawalFeeBPS.String())
381}
382
383// GetWithdrawalFeeBPS retrieves the withdrawal fee in basis points.
384// Returns:
385// - withdrawalFeeBPS: withdrawal fee expressed in basis points (1/100 of a
386// percent); panics if the KV value cannot be read or has the wrong type.
387func (s *poolStore) GetWithdrawalFeeBPS() uint64 {
388 result, err := s.kvStore.Get(StoreKeyWithdrawalFeeBPS.String())
389 if err != nil {
390 panic(err)
391 }
392
393 withdrawalFeeBPS, ok := result.(uint64)
394 if !ok {
395 panic(ufmt.Sprintf("failed to cast result to uint64: %T", result))
396 }
397
398 return withdrawalFeeBPS
399}
400
401// SetWithdrawalFeeBPS stores the withdrawal fee in basis points.
402// Parameters:
403// - _: leading realm-call discriminator; callers pass 0.
404// - rlm: propagated realm context; it must be the current realm or the method
405// returns ErrSpoofedRealm before writing.
406// - withdrawalFeeBPS: withdrawal fee in basis points, where 100 basis points
407// equals one percent.
408//
409// Returns:
410// - err: nil when the fee is stored; ErrSpoofedRealm for a non-current realm,
411// or the underlying KV-store write error.
412func (s *poolStore) SetWithdrawalFeeBPS(_ int, rlm realm, withdrawalFeeBPS uint64) error {
413 if !rlm.IsCurrent() {
414 return errors.New(ErrSpoofedRealm)
415 }
416
417 return s.kvStore.Set(0, rlm, StoreKeyWithdrawalFeeBPS.String(), withdrawalFeeBPS)
418}
419
420// Returns:
421// - exists: true when the global unlocked-state key is present in the KV store.
422func (s *poolStore) HasUnlocked() bool {
423 return s.kvStore.Has(StoreKeyUnlocked.String())
424}
425
426// Returns:
427// - unlocked: the persisted global reentrancy-lock state; panics if the KV
428// value cannot be read or has the wrong type.
429func (s *poolStore) GetUnlocked() bool {
430 result, err := s.kvStore.Get(StoreKeyUnlocked.String())
431 if err != nil {
432 panic(err)
433 }
434
435 unlocked, ok := result.(bool)
436 if !ok {
437 panic(ufmt.Sprintf("failed to cast result to bool: %T", result))
438 }
439
440 return unlocked
441}
442
443// Parameters:
444// - _: leading realm-call discriminator; callers pass 0.
445// - rlm: propagated realm context; it must be the current realm or the method
446// returns ErrSpoofedRealm before writing.
447// - unlocked: true when pool operations may proceed without the global lock.
448//
449// Returns:
450// - err: nil when the lock state is stored; ErrSpoofedRealm for a non-current
451// realm, or the underlying KV-store write error.
452func (s *poolStore) SetUnlocked(_ int, rlm realm, unlocked bool) error {
453 if !rlm.IsCurrent() {
454 return errors.New(ErrSpoofedRealm)
455 }
456
457 return s.kvStore.Set(0, rlm, StoreKeyUnlocked.String(), unlocked)
458}
459
460// HasSwapStartHook checks if the swap start hook is set.
461// Returns:
462// - exists: true when a swap-start hook key is present in the KV store.
463func (s *poolStore) HasSwapStartHook() bool {
464 return s.kvStore.Has(StoreKeySwapStartHook.String())
465}
466
467// GetSwapStartHook retrieves the swap start hook function.
468// Returns:
469// - swapStartHook: stored callback invoked at swap start with the current
470// realm, pool path, and block timestamp; panics if the KV value cannot be
471// read or has the wrong function type.
472func (s *poolStore) GetSwapStartHook() func(cur realm, poolPath string, timestamp int64) {
473 result, err := s.kvStore.Get(StoreKeySwapStartHook.String())
474 if err != nil {
475 panic(err)
476 }
477
478 swapStartHook, ok := result.(func(cur realm, poolPath string, timestamp int64))
479 if !ok {
480 panic(ufmt.Sprintf("failed to cast result to func(poolPath string, timestamp int64): %T", result))
481 }
482
483 return swapStartHook
484}
485
486// SetSwapStartHook stores the swap start hook function.
487// Parameters:
488// - _: leading realm-call discriminator; callers pass 0.
489// - rlm: propagated realm context; it must be the current realm or the method
490// returns ErrSpoofedRealm before writing.
491// - swapStartHook: callback receiving the current realm, pool path, and
492// block timestamp when a swap starts.
493//
494// Returns:
495// - err: nil when the callback is stored; ErrSpoofedRealm for a non-current
496// realm, or the underlying KV-store write error.
497func (s *poolStore) SetSwapStartHook(_ int, rlm realm, swapStartHook func(cur realm, poolPath string, timestamp int64)) error {
498 if !rlm.IsCurrent() {
499 return errors.New(ErrSpoofedRealm)
500 }
501
502 return s.kvStore.Set(0, rlm, StoreKeySwapStartHook.String(), swapStartHook)
503}
504
505// HasSwapEndHook checks if the swap end hook is set.
506// Returns:
507// - exists: true when a swap-end hook key is present in the KV store.
508func (s *poolStore) HasSwapEndHook() bool {
509 return s.kvStore.Has(StoreKeySwapEndHook.String())
510}
511
512// GetSwapEndHook retrieves the swap end hook function.
513// Returns:
514// - swapEndHook: stored callback receiving the current realm and pool path at
515// swap end and returning an error; panics if the KV value cannot be read or
516// has the wrong function type.
517func (s *poolStore) GetSwapEndHook() func(cur realm, poolPath string) error {
518 result, err := s.kvStore.Get(StoreKeySwapEndHook.String())
519 if err != nil {
520 panic(err)
521 }
522
523 swapEndHook, ok := result.(func(cur realm, poolPath string) error)
524 if !ok {
525 panic(ufmt.Sprintf("failed to cast result to func(poolPath string): %T", result))
526 }
527
528 return swapEndHook
529}
530
531// SetSwapEndHook stores the swap end hook function.
532// Parameters:
533// - _: leading realm-call discriminator; callers pass 0.
534// - rlm: propagated realm context; it must be the current realm or the method
535// returns ErrSpoofedRealm before writing.
536// - swapEndHook: callback receiving the current realm and pool path at swap
537// end and returning any hook error.
538//
539// Returns:
540// - err: nil when the callback is stored; ErrSpoofedRealm for a non-current
541// realm, or the underlying KV-store write error.
542func (s *poolStore) SetSwapEndHook(_ int, rlm realm, swapEndHook func(cur realm, poolPath string) error) error {
543 if !rlm.IsCurrent() {
544 return errors.New(ErrSpoofedRealm)
545 }
546
547 return s.kvStore.Set(0, rlm, StoreKeySwapEndHook.String(), swapEndHook)
548}
549
550// HasTickCrossHook checks if the tick cross hook is set.
551// Returns:
552// - exists: true when a tick-cross hook key is present in the KV store.
553func (s *poolStore) HasTickCrossHook() bool {
554 return s.kvStore.Has(StoreKeyTickCrossHook.String())
555}
556
557// GetTickCrossHook retrieves the tick cross hook function.
558// Returns:
559// - tickCrossHook: stored callback receiving the current realm, pool path,
560// crossed tick, swap direction, and block timestamp; panics if the KV value
561// cannot be read or has the wrong function type.
562func (s *poolStore) GetTickCrossHook() func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64) {
563 result, err := s.kvStore.Get(StoreKeyTickCrossHook.String())
564 if err != nil {
565 panic(err)
566 }
567
568 tickCrossHook, ok := result.(func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64))
569 if !ok {
570 panic(ufmt.Sprintf("failed to cast result to func(poolPath string, tickId int32, zeroForOne bool, timestamp int64): %T", result))
571 }
572
573 return tickCrossHook
574}
575
576// SetTickCrossHook stores the tick cross hook function.
577// Parameters:
578// - _: leading realm-call discriminator; callers pass 0.
579// - rlm: propagated realm context; it must be the current realm or the method
580// returns ErrSpoofedRealm before writing.
581// - tickCrossHook: callback receiving the current realm, pool path, crossed
582// tick ID, swap direction, and block timestamp.
583//
584// Returns:
585// - err: nil when the callback is stored; ErrSpoofedRealm for a non-current
586// realm, or the underlying KV-store write error.
587func (s *poolStore) SetTickCrossHook(_ int, rlm realm, tickCrossHook func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64)) error {
588 if !rlm.IsCurrent() {
589 return errors.New(ErrSpoofedRealm)
590 }
591
592 return s.kvStore.Set(0, rlm, StoreKeyTickCrossHook.String(), tickCrossHook)
593}
594
595// NewPoolStore creates a new pool store instance with the provided KV store.
596// This function is used by the upgrade system to create storage instances for each implementation.
597// Parameters:
598// - kvStore: KV store used to persist and retrieve pool-domain state.
599//
600// Returns:
601// - poolStore: an IPoolStore implementation backed by kvStore.
602func NewPoolStore(kvStore store.KVStore) IPoolStore {
603 return &poolStore{
604 kvStore: kvStore,
605 }
606}