Kind Realm (renderable)
Name v1
Namespace gnoswap / position
Exported functions 1
Module gno.land/r/gnoswap/position/v1
gno 0.9 position.gno gno
⧉
1 package position 2 3 import ( 4 "chain" 5 6 "gno.land/p/gnoswap/gnsmath/v1" 7 u256 "gno.land/p/gnoswap/uint256/v1" 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.
"gno.land/p/gnoswap/utils/v1"
9 ufmt "gno.land/p/nt/ufmt/v0"
10 "gno.land/r/gnoswap/access/v1"
11 "gno.land/r/gnoswap/common"
12 "gno.land/r/gnoswap/emission"
13 "gno.land/r/gnoswap/halt/v1"
14 pl "gno.land/r/gnoswap/pool"
15 pos "gno.land/r/gnoswap/position"
16 "gno.land/r/gnoswap/referral/v1"
17 "gno.land/r/gnoswap/staker"
18 )
19
20 // Mint creates a new liquidity position NFT.
21 //
22 // Parameters:
23 // - _: Noncrossing implementation-call discriminator; pass 0.
24 // - rlm: Current realm context forwarded unchanged by the position proxy.
25 // - token0: token0 contract path for the pool
26 // - token1: token1 contract path for the pool
27 // - fee: pool fee tier used to select the pool
28 // - tickLower: lower tick boundary of the position's range
29 // - tickUpper: upper tick boundary of the position's range
30 // - amount0Desired: desired token0 amount, supplied as a decimal string
31 // - amount1Desired: desired token1 amount, supplied as a decimal string
32 // - amount0Min: minimum acceptable token0 amount for slippage protection
33 // - amount1Min: minimum acceptable token1 amount for slippage protection
34 // - deadline: Unix timestamp after which the transaction is rejected
35 // - mintTo: address that receives the newly minted position NFT
36 // - referrer: referral value submitted for registration with the caller
37 //
38 // Returns:
39 // - positionId: newly minted position NFT ID
40 // - liquidity: liquidity amount minted for the position
41 // - amount0: token0 amount actually deposited into the pool
42 // - amount1: token1 amount actually deposited into the pool
43 //
44 // Note: Slippage protection via amount0Min/amount1Min.
45 func (p *positionV1) Mint(
46 _ int ,
47 rlm realm,
48 token0 string ,
49 token1 string ,
50 fee uint32 ,
51 tickLower int32 ,
52 tickUpper int32 ,
53 amount0Desired string ,
54 amount1Desired string ,
55 amount0Min string ,
56 amount1Min string ,
57 deadline int64 ,
58 mintTo address,
59 referrer string ,
60 ) ( uint64 , string , string , string ) {
61 access.AssertIsRlmCurrent( 0 , rlm)
62
63 halt.AssertIsNotHaltedPosition()
64 access.AssertIsValidAddress(mintTo)
65
66 previousRealm := rlm.Previous()
67 caller := previousRealm.Address()
68
69 assertIsNotMintToStaker(mintTo)
70 assertValidNumberString(amount0Desired)
71 assertValidNumberString(amount1Desired)
72 assertValidNumberString(amount0Min)
73 assertValidNumberString(amount1Min)
74
75 // assert that the user has sent the correct amount of native coin
76 common.AssertIsNotHandleNativeCoin()
77 assertIsNotExpired(deadline)
78
79 actualReferrer := referral.TryRegister(cross(rlm), caller, referrer)
80
81 emission.MintAndDistributeGns(cross(rlm))
82
83 mintInput := MintInput{
84 token0: token0,
85 token1: token1,
86 fee: fee,
87 tickLower: tickLower,
88 tickUpper: tickUpper,
89 amount0Desired: amount0Desired,
90 amount1Desired: amount1Desired,
91 amount0Min: amount0Min,
92 amount1Min: amount1Min,
93 deadline: deadline,
94 mintTo: mintTo,
95 caller: caller,
96 }
97
98 processedInput, err := p.processMintInput(mintInput)
99 if err != nil {
100 panic(newErrorWithDetail(errInvalidInput, err.Error()))
101 }
102
103 // mint liquidity
104 params := newMintParams(processedInput, mintInput)
105 id, liquidity, amount0, amount1 := p.mint( 0 , rlm, params)
106
107 pool := mustGetPool(processedInput.poolPath)
108 positionLiquidity, err := p.GetPositionLiquidity(id)
109 if err != nil {
110 panic(err)
111 }
112 tickCumulative, secondsPerLiquidityCumulativeX128, observationTimestamp, err := currentPoolObservation(processedInput.poolPath)
113 if err != nil {
114 panic(err)
115 }
116
117 chain.Emit(
118 "Mint" ,
119 "prevAddr" , caller.String(),
120 "prevRealm" , previousRealm.PkgPath(),
121 "tickLower" , utils.FormatInt(processedInput.tickLower),
122 "tickUpper" , utils.FormatInt(processedInput.tickUpper),
123 "poolPath" , processedInput.poolPath,
124 "mintTo" , mintTo.String(),
125 "caller" , caller.String(),
126 "lpPositionId" , utils.FormatUint(id),
127 "liquidityDelta" , liquidity.ToString(),
128 "amount0" , amount0.ToString(),
129 "amount1" , amount1.ToString(),
130 "sqrtPriceX96" , pool.Slot0SqrtPriceX96().ToString(),
131 "positionLiquidity" , positionLiquidity,
132 "poolLiquidity" , pool.Liquidity().ToString(),
133 "token0Balance" , utils.FormatInt(pool.BalanceToken0()),
134 "token1Balance" , utils.FormatInt(pool.BalanceToken1()),
135 "tickCumulative" , utils.FormatInt(tickCumulative),
136 "secondsPerLiquidityCumulativeX128" , secondsPerLiquidityCumulativeX128,
137 "observationTimestamp" , utils.FormatInt(observationTimestamp),
138 "referrer" , actualReferrer,
139 )
140
141 return id, liquidity.ToString(), amount0.ToString(), amount1.ToString()
142 }
143
144 // IncreaseLiquidity increases liquidity of an existing position.
145 //
146 // Adds more liquidity to existing NFT position.
147 // Maintains same price range as original position.
148 // Calculates optimal token ratio for current price.
149 //
150 // Parameters:
151 // - _: Noncrossing implementation-call discriminator; pass 0.
152 // - rlm: Current realm context forwarded unchanged by the position proxy.
153 // - positionId: NFT token ID to increase
154 // - amount0DesiredStr: desired token0 amount, supplied as a decimal string
155 // - amount1DesiredStr: desired token1 amount, supplied as a decimal string
156 // - amount0MinStr: minimum acceptable token0 amount for slippage protection
157 // - amount1MinStr: minimum acceptable token1 amount for slippage protection
158 // - deadline: Unix timestamp after which the transaction is rejected
159 //
160 // Returns:
161 // - positionId: same NFT ID
162 // - liquidity: liquidity amount added (the delta, not total)
163 // - amount0: token0 amount actually deposited
164 // - amount1: token1 amount actually deposited
165 // - poolPath: pool identifier for the position
166 //
167 // Requirements:
168 // - Caller must own the position NFT
169 // - Sufficient token balances and approvals
170 func (p *positionV1) IncreaseLiquidity(
171 _ int ,
172 rlm realm,
173 positionId uint64 ,
174 amount0DesiredStr string ,
175 amount1DesiredStr string ,
176 amount0MinStr string ,
177 amount1MinStr string ,
178 deadline int64 ,
179 ) ( uint64 , string , string , string , string ) {
180 access.AssertIsRlmCurrent( 0 , rlm)
181
182 halt.AssertIsNotHaltedPosition()
183
184 previousRealm := rlm.Previous()
185 caller := previousRealm.Address()
186 assertIsOwnerForToken(p, positionId, caller)
187
188 assertValidNumberString(amount0DesiredStr)
189 assertValidNumberString(amount1DesiredStr)
190 assertValidNumberString(amount0MinStr)
191 assertValidNumberString(amount1MinStr)
192 assertIsNotExpired(deadline)
193
194 emission.MintAndDistributeGns(cross(rlm))
195
196 position := p.mustGetPosition(positionId)
197 token0, token1, _ := splitOf(position.PoolKey())
198
199 common.AssertIsNotHandleNativeCoin()
200
201 err := validateTokenPath(token0, token1)
202 if err != nil {
203 panic(newErrorWithDetail(err.Error(), ufmt.Sprintf( "token0(%s), token1(%s)" , token0, token1)))
204 }
205
206 amount0Desired, amount1Desired, amount0Min, amount1Min := parseAmounts(amount0DesiredStr, amount1DesiredStr, amount0MinStr, amount1MinStr)
207 increaseLiquidityParams := IncreaseLiquidityParams{
208 positionId: positionId,
209 amount0Desired: amount0Desired,
210 amount1Desired: amount1Desired,
211 amount0Min: amount0Min,
212 amount1Min: amount1Min,
213 deadline: deadline,
214 caller: caller,
215 }
216
217 _, liquidity, amount0, amount1, poolPath, err := p.increaseLiquidity( 0 , rlm, increaseLiquidityParams)
218 if err != nil {
219 panic(err)
220 }
221
222 pool := mustGetPool(poolPath)
223 positionLiquidity, err := p.GetPositionLiquidity(positionId)
224 if err != nil {
225 panic(err)
226 }
227 tickCumulative, secondsPerLiquidityCumulativeX128, observationTimestamp, err := currentPoolObservation(poolPath)
228 if err != nil {
229 panic(err)
230 }
231 chain.Emit(
232 "IncreaseLiquidity" ,
233 "prevAddr" , previousRealm.Address().String(),
234 "prevRealm" , previousRealm.PkgPath(),
235 "poolPath" , poolPath,
236 "tickLower" , utils.FormatInt(position.TickLower()),
237 "tickUpper" , utils.FormatInt(position.TickUpper()),
238 "caller" , caller.String(),
239 "lpPositionId" , utils.FormatUint(positionId),
240 "liquidityDelta" , liquidity.ToString(),
241 "amount0" , amount0.ToString(),
242 "amount1" , amount1.ToString(),
243 "sqrtPriceX96" , pool.Slot0SqrtPriceX96().ToString(),
244 "positionLiquidity" , positionLiquidity,
245 "poolLiquidity" , pool.Liquidity().ToString(),
246 "token0Balance" , utils.FormatInt(pool.BalanceToken0()),
247 "token1Balance" , utils.FormatInt(pool.BalanceToken1()),
248 "tickCumulative" , utils.FormatInt(tickCumulative),
249 "secondsPerLiquidityCumulativeX128" , secondsPerLiquidityCumulativeX128,
250 "observationTimestamp" , utils.FormatInt(observationTimestamp),
251 )
252
253 return positionId, liquidity.ToString(), amount0.ToString(), amount1.ToString(), poolPath
254 }
255
256 // DecreaseLiquidity decreases liquidity of an existing position.
257 //
258 // Removes liquidity but keeps NFT ownership. This is one atomic public
259 // operation: accrued swap fees are collected first, then liquidity is burned,
260 // and principal is collected through the pool's fee-free `Collect` path.
261 //
262 // Parameters:
263 // - _: Noncrossing implementation-call discriminator; pass 0.
264 // - rlm: Current realm context forwarded unchanged by the position proxy.
265 // - positionId: NFT token ID
266 // - liquidityStr: amount of liquidity to remove, supplied as a decimal string
267 // - amount0MinStr: minimum token0 principal to receive, for slippage protection
268 // - amount1MinStr: minimum token1 principal to receive, for slippage protection
269 // - deadline: Unix timestamp after which the transaction is rejected
270 //
271 // Returns:
272 // - positionId: same NFT ID
273 // - liquidity: amount of liquidity removed (the delta)
274 // - fee0: token0 fee amount returned net of the withdrawal fee
275 // - fee1: token1 fee amount returned net of the withdrawal fee
276 // - amount0: token0 principal collected without a withdrawal fee
277 // - amount1: token1 principal collected without a withdrawal fee
278 // - poolPath: pool identifier
279 //
280 // Amount-minimum checks apply to the principal actually collected.
281 func (p *positionV1) DecreaseLiquidity(
282 _ int ,
283 rlm realm,
284 positionId uint64 ,
285 liquidityStr string ,
286 amount0MinStr string ,
287 amount1MinStr string ,
288 deadline int64 ,
289 ) ( uint64 , string , string , string , string , string , string ) {
290 access.AssertIsRlmCurrent( 0 , rlm)
291
292 halt.AssertIsNotHaltedWithdraw()
293
294 previousRealm := rlm.Previous()
295 caller := previousRealm.Address()
296 assertIsOwnerForToken(p, positionId, caller)
297 assertIsNotExpired(deadline)
298 assertValidLiquidityAmount(liquidityStr)
299
300 emission.MintAndDistributeGns(cross(rlm))
301
302 amount0Min := u256.MustFromDecimal(amount0MinStr)
303 amount1Min := u256.MustFromDecimal(amount1MinStr)
304 decreaseLiquidityParams := DecreaseLiquidityParams{
305 positionId: positionId,
306 liquidity: liquidityStr,
307 amount0Min: amount0Min,
308 amount1Min: amount1Min,
309 deadline: deadline,
310 caller: caller,
311 }
312
313 position := p.mustGetPosition(positionId)
314 tickLower := position.TickLower()
315 tickUpper := position.TickUpper()
316
317 positionId, liquidity, fee0, fee1, amount0, amount1, poolPath, err := p.decreaseLiquidity( 0 , rlm, decreaseLiquidityParams)
318 if err != nil {
319 panic(err)
320 }
321
322 pool := mustGetPool(poolPath)
323 positionLiquidity, err := p.GetPositionLiquidity(positionId)
324 if err != nil {
325 panic(err)
326 }
327 tickCumulative, secondsPerLiquidityCumulativeX128, observationTimestamp, err := currentPoolObservation(poolPath)
328 if err != nil {
329 panic(err)
330 }
331 chain.Emit(
332 "DecreaseLiquidity" ,
333 "prevAddr" , previousRealm.Address().String(),
334 "prevRealm" , previousRealm.PkgPath(),
335 "lpPositionId" , utils.FormatUint(positionId),
336 "poolPath" , poolPath,
337 "tickLower" , utils.FormatInt(tickLower),
338 "tickUpper" , utils.FormatInt(tickUpper),
339 "liquidityDelta" , liquidity,
340 "feeAmount0" , fee0,
341 "feeAmount1" , fee1,
342 "amount0" , amount0,
343 "amount1" , amount1,
344 "sqrtPriceX96" , pool.Slot0SqrtPriceX96().ToString(),
345 "positionLiquidity" , positionLiquidity,
346 "poolLiquidity" , pool.Liquidity().ToString(),
347 "token0Balance" , utils.FormatInt(pool.BalanceToken0()),
348 "token1Balance" , utils.FormatInt(pool.BalanceToken1()),
349 "tickCumulative" , utils.FormatInt(tickCumulative),
350 "secondsPerLiquidityCumulativeX128" , secondsPerLiquidityCumulativeX128,
351 "observationTimestamp" , utils.FormatInt(observationTimestamp),
352 )
353
354 return positionId, liquidity, fee0, fee1, amount0, amount1, poolPath
355 }
356
357 // CollectFee collects swap fee from the position.
358 //
359 // Claims accumulated fees without removing liquidity.
360 // Useful for active positions earning ongoing fees.
361 // Applies the configured withdrawal fee to the collected swap fees.
362 //
363 // Parameters:
364 // - _: Noncrossing implementation-call discriminator; pass 0.
365 // - rlm: Current realm context forwarded unchanged by the position proxy.
366 // - positionId: NFT token ID whose accrued swap fees are collected
367 //
368 // Returns:
369 // - positionId: same NFT ID
370 // - tokensCollected0: token0 amount sent to caller after the withdrawal fee
371 // - tokensCollected1: token1 amount sent to caller after the withdrawal fee
372 // - poolPath: pool identifier
373 // - totalAmount0: raw token0 amount collected before the withdrawal fee
374 // - totalAmount1: raw token1 amount collected before the withdrawal fee
375 //
376 // Requirements:
377 // - Caller must own an unstaked NFT; a staked position uses its configured operator
378 // - If no fees are owed, the fee amounts returned are zero
379 func (p *positionV1) CollectFee(_ int , rlm realm, positionId uint64 ) ( uint64 , string , string , string , string , string ) {
380 access.AssertIsRlmCurrent( 0 , rlm)
381
382 halt.AssertIsNotHaltedWithdraw()
383
384 caller := rlm.Previous().Address()
385 assertIsOwnerOrOperatorForToken(p, positionId, caller)
386
387 emission.MintAndDistributeGns(cross(rlm))
388
389 return p.collectFee( 0 , rlm, positionId, caller)
390 }
391
392 // collectFee performs fee collection and withdrawal fee calculation.
393 func (p *positionV1) collectFee(_ int , rlm realm, positionId uint64 , caller address) ( uint64 , string , string , string , string , string ) {
394 // verify position
395 position := p.mustGetPosition(positionId)
396 token0, token1, fee := splitOf(position.PoolKey())
397
398 pl.Burn(
399 cross(rlm),
400 token0,
401 token1,
402 fee,
403 position.TickLower(),
404 position.TickUpper(),
405 "0" , // burn '0' liquidity to collect fee
406 caller,
407 )
408
409 currentFeeGrowth, err := p.getCurrentFeeGrowth(position, caller)
410 if err != nil {
411 panic(newErrorWithDetail(err.Error(), "failed to get current fee growth" ))
412 }
413
414 tokensOwed0, tokensOwed1 := p.calculateFees(position, currentFeeGrowth)
415
416 position.SetFeeGrowthInside0LastX128(currentFeeGrowth.feeGrowthInside0LastX128.ToString())
417 position.SetFeeGrowthInside1LastX128(currentFeeGrowth.feeGrowthInside1LastX128.ToString())
418
419 // Collect the fee. The pool withholds the withdrawal fee and pays the
420 // remainder straight to the caller.
421 amount0, amount1, fee0Str, fee1Str := pl.CollectSwapFee(
422 cross(rlm),
423 token0, token1, fee,
424 caller,
425 position.TickLower(), position.TickUpper(),
426 utils.FormatInt(tokensOwed0), utils.FormatInt(tokensOwed1),
427 )
428 amount0Uint256 := u256.MustFromDecimal(amount0)
429 amount1Uint256 := u256.MustFromDecimal(amount1)
430 amount0Int64 := gnsmath.SafeConvertToInt64(amount0Uint256)
431 amount1Int64 := gnsmath.SafeConvertToInt64(amount1Uint256)
432
433 // sometimes there will be a few less uBase amount than expected due to rounding down in core, but we just subtract the full amount expected
434 // instead of the actual amount so we can burn the token
435 if tokensOwed0 < amount0Int64 {
436 panic(newErrorWithDetail(errUnderflow, "tokensOwed0 - amount0 underflow" ))
437 }
438 position.SetTokensOwed0(gnsmath.SafeSubInt64(tokensOwed0, amount0Int64))
439
440 if tokensOwed1 < amount1Int64 {
441 panic(newErrorWithDetail(errUnderflow, "tokensOwed1 - amount1 underflow" ))
442 }
443 position.SetTokensOwed1(gnsmath.SafeSubInt64(tokensOwed1, amount1Int64))
444 p.mustUpdatePosition( 0 , rlm, positionId, *position)
445
446 // The pool already paid these out; recomputing them here only feeds the
447 // events and the return value.
448 amount0WithoutFeeStr := utils.FormatInt(gnsmath.SafeSubInt64(amount0Int64, utils.SafeParseInt64(fee0Str)))
449 amount1WithoutFeeStr := utils.FormatInt(gnsmath.SafeSubInt64(amount1Int64, utils.SafeParseInt64(fee1Str)))
450
451 poolPath := position.PoolKey()
452
453 previousRealm := rlm.Previous()
454 chain.Emit(
455 "CollectSwapFee" ,
456 "prevAddr" , previousRealm.Address().String(),
457 "prevRealm" , previousRealm.PkgPath(),
458 "lpPositionId" , utils.FormatUint(positionId),
459 "feeAmount0" , amount0WithoutFeeStr,
460 "feeAmount1" , amount1WithoutFeeStr,
461 "poolPath" , poolPath,
462 "poolTier" , utils.FormatUint(staker.GetPoolTier(poolPath)),
463 "feeGrowthInside0LastX128" , position.FeeGrowthInside0LastX128(),
464 "feeGrowthInside1LastX128" , position.FeeGrowthInside1LastX128(),
465 )
466
467 chain.Emit(
468 "WithdrawalFee" ,
469 "prevAddr" , previousRealm.Address().String(),
470 "prevRealm" , previousRealm.PkgPath(),
471 "lpTokenId" , utils.FormatUint(positionId),
472 "poolPath" , poolPath,
473 "feeAmount0" , fee0Str,
474 "feeAmount1" , fee1Str,
475 "amount0WithoutFee" , amount0WithoutFeeStr,
476 "amount1WithoutFee" , amount1WithoutFeeStr,
477 )
478
479 return positionId, amount0WithoutFeeStr, amount1WithoutFeeStr, position.PoolKey(), amount0, amount1
480 }
481
482 // SetPositionOperator sets an operator for a position.
483 // Only staker can call this function.
484 //
485 // Parameters:
486 // - _: Noncrossing implementation-call discriminator; pass 0.
487 // - rlm: Current realm context forwarded unchanged by the position proxy.
488 // - id: position NFT ID whose operator is changed
489 // - operator: valid address to approve, or the empty address to remove the operator
490 func (p *positionV1) SetPositionOperator(_ int , rlm realm, id uint64 , operator address) {
491 access.AssertIsRlmCurrent( 0 , rlm)
492
493 previousRealm := rlm.Previous()
494 access.AssertIsStaker(previousRealm.Address())
495
496 assertValidOperatorAddress(operator)
497
498 position := p.mustGetPosition(id)
499 prevOperator := position.Operator()
500 position.SetOperator(operator)
501
502 p.mustUpdatePosition( 0 , rlm, id, *position)
503
504 chain.Emit(
505 "SetPositionOperator" ,
506 "prevAddr" , previousRealm.Address().String(),
507 "prevRealm" , previousRealm.PkgPath(),
508 "lpPositionId" , utils.FormatUint(id),
509 "prevOperator" , prevOperator.String(),
510 "newOperator" , operator.String(),
511 )
512 }
513
514 // getCurrentFeeGrowth retrieves current fee growth values for a position.
515 func (p *positionV1) getCurrentFeeGrowth(position *pos.Position, owner address) (FeeGrowthInside, error ) {
516 positionKey := computePositionKey(position.TickLower(), position.TickUpper())
517 feeGrowthInside0LastX128, feeGrowthInside1LastX128, err := pl.GetPositionFeeGrowthInsideLastX128(position.PoolKey(), positionKey)
518 if err != nil {
519 return FeeGrowthInside{}, err
520 }
521
522 feeGrowthInside := FeeGrowthInside{
523 feeGrowthInside0LastX128: u256.MustFromDecimal(feeGrowthInside0LastX128),
524 feeGrowthInside1LastX128: u256.MustFromDecimal(feeGrowthInside1LastX128),
525 }
526
527 return feeGrowthInside, nil
528 }
529
530 // computePositionKey generates a compact deterministic key for a liquidity position.
531 func computePositionKey(tickLower, tickUpper int32 ) string {
532 return pl.EncodePositionKey(tickLower, tickUpper)
533 }
534
535 // calculatePositionBalances computes token balances for a position at current price.
536 // Returns calculated token0 and token1 balances based on position liquidity and price range.
537 func calculatePositionBalances(position *pos.Position) ( int64 , int64 ) {
538 liquidity := u256.MustFromDecimal(position.Liquidity())
539 if liquidity.IsZero() {
540 return 0 , 0
541 }
542
543 sqrtPriceX96, err := pl.GetSlot0SqrtPriceX96(position.PoolKey())
544 if err != nil {
545 panic(err)
546 }
547 token0Balance, token1Balance := gnsmath.GetAmountsForLiquidity(
548 u256.MustFromDecimal(sqrtPriceX96), // currentSqrtPriceX96
549 gnsmath.TickMathGetSqrtRatioAtTick(position.TickLower()),
550 gnsmath.TickMathGetSqrtRatioAtTick(position.TickUpper()),
551 liquidity,
552 )
553
554 return gnsmath.SafeConvertToInt64(token0Balance), gnsmath.SafeConvertToInt64(token1Balance)
555 }
556