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}