pool.gno
15.15 Kb · 455 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
123func (p *Pool) SetTickBitmaps(tickBitmaps map[int16]string) {
124 p.tickBitmaps = tickBitmaps
125}
126
127func (p *Pool) SetPositions(positions *bptree.BPTree) {
128 p.positions = positions
129}
130
131func (p *Pool) SetPosition(posKey string, positionInfo PositionInfo) {
132 p.positions.Set(posKey, positionInfo)
133}
134
135func (p *Pool) SetObservationState(observationState *ObservationState) {
136 p.observationState = observationState
137}
138
139func (p *Pool) HasTick(tick int32) bool {
140 tickKey := EncodeTickKey(tick)
141 return p.ticks.Has(tickKey)
142}
143
144func (p *Pool) GetTick(tick int32) (TickInfo, error) {
145 tickKey := EncodeTickKey(tick)
146
147 iTickInfo, ok := p.ticks.Get(tickKey)
148 if !ok {
149 return TickInfo{}, ufmt.Errorf("tick %d not found", tick)
150 }
151
152 tickInfo, ok := iTickInfo.(TickInfo)
153 if !ok {
154 panic(ufmt.Sprintf("failed to cast tickInfo to TickInfo: %T", iTickInfo))
155 }
156
157 return tickInfo, nil
158}
159
160func (p *Pool) SetTick(tick int32, tickInfo TickInfo) {
161 tickKey := EncodeTickKey(tick)
162 p.ticks.Set(tickKey, tickInfo)
163}
164
165func (p *Pool) DeleteTick(tick int32) {
166 tickKey := EncodeTickKey(tick)
167 p.ticks.Remove(tickKey)
168}
169
170func (p *Pool) IterateTicks(startTick int32, endTick int32, fn func(tick int32, tickInfo TickInfo) bool) {
171 startTickKey := EncodeTickKey(startTick)
172 endTickKey := EncodeTickKey(endTick + 1) // endTick inclusive
173
174 p.ticks.Iterate(startTickKey, endTickKey, func(key string, value any) bool {
175 tick := DecodeTickKey(key)
176
177 tickInfo, ok := value.(TickInfo)
178 if !ok {
179 return false
180 }
181
182 return fn(tick, tickInfo)
183 })
184}
185
186func (p *Pool) Clone() *Pool {
187 return &Pool{
188 token0Path: p.token0Path,
189 token1Path: p.token1Path,
190 fee: p.fee,
191 tickSpacing: p.tickSpacing,
192 slot0: Slot0{
193 sqrtPriceX96: p.slot0.sqrtPriceX96.Clone(),
194 tick: p.slot0.tick,
195 feeProtocol: p.slot0.feeProtocol,
196 unlocked: p.slot0.unlocked,
197 },
198 balances: p.balances,
199 protocolFees: p.protocolFees,
200 feeGrowthGlobal0X128: p.feeGrowthGlobal0X128.Clone(),
201 feeGrowthGlobal1X128: p.feeGrowthGlobal1X128.Clone(),
202 liquidity: p.liquidity.Clone(),
203 ticks: clonePoolTicks(p.ticks),
204 tickBitmaps: clonePoolTickBitmaps(p.tickBitmaps),
205 positions: bptree.NewBPTreeN(16),
206 observationState: NewObservationState(time.Now().Unix()),
207 }
208}
209
210func NewPool(
211 token0Path string,
212 token1Path string,
213 fee uint32,
214 sqrtPriceX96 *u256.Uint,
215 tickSpacing int32,
216 tick int32,
217 slot0FeeProtocol uint8,
218) *Pool {
219 slot0 := NewSlot0(sqrtPriceX96, tick, slot0FeeProtocol, true)
220
221 return &Pool{
222 token0Path: token0Path,
223 token1Path: token1Path,
224 balances: NewTokenPair(),
225 fee: fee,
226 tickSpacing: tickSpacing,
227 slot0: slot0,
228 feeGrowthGlobal0X128: u256.Zero(),
229 feeGrowthGlobal1X128: u256.Zero(),
230 protocolFees: NewTokenPair(),
231 liquidity: u256.Zero(),
232 ticks: bptree.NewBPTreeN(32),
233 tickBitmaps: make(map[int16]string),
234 positions: bptree.NewBPTreeN(16),
235 observationState: NewObservationState(time.Now().Unix()),
236 }
237}
238
239func NewPoolsTree() *bptree.BPTree {
240 return bptree.NewBPTreeN(32)
241}
242
243// NewPoolTicksTree creates a BPTree for storing pool tick info (fanout 32),
244// owned by the pool domain realm so leaf-slot writes are not readonly tainted.
245func NewPoolTicksTree() *bptree.BPTree {
246 return bptree.NewBPTreeN(32)
247}
248
249// NewPoolPositionsTree creates a BPTree for storing pool position info (fanout 16),
250// owned by the pool domain realm so leaf-slot writes are not readonly tainted.
251func NewPoolPositionsTree() *bptree.BPTree {
252 return bptree.NewBPTreeN(16)
253}
254
255type TokenPair struct {
256 token0, token1 int64
257}
258
259func NewTokenPair() TokenPair {
260 return TokenPair{
261 token0: 0,
262 token1: 0,
263 }
264}
265
266func (p *TokenPair) Token0() int64 { return p.token0 }
267func (p *TokenPair) Token1() int64 { return p.token1 }
268func (p *TokenPair) SetToken0(token0 int64) { p.token0 = token0 }
269func (p *TokenPair) SetToken1(token1 int64) { p.token1 = token1 }
270
271type Slot0 struct {
272 sqrtPriceX96 *u256.Uint // current price of the pool as a sqrt(token1/token0) Q96 value
273 tick int32 // current tick of the pool, i.e according to the last tick transition that was run
274 feeProtocol uint8 // protocol fee for both tokens of the pool
275 unlocked bool // whether the pool is currently locked to reentrancy
276}
277
278func (s *Slot0) SqrtPriceX96() *u256.Uint { return s.sqrtPriceX96 }
279func (s *Slot0) Tick() int32 { return s.tick }
280func (s *Slot0) FeeProtocol() uint8 { return s.feeProtocol }
281func (s *Slot0) Unlocked() bool { return s.unlocked }
282
283func (s *Slot0) SetSqrtPriceX96(sqrtPriceX96 *u256.Uint) { s.sqrtPriceX96 = sqrtPriceX96 }
284func (s *Slot0) SetTick(tick int32) { s.tick = tick }
285func (s *Slot0) SetFeeProtocol(feeProtocol uint8) { s.feeProtocol = feeProtocol }
286func (s *Slot0) SetUnlocked(unlocked bool) { s.unlocked = unlocked }
287
288func NewSlot0(
289 sqrtPriceX96 *u256.Uint,
290 tick int32,
291 feeProtocol uint8,
292 unlocked bool,
293) Slot0 {
294 return Slot0{
295 sqrtPriceX96: sqrtPriceX96.Clone(),
296 tick: tick,
297 feeProtocol: feeProtocol,
298 unlocked: unlocked,
299 }
300}
301
302// TickInfo stores information about a specific tick in the pool.
303// TIcks represent discrete price points that can be used as boundaries for positions.
304type TickInfo struct {
305 liquidityGross string // total position liquidity that references this tick
306 liquidityNet string // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)
307
308 // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
309 // only has relative meaning, not absolute — the value depends on when the tick is initialized
310 feeGrowthOutside0X128 string
311 feeGrowthOutside1X128 string
312
313 tickCumulativeOutside int64 // cumulative tick value on the other side of the tick
314
315 // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
316 // only has relative meaning, not absolute — the value depends on when the tick is initialized
317 secondsPerLiquidityOutsideX128 string
318
319 // the seconds spent on the other side of the tick (relative to the current tick)
320 // only has relative meaning, not absolute — the value depends on when the tick is initialized
321 secondsOutside uint32
322
323 initialized bool // whether the tick is initialized
324}
325
326// TickInfo Getters methods
327func (t *TickInfo) LiquidityGross() string { return t.liquidityGross }
328func (t *TickInfo) LiquidityNet() string { return t.liquidityNet }
329func (t *TickInfo) FeeGrowthOutside0X128() string { return t.feeGrowthOutside0X128 }
330func (t *TickInfo) FeeGrowthOutside1X128() string { return t.feeGrowthOutside1X128 }
331func (t *TickInfo) SecondsPerLiquidityOutsideX128() string {
332 return t.secondsPerLiquidityOutsideX128
333}
334func (t *TickInfo) SecondsOutside() uint32 { return t.secondsOutside }
335func (t *TickInfo) Initialized() bool { return t.initialized }
336func (t *TickInfo) TickCumulativeOutside() int64 { return t.tickCumulativeOutside }
337
338// TickInfo Setters methods
339func (t *TickInfo) SetLiquidityGross(liquidityGross string) {
340 t.liquidityGross = liquidityGross
341}
342
343func (t *TickInfo) SetLiquidityNet(liquidityNet string) {
344 t.liquidityNet = liquidityNet
345}
346
347func (t *TickInfo) SetFeeGrowthOutside0X128(feeGrowthOutside0X128 string) {
348 t.feeGrowthOutside0X128 = feeGrowthOutside0X128
349}
350
351func (t *TickInfo) SetFeeGrowthOutside1X128(feeGrowthOutside1X128 string) {
352 t.feeGrowthOutside1X128 = feeGrowthOutside1X128
353}
354
355func (t *TickInfo) SetSecondsPerLiquidityOutsideX128(secondsPerLiquidityOutsideX128 string) {
356 t.secondsPerLiquidityOutsideX128 = secondsPerLiquidityOutsideX128
357}
358
359func (t *TickInfo) SetSecondsOutside(secondsOutside uint32) {
360 t.secondsOutside = secondsOutside
361}
362
363func (t *TickInfo) SetInitialized(initialized bool) {
364 t.initialized = initialized
365}
366
367func (t *TickInfo) SetTickCumulativeOutside(tickCumulativeOutside int64) {
368 t.tickCumulativeOutside = tickCumulativeOutside
369}
370
371func (t *TickInfo) Clone() TickInfo {
372 return TickInfo{
373 feeGrowthOutside0X128: t.feeGrowthOutside0X128,
374 feeGrowthOutside1X128: t.feeGrowthOutside1X128,
375 liquidityGross: t.liquidityGross,
376 liquidityNet: t.liquidityNet,
377 tickCumulativeOutside: t.tickCumulativeOutside,
378 secondsPerLiquidityOutsideX128: t.secondsPerLiquidityOutsideX128,
379 secondsOutside: t.secondsOutside,
380 initialized: t.initialized,
381 }
382}
383
384func NewTickInfo() TickInfo {
385 return TickInfo{
386 liquidityGross: "0",
387 liquidityNet: "0",
388 feeGrowthOutside0X128: "0",
389 feeGrowthOutside1X128: "0",
390 secondsPerLiquidityOutsideX128: "0",
391 secondsOutside: 0,
392 initialized: false,
393 tickCumulativeOutside: 0,
394 }
395}
396
397// PositionInfo stores liquidity and fee state for a position.
398// Liquidity and fee-growth fields are stored as decimal strings to reduce storage cost,
399// while transferable owed amounts stay as int64 for token transfer boundaries.
400type PositionInfo struct {
401 liquidity string // amount of liquidity owned by this position
402 feeGrowthInside0LastX128 string // fee growth per unit of liquidity for token0 as of last update
403 feeGrowthInside1LastX128 string // fee growth per unit of liquidity for token1 as of last update
404
405 // accumulated fees in token0 waiting to be collected
406 tokensOwed0 int64
407
408 // accumulated fees in token1 waiting to be collected
409 tokensOwed1 int64
410}
411
412func (p *PositionInfo) Liquidity() string { return p.liquidity }
413func (p *PositionInfo) FeeGrowthInside0LastX128() string { return p.feeGrowthInside0LastX128 }
414func (p *PositionInfo) FeeGrowthInside1LastX128() string { return p.feeGrowthInside1LastX128 }
415func (p *PositionInfo) TokensOwed0() int64 { return p.tokensOwed0 }
416func (p *PositionInfo) TokensOwed1() int64 { return p.tokensOwed1 }
417
418func (p *PositionInfo) SetLiquidity(liquidity string) {
419 p.liquidity = liquidity
420}
421
422func (p *PositionInfo) SetFeeGrowthInside0LastX128(feeGrowthInside0LastX128 string) {
423 p.feeGrowthInside0LastX128 = feeGrowthInside0LastX128
424}
425
426func (p *PositionInfo) SetFeeGrowthInside1LastX128(feeGrowthInside1LastX128 string) {
427 p.feeGrowthInside1LastX128 = feeGrowthInside1LastX128
428}
429
430func (p *PositionInfo) SetTokensOwed0(tokensOwed0 int64) {
431 p.tokensOwed0 = tokensOwed0
432}
433
434func (p *PositionInfo) SetTokensOwed1(tokensOwed1 int64) {
435 p.tokensOwed1 = tokensOwed1
436}
437
438func NewPositionInfo() PositionInfo {
439 return PositionInfo{
440 liquidity: "0",
441 feeGrowthInside0LastX128: "0",
442 feeGrowthInside1LastX128: "0",
443 tokensOwed0: 0,
444 tokensOwed1: 0,
445 }
446}
447
448func NewDefaultFeeAmountTickSpacing() map[uint32]int32 {
449 return map[uint32]int32{
450 100: 1,
451 500: 10,
452 3000: 60,
453 10000: 200,
454 }
455}