mint.gno
5.88 Kb · 184 lines
1package v1
2
3import (
4 u256 "gno.land/p/gnoswap/uint256"
5 ufmt "gno.land/p/nt/ufmt/v0"
6
7 "gno.land/r/gnoswap/common"
8 pl "gno.land/r/gnoswap/pool"
9 "gno.land/r/gnoswap/position"
10)
11
12// mint creates a new liquidity position by adding liquidity to a pool and minting an NFT.
13// Panics if position ID already exists or adding liquidity fails.
14func (p *positionV1) mint(_ int, rlm realm, params MintParams) (uint64, *u256.Uint, *u256.Uint, *u256.Uint) {
15 poolKey := pl.GetPoolPath(params.token0, params.token1, params.fee)
16 liquidity, amount0, amount1 := p.addLiquidity(
17 0,
18 rlm,
19 AddLiquidityParams{
20 poolKey: poolKey,
21 tickLower: params.tickLower,
22 tickUpper: params.tickUpper,
23 amount0Desired: params.amount0Desired,
24 amount1Desired: params.amount1Desired,
25 amount0Min: params.amount0Min,
26 amount1Min: params.amount1Min,
27 caller: params.caller,
28 },
29 )
30 // Ensure liquidity is not zero before minting NFT
31 if liquidity.IsZero() {
32 panic(newErrorWithDetail(
33 errZeroLiquidity,
34 "Liquidity is zero, cannot mint position.",
35 ))
36 }
37
38 id := p.getNextId()
39
40 if p.ExistPosition(id) {
41 panic(newErrorWithDetail(
42 errPositionExist,
43 ufmt.Sprintf("positionId(%d)", id),
44 ))
45 }
46
47 p.nftAccessor.Mint(0, rlm, params.mintTo, positionIdFrom(id))
48
49 positionKey := computePositionKey(params.tickLower, params.tickUpper)
50 feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pl.GetPositionFeeGrowthInsideLastX128(poolKey, positionKey)
51
52 position := position.NewPosition(
53 poolKey,
54 params.tickLower,
55 params.tickUpper,
56 liquidity.ToString(),
57 feeGrowthInside0LastX128,
58 feeGrowthInside1LastX128,
59 0,
60 0,
61 false,
62 zeroAddress,
63 )
64
65 // The position ID should not exist at the time of minting
66 p.mustUpdatePosition(0, rlm, id, *position)
67 p.incrementNextId(0, rlm)
68
69 return id, liquidity, amount0, amount1
70}
71
72// processMintInput processes and validates user input for minting liquidity.
73// It handles token ordering and amount validation.
74func (p *positionV1) processMintInput(input MintInput) (ProcessedMintInput, error) {
75 token0, token1 := input.token0, input.token1
76 if err := validateTokenPath(token0, token1); err != nil {
77 return ProcessedMintInput{}, makeErrorWithDetails(err, ufmt.Sprintf("token0(%s), token1(%s)", token0, token1))
78 }
79
80 pair := TokenPair{
81 token0: token0,
82 token1: token1,
83 }
84
85 // parse amounts
86 amount0Desired, amount1Desired, amount0Min, amount1Min := parseAmounts(input.amount0Desired, input.amount1Desired, input.amount0Min, input.amount1Min)
87
88 tickLower, tickUpper := input.tickLower, input.tickUpper
89
90 // swap if token1 < token0
91 if token1 < token0 {
92 pair.token0, pair.token1 = pair.token1, pair.token0
93 amount0Desired, amount1Desired = amount1Desired, amount0Desired
94 amount0Min, amount1Min = amount1Min, amount0Min
95 tickLower, tickUpper = -tickUpper, -tickLower
96 }
97
98 return ProcessedMintInput{
99 tokenPair: pair,
100 amount0Desired: amount0Desired,
101 amount1Desired: amount1Desired,
102 amount0Min: amount0Min,
103 amount1Min: amount1Min,
104 tickLower: tickLower,
105 tickUpper: tickUpper,
106 poolPath: pl.GetPoolPath(pair.token0, pair.token1, input.fee),
107 }, nil
108}
109
110// increaseLiquidity increases the liquidity of an existing position.
111func (p *positionV1) increaseLiquidity(_ int, rlm realm, params IncreaseLiquidityParams) (uint64, *u256.Uint, *u256.Uint, *u256.Uint, string, error) {
112 caller := params.caller
113 position := p.mustGetPosition(params.positionId)
114
115 liquidity, amount0, amount1 := p.addLiquidity(
116 0,
117 rlm,
118 AddLiquidityParams{
119 poolKey: position.PoolKey(),
120 tickLower: position.TickLower(),
121 tickUpper: position.TickUpper(),
122 amount0Desired: params.amount0Desired,
123 amount1Desired: params.amount1Desired,
124 amount0Min: params.amount0Min,
125 amount1Min: params.amount1Min,
126 caller: caller,
127 },
128 )
129
130 positionKey := computePositionKey(position.TickLower(), position.TickUpper())
131 feeGrowthInside0LastX128Str, feeGrowthInside1LastX128Str := pl.GetPositionFeeGrowthInsideLastX128(position.PoolKey(), positionKey)
132
133 feeGrowth := FeeGrowthInside{
134 feeGrowthInside0LastX128: u256.MustFromDecimal(feeGrowthInside0LastX128Str),
135 feeGrowthInside1LastX128: u256.MustFromDecimal(feeGrowthInside1LastX128Str),
136 }
137 tokensOwed0, tokensOwed1 := p.calculateFees(position, feeGrowth)
138
139 posLiquidity := u256.MustFromDecimal(position.Liquidity())
140 liquidityAmount, overflow := u256.Zero().AddOverflow(posLiquidity, liquidity)
141 if overflow {
142 return 0, nil, nil, nil, "", errOverflow
143 }
144
145 position.SetTokensOwed0(tokensOwed0)
146 position.SetTokensOwed1(tokensOwed1)
147
148 position.SetFeeGrowthInside0LastX128(feeGrowthInside0LastX128Str)
149 position.SetFeeGrowthInside1LastX128(feeGrowthInside1LastX128Str)
150
151 position.SetLiquidity(liquidityAmount.ToString())
152 position.SetBurned(false)
153
154 err := p.setPosition(0, rlm, params.positionId, *position)
155 if err != nil {
156 return 0, nil, nil, nil, "", makeErrorWithDetails(
157 errPositionDoesNotExist,
158 ufmt.Sprintf("cannot increase liquidity for non-existent position(%d)", params.positionId),
159 )
160 }
161
162 return params.positionId, liquidity, amount0, amount1, position.PoolKey(), nil
163}
164
165// validateTokenPath validates token paths are not identical, not conflicting, and in valid format.
166func validateTokenPath(token0, token1 string) error {
167 if token0 == token1 {
168 return errInvalidTokenPath
169 }
170 if !isValidTokenPath(token0) || !isValidTokenPath(token1) {
171 return errInvalidTokenPath
172 }
173 return nil
174}
175
176// isValidTokenPath checks if the token path is registered in the system.
177func isValidTokenPath(tokenPath string) bool {
178 return common.IsRegistered(tokenPath) == nil
179}
180
181// parseAmounts converts amount strings to u256.Uint values.
182func parseAmounts(amount0Desired, amount1Desired, amount0Min, amount1Min string) (*u256.Uint, *u256.Uint, *u256.Uint, *u256.Uint) {
183 return u256.MustFromDecimal(amount0Desired), u256.MustFromDecimal(amount1Desired), u256.MustFromDecimal(amount0Min), u256.MustFromDecimal(amount1Min)
184}