Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

burn.gno

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