Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}