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/pool/v1

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v1
Namespace
gnoswap / pool
Files
24 (README)(gnomod.toml)
Exported functions
3
Module
gno.land/r/gnoswap/pool/v1
gno
0.9

Files (24)

  • README.mdmarkdown
  • gnomod.tomltoml
  • assert.gnogno
  • doc.gnogno
  • errors.gnogno
  • event_info.gnogno
  • factory_param.gnogno
  • getter_utils.gnogno
  • getter.gnogno
  • init.gnogno
  • instance.gnogno
  • lock.gnogno
  • manager.gnogno
  • oracle.gnogno
  • pool.gnogno
  • position.gnogno
  • protocol_fee.gnogno
  • render.gnogno
  • swap.gnogno
  • tick_bitmap.gnogno
  • tick.gnogno
  • transfer.gnogno
  • type.gnogno
  • utils.gnogno
  • pool.gnogno
    1package pool23import (4	"chain"5	"errors"67	"gno.land/r/gnoswap/common"8

    Functions

    • GetPoolPath(token0Path string, token1Path string, fee uint32) string

    • NewPoolV1(store interface {GetFeeAmountTickSpacing func() map[uint32]int32; GetObservations func() *gno.land/p/nt/bptree/v0.BPTree; GetPendingProtocolFee func(string) int64; GetPendingProtocolFees func() map[string]int64; GetPoolCreationFee func() int64; GetPools func() *gno.land/p/nt/bptree/v0.BPTree; GetSlot0FeeProtocol func() uint8; GetSwapEndHook func() func(.uverse.realm, string) .uverse.error; GetSwapStartHook func() func(.uverse.realm, string, int64); GetTickCrossHook func() func(.uverse.realm, string, int32, bool, int64); GetUnlocked func() bool; GetWithdrawalFeeBPS func() uint64; HasFeeAmountTickSpacing func() bool; HasObservations func() bool; HasPendingProtocolFees func() bool; HasPoolCreationFee func() bool; HasPools func() bool; HasSlot0FeeProtocol func() bool; HasSwapEndHook func() bool; HasSwapStartHook func() bool; HasTickCrossHook func() bool; HasUnlocked func() bool; HasWithdrawalFeeBPS func() bool; RemovePendingProtocolFee func(int, .uverse.realm, string) .uverse.error; SetFeeAmountTickSpacing func(int, .uverse.realm, map[uint32]int32) .uverse.error; SetObservations func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetPendingProtocolFee func(int, .uverse.realm, string, int64) .uverse.error; SetPendingProtocolFees func(int, .uverse.realm, map[string]int64) .uverse.error; SetPoolCreationFee func(int, .uverse.realm, int64) .uverse.error; SetPools func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetSlot0FeeProtocol func(int, .uverse.realm, uint8) .uverse.error; SetSwapEndHook func(int, .uverse.realm, func(.uverse.realm, string) .uverse.error) .uverse.error; SetSwapStartHook func(int, .uverse.realm, func(.uverse.realm, string, int64)) .uverse.error; SetTickCrossHook func(int, .uverse.realm, func(.uverse.realm, string, int32, bool, int64)) .uverse.error; SetUnlocked func(int, .uverse.realm, bool) .uverse.error; SetWithdrawalFeeBPS func(int, .uverse.realm, uint64) .uverse.error}) interface {Burn func(int, .uverse.realm, string, string, uint32, int32, int32, string, .uverse.address) (string, string); Collect func(int, .uverse.realm, string, string, uint32, .uverse.address, int32, int32, string, string) (string, string); CollectProtocol func(int, .uverse.realm, string, string, uint32, .uverse.address, string, string) (string, string); CollectSwapFee func(int, .uverse.realm, string, string, uint32, .uverse.address, int32, int32, string, string) (string, string, string, string); CreatePool func(int, .uverse.realm, string, string, uint32, string); DrySwap func(string, string, uint32, bool, string, string) (string, string, .uverse.error); ExistsPoolPath func(string) bool; GetBalanceToken0 func(string) (int64, .uverse.error); GetBalanceToken1 func(string) (int64, .uverse.error); GetFee func(string) (uint32, .uverse.error); GetFeeAmountTickSpacing func(uint32) (int32, .uverse.error); GetFeeAmountTickSpacings func() map[uint32]int32; GetFeeGrowthGlobal0X128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetFeeGrowthGlobal1X128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetFeeGrowthGlobalX128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, *gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetInitializedTicksInRange func(string, int32, int32) ([]int32, .uverse.error); GetLiquidity func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetObservationAt func(string, uint16) (gno.land/r/gnoswap/pool.Observation, .uverse.error); GetPendingProtocolFees func() map[string]int64; GetPoolCreationFee func() int64; GetPoolPositions func(string) *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetPools func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetPositionFeeGrowthInside0LastX128 func(string, string) (string, .uverse.error); GetPositionFeeGrowthInside1LastX128 func(string, string) (string, .uverse.error); GetPositionFeeGrowthInsideLastX128 func(string, string) (string, string, .uverse.error); GetPositionLiquidity func(string, string) (string, .uverse.error); GetPositionTokensOwed0 func(string, string) (int64, .uverse.error); GetPositionTokensOwed1 func(string, string) (int64, .uverse.error); GetProtocolFeesToken0 func(string) (int64, .uverse.error); GetProtocolFeesToken1 func(string) (int64, .uverse.error); GetSlot0 func(string) gno.land/r/gnoswap/pool.Slot0; GetSlot0FeeProtocol func(string) (uint8, .uverse.error); GetSlot0SqrtPriceX96 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetSlot0Tick func(string) (int32, .uverse.error); GetSlot0Unlocked func(string) (bool, .uverse.error); GetTickBitmaps func(string, int16) (string, .uverse.error); GetTickCumulativeOutside func(string, int32) (int64, .uverse.error); GetTickFeeGrowthOutside0X128 func(string, int32) (string, .uverse.error); GetTickFeeGrowthOutside1X128 func(string, int32) (string, .uverse.error); GetTickFeeGrowthOutsideX128 func(string, int32) (string, string, .uverse.error); GetTickInfo func(string, int32) (gno.land/r/gnoswap/pool.TickInfo, .uverse.error); GetTickInitialized func(string, int32) (bool, .uverse.error); GetTickLiquidityGross func(string, int32) (string, .uverse.error); GetTickLiquidityNet func(string, int32) (string, .uverse.error); GetTickSecondsOutside func(string, int32) (uint32, .uverse.error); GetTickSecondsPerLiquidityOutsideX128 func(string, int32) (string, .uverse.error); GetTickSpacing func(string) (int32, .uverse.error); GetToken0Path func(string) (string, .uverse.error); GetToken1Path func(string) (string, .uverse.error); GetWithdrawalFee func() uint64; IncreaseObservationCardinalityNext func(int, .uverse.realm, string, string, uint32, uint16); Mint func(int, .uverse.realm, string, string, uint32, int32, int32, string, .uverse.address) (string, string); Observe func(string, []uint32) ([]int64, []string, .uverse.error); OracleConsult func(string, uint32) (int32, *gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); Render func(string) string; SetFeeProtocol func(int, .uverse.realm, uint8, uint8); SetPoolCreationFee func(int, .uverse.realm, int64); SetSwapEndHook func(int, .uverse.realm, func(.uverse.realm, string) .uverse.error); SetSwapStartHook func(int, .uverse.realm, func(.uverse.realm, string, int64)); SetTickCrossHook func(int, .uverse.realm, func(.uverse.realm, string, int32, bool, int64)); SetWithdrawalFee func(int, .uverse.realm, uint64); SnapshotCumulativesInside func(string, int32, int32) (int64, *gno.land/p/gnoswap/uint256/v1.Uint, uint32, .uverse.error); Swap func(int, .uverse.realm, string, string, uint32, .uverse.address, bool, string, string, func(.uverse.realm, int64, int64, *gno.land/r/gnoswap/pool.CallbackMarker) .uverse.error) (string, string)}

    • NewTickEventInfo(tickID int32, tickInfo struct{liquidityGross string; liquidityNet string; feeGrowthOutside0X128 string; feeGrowthOutside1X128 string; tickCumulativeOutside int64; secondsPerLiquidityOutsideX128 string; secondsOutside uint32; initialized bool}) *gno.land/r/gnoswap/pool/v1.tickEventInfo

    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/r/gnoswap/halt/v1"
    9 pl "gno.land/r/gnoswap/pool"
    10
    11 "gno.land/p/gnoswap/gnsmath/v1"
    12 i256 "gno.land/p/gnoswap/int256/v1"
    13 u256 "gno.land/p/gnoswap/uint256/v1"
    14 "gno.land/p/gnoswap/utils/v1"
    15 ufmt "gno.land/p/nt/ufmt/v0"
    16
    17 prabc "gno.land/p/gnoswap/rbac/v1"
    18 _ "gno.land/r/gnoswap/rbac/v1"
    19
    20 "gno.land/r/gnoswap/access/v1"
    21)
    22
    23// Mint adds liquidity to a pool position.
    24//
    25// Increases liquidity for a position within specified tick range.
    26// Calculates required token amounts based on current pool price.
    27// Updates tick state and transfers tokens atomically.
    28//
    29// Parameters:
    30// - _: Noncrossing implementation-call discriminator; pass 0.
    31// - rlm: Current realm context forwarded unchanged by the pool proxy.
    32// - token0Path: Registered token contract path for token0.
    33// - token1Path: Registered token contract path for token1.
    34// - fee: Fee tier; supported tiers are 100, 500, 3000, and 10000 (0.01%, 0.05%, 0.3%, and 1%).
    35// - tickLower: Inclusive lower price-range tick; must be aligned to the pool tick spacing.
    36// - tickUpper: Exclusive upper price-range tick; must be aligned to the pool tick spacing.
    37// - liquidityAmount: Positive decimal string specifying liquidity to add; it is converted to uint256 and then int128.
    38// - positionCaller: Nonzero position-contract address that supplies tokens for this mint.
    39//
    40// Returns:
    41// - amount0: Token0 amount consumed, returned as a decimal string.
    42// - amount1: Token1 amount consumed, returned as a decimal string.
    43//
    44// Requirements:
    45// - Pool must exist for token pair and fee
    46// - Liquidity amount must be positive
    47// - Ticks must be valid and aligned to spacing
    48//
    49// Only callable by position contract.
    50func (i *poolV1) Mint(
    51 _ int,
    52 rlm realm,
    53 token0Path string,
    54 token1Path string,
    55 fee uint32,
    56 tickLower int32,
    57 tickUpper int32,
    58 liquidityAmount string,
    59 positionCaller address,
    60) (string, string) {
    61 access.AssertIsRlmCurrent(0, rlm)
    62
    63 i.assertPoolUnlocked()
    64 halt.AssertIsNotHaltedPool()
    65
    66 caller := rlm.Previous().Address()
    67 access.AssertIsPosition(caller)
    68 access.AssertIsValidAddress(positionCaller)
    69
    70 i.lockPool(0, rlm)
    71 defer i.unlockPool(0, rlm)
    72
    73 liquidity := u256.MustFromDecimal(liquidityAmount)
    74 if liquidity.IsZero() {
    75 panic(errors.New(errZeroLiquidity))
    76 }
    77
    78 pool := i.mustGetPoolBy(token0Path, token1Path, fee)
    79
    80 tickSpacing := pool.TickSpacing()
    81 checkTickSpacing(tickLower, tickSpacing)
    82 checkTickSpacing(tickUpper, tickSpacing)
    83
    84 liquidityDelta := gnsmath.SafeConvertToInt128(liquidity)
    85 positionParam := newModifyPositionParams(positionCaller, tickLower, tickUpper, liquidityDelta)
    86 observations := i.mustGetObservations(pool.PoolPath())
    87 _, amount0, amount1, err := modifyPosition(pool, observations, positionParam)
    88 if err != nil {
    89 panic(err)
    90 }
    91
    92 poolAddr := access.MustGetAddress(prabc.ROLE_POOL.String())
    93
    94 if amount0.Gt(u256.Zero()) {
    95 i.safeTransferFrom(0, rlm, pool, positionCaller, poolAddr, pool.Token0Path(), amount0, true)
    96 }
    97
    98 if amount1.Gt(u256.Zero()) {
    99 i.safeTransferFrom(0, rlm, pool, positionCaller, poolAddr, pool.Token1Path(), amount1, false)
    100 }
    101
    102 // Save pool state after modifyPosition may have updated liquidity
    103 err = i.savePool(0, rlm, pool)
    104 if err != nil {
    105 panic(err)
    106 }
    107
    108 return amount0.ToString(), amount1.ToString()
    109}
    110
    111// Burn removes liquidity from a pool position and credits principal as tokens
    112// owed to that pool-level position entry.
    113//
    114// The pool operation itself does not transfer tokens; a subsequent Collect call
    115// pays the credited principal. Position.DecreaseLiquidity wraps Burn and the
    116// fee-free Collect call within one atomic public operation.
    117//
    118// Parameters:
    119// - _: Noncrossing implementation-call discriminator; pass 0.
    120// - rlm: Current realm context forwarded unchanged by the pool proxy.
    121// - token0Path: Registered token contract path for token0.
    122// - token1Path: Registered token contract path for token1.
    123// - fee: Fee tier identifying the pool.
    124// - tickLower: Lower tick of the position's price range; must match pool spacing.
    125// - tickUpper: Upper tick of the position's price range; must match pool spacing.
    126// - liquidityAmount: Non-negative decimal liquidity amount, at most 2^127-1 after conversion to int128.
    127// - positionCaller: Nonzero position-contract address associated with the pool position.
    128//
    129// Returns:
    130// - amount0: Token0 principal credited to the pool position, as a decimal string.
    131// - amount1: Token1 principal credited to the pool position, as a decimal string.
    132//
    133// Note: Tokens remain in pool until Collect is called.
    134// Only callable by position contract.
    135func (i *poolV1) Burn(
    136 _ int,
    137 rlm realm,
    138 token0Path string,
    139 token1Path string,
    140 fee uint32,
    141 tickLower int32,
    142 tickUpper int32,
    143 liquidityAmount string, // parsed as uint256, then checked against int128
    144 positionCaller address,
    145) (string, string) {
    146 access.AssertIsRlmCurrent(0, rlm)
    147
    148 i.assertPoolUnlocked()
    149 halt.AssertIsNotHaltedWithdraw()
    150
    151 caller := rlm.Previous().Address()
    152 access.AssertIsPosition(caller)
    153 access.AssertIsValidAddress(positionCaller)
    154
    155 i.lockPool(0, rlm)
    156 defer i.unlockPool(0, rlm)
    157
    158 liqAmount := u256.MustFromDecimal(liquidityAmount)
    159 liqAmountInt256 := gnsmath.SafeConvertToInt128(liqAmount)
    160 liqDelta := i256.Zero().Neg(liqAmountInt256)
    161
    162 posParams := newModifyPositionParams(positionCaller, tickLower, tickUpper, liqDelta)
    163 pool := i.mustGetPoolBy(token0Path, token1Path, fee)
    164 observations := i.mustGetObservations(pool.PoolPath())
    165 position, amount0, amount1, err := modifyPosition(pool, observations, posParams)
    166 if err != nil {
    167 panic(err)
    168 }
    169
    170 if amount0.Gt(u256.Zero()) || amount1.Gt(u256.Zero()) {
    171 amount0 = toUint128(amount0)
    172 amount1 = toUint128(amount1)
    173
    174 position.SetTokensOwed0(gnsmath.SafeAddInt64(position.TokensOwed0(), gnsmath.SafeConvertToInt64(amount0)))
    175 position.SetTokensOwed1(gnsmath.SafeAddInt64(position.TokensOwed1(), gnsmath.SafeConvertToInt64(amount1)))
    176 }
    177
    178 positionKey := getPositionKey(tickLower, tickUpper)
    179
    180 setPosition(pool, positionKey, position)
    181
    182 err = i.savePool(0, rlm, pool)
    183 if err != nil {
    184 panic(err)
    185 }
    186
    187 // actual token transfer happens in Collect()
    188 return amount0.ToString(), amount1.ToString()
    189}
    190
    191// CollectSwapFee pays out accrued swap fees for a position.
    192//
    193// The withdrawal fee is deducted from the collected amount and settled to the
    194// protocol fee realm; the remainder is transferred to recipient. Only accrued
    195// swap fees belong on this path - principal returned by Burn is not fee
    196// bearing and must go through Collect.
    197//
    198// Parameters:
    199// - _: Noncrossing implementation-call discriminator; pass 0.
    200// - rlm: Current realm context forwarded unchanged by the pool proxy.
    201// - token0Path: Registered token contract path for token0.
    202// - token1Path: Registered token contract path for token1.
    203// - fee: Fee tier identifying the pool.
    204// - recipient: Nonzero address receiving the post-fee token amounts.
    205// - tickLower: Lower tick of the position's price range.
    206// - tickUpper: Upper tick of the position's price range.
    207// - amount0Requested: Non-negative decimal int64 amount of token0 requested; the int64 maximum requests all owed token0.
    208// - amount1Requested: Non-negative decimal int64 amount of token1 requested; the int64 maximum requests all owed token1.
    209//
    210// Returns:
    211// - amount0: Token0 amount collected before withdrawal-fee deduction, as a decimal string.
    212// - amount1: Token1 amount collected before withdrawal-fee deduction, as a decimal string.
    213// - fee0: Withdrawal fee withheld from token0, as a decimal string.
    214// - fee1: Withdrawal fee withheld from token1, as a decimal string.
    215//
    216// Only callable by position contract.
    217func (i *poolV1) CollectSwapFee(
    218 _ int,
    219 rlm realm,
    220 token0Path string,
    221 token1Path string,
    222 fee uint32,
    223 recipient address,
    224 tickLower int32,
    225 tickUpper int32,
    226 amount0Requested string,
    227 amount1Requested string,
    228) (amount0, amount1, fee0, fee1 string) {
    229 collected0, collected1, withheld0, withheld1 := i.collect(
    230 0, rlm,
    231 token0Path, token1Path, fee,
    232 recipient,
    233 tickLower, tickUpper,
    234 amount0Requested, amount1Requested,
    235 true,
    236 )
    237
    238 return utils.FormatInt(collected0), utils.FormatInt(collected1),
    239 utils.FormatInt(withheld0), utils.FormatInt(withheld1)
    240}
    241
    242// Collect pays out tokens owed to a position, transferring the full amount to
    243// recipient.
    244//
    245// No withdrawal fee is charged: this path carries principal credited by Burn,
    246// which the protocol does not tax. Accrued swap fees belong on CollectSwapFee.
    247//
    248// Parameters:
    249// - _: Noncrossing implementation-call discriminator; pass 0.
    250// - rlm: Current realm context forwarded unchanged by the pool proxy.
    251// - token0Path: Registered token contract path for token0.
    252// - token1Path: Registered token contract path for token1.
    253// - fee: Fee tier identifying the pool.
    254// - recipient: Nonzero address receiving the owed token amounts.
    255// - tickLower: Lower tick of the position's price range.
    256// - tickUpper: Upper tick of the position's price range.
    257// - amount0Requested: Non-negative decimal int64 amount of token0 requested; the int64 maximum requests all owed token0.
    258// - amount1Requested: Non-negative decimal int64 amount of token1 requested; the int64 maximum requests all owed token1.
    259//
    260// Returns:
    261// - amount0: Token0 principal transferred to recipient, as a decimal string.
    262// - amount1: Token1 principal transferred to recipient, as a decimal string.
    263//
    264// Only callable by position contract.
    265func (i *poolV1) Collect(
    266 _ int,
    267 rlm realm,
    268 token0Path string,
    269 token1Path string,
    270 fee uint32,
    271 recipient address,
    272 tickLower int32,
    273 tickUpper int32,
    274 amount0Requested string,
    275 amount1Requested string,
    276) (amount0, amount1 string) {
    277 collected0, collected1, _, _ := i.collect(
    278 0, rlm,
    279 token0Path, token1Path, fee,
    280 recipient,
    281 tickLower, tickUpper,
    282 amount0Requested, amount1Requested,
    283 false,
    284 )
    285
    286 return utils.FormatInt(collected0), utils.FormatInt(collected1)
    287}
    288
    289// collect settles the pool ledger for a position and pays the recipient.
    290//
    291// The payout is capped by the position's tokensOwed, never by a caller
    292// supplied number, and the ledger is written before any token leaves the
    293// realm. Both happen under a single pool lock, so the pool's accounting and
    294// its token balance are never observably out of step.
    295//
    296// The collected amount is NOT capped by the pool's internal balance: a balance
    297// short of the owed amount means the internal ledger has drifted, so the call
    298// reverts rather than silently paying out less.
    299//
    300// applyWithdrawalFee selects the fee policy; pass false for a fee-free payout.
    301// It is the entry point, not the configured rate, that decides whether the
    302// protocol fee is settled: a rate of zero must still flush a backlog left
    303// pending by an earlier halted settlement.
    304func (i *poolV1) collect(
    305 _ int,
    306 rlm realm,
    307 token0Path string,
    308 token1Path string,
    309 fee uint32,
    310 recipient address,
    311 tickLower int32,
    312 tickUpper int32,
    313 amount0Requested string,
    314 amount1Requested string,
    315 applyWithdrawalFee bool,
    316) (amount0, amount1, fee0, fee1 int64) {
    317 access.AssertIsRlmCurrent(0, rlm)
    318
    319 i.assertPoolUnlocked()
    320 halt.AssertIsNotHaltedWithdraw()
    321
    322 caller := rlm.Previous().Address()
    323 access.AssertIsPosition(caller)
    324 access.AssertIsValidAddress(recipient)
    325
    326 i.lockPool(0, rlm)
    327 defer i.unlockPool(0, rlm)
    328
    329 amount0Req := utils.SafeParseInt64(amount0Requested)
    330 amount1Req := utils.SafeParseInt64(amount1Requested)
    331
    332 if amount0Req < 0 || amount1Req < 0 {
    333 panic(errors.New(errInvalidInput))
    334 }
    335
    336 pool := i.mustGetPoolBy(token0Path, token1Path, fee)
    337 // The pool position key encodes only the lower and upper ticks. It is scoped
    338 // to this pool, so NFTs sharing a range use the same aggregate pool entry.
    339 positionKey := getPositionKey(tickLower, tickUpper)
    340 position, err := pool.GetPosition(positionKey)
    341 if err != nil {
    342 panic(newErrorWithDetail(
    343 errDataNotFound,
    344 ufmt.Sprintf("positionKey(%s) does not exist", positionKey),
    345 ))
    346 }
    347
    348 amount0 = minRequestedAmount(amount0Req, position.TokensOwed0())
    349 amount1 = minRequestedAmount(amount1Req, position.TokensOwed1())
    350
    351 if amount0 > 0 {
    352 tokenOwed0 := gnsmath.SafeSubInt64(position.TokensOwed0(), amount0)
    353 token0Balance, err := updatePoolBalance(pool.BalanceToken0(), pool.BalanceToken1(), amount0, true)
    354 if err != nil {
    355 panic(err)
    356 }
    357
    358 position.SetTokensOwed0(tokenOwed0)
    359 pool.SetBalanceToken0(token0Balance)
    360 }
    361 if amount1 > 0 {
    362 tokenOwed1 := gnsmath.SafeSubInt64(position.TokensOwed1(), amount1)
    363 token1Balance, err := updatePoolBalance(pool.BalanceToken0(), pool.BalanceToken1(), amount1, false)
    364 if err != nil {
    365 panic(err)
    366 }
    367
    368 position.SetTokensOwed1(tokenOwed1)
    369 pool.SetBalanceToken1(token1Balance)
    370 }
    371
    372 setPosition(pool, positionKey, position)
    373
    374 if err := i.savePool(0, rlm, pool); err != nil {
    375 panic(err)
    376 }
    377
    378 withdrawalFeeBPS := ZeroBps
    379 if applyWithdrawalFee {
    380 withdrawalFeeBPS = i.store.GetWithdrawalFeeBPS()
    381 }
    382
    383 // Effects are persisted above; everything below leaves the realm.
    384 fee0, amount0AfterFee := deductWithdrawalFee(amount0, withdrawalFeeBPS)
    385 fee1, amount1AfterFee := deductWithdrawalFee(amount1, withdrawalFeeBPS)
    386
    387 if applyWithdrawalFee {
    388 // Called even for a zero fee, and for a zero withdrawal fee rate, so that
    389 // anything left pending for these tokens by an earlier halted settlement
    390 // is flushed.
    391 i.settleProtocolFee(0, rlm, token0Path, fee0)
    392 i.settleProtocolFee(0, rlm, token1Path, fee1)
    393 }
    394
    395 if amount0AfterFee > 0 {
    396 common.SafeGRC20Transfer(0, rlm, token0Path, recipient, amount0AfterFee)
    397 }
    398 if amount1AfterFee > 0 {
    399 common.SafeGRC20Transfer(0, rlm, token1Path, recipient, amount1AfterFee)
    400 }
    401
    402 return amount0, amount1, fee0, fee1
    403}
    404
    405// CollectProtocol collects accumulated protocol fees from swap operations.
    406// Only callable by admin or governance.
    407//
    408// Parameters:
    409// - _: Noncrossing implementation-call discriminator; pass 0.
    410// - rlm: Current realm context forwarded unchanged by the pool proxy.
    411// - token0Path: Registered token contract path for token0.
    412// - token1Path: Registered token contract path for token1.
    413// - fee: Fee tier identifying the pool.
    414// - recipient: Nonzero address receiving the collected protocol fees.
    415// - amount0Requested: Non-negative decimal amount of token0 protocol fees requested; capped at the available balance.
    416// - amount1Requested: Non-negative decimal amount of token1 protocol fees requested; capped at the available balance.
    417//
    418// Returns:
    419// - amount0: Token0 protocol fees transferred to recipient, as a decimal string.
    420// - amount1: Token1 protocol fees transferred to recipient, as a decimal string.
    421func (i *poolV1) CollectProtocol(
    422 _ int,
    423 rlm realm,
    424 token0Path string,
    425 token1Path string,
    426 fee uint32,
    427 recipient address,
    428 amount0Requested string, // uint128
    429 amount1Requested string, // uint128
    430) (string, string) {
    431 access.AssertIsRlmCurrent(0, rlm)
    432
    433 i.assertPoolUnlocked()
    434 halt.AssertIsNotHaltedWithdraw()
    435
    436 previousRealm := rlm.Previous()
    437 caller := previousRealm.Address()
    438 access.AssertIsAdminOrGovernance(caller)
    439
    440 common.MustRegistered(token0Path, token1Path)
    441
    442 i.lockPool(0, rlm)
    443 defer i.unlockPool(0, rlm)
    444
    445 amount0, amount1 := i.collectProtocol(
    446 0,
    447 rlm,
    448 token0Path,
    449 token1Path,
    450 fee,
    451 recipient,
    452 amount0Requested,
    453 amount1Requested,
    454 )
    455
    456 chain.Emit(
    457 "CollectProtocol",
    458 "prevAddr", caller.String(),
    459 "prevRealm", previousRealm.PkgPath(),
    460 "token0Path", token0Path,
    461 "token1Path", token1Path,
    462 "fee", utils.FormatUint(fee),
    463 "recipient", recipient.String(),
    464 "internal_amount0", amount0,
    465 "internal_amount1", amount1,
    466 )
    467
    468 return amount0, amount1
    469}
    470
    471// collectProtocol performs the actual protocol fee collection.
    472// It ensures requested amounts don't exceed available protocol fees.
    473// Returns amount0, amount1 as strings representing collected fees.
    474func (i *poolV1) collectProtocol(
    475 _ int,
    476 rlm realm,
    477 token0Path string,
    478 token1Path string,
    479 fee uint32,
    480 recipient address,
    481 amount0Requested string,
    482 amount1Requested string,
    483) (string, string) {
    484 pool := i.mustGetPoolBy(token0Path, token1Path, fee)
    485
    486 amount0Req := utils.SafeParseInt64(amount0Requested)
    487 amount1Req := utils.SafeParseInt64(amount1Requested)
    488
    489 if amount0Req < 0 || amount1Req < 0 {
    490 panic(errors.New(errInvalidInput))
    491 }
    492
    493 amount0 := minRequestedAmount(amount0Req, pool.ProtocolFeesToken0())
    494 amount1 := minRequestedAmount(amount1Req, pool.ProtocolFeesToken1())
    495
    496 amount0, amount1 = i.saveProtocolFees(pool, amount0, amount1)
    497
    498 newBalanceToken0, err := updatePoolBalance(pool.BalanceToken0(), pool.BalanceToken1(), amount0, true)
    499 if err != nil {
    500 panic(err)
    501 }
    502 pool.SetBalanceToken0(newBalanceToken0)
    503
    504 newBalanceToken1, err := updatePoolBalance(pool.BalanceToken0(), pool.BalanceToken1(), amount1, false)
    505 if err != nil {
    506 panic(err)
    507 }
    508 pool.SetBalanceToken1(newBalanceToken1)
    509
    510 err = i.savePool(0, rlm, pool)
    511 if err != nil {
    512 panic(err)
    513 }
    514
    515 common.SafeGRC20Transfer(0, rlm, pool.Token0Path(), recipient, amount0)
    516 common.SafeGRC20Transfer(0, rlm, pool.Token1Path(), recipient, amount1)
    517
    518 return utils.FormatInt(amount0), utils.FormatInt(amount1)
    519}
    520
    521// saveProtocolFees updates the protocol fee balances after collection.
    522// Returns amount0, amount1 representing the fees deducted from protocol reserves.
    523func (i *poolV1) saveProtocolFees(pool *pl.Pool, amount0, amount1 int64) (int64, int64) {
    524 if pool.ProtocolFeesToken0() < amount0 {
    525 panic(errors.New(errUnderflow))
    526 }
    527 pool.SetProtocolFeesToken0(gnsmath.SafeSubInt64(pool.ProtocolFeesToken0(), amount0))
    528
    529 if pool.ProtocolFeesToken1() < amount1 {
    530 panic(errors.New(errUnderflow))
    531 }
    532 pool.SetProtocolFeesToken1(gnsmath.SafeSubInt64(pool.ProtocolFeesToken1(), amount1))
    533
    534 return amount0, amount1
    535}
    536
    537func minRequestedAmount(request, available int64) int64 {
    538 if request > available {
    539 return available
    540 }
    541
    542 return request
    543}
    544
    545// IncreaseObservationCardinalityNext schedules growth of a pool's circular observation buffer.
    546//
    547// Parameters:
    548// - _: Noncrossing implementation-call discriminator; pass 0.
    549// - rlm: Current realm context forwarded unchanged by the pool proxy.
    550// - token0Path: Registered token contract path for token0.
    551// - token1Path: Registered token contract path for token1.
    552// - fee: Fee tier identifying the pool.
    553// - cardinalityNext: Requested observation-buffer capacity; must not exceed the configured maximum.
    554func (i *poolV1) IncreaseObservationCardinalityNext(
    555 _ int,
    556 rlm realm,
    557 token0Path string,
    558 token1Path string,
    559 fee uint32,
    560 cardinalityNext uint16,
    561) {
    562 access.AssertIsRlmCurrent(0, rlm)
    563
    564 i.assertPoolUnlocked()
    565 halt.AssertIsNotHaltedPool()
    566
    567 pool := i.mustGetPoolBy(token0Path, token1Path, fee)
    568 slot0Before := pool.Slot0()
    569 observationCardinalityNextOld := slot0Before.ObservationCardinalityNext()
    570
    571 i.lockPool(0, rlm)
    572 defer i.unlockPool(0, rlm)
    573
    574 observations := i.mustGetObservations(pool.PoolPath())
    575 if cardinalityNext > maxObservationCardinality {
    576 panic("observation cardinality next exceeds maximum")
    577 }
    578 observationCardinalityNextNew, err := grow(
    579 observations,
    580 slot0Before.ObservationCardinalityNext(),
    581 cardinalityNext,
    582 )
    583 if err != nil {
    584 panic(err)
    585 }
    586 slot0After := pool.Slot0()
    587 slot0After.SetObservationCardinalityNext(observationCardinalityNextNew)
    588 pool.SetSlot0(slot0After)
    589
    590 if observationCardinalityNextOld != observationCardinalityNextNew {
    591 previousRealm := rlm.Previous()
    592 chain.Emit(
    593 "IncreaseObservationCardinalityNext",
    594 "prevAddr", previousRealm.Address().String(),
    595 "prevRealm", previousRealm.PkgPath(),
    596 "poolPath", pool.PoolPath(),
    597 "cardinalityNext", utils.FormatUint(observationCardinalityNextNew),
    598 )
    599 }
    600}
    601