Kind Realm (renderable)
Name v1
Namespace gnoswap / position
Exported functions 1
Module gno.land/r/gnoswap/position/v1
gno 0.9 mint.gno gno
⧉
1 package position 2 3 import ( 4 "errors" 5 6 u256 "gno.land/p/gnoswap/uint256/v1" 7 ufmt "gno.land/p/nt/ufmt/v0" 8 NewPositionV1 (positionStore interface {GetPosition func(uint64) (gno.land/r/gnoswap/position.Position, bool); GetPositionNextID func() uint64; GetPositions func() *gno.land/p/nt/bptree/v0.BPTree; HasPosition func(uint64) bool; HasPositionNextIDStoreKey func() bool; HasPositionsStoreKey func() bool; RemovePosition func(int, .uverse.realm, uint64) .uverse.error; SetPosition func(int, .uverse.realm, uint64, gno.land/r/gnoswap/position.Position) .uverse.error; SetPositionNextID func(int, .uverse.realm, uint64) .uverse.error; SetPositions func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error}, accessor interface {Approve func(int, .uverse.realm, .uverse.address, gno.land/p/nt/grc721/v0.TokenID) .uverse.error; Burn func(int, .uverse.realm, gno.land/p/nt/grc721/v0.TokenID); Exists func(gno.land/p/nt/grc721/v0.TokenID) bool; Mint func(int, .uverse.realm, .uverse.address, gno.land/p/nt/grc721/v0.TokenID) gno.land/p/nt/grc721/v0.TokenID; OwnerOf func(gno.land/p/nt/grc721/v0.TokenID) (.uverse.address, .uverse.error); TotalSupply func() int64}) interface {CollectFee func(int, .uverse.realm, uint64) (uint64, string, string, string, string, string); DecreaseLiquidity func(int, .uverse.realm, uint64, string, string, string, int64) (uint64, string, string, string, string, string, string); GetPositionFeeGrowthInside0LastX128 func(uint64) (string, .uverse.error); GetPositionFeeGrowthInside1LastX128 func(uint64) (string, .uverse.error); GetPositionFeeGrowthInsideLastX128 func(uint64) (string, string, .uverse.error); GetPositionLiquidity func(uint64) (string, .uverse.error); GetPositionOperator func(uint64) (.uverse.address, .uverse.error); GetPositionOwner func(uint64) (.uverse.address, .uverse.error); GetPositionPoolKey func(uint64) (string, .uverse.error); GetPositionTickLower func(uint64) (int32, .uverse.error); GetPositionTickUpper func(uint64) (int32, .uverse.error); GetPositionTicks func(uint64) (int32, int32, .uverse.error); GetPositionTokenBalances func(uint64) (int64, int64, .uverse.error); GetPositionTokensOwed func(uint64) (int64, int64, .uverse.error); GetPositionTokensOwed0 func(uint64) (int64, .uverse.error); GetPositionTokensOwed1 func(uint64) (int64, .uverse.error); GetPositions func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetUnclaimedFee func(uint64) (*gno.land/p/gnoswap/uint256/v1.Uint, *gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); IncreaseLiquidity func(int, .uverse.realm, uint64, string, string, string, string, int64) (uint64, string, string, string, string); IsBurned func(uint64) (bool, .uverse.error); IsInRange func(uint64) (bool, .uverse.error); Mint func(int, .uverse.realm, string, string, uint32, int32, int32, string, string, string, string, int64, .uverse.address, string) (uint64, string, string, string); Render func(string) string; Reposition func(int, .uverse.realm, uint64, int32, int32, string, string, string, string, int64) (uint64, string, int32, int32, string, string); SetPositionOperator func(int, .uverse.realm, uint64, .uverse.address)}
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
This realm does not provide renderable output (vm/qrender → NoRenderDeclError).
View on gnoweb ↗ vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
9 "gno.land/r/gnoswap/common"
10 pl "gno.land/r/gnoswap/pool"
11 "gno.land/r/gnoswap/position"
12 )
13
14 // mint creates a new liquidity position by adding liquidity to a pool and minting an NFT.
15 // Panics if position ID already exists or adding liquidity fails.
16 func (p *positionV1) mint(_ int , rlm realm, params MintParams) ( uint64 , *u256.Uint, *u256.Uint, *u256.Uint) {
17 poolKey := pl.GetPoolPath(params.token0, params.token1, params.fee)
18 liquidity, amount0, amount1 := p.addLiquidity(
19 0 ,
20 rlm,
21 AddLiquidityParams{
22 poolKey: poolKey,
23 tickLower: params.tickLower,
24 tickUpper: params.tickUpper,
25 amount0Desired: params.amount0Desired,
26 amount1Desired: params.amount1Desired,
27 amount0Min: params.amount0Min,
28 amount1Min: params.amount1Min,
29 caller: params.caller,
30 },
31 )
32 // Ensure liquidity is not zero before minting NFT
33 if liquidity.IsZero() {
34 panic(newErrorWithDetail(
35 errZeroLiquidity,
36 "Liquidity is zero, cannot mint position." ,
37 ))
38 }
39
40 id := p.getNextId()
41
42 if p.ExistPosition(id) {
43 panic(newErrorWithDetail(
44 errPositionExist,
45 ufmt.Sprintf( "positionId(%d)" , id),
46 ))
47 }
48
49 p.nftAccessor.Mint( 0 , rlm, params.mintTo, positionIdFrom(id))
50
51 positionKey := computePositionKey(params.tickLower, params.tickUpper)
52 feeGrowthInside0LastX128, feeGrowthInside1LastX128, err := pl.GetPositionFeeGrowthInsideLastX128(poolKey, positionKey)
53 if err != nil {
54 panic(err)
55 }
56
57 position := position.NewPosition(
58 poolKey,
59 params.tickLower,
60 params.tickUpper,
61 liquidity.ToString(),
62 feeGrowthInside0LastX128,
63 feeGrowthInside1LastX128,
64 0 ,
65 0 ,
66 false ,
67 zeroAddress,
68 )
69
70 // The position ID should not exist at the time of minting
71 p.mustUpdatePosition( 0 , rlm, id, *position)
72 p.incrementNextId( 0 , rlm)
73
74 return id, liquidity, amount0, amount1
75 }
76
77 // processMintInput processes and validates user input for minting liquidity.
78 // It handles token ordering and amount validation.
79 func (p *positionV1) processMintInput(input MintInput) (ProcessedMintInput, error ) {
80 token0, token1 := input.token0, input.token1
81 if err := validateTokenPath(token0, token1); err != nil {
82 return ProcessedMintInput{}, makeErrorWithDetails(err.Error(), ufmt.Sprintf( "token0(%s), token1(%s)" , token0, token1))
83 }
84
85 pair := TokenPair{
86 token0: token0,
87 token1: token1,
88 }
89
90 // parse amounts
91 amount0Desired, amount1Desired, amount0Min, amount1Min := parseAmounts(input.amount0Desired, input.amount1Desired, input.amount0Min, input.amount1Min)
92
93 tickLower, tickUpper := input.tickLower, input.tickUpper
94
95 // swap if token1 < token0
96 if token1 < token0 {
97 pair.token0, pair.token1 = pair.token1, pair.token0
98 amount0Desired, amount1Desired = amount1Desired, amount0Desired
99 amount0Min, amount1Min = amount1Min, amount0Min
100 tickLower, tickUpper = -tickUpper, -tickLower
101 }
102
103 return ProcessedMintInput{
104 tokenPair: pair,
105 amount0Desired: amount0Desired,
106 amount1Desired: amount1Desired,
107 amount0Min: amount0Min,
108 amount1Min: amount1Min,
109 tickLower: tickLower,
110 tickUpper: tickUpper,
111 poolPath: pl.GetPoolPath(pair.token0, pair.token1, input.fee),
112 }, nil
113 }
114
115 // increaseLiquidity increases the liquidity of an existing position.
116 func (p *positionV1) increaseLiquidity(_ int , rlm realm, params IncreaseLiquidityParams) ( uint64 , *u256.Uint, *u256.Uint, *u256.Uint, string , error ) {
117 caller := params.caller
118 position := p.mustGetPosition(params.positionId)
119
120 liquidity, amount0, amount1 := p.addLiquidity(
121 0 ,
122 rlm,
123 AddLiquidityParams{
124 poolKey: position.PoolKey(),
125 tickLower: position.TickLower(),
126 tickUpper: position.TickUpper(),
127 amount0Desired: params.amount0Desired,
128 amount1Desired: params.amount1Desired,
129 amount0Min: params.amount0Min,
130 amount1Min: params.amount1Min,
131 caller: caller,
132 },
133 )
134
135 positionKey := computePositionKey(position.TickLower(), position.TickUpper())
136 feeGrowthInside0LastX128Str, feeGrowthInside1LastX128Str, err := pl.GetPositionFeeGrowthInsideLastX128(position.PoolKey(), positionKey)
137 if err != nil {
138 return 0 , nil , nil , nil , "" , err
139 }
140
141 feeGrowth := FeeGrowthInside{
142 feeGrowthInside0LastX128: u256.MustFromDecimal(feeGrowthInside0LastX128Str),
143 feeGrowthInside1LastX128: u256.MustFromDecimal(feeGrowthInside1LastX128Str),
144 }
145 tokensOwed0, tokensOwed1 := p.calculateFees(position, feeGrowth)
146
147 posLiquidity := u256.MustFromDecimal(position.Liquidity())
148 liquidityAmount, overflow := u256.Zero().AddOverflow(posLiquidity, liquidity)
149 if overflow {
150 return 0 , nil , nil , nil , "" , errors.New(errOverflow)
151 }
152
153 position.SetTokensOwed0(tokensOwed0)
154 position.SetTokensOwed1(tokensOwed1)
155
156 position.SetFeeGrowthInside0LastX128(feeGrowthInside0LastX128Str)
157 position.SetFeeGrowthInside1LastX128(feeGrowthInside1LastX128Str)
158
159 position.SetLiquidity(liquidityAmount.ToString())
160 position.SetBurned( false )
161
162 err = p.setPosition( 0 , rlm, params.positionId, *position)
163 if err != nil {
164 return 0 , nil , nil , nil , "" , makeErrorWithDetails(
165 errPositionDoesNotExist,
166 ufmt.Sprintf( "cannot increase liquidity for non-existent position(%d)" , params.positionId),
167 )
168 }
169
170 return params.positionId, liquidity, amount0, amount1, position.PoolKey(), nil
171 }
172
173 // validateTokenPath validates token paths are not identical, not conflicting, and in valid format.
174 func validateTokenPath(token0, token1 string ) error {
175 if token0 == token1 {
176 return errors.New(errInvalidTokenPath)
177 }
178 if !isValidTokenPath(token0) || !isValidTokenPath(token1) {
179 return errors.New(errInvalidTokenPath)
180 }
181 return nil
182 }
183
184 // isValidTokenPath checks if the token path is registered in the system.
185 func isValidTokenPath(tokenPath string ) bool {
186 return common.IsRegistered(tokenPath) == nil
187 }
188
189 // parseAmounts converts amount strings to u256.Uint values.
190 func parseAmounts(amount0Desired, amount1Desired, amount0Min, amount1Min string ) (*u256.Uint, *u256.Uint, *u256.Uint, *u256.Uint) {
191 return u256.MustFromDecimal(amount0Desired), u256.MustFromDecimal(amount1Desired), u256.MustFromDecimal(amount0Min), u256.MustFromDecimal(amount1Min)
192 }
193