Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions 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}