Search Apps Documentation Source Content File Folder Download Copy Actions Download

reposition.gno

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