pool.gno
15.32 Kb · 460 lines
1package pool
2
3import (
4 "time"
5
6 bptree "gno.land/p/nt/bptree/v0"
7 ufmt "gno.land/p/nt/ufmt/v0"
8
9 u256 "gno.land/p/gnoswap/uint256"
10)
11
12// type Pool describes a single Pool's state
13// A pool is identificed with a unique key (token0, token1, fee), where token0 < token1
14type Pool struct {
15 // token0/token1 path of the pool
16 token0Path string
17 token1Path string
18 fee uint32 // fee tier of the pool
19 tickSpacing int32 // spacing between ticks
20 slot0 Slot0
21 balances TokenPair // balances of the pool
22 protocolFees TokenPair
23 feeGrowthGlobal0X128 *u256.Uint // uint256
24 feeGrowthGlobal1X128 *u256.Uint // uint256
25 liquidity *u256.Uint // total amount of active liquidity in the pool (within current tick range)
26 ticks *bptree.BPTree // tick(int32) -> TickInfo
27 tickBitmaps map[int16]string // tick(wordPos)(int16) -> bitMap(tickWord ^ mask)(string)
28 positions *bptree.BPTree // maps the key (caller, lower tick, upper tick) to a unique position
29
30 observationState *ObservationState // oracle state with historical observations
31}
32
33// Pool Getters methods
34func (p *Pool) PoolPath() string { return GetPoolPath(p.token0Path, p.token1Path, p.fee) }
35func (p *Pool) Token0Path() string { return p.token0Path }
36func (p *Pool) Token1Path() string { return p.token1Path }
37func (p *Pool) Fee() uint32 { return p.fee }
38func (p *Pool) Balances() TokenPair { return p.balances }
39func (p *Pool) BalanceToken0() int64 { return p.balances.token0 }
40func (p *Pool) BalanceToken1() int64 { return p.balances.token1 }
41func (p *Pool) TickSpacing() int32 { return p.tickSpacing }
42func (p *Pool) Slot0() Slot0 { return p.slot0 }
43func (p *Pool) Slot0SqrtPriceX96() *u256.Uint { return p.slot0.sqrtPriceX96 }
44func (p *Pool) Slot0Tick() int32 { return p.slot0.tick }
45func (p *Pool) Slot0FeeProtocol() uint8 { return p.slot0.feeProtocol }
46func (p *Pool) Slot0Unlocked() bool { return p.slot0.unlocked }
47func (p *Pool) FeeGrowthGlobal0X128() *u256.Uint { return p.feeGrowthGlobal0X128 }
48func (p *Pool) FeeGrowthGlobal1X128() *u256.Uint { return p.feeGrowthGlobal1X128 }
49func (p *Pool) ProtocolFees() TokenPair { return p.protocolFees }
50func (p *Pool) ProtocolFeesToken0() int64 { return p.protocolFees.token0 }
51func (p *Pool) ProtocolFeesToken1() int64 { return p.protocolFees.token1 }
52func (p *Pool) Liquidity() *u256.Uint { return p.liquidity }
53func (p *Pool) Ticks() *bptree.BPTree { return p.ticks }
54func (p *Pool) TickBitmaps() map[int16]string { return p.tickBitmaps }
55func (p *Pool) Positions() *bptree.BPTree { return p.positions }
56func (p *Pool) ObservationState() *ObservationState { return p.observationState }
57
58// Pool Setters methods
59func (p *Pool) SetToken0Path(token0Path string) {
60 p.token0Path = token0Path
61}
62
63func (p *Pool) SetToken1Path(token1Path string) {
64 p.token1Path = token1Path
65}
66
67func (p *Pool) SetFee(fee uint32) {
68 p.fee = fee
69}
70
71func (p *Pool) SetBalances(balances TokenPair) {
72 p.balances = balances
73}
74
75func (p *Pool) SetBalanceToken0(token0 int64) {
76 p.balances.token0 = token0
77}
78
79func (p *Pool) SetBalanceToken1(token1 int64) {
80 p.balances.token1 = token1
81}
82
83func (p *Pool) SetTickSpacing(tickSpacing int32) {
84 p.tickSpacing = tickSpacing
85}
86
87func (p *Pool) SetSlot0(slot0 Slot0) {
88 p.slot0 = slot0
89}
90
91func (p *Pool) SetFeeGrowthGlobal0X128(feeGrowthGlobal0X128 *u256.Uint) {
92 p.feeGrowthGlobal0X128 = u256.Zero().Set(feeGrowthGlobal0X128)
93}
94
95func (p *Pool) SetFeeGrowthGlobal1X128(feeGrowthGlobal1X128 *u256.Uint) {
96 p.feeGrowthGlobal1X128 = u256.Zero().Set(feeGrowthGlobal1X128)
97}
98
99func (p *Pool) SetProtocolFees(protocolFees TokenPair) {
100 p.protocolFees = protocolFees
101}
102
103func (p *Pool) SetProtocolFeesToken0(token0 int64) {
104 p.protocolFees.token0 = token0
105}
106
107func (p *Pool) SetProtocolFeesToken1(token1 int64) {
108 p.protocolFees.token1 = token1
109}
110
111func (p *Pool) SetLiquidity(liquidity *u256.Uint) {
112 p.liquidity = u256.Zero().Set(liquidity)
113}
114
115func (p *Pool) SetTicks(ticks *bptree.BPTree) {
116 p.ticks = ticks
117}
118
119func (p *Pool) SetTickBitmap(wordPos int16, tickBitmap string) {
120 p.tickBitmaps[wordPos] = tickBitmap
121}
122
123// DeleteTickBitmap deletes the tick bitmap for the given word position.
124func (p *Pool) DeleteTickBitmap(wordPos int16) {
125 delete(p.tickBitmaps, wordPos)
126}
127
128func (p *Pool) SetTickBitmaps(tickBitmaps map[int16]string) {
129 p.tickBitmaps = tickBitmaps
130}
131
132func (p *Pool) SetPositions(positions *bptree.BPTree) {
133 p.positions = positions
134}
135
136func (p *Pool) SetPosition(posKey string, positionInfo PositionInfo) {
137 p.positions.Set(posKey, positionInfo)
138}
139
140func (p *Pool) SetObservationState(observationState *ObservationState) {
141 p.observationState = observationState
142}
143
144func (p *Pool) HasTick(tick int32) bool {
145 tickKey := EncodeTickKey(tick)
146 return p.ticks.Has(tickKey)
147}
148
149func (p *Pool) GetTick(tick int32) (TickInfo, error) {
150 tickKey := EncodeTickKey(tick)
151
152 iTickInfo := p.ticks.Get(tickKey)
153 if iTickInfo == nil {
154 return TickInfo{}, ufmt.Errorf("tick %d not found", tick)
155 }
156
157 tickInfo, ok := iTickInfo.(TickInfo)
158 if !ok {
159 panic(ufmt.Sprintf("failed to cast tickInfo to TickInfo: %T", iTickInfo))
160 }
161
162 return tickInfo, nil
163}
164
165func (p *Pool) SetTick(tick int32, tickInfo TickInfo) {
166 tickKey := EncodeTickKey(tick)
167 p.ticks.Set(tickKey, tickInfo)
168}
169
170func (p *Pool) DeleteTick(tick int32) {
171 tickKey := EncodeTickKey(tick)
172 p.ticks.Remove(tickKey)
173}
174
175func (p *Pool) IterateTicks(startTick int32, endTick int32, fn func(tick int32, tickInfo TickInfo) bool) {
176 startTickKey := EncodeTickKey(startTick)
177 endTickKey := EncodeTickKey(endTick + 1) // endTick inclusive
178
179 p.ticks.Iterate(startTickKey, endTickKey, func(key string, value any) bool {
180 tick := DecodeTickKey(key)
181
182 tickInfo, ok := value.(TickInfo)
183 if !ok {
184 return false
185 }
186
187 return fn(tick, tickInfo)
188 })
189}
190
191func (p *Pool) Clone() *Pool {
192 return &Pool{
193 token0Path: p.token0Path,
194 token1Path: p.token1Path,
195 fee: p.fee,
196 tickSpacing: p.tickSpacing,
197 slot0: Slot0{
198 sqrtPriceX96: p.slot0.sqrtPriceX96.Clone(),
199 tick: p.slot0.tick,
200 feeProtocol: p.slot0.feeProtocol,
201 unlocked: p.slot0.unlocked,
202 },
203 balances: p.balances,
204 protocolFees: p.protocolFees,
205 feeGrowthGlobal0X128: p.feeGrowthGlobal0X128.Clone(),
206 feeGrowthGlobal1X128: p.feeGrowthGlobal1X128.Clone(),
207 liquidity: p.liquidity.Clone(),
208 ticks: clonePoolTicks(p.ticks),
209 tickBitmaps: clonePoolTickBitmaps(p.tickBitmaps),
210 positions: bptree.NewBPTreeN(16),
211 observationState: NewObservationState(time.Now().Unix()),
212 }
213}
214
215func NewPool(
216 token0Path string,
217 token1Path string,
218 fee uint32,
219 sqrtPriceX96 *u256.Uint,
220 tickSpacing int32,
221 tick int32,
222 slot0FeeProtocol uint8,
223) *Pool {
224 slot0 := NewSlot0(sqrtPriceX96, tick, slot0FeeProtocol, true)
225
226 return &Pool{
227 token0Path: token0Path,
228 token1Path: token1Path,
229 balances: NewTokenPair(),
230 fee: fee,
231 tickSpacing: tickSpacing,
232 slot0: slot0,
233 feeGrowthGlobal0X128: u256.Zero(),
234 feeGrowthGlobal1X128: u256.Zero(),
235 protocolFees: NewTokenPair(),
236 liquidity: u256.Zero(),
237 ticks: bptree.NewBPTreeN(32),
238 tickBitmaps: make(map[int16]string),
239 positions: bptree.NewBPTreeN(16),
240 observationState: NewObservationState(time.Now().Unix()),
241 }
242}
243
244func NewPoolsTree() *bptree.BPTree {
245 return bptree.NewBPTreeN(32)
246}
247
248// NewPoolTicksTree creates a BPTree for storing pool tick info (fanout 32),
249// owned by the pool domain realm so leaf-slot writes are not readonly tainted.
250func NewPoolTicksTree() *bptree.BPTree {
251 return bptree.NewBPTreeN(32)
252}
253
254// NewPoolPositionsTree creates a BPTree for storing pool position info (fanout 16),
255// owned by the pool domain realm so leaf-slot writes are not readonly tainted.
256func NewPoolPositionsTree() *bptree.BPTree {
257 return bptree.NewBPTreeN(16)
258}
259
260type TokenPair struct {
261 token0, token1 int64
262}
263
264func NewTokenPair() TokenPair {
265 return TokenPair{
266 token0: 0,
267 token1: 0,
268 }
269}
270
271func (p *TokenPair) Token0() int64 { return p.token0 }
272func (p *TokenPair) Token1() int64 { return p.token1 }
273func (p *TokenPair) SetToken0(token0 int64) { p.token0 = token0 }
274func (p *TokenPair) SetToken1(token1 int64) { p.token1 = token1 }
275
276type Slot0 struct {
277 sqrtPriceX96 *u256.Uint // current price of the pool as a sqrt(token1/token0) Q96 value
278 tick int32 // current tick of the pool, i.e according to the last tick transition that was run
279 feeProtocol uint8 // protocol fee for both tokens of the pool
280 unlocked bool // whether the pool is currently locked to reentrancy
281}
282
283func (s *Slot0) SqrtPriceX96() *u256.Uint { return s.sqrtPriceX96.Clone() }
284func (s *Slot0) Tick() int32 { return s.tick }
285func (s *Slot0) FeeProtocol() uint8 { return s.feeProtocol }
286func (s *Slot0) Unlocked() bool { return s.unlocked }
287
288func (s *Slot0) SetSqrtPriceX96(sqrtPriceX96 *u256.Uint) { s.sqrtPriceX96 = sqrtPriceX96.Clone() }
289func (s *Slot0) SetTick(tick int32) { s.tick = tick }
290func (s *Slot0) SetFeeProtocol(feeProtocol uint8) { s.feeProtocol = feeProtocol }
291func (s *Slot0) SetUnlocked(unlocked bool) { s.unlocked = unlocked }
292
293func NewSlot0(
294 sqrtPriceX96 *u256.Uint,
295 tick int32,
296 feeProtocol uint8,
297 unlocked bool,
298) Slot0 {
299 return Slot0{
300 sqrtPriceX96: sqrtPriceX96.Clone(),
301 tick: tick,
302 feeProtocol: feeProtocol,
303 unlocked: unlocked,
304 }
305}
306
307// TickInfo stores information about a specific tick in the pool.
308// TIcks represent discrete price points that can be used as boundaries for positions.
309type TickInfo struct {
310 liquidityGross string // total position liquidity that references this tick
311 liquidityNet string // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)
312
313 // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
314 // only has relative meaning, not absolute — the value depends on when the tick is initialized
315 feeGrowthOutside0X128 string
316 feeGrowthOutside1X128 string
317
318 tickCumulativeOutside int64 // cumulative tick value on the other side of the tick
319
320 // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
321 // only has relative meaning, not absolute — the value depends on when the tick is initialized
322 secondsPerLiquidityOutsideX128 string
323
324 // the seconds spent on the other side of the tick (relative to the current tick)
325 // only has relative meaning, not absolute — the value depends on when the tick is initialized
326 secondsOutside uint32
327
328 initialized bool // whether the tick is initialized
329}
330
331// TickInfo Getters methods
332func (t *TickInfo) LiquidityGross() string { return t.liquidityGross }
333func (t *TickInfo) LiquidityNet() string { return t.liquidityNet }
334func (t *TickInfo) FeeGrowthOutside0X128() string { return t.feeGrowthOutside0X128 }
335func (t *TickInfo) FeeGrowthOutside1X128() string { return t.feeGrowthOutside1X128 }
336func (t *TickInfo) SecondsPerLiquidityOutsideX128() string {
337 return t.secondsPerLiquidityOutsideX128
338}
339func (t *TickInfo) SecondsOutside() uint32 { return t.secondsOutside }
340func (t *TickInfo) Initialized() bool { return t.initialized }
341func (t *TickInfo) TickCumulativeOutside() int64 { return t.tickCumulativeOutside }
342
343// TickInfo Setters methods
344func (t *TickInfo) SetLiquidityGross(liquidityGross string) {
345 t.liquidityGross = liquidityGross
346}
347
348func (t *TickInfo) SetLiquidityNet(liquidityNet string) {
349 t.liquidityNet = liquidityNet
350}
351
352func (t *TickInfo) SetFeeGrowthOutside0X128(feeGrowthOutside0X128 string) {
353 t.feeGrowthOutside0X128 = feeGrowthOutside0X128
354}
355
356func (t *TickInfo) SetFeeGrowthOutside1X128(feeGrowthOutside1X128 string) {
357 t.feeGrowthOutside1X128 = feeGrowthOutside1X128
358}
359
360func (t *TickInfo) SetSecondsPerLiquidityOutsideX128(secondsPerLiquidityOutsideX128 string) {
361 t.secondsPerLiquidityOutsideX128 = secondsPerLiquidityOutsideX128
362}
363
364func (t *TickInfo) SetSecondsOutside(secondsOutside uint32) {
365 t.secondsOutside = secondsOutside
366}
367
368func (t *TickInfo) SetInitialized(initialized bool) {
369 t.initialized = initialized
370}
371
372func (t *TickInfo) SetTickCumulativeOutside(tickCumulativeOutside int64) {
373 t.tickCumulativeOutside = tickCumulativeOutside
374}
375
376func (t *TickInfo) Clone() TickInfo {
377 return TickInfo{
378 feeGrowthOutside0X128: t.feeGrowthOutside0X128,
379 feeGrowthOutside1X128: t.feeGrowthOutside1X128,
380 liquidityGross: t.liquidityGross,
381 liquidityNet: t.liquidityNet,
382 tickCumulativeOutside: t.tickCumulativeOutside,
383 secondsPerLiquidityOutsideX128: t.secondsPerLiquidityOutsideX128,
384 secondsOutside: t.secondsOutside,
385 initialized: t.initialized,
386 }
387}
388
389func NewTickInfo() TickInfo {
390 return TickInfo{
391 liquidityGross: "0",
392 liquidityNet: "0",
393 feeGrowthOutside0X128: "0",
394 feeGrowthOutside1X128: "0",
395 secondsPerLiquidityOutsideX128: "0",
396 secondsOutside: 0,
397 initialized: false,
398 tickCumulativeOutside: 0,
399 }
400}
401
402// PositionInfo stores liquidity and fee state for a position.
403// Liquidity and fee-growth fields are stored as decimal strings to reduce storage cost,
404// while transferable owed amounts stay as int64 for token transfer boundaries.
405type PositionInfo struct {
406 liquidity string // amount of liquidity owned by this position
407 feeGrowthInside0LastX128 string // fee growth per unit of liquidity for token0 as of last update
408 feeGrowthInside1LastX128 string // fee growth per unit of liquidity for token1 as of last update
409
410 // accumulated fees in token0 waiting to be collected
411 tokensOwed0 int64
412
413 // accumulated fees in token1 waiting to be collected
414 tokensOwed1 int64
415}
416
417func (p *PositionInfo) Liquidity() string { return p.liquidity }
418func (p *PositionInfo) FeeGrowthInside0LastX128() string { return p.feeGrowthInside0LastX128 }
419func (p *PositionInfo) FeeGrowthInside1LastX128() string { return p.feeGrowthInside1LastX128 }
420func (p *PositionInfo) TokensOwed0() int64 { return p.tokensOwed0 }
421func (p *PositionInfo) TokensOwed1() int64 { return p.tokensOwed1 }
422
423func (p *PositionInfo) SetLiquidity(liquidity string) {
424 p.liquidity = liquidity
425}
426
427func (p *PositionInfo) SetFeeGrowthInside0LastX128(feeGrowthInside0LastX128 string) {
428 p.feeGrowthInside0LastX128 = feeGrowthInside0LastX128
429}
430
431func (p *PositionInfo) SetFeeGrowthInside1LastX128(feeGrowthInside1LastX128 string) {
432 p.feeGrowthInside1LastX128 = feeGrowthInside1LastX128
433}
434
435func (p *PositionInfo) SetTokensOwed0(tokensOwed0 int64) {
436 p.tokensOwed0 = tokensOwed0
437}
438
439func (p *PositionInfo) SetTokensOwed1(tokensOwed1 int64) {
440 p.tokensOwed1 = tokensOwed1
441}
442
443func NewPositionInfo() PositionInfo {
444 return PositionInfo{
445 liquidity: "0",
446 feeGrowthInside0LastX128: "0",
447 feeGrowthInside1LastX128: "0",
448 tokensOwed0: 0,
449 tokensOwed1: 0,
450 }
451}
452
453func NewDefaultFeeAmountTickSpacing() map[uint32]int32 {
454 return map[uint32]int32{
455 100: 1,
456 500: 10,
457 3000: 60,
458 10000: 200,
459 }
460}