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}