Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}