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