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