Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}