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

getter.gno

14.94 Kb · 432 lines
  1package position
  2
  3import (
  4	"errors"
  5
  6	"gno.land/p/gnoswap/consts/v1"
  7	u256 "gno.land/p/gnoswap/uint256/v1"
  8	rotree "gno.land/p/nt/bptree/rotree/v0"
  9	ufmt "gno.land/p/nt/ufmt/v0"
 10
 11	pl "gno.land/r/gnoswap/pool"
 12	"gno.land/r/gnoswap/position"
 13)
 14
 15// getPosition returns a position for a given position ID.
 16// Returns an error if the position doesn't exist.
 17func (p *positionV1) getPosition(positionId uint64) (position.Position, error) {
 18	pos, exists := p.store.GetPosition(positionId)
 19	if !exists {
 20		return pos, errors.New(newErrorWithDetail(
 21			errPositionDoesNotExist,
 22			ufmt.Sprintf("position with position ID(%d) doesn't exist", positionId),
 23		))
 24	}
 25
 26	return pos, nil
 27}
 28
 29// mustGetPosition returns a position for a given position ID.
 30// panics if position doesn't exist
 31func (p *positionV1) mustGetPosition(positionId uint64) *position.Position {
 32	position, err := p.getPosition(positionId)
 33	if err != nil {
 34		panic(err.Error())
 35	}
 36
 37	return &position
 38}
 39
 40// GetPositions returns a read-only view of every position, keyed by the decimal
 41// string form of the position ID.
 42//
 43// Returns:
 44//   - positions: read-only tree exposing the position store without mutation methods
 45func (p *positionV1) GetPositions() *rotree.ReadOnlyTree {
 46	// makeEntrySafeFn is a function that makes an entry safe to read.
 47	// But Position is immutable, so we can just return the entry as is.
 48	return rotree.Wrap(p.store.GetPositions(), nil)
 49}
 50
 51// ExistPosition reports whether a position exists for a given position ID.
 52//
 53// Parameters:
 54//   - positionId: numeric ID to test in the position store
 55//
 56// Returns:
 57//   - exists: true when the position store contains positionId
 58func (p *positionV1) ExistPosition(positionId uint64) bool {
 59	return p.store.HasPosition(positionId)
 60}
 61
 62// IsBurned reports the empty-position marker stored for a position.
 63//
 64// Parameters:
 65//   - positionId: numeric ID whose burned marker is requested
 66//
 67// Returns:
 68//   - burned: true when the position is marked empty; the NFT record itself may still exist
 69//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
 70func (p *positionV1) IsBurned(positionId uint64) (bool, error) {
 71	position, err := p.getPosition(positionId)
 72	if err != nil {
 73		return false, err
 74	}
 75
 76	return position.Burned(), nil
 77}
 78
 79// IsInRange reports whether the pool's current tick is inside a position's range.
 80//
 81// Parameters:
 82//   - positionId: numeric ID whose tick range and pool are inspected
 83//
 84// Returns:
 85//   - inRange: true when tickLower <= current pool tick < tickUpper
 86//   - err: nil on success; an error when the position or its pool tick cannot be read
 87func (p *positionV1) IsInRange(positionId uint64) (bool, error) {
 88	position, err := p.getPosition(positionId)
 89	if err != nil {
 90		return false, err
 91	}
 92
 93	poolPath := position.PoolKey()
 94	poolCurrentTick, err := pl.GetSlot0Tick(poolPath)
 95	if err != nil {
 96		return false, err
 97	}
 98
 99	return position.TickLower() <= poolCurrentTick && poolCurrentTick < position.TickUpper(), nil
100}
101
102// GetPositionOperator returns the address approved to operate on a position.
103//
104// Parameters:
105//   - positionId: numeric ID whose operator is requested
106//
107// Returns:
108//   - operator: approved operator address, or the empty address when none is set
109//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
110func (p *positionV1) GetPositionOperator(positionId uint64) (address, error) {
111	position, err := p.getPosition(positionId)
112	if err != nil {
113		return address(""), err
114	}
115
116	return position.Operator(), nil
117}
118
119// GetPositionPoolKey returns the pool identifier stored on a position.
120//
121// Parameters:
122//   - positionId: numeric ID whose pool key is requested
123//
124// Returns:
125//   - poolKey: pool key encoding the position's token pair and fee tier
126//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
127func (p *positionV1) GetPositionPoolKey(positionId uint64) (string, error) {
128	position, err := p.getPosition(positionId)
129	if err != nil {
130		return "", err
131	}
132
133	return position.PoolKey(), nil
134}
135
136// GetPositionTickLower returns the lower tick boundary stored on a position.
137//
138// Parameters:
139//   - positionId: numeric ID whose lower tick is requested
140//
141// Returns:
142//   - tickLower: lower tick boundary, inclusive in range checks
143//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
144func (p *positionV1) GetPositionTickLower(positionId uint64) (int32, error) {
145	position, err := p.getPosition(positionId)
146	if err != nil {
147		return 0, err
148	}
149
150	return position.TickLower(), nil
151}
152
153// GetPositionTickUpper returns the upper tick boundary stored on a position.
154//
155// Parameters:
156//   - positionId: numeric ID whose upper tick is requested
157//
158// Returns:
159//   - tickUpper: upper tick boundary, exclusive in range checks
160//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
161func (p *positionV1) GetPositionTickUpper(positionId uint64) (int32, error) {
162	position, err := p.getPosition(positionId)
163	if err != nil {
164		return 0, err
165	}
166
167	return position.TickUpper(), nil
168}
169
170// GetPositionLiquidity returns the decimal-encoded liquidity stored on a position.
171//
172// Parameters:
173//   - positionId: numeric ID whose liquidity is requested
174//
175// Returns:
176//   - liquidity: decimal string representing the position's current liquidity
177//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
178func (p *positionV1) GetPositionLiquidity(positionId uint64) (string, error) {
179	position, err := p.getPosition(positionId)
180	if err != nil {
181		return "", err
182	}
183
184	return position.Liquidity(), nil
185}
186
187// GetPositionTokenBalances returns balances derived from current pool price,
188// position range, and liquidity; token balances are not stored snapshots.
189//
190// Parameters:
191//   - positionId: numeric ID whose current token balances are calculated
192//
193// Returns:
194//   - token0Balance: current token0 amount represented by the position's liquidity and range
195//   - token1Balance: current token1 amount represented by the position's liquidity and range
196//   - err: nil when the position exists; errPositionDoesNotExist when it is absent (pool price lookup failures panic during calculation)
197func (p *positionV1) GetPositionTokenBalances(positionId uint64) (int64, int64, error) {
198	position, err := p.getPosition(positionId)
199	if err != nil {
200		return 0, 0, err
201	}
202
203	balance0, balance1 := calculatePositionBalances(&position)
204	return balance0, balance1, nil
205}
206
207// GetPositionFeeGrowthInside0LastX128 returns the token0 fee-growth checkpoint
208// stored on a position.
209//
210// Parameters:
211//   - positionId: numeric ID whose token0 fee-growth checkpoint is requested
212//
213// Returns:
214//   - feeGrowthInside0LastX128: decimal-encoded Q128 token0 fee growth inside the position's range at its last update
215//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
216func (p *positionV1) GetPositionFeeGrowthInside0LastX128(positionId uint64) (string, error) {
217	position, err := p.getPosition(positionId)
218	if err != nil {
219		return "", err
220	}
221
222	return position.FeeGrowthInside0LastX128(), nil
223}
224
225// GetPositionFeeGrowthInside1LastX128 returns the token1 fee-growth checkpoint
226// stored on a position.
227//
228// Parameters:
229//   - positionId: numeric ID whose token1 fee-growth checkpoint is requested
230//
231// Returns:
232//   - feeGrowthInside1LastX128: decimal-encoded Q128 token1 fee growth inside the position's range at its last update
233//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
234func (p *positionV1) GetPositionFeeGrowthInside1LastX128(positionId uint64) (string, error) {
235	position, err := p.getPosition(positionId)
236	if err != nil {
237		return "", err
238	}
239
240	return position.FeeGrowthInside1LastX128(), nil
241}
242
243// GetPositionFeeGrowthInsideLastX128 returns both fee-growth checkpoints stored
244// on a position, in token0 then token1 order.
245//
246// Parameters:
247//   - positionId: numeric ID whose fee-growth checkpoints are requested
248//
249// Returns:
250//   - feeGrowthInside0LastX128: decimal-encoded Q128 token0 fee growth checkpoint
251//   - feeGrowthInside1LastX128: decimal-encoded Q128 token1 fee growth checkpoint
252//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
253func (p *positionV1) GetPositionFeeGrowthInsideLastX128(positionId uint64) (string, string, error) {
254	position, err := p.getPosition(positionId)
255	if err != nil {
256		return "", "", err
257	}
258
259	return position.FeeGrowthInside0LastX128(), position.FeeGrowthInside1LastX128(), nil
260}
261
262// GetPositionTicks returns both tick boundaries stored on a position.
263//
264// Parameters:
265//   - positionId: numeric ID whose tick boundaries are requested
266//
267// Returns:
268//   - tickLower: lower tick boundary, inclusive in range checks
269//   - tickUpper: upper tick boundary, exclusive in range checks
270//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
271func (p *positionV1) GetPositionTicks(positionId uint64) (int32, int32, error) {
272	position, err := p.getPosition(positionId)
273	if err != nil {
274		return 0, 0, err
275	}
276
277	return position.TickLower(), position.TickUpper(), nil
278}
279
280// GetPositionTokensOwed0 returns the stored token0 amount owed to a position.
281//
282// Parameters:
283//   - positionId: numeric ID whose token0 owed amount is requested
284//
285// Returns:
286//   - tokensOwed0: accrued token0 amount awaiting collection, in token units
287//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
288func (p *positionV1) GetPositionTokensOwed0(positionId uint64) (int64, error) {
289	position, err := p.getPosition(positionId)
290	if err != nil {
291		return 0, err
292	}
293
294	return position.TokensOwed0(), nil
295}
296
297// GetPositionTokensOwed1 returns the stored token1 amount owed to a position.
298//
299// Parameters:
300//   - positionId: numeric ID whose token1 owed amount is requested
301//
302// Returns:
303//   - tokensOwed1: accrued token1 amount awaiting collection, in token units
304//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
305func (p *positionV1) GetPositionTokensOwed1(positionId uint64) (int64, error) {
306	position, err := p.getPosition(positionId)
307	if err != nil {
308		return 0, err
309	}
310
311	return position.TokensOwed1(), nil
312}
313
314// GetPositionTokensOwed returns both stored token amounts owed to a position,
315// in token0 then token1 order.
316//
317// Parameters:
318//   - positionId: numeric ID whose owed amounts are requested
319//
320// Returns:
321//   - tokensOwed0: accrued token0 amount awaiting collection, in token units
322//   - tokensOwed1: accrued token1 amount awaiting collection, in token units
323//   - err: nil on lookup success; errPositionDoesNotExist when the position is absent
324func (p *positionV1) GetPositionTokensOwed(positionId uint64) (int64, int64, error) {
325	position, err := p.getPosition(positionId)
326	if err != nil {
327		return 0, 0, err
328	}
329
330	return position.TokensOwed0(), position.TokensOwed1(), nil
331}
332
333// GetPositionOwner returns the address that owns the position NFT.
334//
335// Parameters:
336//   - positionId: numeric ID whose NFT ownership is requested
337//
338// Returns:
339//   - owner: current owner address reported by the NFT accessor
340//   - err: nil when the NFT accessor finds the token; otherwise its ownership lookup error
341func (p *positionV1) GetPositionOwner(positionId uint64) (address, error) {
342	owner, err := p.nftAccessor.OwnerOf(positionIdFrom(positionId))
343	if err != nil {
344		return address(""), err
345	}
346
347	return owner, nil
348}
349
350// GetUnclaimedFee calculates unclaimed token fees from current in-range
351// fee growth, the position's checkpoints, and its liquidity.
352//
353// Parameters:
354//   - positionId: numeric ID whose unclaimed token fees are calculated
355//
356// Returns:
357//   - unclaimedFee0: token0 fees accrued since the stored checkpoint, scaled by position liquidity
358//   - unclaimedFee1: token1 fees accrued since the stored checkpoint, scaled by position liquidity
359//   - err: nil on success; nil fee pointers and the underlying lookup error when position or pool data is unavailable
360func (p *positionV1) GetUnclaimedFee(positionId uint64) (*u256.Uint, *u256.Uint, error) {
361	// ref: https://blog.uniswap.org/uniswap-v3-math-primer-2#calculating-uncollected-fees
362	position, err := p.getPosition(positionId)
363	if err != nil {
364		return nil, nil, err
365	}
366
367	liquidity := u256.MustFromDecimal(position.Liquidity())
368	tickLower := position.TickLower()
369	tickUpper := position.TickUpper()
370
371	poolKey := position.PoolKey()
372
373	currentTick, err := pl.GetSlot0Tick(poolKey)
374	if err != nil {
375		return nil, nil, err
376	}
377
378	feeGrowthGlobal0X128Str, feeGrowthGlobal1X128Str, err := pl.GetFeeGrowthGlobalX128(poolKey)
379	if err != nil {
380		return nil, nil, err
381	}
382	feeGrowthGlobal0X128 := u256.MustFromDecimal(feeGrowthGlobal0X128Str)
383	feeGrowthGlobal1X128 := u256.MustFromDecimal(feeGrowthGlobal1X128Str)
384
385	tickUpperFeeGrowthOutside0X128Str, tickUpperFeeGrowthOutside1X128Str, err := pl.GetTickFeeGrowthOutsideX128(poolKey, tickUpper)
386	if err != nil {
387		return nil, nil, err
388	}
389	tickUpperFeeGrowthOutside0X128 := u256.MustFromDecimal(tickUpperFeeGrowthOutside0X128Str)
390	tickUpperFeeGrowthOutside1X128 := u256.MustFromDecimal(tickUpperFeeGrowthOutside1X128Str)
391
392	tickLowerFeeGrowthOutside0X128Str, tickLowerFeeGrowthOutside1X128Str, err := pl.GetTickFeeGrowthOutsideX128(poolKey, tickLower)
393	if err != nil {
394		return nil, nil, err
395	}
396	tickLowerFeeGrowthOutside0X128 := u256.MustFromDecimal(tickLowerFeeGrowthOutside0X128Str)
397	tickLowerFeeGrowthOutside1X128 := u256.MustFromDecimal(tickLowerFeeGrowthOutside1X128Str)
398
399	feeGrowthInside0LastX128 := u256.MustFromDecimal(position.FeeGrowthInside0LastX128())
400	feeGrowthInside1LastX128 := u256.MustFromDecimal(position.FeeGrowthInside1LastX128())
401
402	var tickLowerFeeGrowthBelow0, tickLowerFeeGrowthBelow1, tickUpperFeeGrowthAbove0, tickUpperFeeGrowthAbove1 *u256.Uint
403
404	if currentTick >= tickUpper {
405		tickUpperFeeGrowthAbove0 = u256.Zero().Sub(feeGrowthGlobal0X128, tickUpperFeeGrowthOutside0X128)
406		tickUpperFeeGrowthAbove1 = u256.Zero().Sub(feeGrowthGlobal1X128, tickUpperFeeGrowthOutside1X128)
407	} else {
408		tickUpperFeeGrowthAbove0 = tickUpperFeeGrowthOutside0X128
409		tickUpperFeeGrowthAbove1 = tickUpperFeeGrowthOutside1X128
410	}
411
412	if currentTick >= tickLower {
413		tickLowerFeeGrowthBelow0 = tickLowerFeeGrowthOutside0X128
414		tickLowerFeeGrowthBelow1 = tickLowerFeeGrowthOutside1X128
415	} else {
416		tickLowerFeeGrowthBelow0 = u256.Zero().Sub(feeGrowthGlobal0X128, tickLowerFeeGrowthOutside0X128)
417		tickLowerFeeGrowthBelow1 = u256.Zero().Sub(feeGrowthGlobal1X128, tickLowerFeeGrowthOutside1X128)
418	}
419
420	feeGrowthInside0X128 := u256.Zero().Sub(feeGrowthGlobal0X128, tickLowerFeeGrowthBelow0)
421	feeGrowthInside0X128 = u256.Zero().Sub(feeGrowthInside0X128, tickUpperFeeGrowthAbove0)
422
423	feeGrowthInside1X128 := u256.Zero().Sub(feeGrowthGlobal1X128, tickLowerFeeGrowthBelow1)
424	feeGrowthInside1X128 = u256.Zero().Sub(feeGrowthInside1X128, tickUpperFeeGrowthAbove1)
425
426	diffGrowthInside0X128 := u256.Zero().Sub(feeGrowthInside0X128, feeGrowthInside0LastX128)
427	unclaimedFee0 := u256.MulDiv(diffGrowthInside0X128, liquidity, consts.Q128())
428
429	diffGrowthInside1X128 := u256.Zero().Sub(feeGrowthInside1X128, feeGrowthInside1LastX128)
430	unclaimedFee1 := u256.MulDiv(diffGrowthInside1X128, liquidity, consts.Q128())
431	return unclaimedFee0, unclaimedFee1, nil
432}