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

reposition.gno

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