pool.gno
23.99 Kb ยท 832 lines
1package staker
2
3import (
4 "errors"
5 "strconv"
6 "time"
7
8 i256 "gno.land/p/gnoswap/int256"
9 u256 "gno.land/p/gnoswap/uint256"
10 "gno.land/p/nt/avl"
11 "gno.land/p/nt/ufmt"
12)
13
14const AllTierCount = 4 // 0, 1, 2, 3
15
16// Pool is a struct for storing an incentivized pool information
17// Each pool stores Incentives and Ticks associated with it.
18//
19// Fields:
20// - poolPath: The path of the pool.
21//
22// - currentStakedLiquidity:
23// The current total staked liquidity of the in-range positions for the pool.
24// Updated when tick cross happens or stake/unstake happens.
25// Used to calculate the global reward ratio accumulation or
26// decide whether to enter/exit unclaimable period.
27//
28// - lastUnclaimableTime:
29// The time at which the unclaimable period started.
30// Set to 0 when the pool is not in an unclaimable period.
31//
32// - unclaimableAcc:
33// The accumulated undisributed unclaimable reward.
34// Reset to 0 when processUnclaimableReward is called and sent to community pool.
35//
36// - rewardCache:
37// The cached per-second reward emitted for this pool.
38// Stores new entry only when the reward is changed.
39// PoolTier.cacheReward() updates this.
40//
41// - incentives: The external incentives associated with the pool.
42//
43// - ticks: The Ticks associated with the pool.
44//
45// - globalRewardRatioAccumulation:
46// Global ratio of Time / TotalStake accumulation(since the pool creation)
47// Stores new entry only when tick cross or stake/unstake happens.
48// It is used to calculate the reward for a staked position at certain time.
49//
50// - historicalTick:
51// The historical tick for the pool at a given time.
52// It does not reflect the exact tick at the timestamp,
53// but it provides correct ordering for the staked position's ticks.
54// Therefore, you should not compare it for equality, only for ordering.
55// Set when tick cross happens or a new position is created.
56type Pool struct {
57 poolPath string
58
59 stakedLiquidity *UintTree // uint64 timestamp -> *u256.Uint(Q128)
60
61 lastUnclaimableTime int64
62 unclaimableAcc int64
63
64 rewardCache *UintTree // uint64 timestamp -> int64 gnsReward
65
66 incentives *Incentives
67
68 ticks Ticks // int32 tickId -> Tick tick
69
70 globalRewardRatioAccumulation *UintTree // uint64 timestamp -> *u256.Uint(Q128) rewardRatioAccumulation
71
72 historicalTick *UintTree // uint64 timestamp -> int32 tickId
73}
74
75// Pool Getter/Setter methods
76
77// PoolPath returns the pool path
78func (p *Pool) PoolPath() string {
79 return p.poolPath
80}
81
82// SetPoolPath sets the pool path
83func (p *Pool) SetPoolPath(poolPath string) {
84 p.poolPath = poolPath
85}
86
87// StakedLiquidity returns the staked liquidity tree
88func (p *Pool) StakedLiquidity() *UintTree {
89 return p.stakedLiquidity
90}
91
92// SetStakedLiquidity sets the staked liquidity tree
93func (p *Pool) SetStakedLiquidity(stakedLiquidity *UintTree) {
94 p.stakedLiquidity = stakedLiquidity
95}
96
97// LastUnclaimableTime returns the last unclaimable time
98func (p *Pool) LastUnclaimableTime() int64 {
99 return p.lastUnclaimableTime
100}
101
102// SetLastUnclaimableTime sets the last unclaimable time
103func (p *Pool) SetLastUnclaimableTime(lastUnclaimableTime int64) {
104 p.lastUnclaimableTime = lastUnclaimableTime
105}
106
107// UnclaimableAcc returns the unclaimable accumulation
108func (p *Pool) UnclaimableAcc() int64 {
109 return p.unclaimableAcc
110}
111
112// SetUnclaimableAcc sets the unclaimable accumulation
113func (p *Pool) SetUnclaimableAcc(unclaimableAcc int64) {
114 p.unclaimableAcc = unclaimableAcc
115}
116
117// RewardCache returns the reward cache tree
118func (p *Pool) RewardCache() *UintTree {
119 return p.rewardCache
120}
121
122// SetRewardCache sets the reward cache tree
123func (p *Pool) SetRewardCache(rewardCache *UintTree) {
124 p.rewardCache = rewardCache
125}
126
127// Incentives returns the incentives
128func (p *Pool) Incentives() *Incentives {
129 return p.incentives
130}
131
132// SetIncentives sets the incentives
133func (p *Pool) SetIncentives(incentives *Incentives) {
134 p.incentives = incentives
135}
136
137// Ticks returns the ticks
138func (p *Pool) Ticks() *Ticks {
139 return &p.ticks
140}
141
142// SetTicks sets the ticks
143func (p *Pool) SetTicks(ticks Ticks) {
144 p.ticks = ticks
145}
146
147// GlobalRewardRatioAccumulation returns the global reward ratio accumulation tree
148func (p *Pool) GlobalRewardRatioAccumulation() *UintTree {
149 return p.globalRewardRatioAccumulation
150}
151
152// SetGlobalRewardRatioAccumulation sets the global reward ratio accumulation tree
153func (p *Pool) SetGlobalRewardRatioAccumulation(globalRewardRatioAccumulation *UintTree) {
154 p.globalRewardRatioAccumulation = globalRewardRatioAccumulation
155}
156
157// HistoricalTick returns the historical tick tree
158func (p *Pool) HistoricalTick() *UintTree {
159 return p.historicalTick
160}
161
162// SetHistoricalTick sets the historical tick tree
163func (p *Pool) SetHistoricalTick(historicalTick *UintTree) {
164 p.historicalTick = historicalTick
165}
166
167// Clone returns a deep copy of the pool.
168func (p *Pool) Clone() *Pool {
169 if p == nil {
170 return nil
171 }
172
173 return &Pool{
174 poolPath: p.poolPath,
175 stakedLiquidity: nil,
176 lastUnclaimableTime: p.lastUnclaimableTime,
177 unclaimableAcc: p.unclaimableAcc,
178 rewardCache: nil,
179 incentives: nil,
180 ticks: NewTicks(),
181 globalRewardRatioAccumulation: nil,
182 historicalTick: nil,
183 }
184}
185
186// NewPool creates a new pool with the given poolPath and currentHeight.
187func NewPool(poolPath string, currentTime int64) *Pool {
188 pool := &Pool{
189 poolPath: poolPath,
190 stakedLiquidity: NewUintTree(),
191 // lastUnclaimableTime is initialized to 0, which means "tracking not started yet".
192 // When the pool receives a tier assignment (or external incentive), `cacheReward` will be called,
193 // which will automatically call `startUnclaimablePeriod` if the pool has zero liquidity.
194 // This ensures proper unclaimable period tracking from the moment rewards start emitting.
195 lastUnclaimableTime: 0,
196 unclaimableAcc: 0,
197 rewardCache: NewUintTree(),
198 incentives: NewIncentives(poolPath),
199 ticks: NewTicks(),
200 globalRewardRatioAccumulation: NewUintTree(),
201 historicalTick: NewUintTree(),
202 }
203
204 pool.GlobalRewardRatioAccumulation().Set(currentTime, u256.Zero())
205
206 // Initialize rewardCache to 0 to ensure `cacheReward` will trigger on first tier assignment
207 pool.RewardCache().Set(currentTime, int64(0))
208 pool.StakedLiquidity().Set(currentTime, u256.Zero())
209
210 return pool
211}
212
213// Incentives represents a collection of external incentives for a specific pool.
214//
215// Fields:
216//
217// - incentives: AVL tree storing ExternalIncentive objects indexed by incentiveId
218// The incentiveId serves as the key to efficiently lookup incentive details
219//
220// - targetPoolPath: String identifier for the pool this incentive collection belongs to
221// Used to associate incentives with their corresponding liquidity pool
222//
223// - unclaimablePeriods: Tree storing periods when rewards cannot be claimed
224// Maps start timestamp (key) to end timestamp (value)
225// An end timestamp of 0 indicates an ongoing unclaimable period
226// Used to track intervals when staking rewards are not claimable
227type Incentives struct {
228 incentives *avl.Tree // (incentiveId) => ExternalIncentive
229
230 targetPoolPath string // The target pool path for this incentive collection
231
232 unclaimablePeriods *UintTree // blockTimestamp -> any
233}
234
235// Incentives Getter/Setter methods
236
237// Incentives returns the incentives tree
238func (i *Incentives) IncentiveTrees() *avl.Tree {
239 return i.incentives
240}
241
242// SetIncentives sets the incentives tree
243func (i *Incentives) SetIncentives(incentives *avl.Tree) {
244 i.incentives = incentives
245}
246
247// TargetPoolPath returns the target pool path
248func (i *Incentives) TargetPoolPath() string {
249 return i.targetPoolPath
250}
251
252// SetTargetPoolPath sets the target pool path
253func (i *Incentives) SetTargetPoolPath(targetPoolPath string) {
254 i.targetPoolPath = targetPoolPath
255}
256
257// UnclaimablePeriods returns the unclaimable periods tree
258func (i *Incentives) UnclaimablePeriods() *UintTree {
259 return i.unclaimablePeriods
260}
261
262// SetUnclaimablePeriods sets the unclaimable periods tree
263func (i *Incentives) SetUnclaimablePeriods(unclaimablePeriods *UintTree) {
264 i.unclaimablePeriods = unclaimablePeriods
265}
266
267// Incentive returns an incentive by ID
268func (i *Incentives) Incentive(incentiveId string) (*ExternalIncentive, bool) {
269 value, exists := i.incentives.Get(incentiveId)
270 if !exists {
271 return nil, false
272 }
273 incentive, ok := value.(*ExternalIncentive)
274 return incentive, ok
275}
276
277// SetIncentive sets an incentive by ID
278func (i *Incentives) SetIncentive(incentiveId string, incentive *ExternalIncentive) {
279 i.incentives.Set(incentiveId, incentive)
280}
281
282// IterateIncentives iterates over all incentives
283func (i *Incentives) IterateIncentives(fn func(incentiveId string, incentive *ExternalIncentive) bool) {
284 i.incentives.Iterate("", "", func(key string, value interface{}) bool {
285 if incentive, ok := value.(*ExternalIncentive); ok {
286 return fn(key, incentive)
287 }
288 return false
289 })
290}
291
292func NewIncentives(targetPoolPath string) *Incentives {
293 result := &Incentives{
294 targetPoolPath: targetPoolPath,
295 unclaimablePeriods: NewUintTree(),
296 incentives: avl.NewTree(),
297 }
298
299 // initial unclaimable period starts, as there cannot be any staked positions yet.
300 currentTimestamp := time.Now().Unix()
301 result.unclaimablePeriods.Set(currentTimestamp, int64(0))
302 return result
303}
304
305type ExternalIncentive struct {
306 incentiveId string // incentive id
307 startTimestamp int64 // start time for external reward
308 endTimestamp int64 // end time for external reward
309 createdHeight int64 // block height when the incentive was created
310 createdTimestamp int64 // timestamp when the incentive was created
311 depositGnsAmount int64 // deposited gns amount
312 targetPoolPath string // external reward target pool path
313 rewardToken string // external reward token path
314 totalRewardAmount int64 // total reward amount
315 rewardAmount int64 // to be distributed reward amount
316 rewardPerSecond int64 // reward per second
317 distributedRewardAmount int64 // distributed reward amount, when un-staked and refunded
318 refundee address // refundee address
319
320 refunded bool // whether incentive has been refunded (includes GNS deposit and unclaimed rewards)
321 isRequestUnwrap bool // whether the original deposit was native GNOT (needs unwrap on refund)
322}
323
324// ExternalIncentive Getter/Setter methods
325
326// IncentiveId returns the incentive ID
327func (e *ExternalIncentive) IncentiveId() string {
328 return e.incentiveId
329}
330
331// SetIncentiveId sets the incentive ID
332func (e *ExternalIncentive) SetIncentiveId(incentiveId string) {
333 e.incentiveId = incentiveId
334}
335
336// StartTimestamp returns the start timestamp
337func (e *ExternalIncentive) StartTimestamp() int64 {
338 return e.startTimestamp
339}
340
341// SetStartTimestamp sets the start timestamp
342func (e *ExternalIncentive) SetStartTimestamp(startTimestamp int64) {
343 e.startTimestamp = startTimestamp
344}
345
346// EndTimestamp returns the end timestamp
347func (e *ExternalIncentive) EndTimestamp() int64 {
348 return e.endTimestamp
349}
350
351// SetEndTimestamp sets the end timestamp
352func (e *ExternalIncentive) SetEndTimestamp(endTimestamp int64) {
353 e.endTimestamp = endTimestamp
354}
355
356// CreatedHeight returns the created height
357func (e *ExternalIncentive) CreatedHeight() int64 {
358 return e.createdHeight
359}
360
361// SetCreatedHeight sets the created height
362func (e *ExternalIncentive) SetCreatedHeight(createdHeight int64) {
363 e.createdHeight = createdHeight
364}
365
366// CreatedTimestamp returns the created timestamp
367func (e *ExternalIncentive) CreatedTimestamp() int64 {
368 return e.createdTimestamp
369}
370
371// SetCreatedTimestamp sets the created timestamp
372func (e *ExternalIncentive) SetCreatedTimestamp(createdTimestamp int64) {
373 e.createdTimestamp = createdTimestamp
374}
375
376// DepositGnsAmount returns the deposit GNS amount
377func (e *ExternalIncentive) DepositGnsAmount() int64 {
378 return e.depositGnsAmount
379}
380
381// SetDepositGnsAmount sets the deposit GNS amount
382func (e *ExternalIncentive) SetDepositGnsAmount(depositGnsAmount int64) {
383 e.depositGnsAmount = depositGnsAmount
384}
385
386// TargetPoolPath returns the target pool path
387func (e *ExternalIncentive) TargetPoolPath() string {
388 return e.targetPoolPath
389}
390
391// SetTargetPoolPath sets the target pool path
392func (e *ExternalIncentive) SetTargetPoolPath(targetPoolPath string) {
393 e.targetPoolPath = targetPoolPath
394}
395
396// RewardToken returns the reward token
397func (e *ExternalIncentive) RewardToken() string {
398 return e.rewardToken
399}
400
401// SetRewardToken sets the reward token
402func (e *ExternalIncentive) SetRewardToken(rewardToken string) {
403 e.rewardToken = rewardToken
404}
405
406// TotalRewardAmount returns the total reward amount
407func (e *ExternalIncentive) TotalRewardAmount() int64 {
408 return e.totalRewardAmount
409}
410
411// SetTotalRewardAmount sets the total reward amount
412func (e *ExternalIncentive) SetTotalRewardAmount(totalRewardAmount int64) {
413 e.totalRewardAmount = totalRewardAmount
414}
415
416// RewardAmount returns the reward amount
417func (e *ExternalIncentive) RewardAmount() int64 {
418 return e.rewardAmount
419}
420
421// SetRewardAmount sets the reward amount
422func (e *ExternalIncentive) SetRewardAmount(rewardAmount int64) {
423 e.rewardAmount = rewardAmount
424}
425
426// RewardPerSecond returns the reward per second
427func (e *ExternalIncentive) RewardPerSecond() int64 {
428 return e.rewardPerSecond
429}
430
431// SetRewardPerSecond sets the reward per second
432func (e *ExternalIncentive) SetRewardPerSecond(rewardPerSecond int64) {
433 e.rewardPerSecond = rewardPerSecond
434}
435
436// DistributedRewardAmount returns the distributed reward amount
437func (e *ExternalIncentive) DistributedRewardAmount() int64 {
438 return e.distributedRewardAmount
439}
440
441// SetDistributedRewardAmount sets the distributed reward amount
442func (e *ExternalIncentive) SetDistributedRewardAmount(distributedRewardAmount int64) {
443 e.distributedRewardAmount = distributedRewardAmount
444}
445
446// Refundee returns the refundee address
447func (e *ExternalIncentive) Refundee() address {
448 return e.refundee
449}
450
451// SetRefundee sets the refundee address
452func (e *ExternalIncentive) SetRefundee(refundee address) {
453 e.refundee = refundee
454}
455
456// Refunded returns the refunded status
457func (e *ExternalIncentive) Refunded() bool {
458 return e.refunded
459}
460
461// SetRefunded sets the refunded status
462func (e *ExternalIncentive) SetRefunded(refunded bool) {
463 e.refunded = refunded
464}
465
466// IsRequestUnwrap returns the request unwrap status
467func (e *ExternalIncentive) IsRequestUnwrap() bool {
468 return e.isRequestUnwrap
469}
470
471// SetIsRequestUnwrap sets the request unwrap status
472func (e *ExternalIncentive) SetIsRequestUnwrap(isRequestUnwrap bool) {
473 e.isRequestUnwrap = isRequestUnwrap
474}
475
476func (e *ExternalIncentive) Clone() *ExternalIncentive {
477 return &ExternalIncentive{
478 incentiveId: e.incentiveId,
479 startTimestamp: e.startTimestamp,
480 endTimestamp: e.endTimestamp,
481 createdHeight: e.createdHeight,
482 createdTimestamp: e.createdTimestamp,
483 depositGnsAmount: e.depositGnsAmount,
484 targetPoolPath: e.targetPoolPath,
485 rewardToken: e.rewardToken,
486 totalRewardAmount: e.totalRewardAmount,
487 rewardAmount: e.rewardAmount,
488 rewardPerSecond: e.rewardPerSecond,
489 refundee: e.refundee,
490 refunded: e.refunded,
491 distributedRewardAmount: e.distributedRewardAmount,
492 isRequestUnwrap: e.isRequestUnwrap,
493 }
494}
495
496// NewExternalIncentive creates a new external incentive
497func NewExternalIncentive(
498 incentiveId string,
499 targetPoolPath string,
500 rewardToken string,
501 rewardAmount int64,
502 startTimestamp int64, // timestamp is in unix time(seconds)
503 endTimestamp int64,
504 refundee address,
505 depositGnsAmount int64,
506 createdHeight int64,
507 currentTime int64, // current time in unix time(seconds)
508 isRequestUnwrap bool, // whether original deposit was native GNOT
509) *ExternalIncentive {
510 incentiveDuration := endTimestamp - startTimestamp
511 rewardPerSecond := rewardAmount / incentiveDuration
512
513 return &ExternalIncentive{
514 incentiveId: incentiveId,
515 targetPoolPath: targetPoolPath,
516 rewardToken: rewardToken,
517 totalRewardAmount: rewardAmount,
518 rewardAmount: rewardAmount,
519 startTimestamp: startTimestamp,
520 endTimestamp: endTimestamp,
521 rewardPerSecond: rewardPerSecond,
522 distributedRewardAmount: 0,
523 refundee: refundee,
524 createdHeight: createdHeight,
525 createdTimestamp: currentTime,
526 depositGnsAmount: depositGnsAmount,
527 refunded: false,
528 isRequestUnwrap: isRequestUnwrap,
529 }
530}
531
532// Tick mapping for each pool
533type Ticks struct {
534 tree *avl.Tree // int32 tickId -> tick
535}
536
537// Ticks Getter/Setter methods
538
539// Tree returns the ticks tree
540func (t *Ticks) Tree() *avl.Tree {
541 return t.tree
542}
543
544// SetTree sets the ticks tree
545func (t *Ticks) SetTree(tree *avl.Tree) {
546 t.tree = tree
547}
548
549func (t *Ticks) Get(tickId int32) *Tick {
550 v, ok := t.tree.Get(EncodeInt(tickId))
551 if !ok {
552 tick := &Tick{
553 id: tickId,
554 stakedLiquidityGross: u256.Zero(),
555 stakedLiquidityDelta: i256.Zero(),
556 outsideAccumulation: NewUintTree(),
557 }
558 t.tree.Set(EncodeInt(tickId), tick)
559 return tick
560 }
561
562 tick, ok := v.(*Tick)
563 if !ok {
564 panic("failed to cast value to *Tick")
565 }
566 return tick
567}
568
569func (self *Ticks) Has(tickId int32) bool {
570 return self.tree.Has(EncodeInt(tickId))
571}
572
573// SetTick sets a tick by ID
574func (t *Ticks) SetTick(tickId int32, tick *Tick) {
575 if tick.stakedLiquidityGross.IsZero() {
576 t.tree.Remove(EncodeInt(tickId))
577 return
578 }
579
580 t.tree.Set(EncodeInt(tickId), tick)
581}
582
583// IterateTicks iterates over all ticks
584func (t *Ticks) IterateTicks(fn func(tickId int32, tick *Tick) bool) {
585 t.tree.Iterate("", "", func(key string, value interface{}) bool {
586 tick, ok := value.(*Tick)
587 if !ok {
588 return false
589 }
590
591 // Convert string key back to int32
592 tickId, err := strconv.Atoi(key)
593 if err != nil {
594 return false // skip invalid keys
595 }
596
597 return fn(int32(tickId), tick)
598 })
599}
600
601// Clone returns a deep copy of ticks.
602func (t Ticks) Clone() Ticks {
603 cloned := avl.NewTree()
604 t.tree.Iterate("", "", func(key string, value any) bool {
605 tick, ok := value.(*Tick)
606 if !ok {
607 panic("failed to cast value to *Tick")
608 }
609 cloned.Set(key, tick.Clone())
610 return false
611 })
612 return Ticks{tree: cloned}
613}
614
615func NewTicks() Ticks {
616 return Ticks{
617 tree: avl.NewTree(),
618 }
619}
620
621// Tick represents the state of a specific tick in a pool.
622//
623// Fields:
624// - id (int32): The ID of the tick.
625// - stakedLiquidityGross (*u256.Uint): Total gross staked liquidity at this tick.
626// - stakedLiquidityDelta (*i256.Int): Net change in staked liquidity at this tick.
627// - outsideAccumulation (*UintTree): RewardRatioAccumulation outside the tick.
628type Tick struct {
629 id int32
630
631 // conceptually equal with Pool.liquidityGross but only for the staked positions
632 stakedLiquidityGross *u256.Uint
633
634 // conceptually equal with Pool.liquidityNet but only for the staked positions
635 stakedLiquidityDelta *i256.Int
636
637 // currentOutsideAccumulation is the accumulation of the time / TotalStake outside the tick.
638 // It is calculated by subtracting the current tick's currentOutsideAccumulation from the global reward ratio accumulation.
639 outsideAccumulation *UintTree // timestamp -> *u256.Uint
640}
641
642// Tick Getter/Setter methods
643
644// Id returns the tick ID
645func (t *Tick) Id() int32 {
646 return t.id
647}
648
649// SetId sets the tick ID
650func (t *Tick) SetId(id int32) {
651 t.id = id
652}
653
654// StakedLiquidityGross returns the staked liquidity gross
655func (t *Tick) StakedLiquidityGross() *u256.Uint {
656 return t.stakedLiquidityGross
657}
658
659// SetStakedLiquidityGross sets the staked liquidity gross
660func (t *Tick) SetStakedLiquidityGross(stakedLiquidityGross *u256.Uint) {
661 t.stakedLiquidityGross = stakedLiquidityGross
662}
663
664// StakedLiquidityDelta returns the staked liquidity delta
665func (t *Tick) StakedLiquidityDelta() *i256.Int {
666 return t.stakedLiquidityDelta
667}
668
669// SetStakedLiquidityDelta sets the staked liquidity delta
670func (t *Tick) SetStakedLiquidityDelta(stakedLiquidityDelta *i256.Int) {
671 t.stakedLiquidityDelta = stakedLiquidityDelta
672}
673
674// OutsideAccumulation returns the outside accumulation tree
675func (t *Tick) OutsideAccumulation() *UintTree {
676 return t.outsideAccumulation
677}
678
679// SetOutsideAccumulation sets the outside accumulation tree
680func (t *Tick) SetOutsideAccumulation(outsideAccumulation *UintTree) {
681 t.outsideAccumulation = outsideAccumulation
682}
683
684// Clone returns a deep copy of the tick.
685func (t *Tick) Clone() *Tick {
686 if t == nil {
687 return nil
688 }
689
690 return &Tick{
691 id: t.id,
692 stakedLiquidityGross: t.stakedLiquidityGross.Clone(),
693 stakedLiquidityDelta: t.stakedLiquidityDelta.Clone(),
694 outsideAccumulation: t.outsideAccumulation.Clone(),
695 }
696}
697
698func NewTick(tickId int32) *Tick {
699 return &Tick{
700 id: tickId,
701 stakedLiquidityGross: u256.Zero(),
702 stakedLiquidityDelta: i256.Zero(),
703 outsideAccumulation: NewUintTree(),
704 }
705}
706
707// 100%, 0%, 0% if no tier2 and tier3
708// 80%, 0%, 20% if no tier2
709// 70%, 30%, 0% if no tier3
710// 50%, 30%, 20% if has tier2 and tier3
711type TierRatio struct {
712 Tier1 uint64
713 Tier2 uint64
714 Tier3 uint64
715}
716
717// Get returns the ratio(scaled up by 100) for the given tier.
718func (ratio *TierRatio) Get(tier uint64) (uint64, error) {
719 switch tier {
720 case 1:
721 return ratio.Tier1, nil
722 case 2:
723 return ratio.Tier2, nil
724 case 3:
725 return ratio.Tier3, nil
726 default:
727 return 0, errors.New(ufmt.Sprintf("unsupported tier(%d)", tier))
728 }
729}
730
731// SwapBatchProcessor processes tick crosses in batch for a swap
732// This processor accumulates all tick crosses that occur during a single swap
733// and processes them together at the end, reducing redundant calculations
734// and state updates that would occur with individual tick processing
735type SwapBatchProcessor struct {
736 poolPath string // The pool path identifier for this swap
737 pool *Pool // Reference to the pool being swapped in
738 crosses []*SwapTickCross // Accumulated tick crosses during the swap
739 timestamp int64 // Timestamp when the swap started
740 isActive bool // Flag to prevent accumulation after swap ends
741}
742
743func (s *SwapBatchProcessor) PoolPath() string {
744 return s.poolPath
745}
746
747func (s *SwapBatchProcessor) SetPoolPath(poolPath string) {
748 s.poolPath = poolPath
749}
750
751func (s *SwapBatchProcessor) Pool() *Pool {
752 return s.pool
753}
754
755func (s *SwapBatchProcessor) SetPool(pool *Pool) {
756 s.pool = pool
757}
758
759func (s *SwapBatchProcessor) Crosses() []*SwapTickCross {
760 return s.crosses
761}
762
763func (s *SwapBatchProcessor) SetCrosses(crosses []*SwapTickCross) {
764 s.crosses = crosses
765}
766
767func (s *SwapBatchProcessor) Timestamp() int64 {
768 return s.timestamp
769}
770
771func (s *SwapBatchProcessor) SetTimestamp(timestamp int64) {
772 s.timestamp = timestamp
773}
774
775func (s *SwapBatchProcessor) IsActive() bool {
776 return s.isActive
777}
778
779func (s *SwapBatchProcessor) SetIsActive(isActive bool) {
780 s.isActive = isActive
781}
782
783func (s *SwapBatchProcessor) LastCross() *SwapTickCross {
784 if len(s.crosses) == 0 {
785 return nil
786 }
787
788 return s.crosses[len(s.crosses)-1]
789}
790
791func (s *SwapBatchProcessor) AddCross(tickCross *SwapTickCross) {
792 s.crosses = append(s.crosses, tickCross)
793}
794
795func NewSwapBatchProcessor(poolPath string, pool *Pool, timestamp int64) *SwapBatchProcessor {
796 return &SwapBatchProcessor{
797 poolPath: poolPath,
798 pool: pool,
799 crosses: make([]*SwapTickCross, 0),
800 timestamp: timestamp,
801 isActive: true,
802 }
803}
804
805// SwapTickCross stores information about a tick cross during a swap
806// This struct is used to accumulate tick cross events during a single swap transaction
807// for batch processing to optimize gas usage and computational efficiency
808type SwapTickCross struct {
809 tickID int32 // The tick index that was crossed
810 zeroForOne bool // Direction of the swap (true: token0->token1, false: token1->token0)
811 delta *i256.Int // Pre-calculated liquidity delta for this tick cross
812}
813
814func (s *SwapTickCross) TickID() int32 {
815 return s.tickID
816}
817
818func (s *SwapTickCross) ZeroForOne() bool {
819 return s.zeroForOne
820}
821
822func (s *SwapTickCross) Delta() *i256.Int {
823 return s.delta
824}
825
826func NewSwapTickCross(tickID int32, zeroForOne bool, delta *i256.Int) *SwapTickCross {
827 return &SwapTickCross{
828 tickID: tickID,
829 zeroForOne: zeroForOne,
830 delta: delta,
831 }
832}