Search Apps Documentation Source Content File Folder Download Copy Actions Download

reposition.gno

4.53 Kb · 146 lines
  1package v1
  2
  3import (
  4	"chain"
  5
  6	u256 "gno.land/p/gnoswap/uint256"
  7	ufmt "gno.land/p/nt/ufmt/v0"
  8
  9	"gno.land/r/gnoswap/common"
 10	"gno.land/r/gnoswap/emission"
 11	"gno.land/r/gnoswap/halt"
 12	pl "gno.land/r/gnoswap/pool"
 13)
 14
 15// Reposition adjusts the price range and liquidity of an existing position.
 16//
 17// Parameters:
 18//   - positionId: NFT token ID to reposition
 19//   - tickLower, tickUpper: new price range boundaries
 20//   - amount0DesiredStr, amount1DesiredStr: desired token amounts for new position
 21//   - amount0MinStr, amount1MinStr: minimum acceptable amounts (slippage protection)
 22//   - deadline: transaction expiration timestamp
 23//
 24// Returns positionId, liquidity, tickLower, tickUpper, amount0, amount1.
 25func (p *positionV1) Reposition(
 26	_ int,
 27	rlm realm,
 28	positionId uint64,
 29	tickLower int32,
 30	tickUpper int32,
 31	amount0DesiredStr string,
 32	amount1DesiredStr string,
 33	amount0MinStr string,
 34	amount1MinStr string,
 35	deadline int64,
 36) (uint64, string, int32, int32, string, string) {
 37	if !rlm.IsCurrent() {
 38		panic(errSpoofedRealm)
 39	}
 40
 41	halt.AssertIsNotHaltedPosition()
 42
 43	previousRealm := rlm.Previous()
 44	caller := previousRealm.Address()
 45	assertIsOwnerForToken(p, positionId, caller)
 46	assertIsNotExpired(deadline)
 47
 48	emission.MintAndDistributeGns(cross(rlm))
 49
 50	// position should be burned to reposition
 51	position := p.mustGetPosition(positionId)
 52
 53	token0, token1, _ := splitOf(position.PoolKey())
 54	common.AssertIsNotHandleNativeCoin()
 55
 56	oldTickLower := position.TickLower()
 57	oldTickUpper := position.TickUpper()
 58
 59	if !position.IsClear() {
 60		panic(newErrorWithDetail(
 61			errNotClear,
 62			ufmt.Sprintf(
 63				"position(%d) isn't clear(liquidity:%s, tokensOwed0:%d, tokensOwed1:%d)",
 64				positionId,
 65				position.Liquidity(),
 66				position.TokensOwed0(),
 67				position.TokensOwed1(),
 68			),
 69		))
 70	}
 71
 72	if err := validateTokenPath(token0, token1); err != nil {
 73		panic(newErrorWithDetail(err, ufmt.Sprintf("token0(%s), token1(%s)", token0, token1)))
 74	}
 75
 76	poolKey := position.PoolKey()
 77
 78	liquidity, amount0, amount1 := p.addLiquidity(
 79		0,
 80		rlm,
 81		AddLiquidityParams{
 82			poolKey:        poolKey,
 83			tickLower:      tickLower,
 84			tickUpper:      tickUpper,
 85			amount0Desired: u256.MustFromDecimal(amount0DesiredStr),
 86			amount1Desired: u256.MustFromDecimal(amount1DesiredStr),
 87			amount0Min:     u256.MustFromDecimal(amount0MinStr),
 88			amount1Min:     u256.MustFromDecimal(amount1MinStr),
 89			caller:         caller,
 90		},
 91	)
 92
 93	// update position tickLower, tickUpper to new value
 94	// because getCurrentFeeGrowth() uses tickLower, tickUpper
 95	position.SetTickLower(tickLower)
 96	position.SetTickUpper(tickUpper)
 97
 98	currentFeeGrowth, err := p.getCurrentFeeGrowth(position, caller)
 99	if err != nil {
100		panic(newErrorWithDetail(err, "failed to get current fee growth"))
101	}
102	position.SetFeeGrowthInside0LastX128(currentFeeGrowth.feeGrowthInside0LastX128.ToString())
103	position.SetFeeGrowthInside1LastX128(currentFeeGrowth.feeGrowthInside1LastX128.ToString())
104
105	position.SetLiquidity(liquidity.ToString())
106
107	// OBS: do not reset feeGrowthInside1LastX128 and feeGrowthInside1LastX128 to zero
108	// if so, ( decrease 100% -> reposition )
109	// > at this point, that position will have unclaimedFee which isn't intended
110	position.SetTokensOwed0(0)
111	position.SetTokensOwed1(0)
112	position.SetBurned(false)
113	p.mustUpdatePosition(0, rlm, positionId, *position)
114
115	poolSqrtPriceX96 := pl.GetSlot0SqrtPriceX96(poolKey)
116	poolToken0Balance := pl.GetBalanceToken0(poolKey)
117	poolToken1Balance := pl.GetBalanceToken1(poolKey)
118
119	tickCumulative, liquidityCumulative, secondsPerLiquidityCumulativeX128, observationTimestamp := pl.GetObservation(poolKey, 0)
120
121	chain.Emit(
122		"Reposition",
123		"prevAddr", previousRealm.Address().String(),
124		"prevRealm", previousRealm.PkgPath(),
125		"lpPositionId", formatUint(positionId),
126		"tickLower", formatInt(tickLower),
127		"tickUpper", formatInt(tickUpper),
128		"liquidityDelta", liquidity.ToString(),
129		"amount0", amount0.ToString(),
130		"amount1", amount1.ToString(),
131		"prevTickLower", formatInt(oldTickLower),
132		"prevTickUpper", formatInt(oldTickUpper),
133		"poolPath", poolKey,
134		"sqrtPriceX96", poolSqrtPriceX96,
135		"positionLiquidity", p.GetPositionLiquidity(positionId),
136		"poolLiquidity", pl.GetLiquidity(poolKey),
137		"token0Balance", formatInt(poolToken0Balance),
138		"token1Balance", formatInt(poolToken1Balance),
139		"tickCumulative", formatInt(tickCumulative),
140		"liquidityCumulative", liquidityCumulative,
141		"secondsPerLiquidityCumulativeX128", secondsPerLiquidityCumulativeX128,
142		"observationTimestamp", formatInt(observationTimestamp),
143	)
144
145	return positionId, liquidity.ToString(), tickLower, tickUpper, amount0.ToString(), amount1.ToString()
146}