PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnoswap/position/v1

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v1
Namespace
gnoswap / position
Files
17 (README)(gnomod.toml)
Exported functions
1
Module
gno.land/r/gnoswap/position/v1
gno
0.9

Files (17)

  • README.mdmarkdown
  • gnomod.tomltoml
  • assert.gnogno
  • burn.gnogno
  • doc.gnogno
  • errors.gnogno
  • getter.gnogno
  • init.gnogno
  • instance.gnogno
  • liquidity_management.gnogno
  • manager.gnogno
  • mint.gnogno
  • position.gnogno
  • render.gnogno
  • reposition.gnogno
  • type.gnogno
  • utils.gnogno
  • position.gnogno
    1package position23import (4	"chain"56	"gno.land/p/gnoswap/gnsmath/v1"7	u256 "gno.land/p/gnoswap/uint256/v1"8

    Functions

    • 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.

    Rendered

    RenderedRawgnoweb ↗
    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.
    45func (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
    170func (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.
    281func (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
    379func (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.
    393func (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
    490func (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.
    515func (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.
    531func 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.
    537func 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