burn.gno
6.05 Kb · 174 lines
1package position
2
3import (
4 "errors"
5 "gno.land/p/gnoswap/consts"
6 "gno.land/p/gnoswap/gnsmath"
7 prabc "gno.land/p/gnoswap/rbac"
8 u256 "gno.land/p/gnoswap/uint256"
9 "gno.land/p/gnoswap/utils"
10 ufmt "gno.land/p/nt/ufmt/v0"
11
12 "gno.land/r/gnoswap/access"
13 "gno.land/r/gnoswap/common"
14 pl "gno.land/r/gnoswap/pool"
15 "gno.land/r/gnoswap/position"
16)
17
18// decreaseLiquidity reduces position liquidity and collects fees.
19// Returns positionId, liquidity, fee0, fee1, amount0, amount1, poolPath.
20func (p *positionV1) decreaseLiquidity(_ int, rlm realm, params DecreaseLiquidityParams) (uint64, string, string, string, string, string, string, error) {
21 caller := params.caller
22
23 // before decrease liquidity, collect fee first
24 _, fee0Str, fee1Str, _, _, _ := p.collectFee(0, rlm, params.positionId, params.caller)
25
26 position := p.mustGetPosition(params.positionId)
27 positionLiquidity := u256.MustFromDecimal(position.Liquidity())
28 if positionLiquidity.IsZero() {
29 return params.positionId,
30 "",
31 fee0Str,
32 fee1Str,
33 "", "",
34 position.PoolKey(),
35 makeErrorWithDetails(
36 errZeroLiquidity,
37 ufmt.Sprintf("position(position ID:%d) has 0 liquidity", params.positionId),
38 )
39 }
40
41 liquidityToRemove := u256.MustFromDecimal(params.liquidity)
42
43 if liquidityToRemove.Gt(positionLiquidity) {
44 return params.positionId,
45 liquidityToRemove.ToString(),
46 fee0Str,
47 fee1Str,
48 "", "",
49 position.PoolKey(),
50 makeErrorWithDetails(
51 errInvalidLiquidity,
52 ufmt.Sprintf("Liquidity requested(%s) is greater than liquidity held(%s)", liquidityToRemove.ToString(), positionLiquidity.ToString()),
53 )
54 }
55
56 pToken0, pToken1, pFee := splitOf(position.PoolKey())
57 burn0, burn1 := pl.Burn(cross(rlm), pToken0, pToken1, pFee, position.TickLower(), position.TickUpper(), liquidityToRemove.ToString(), caller)
58
59 burnedAmount0 := utils.SafeParseInt64(burn0)
60 burnedAmount1 := utils.SafeParseInt64(burn1)
61
62 if burnedAmount0 < 0 || burnedAmount1 < 0 {
63 panic(errors.New(errUnderflow))
64 }
65
66 positionKey := computePositionKey(position.TickLower(), position.TickUpper())
67 feeGrowthInside0LastX128Str, feeGrowthInside1LastX128Str := pl.GetPositionFeeGrowthInsideLastX128(position.PoolKey(), positionKey)
68
69 // Add only burned amounts to tokensOwed since fees were already collected and processed in collectFee
70 tokensOwed0 := gnsmath.SafeAddInt64(position.TokensOwed0(), burnedAmount0)
71 tokensOwed1 := gnsmath.SafeAddInt64(position.TokensOwed1(), burnedAmount1)
72
73 newLiquidity, underflow := u256.Zero().SubOverflow(positionLiquidity, liquidityToRemove)
74 if underflow {
75 panic(newErrorWithDetail(errUnderflow, "positionLiquidity - liquidityToRemove underflow"))
76 }
77
78 position.SetTokensOwed0(tokensOwed0)
79 position.SetTokensOwed1(tokensOwed1)
80 position.SetFeeGrowthInside0LastX128(feeGrowthInside0LastX128Str)
81 position.SetFeeGrowthInside1LastX128(feeGrowthInside1LastX128Str)
82 position.SetLiquidity(newLiquidity.ToString())
83
84 p.mustUpdatePosition(0, rlm, params.positionId, *position)
85
86 collect0, collect1 := pl.Collect(
87 cross(rlm),
88 pToken0,
89 pToken1,
90 pFee,
91 caller,
92 position.TickLower(),
93 position.TickUpper(),
94 burn0,
95 burn1,
96 )
97
98 collectAmount0 := u256.MustFromDecimal(collect0)
99 collectAmount1 := u256.MustFromDecimal(collect1)
100
101 // Slippage check on actually collected amounts to ensure user receives minimum expected tokens
102 if isSlippageExceeded(collectAmount0, collectAmount1, params.amount0Min, params.amount1Min) {
103 return params.positionId,
104 liquidityToRemove.ToString(),
105 fee0Str,
106 fee1Str,
107 collect0,
108 collect1,
109 position.PoolKey(),
110 makeErrorWithDetails(
111 errSlippage,
112 ufmt.Sprintf("collectAmount0(%s) >= amount0Min(%s) && collectAmount1(%s) >= amount1Min(%s)",
113 collectAmount0.ToString(),
114 params.amount0Min.ToString(),
115 collectAmount1.ToString(),
116 params.amount1Min.ToString(),
117 ),
118 )
119 }
120
121 poolAddr := access.MustGetAddress(prabc.ROLE_POOL.String())
122
123 // Check for underflow when subtracting collected amounts from tokens owed
124 collectAmount0Int64 := gnsmath.SafeConvertToInt64(collectAmount0)
125 collectAmount1Int64 := gnsmath.SafeConvertToInt64(collectAmount1)
126
127 if position.TokensOwed0() < collectAmount0Int64 {
128 panic(ufmt.Sprintf("[POSITION] burn.gno | collect() | tokensOwed0(%d) < collectAmount0(%d)", position.TokensOwed0(), collectAmount0Int64))
129 }
130 position.SetTokensOwed0(gnsmath.SafeSubInt64(position.TokensOwed0(), collectAmount0Int64))
131
132 if position.TokensOwed1() < collectAmount1Int64 {
133 panic(ufmt.Sprintf("[POSITION] burn.gno | collect() | tokensOwed1(%d) < collectAmount1(%d)", position.TokensOwed1(), collectAmount1Int64))
134 }
135 position.SetTokensOwed1(gnsmath.SafeSubInt64(position.TokensOwed1(), collectAmount1Int64))
136
137 if position.IsClear() {
138 position.SetBurned(true) // just update flag (we don't want to burn actual position)
139 }
140
141 p.mustUpdatePosition(0, rlm, params.positionId, *position)
142
143 common.SafeGRC20TransferFrom(cross(rlm), pToken0, poolAddr, caller, collectAmount0Int64)
144 common.SafeGRC20TransferFrom(cross(rlm), pToken1, poolAddr, caller, collectAmount1Int64)
145
146 return params.positionId, liquidityToRemove.ToString(), fee0Str, fee1Str, collect0, collect1, position.PoolKey(), nil
147}
148
149// calculateFees calculates the fees for the current position.
150func (p *positionV1) calculateFees(position *position.Position, currentFeeGrowth FeeGrowthInside) (int64, int64) {
151 posLiquidity := u256.MustFromDecimal(position.Liquidity())
152 fee0 := calculateTokensOwed(
153 currentFeeGrowth.feeGrowthInside0LastX128,
154 u256.MustFromDecimal(position.FeeGrowthInside0LastX128()),
155 posLiquidity,
156 )
157
158 fee1 := calculateTokensOwed(
159 currentFeeGrowth.feeGrowthInside1LastX128,
160 u256.MustFromDecimal(position.FeeGrowthInside1LastX128()),
161 posLiquidity,
162 )
163
164 return gnsmath.SafeAddInt64(position.TokensOwed0(), gnsmath.SafeConvertToInt64(fee0)), gnsmath.SafeAddInt64(position.TokensOwed1(), gnsmath.SafeConvertToInt64(fee1))
165}
166
167func calculateTokensOwed(
168 feeGrowthInsideLastX128 *u256.Uint,
169 positionFeeGrowthInsideLastX128 *u256.Uint,
170 positionLiquidity *u256.Uint,
171) *u256.Uint {
172 diff := u256.Zero().Sub(feeGrowthInsideLastX128, positionFeeGrowthInsideLastX128)
173 return u256.MulDiv(diff, positionLiquidity, consts.Q128())
174}