Transaction
7F620913595AC6…2709D0745E03
Block 77,255 · index 0 · indexed
Summary
- Hash
- 7F620913595AC60AD23FC55B204BD947919600CE6D671786B7692709D0745E03
- Block
- 77,255
- Size
- 167326 bytes
- Gas used
- 206,284,217 / 247,541,001
- Fee
- 247542ugnot
- Status
- success
Messages
- Package
- gno.land/r/gnoswap/pool
Arguments · 24
- #1pool
- #2README.md
- #3# Pool Concentrated liquidity AMM pools with tick-based pricing. ## Overview Pool contracts implement Uniswap V3-style concentrated liquidity, allowing LPs to provide liquidity within custom price ranges for maximum capital efficiency. ## Gnoweb The root `Render("")` delegates to the active implementation and shows realm identity, halt flags, stored pool count, creation and withdrawal fees, the four fee tiers and tick spacings, and separate token0/token1 protocol-fee denominators. GNS creation fees use six-decimal base units; withdrawal fees use basis points and swap fee tiers use pips. Rendering reads stored counts and fixed configuration without traversing pools. Unsupported paths return `404`. ## Configuration - **Pool Creation Fee**: 100 GNS (default) - **Protocol Fee**: Disabled (0) or a denominator of 4-10, routing 1/4 to 1/10 of swap fees to the protocol - **Withdrawal Fee**: 1% on fee-bearing collection (configurable) - **Fee Tiers**: 0.01%, 0.05%, 0.3%, 1% - **Tick Spacing**: Auto-set by fee tier - **Max Liquidity Per Tick**: Depends on tick spacing; use `GetMaxLiquidityPerTick` rather than `2^128 - 1` ## Core Concepts ### Concentrated Liquidity Liquidity providers concentrate capital within custom price ranges instead of 0-∞. This allows LPs to allocate capital where it's most likely to generate fees - near the current price for volatile pairs, or within tight ranges for stable pairs. Capital efficiency can improve by orders of magnitude depending on range selection and pair volatility. For more details, check out [GnoSwap Docs](https://docs.gnoswap.io/core-concepts/amm/concentrated-liquidity). ### Tick System - Price space divided into discrete ticks (0.01% apart) - Each tick represents ~0.01% price change - Positions defined by upper/lower tick boundaries - Liquidity activated only when price in range ## Key Functions ### `CreatePool` Deploys a new trading pair. - Requires 100 GNS creation fee by default - Valid fee tier required - Accepts either token path order and canonicalizes token0/token1 - If paths are reversed, the initial square-root price is inverted - Initial `sqrtPriceX96` must be in `[MIN_SQRT_RATIO, MAX_SQRT_RATIO)` - Does not compare the initial price with an oracle or external market price ### `Mint` Adds liquidity to position (called by Position contract). - Calculates token amounts from liquidity - Updates tick bitmap - Transfers tokens from owner - Returns actual amounts used ### `Burn` Removes liquidity without collecting tokens. - Pool-level operation: burn first, then collect owed tokens - Calculates owed principal - Updates position state ### `Collect` Pays tokens owed by a pool position without a withdrawal fee. This fee-free path is normally used for principal after `Burn`. - Transfers the requested portion of `tokensOwed` - Updates `tokensOwed` ### `CollectSwapFee` Pays accrued swap fees through the fee-bearing collection path. - Applies the configured withdrawal fee - Returns gross collected amounts and the fee withheld - `Position.DecreaseLiquidity` and `Position.CollectFee` invoke the appropriate pool paths internally ### `Swap` Core swap execution (called by Router). - Iterates through ticks - Updates price and liquidity - Calculates fees - Maintains TWAP oracle #### Swap Callback The `Swap` function uses a callback pattern for token transfers, following the Uniswap V3 flash swap design. **Callback Signature**: ```go func swapCallback(cur realm, amount0Delta, amount1Delta int64, _ *pool.CallbackMarker) error ``` **Delta Convention**: | Delta | Meaning | |-------|---------| | Positive (`> 0`) | Amount the pool must RECEIVE (input token) | | Negative (`< 0`) | Amount the pool has SENT (output token) | **Swap Direction Examples**: For `zeroForOne = true` (token0 → token1): - `amount0Delta > 0`: Pool receives token0 (input) - `amount1Delta < 0`: Pool sends token1 (output) For `zeroForOne = false` (token1 → token0): - `amount0Delta < 0`: Pool sends token0 (output) - `amount1Delta > 0`: Pool receives token1 (input) **Callback Implementation Example**: ```go func swapCallback(cur realm, amount0Delta, amount1Delta int64, _ *pool.CallbackMarker) error { caller := cur.Previous().Address() poolAddr := chain.PackageAddress("gno.land/r/gnoswap/pool") // Security check: ensure this callback is invoked by the legitimate pool if caller != poolAddr { return errors.New("unauthorized caller") } if amount0Delta > 0 { // Transfer token0 to pool common.SafeGRC20Transfer(0, cur, token0Path, poolAddr, amount0Delta) } if amount1Delta > 0 { // Transfer token1 to pool common.SafeGRC20Transfer(0, cur, token1Path, poolAddr, amount1Delta) } return nil } ``` **Important Notes**: - A custom callback should verify that the caller is the legitimate pool. - In the router flow, the supplied closure performs that pool-origin check before calling `router.SwapCallback`; the Router implementation then checks that its caller is Router v1. - The callback MUST transfer at least the positive delta amount to the pool. - Return `nil` on success, or an error to revert the swap. - Pool validates the balance increase after callback execution. ## Technical Details ### Price Math **Q96 Format**: Prices stored as `sqrtPriceX96 = sqrt(price) * 2^96` ``` Price 1:1 → sqrtPriceX96 = 79228162514264337593543950336 Price 1:4 → sqrtPriceX96 = 39614081257132168796771975168 Price 100:1 → sqrtPriceX96 = 792281625142643375935439503360 ``` **Tick to Price**: `price = 1.0001^tick` ``` tick 0 = price 1 tick 6932 = price ~2 tick -6932 = price ~0.5 ``` **Range Liquidity**: Liquidity is calculated from the token required by the current price: - Below the range (`current < lower`): token0 only - In the range (`lower <= current < upper`): both token0 and token1 - Above the range (`current >= upper`): token1 only The integer formulas use the square-root prices and round in the direction required by the mint or burn operation; there is no single `amount` formula that applies to all three cases. **Impermanent Loss**: - Narrow range: Higher fees, higher IL - Wide range: Lower fees, lower IL - Stable pairs: ±0.1% ranges optimal - Volatile pairs: ±10%+ ranges recommended ### Fee Mechanics **Swap Fees**: - Charged on input amount - Accumulates as feeGrowthGlobal - Distributed pro-rata to in-range liquidity **Fee Calculation**: ``` fees = feeGrowthInside * liquidity feeGrowthInside = feeGrowthGlobal - feeGrowthOutside ``` **Protocol fees**: - `0` disables protocol fee collection - `4` through `10` are denominators: `4` routes 25% and `10` routes 10% of swap fees to the protocol - Governance-managed configuration applies to the pool set, not an independent percentage selected on each pool ## Security ### Reentrancy Protection - The live guard is the pool-wide `Unlocked` key in the pool KV store, managed by `pool/v1/lock.gno`. `Slot0.unlocked` is a separate stored field and is not the guard; `GetSlot0Unlocked` reports that field, not the live lock. - The lock is not swap-specific. `CreatePool`, `Mint`, `Burn`, `Collect`, `CollectSwapFee`, `CollectProtocol`, `SetFeeProtocol`, `SetWithdrawalFee`, `SetPoolCreationFee`, `IncreaseObservationCardinalityNext`, `SetSwapStartHook`, `SetSwapEndHook`, `SetTickCrossHook`, `Swap`, and the read-only `DrySwap` all assert that the pool is unlocked before doing any work. - The unlocked assertion is read-only and runs before the access checks, so a call that aborts on authorization leaves no persisted lock behind. - Settlement order is operation-specific rather than uniformly checks-effects-interactions. `Swap` settles optimistically through the callback and verifies the resulting balance increase afterwards, while `Mint` pulls tokens before its final pool save. Review the specific path rather than assuming every write precedes every external call. ### Price Manipulation - TWAP oracle provides time-weighted observations for monitoring; it is not an automatic initial-price guard - Large swaps limited by liquidity - Slippage protection required ### Pool Creation Griefing **Issue**: `CreatePool` validates the fee tier, token canonicalization, and square-root price bounds, but does not compare the initial price with an oracle or external market price. A pool can therefore be created at an economically inappropriate extreme price. **Impact**: - Pool may be temporarily unusable - No rational LP may provide liquidity at a distorted price - Price cannot self-correct without liquidity **Recovery Mechanism**: Recovery requires coordinated liquidity provision and swaps to move the price toward a desired market rate, followed by liquidity removal. The protocol does not perform this correction automatically, and profitability depends on market conditions, fees, and slippage. **Example Recovery Sequence**: This pseudocode assumes the integrating realm function has a current `cur` token. ``` // Illustrative sequence; the caller must compose and execute these operations 1. position.Mint(cross(cur), ..., fullRange, largeAmount, ...) // Add liquidity 2. router.ExactInSwapRoute(cross(cur), ..., targetRoute, ...) // Fix price via arbitrage 3. position.DecreaseLiquidity(cross(cur), positionId, ...) // Remove liquidity and collect principal 4. position.CollectFee(cross(cur), positionId) // Collect any remaining fees ``` **Prevention**: - 100 GNS creation fee provides deterrent - Consider implementing price oracle validation for high-value pairs - Monitor pool creation events for suspicious activity ### Rounding - Integer math rounds directionally for the input/output invariant; not every division rounds down - Minimum liquidity enforced - Full precision for amounts
- #4errors.gno
- #5package pool const ErrSpoofedRealm = "rlm does not match the current crossing frame" // errUpgradeWhileLocked is returned when UpgradeImpl is invoked while the // pool's reentrancy lock (StoreKeyUnlocked) is held. const errUpgradeWhileLocked = "cannot upgrade pool implementation while pool is locked"
- #6getter.gno
- #7package pool import ( rotree "gno.land/p/nt/bptree/rotree/v0" ) // GetPools returns a read-only view of every pool, keyed by pool path. // Reading an entry yields a clone, so the view cannot mutate realm state. // Returns: // - *rotree.ReadOnlyTree: Read-only tree keyed by canonical pool path; each // entry is cloned before it is exposed. func GetPools() *rotree.ReadOnlyTree { return getImplementation().GetPools() } // ExistsPoolPath checks if a pool exists at the given path. // Parameters: // - poolPath: Canonical pool path identifying the pool to check. // // Returns: // - bool: True when a pool is registered at poolPath; false otherwise. func ExistsPoolPath(poolPath string) bool { return getImplementation().ExistsPoolPath(poolPath) } // GetBalances returns the balances of the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool whose balances are read. // // Returns: // - int64: Current token0 balance held by the pool, in token base units. // - int64: Current token1 balance held by the pool, in token base units. // - error: Non-nil when poolPath does not identify an existing pool. func GetBalances(poolPath string) (int64, int64, error) { bal0, err := getImplementation().GetBalanceToken0(poolPath) if err != nil { return 0, 0, err } bal1, err := getImplementation().GetBalanceToken1(poolPath) if err != nil { return 0, 0, err } return bal0, bal1, nil } // GetBalanceToken0 returns the balance of token0 in the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - int64: Current token0 balance held by the pool, in token base units. // - error: Non-nil when poolPath does not identify an existing pool. func GetBalanceToken0(poolPath string) (int64, error) { return getImplementation().GetBalanceToken0(poolPath) } // GetBalanceToken1 returns the balance of token1 in the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - int64: Current token1 balance held by the pool, in token base units. // - error: Non-nil when poolPath does not identify an existing pool. func GetBalanceToken1(poolPath string) (int64, error) { return getImplementation().GetBalanceToken1(poolPath) } // GetFee returns the fee tier of the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool whose fee tier is read. // // Returns: // - uint32: Configured fee tier for the pool. // - error: Non-nil when poolPath does not identify an existing pool. func GetFee(poolPath string) (uint32, error) { return getImplementation().GetFee(poolPath) } // GetFeeAmountTickSpacings returns all fee tier to tick spacing mappings. // Returns: // - map[uint32]int32: Copy of the configured fee-tier-to-tick-spacing mapping. func GetFeeAmountTickSpacings() map[uint32]int32 { return cloneFeeAmountTickSpacings(getImplementation().GetFeeAmountTickSpacings()) } // GetFeeAmountTickSpacing returns the tick spacing for a given fee tier. // Parameters: // - fee: Fee tier whose supported tick spacing is requested. // // Returns: // - spacing: Tick spacing configured for fee. // - err: Non-nil when fee is not one of the supported fee tiers. func GetFeeAmountTickSpacing(fee uint32) (spacing int32, err error) { return getImplementation().GetFeeAmountTickSpacing(fee) } // GetFeeGrowthGlobal0X128 returns the global fee growth for token0. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - string: Token0 global fee growth, serialized as a base-10 X128 accumulator. // - error: Non-nil when poolPath does not identify an existing pool. func GetFeeGrowthGlobal0X128(poolPath string) (string, error) { feeGrowth, err := getImplementation().GetFeeGrowthGlobal0X128(poolPath) if err != nil { return "", err } return feeGrowth.ToString(), nil } // GetFeeGrowthGlobal1X128 returns the global fee growth for token1. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - string: Token1 global fee growth, serialized as a base-10 X128 accumulator. // - error: Non-nil when poolPath does not identify an existing pool. func GetFeeGrowthGlobal1X128(poolPath string) (string, error) { feeGrowth, err := getImplementation().GetFeeGrowthGlobal1X128(poolPath) if err != nil { return "", err } return feeGrowth.ToString(), nil } // GetFeeGrowthGlobalX128 returns the global fee growth for both tokens. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - string: Token0 global fee growth, serialized as a base-10 X128 accumulator. // - string: Token1 global fee growth, serialized as a base-10 X128 accumulator. // - error: Non-nil when poolPath does not identify an existing pool. func GetFeeGrowthGlobalX128(poolPath string) (string, string, error) { fg0, fg1, err := getImplementation().GetFeeGrowthGlobalX128(poolPath) if err != nil { return "", "", err } return fg0.ToString(), fg1.ToString(), nil } // GetLiquidity returns the current liquidity in the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool whose liquidity is read. // // Returns: // - string: Current pool liquidity, serialized as a base-10 unsigned integer. // - error: Non-nil when poolPath does not identify an existing pool. func GetLiquidity(poolPath string) (string, error) { liquidity, err := getImplementation().GetLiquidity(poolPath) if err != nil { return "", err } return liquidity.ToString(), nil } // GetPoolCreationFee returns the current pool creation fee. // Returns: // - int64: Pool creation fee charged when a new pool is created, in configured // native-token base units. func GetPoolCreationFee() int64 { return getImplementation().GetPoolCreationFee() } // GetPositionFeeGrowthInside0LastX128 returns the last recorded fee growth inside for token0. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - string: Position's last token0 fee-growth-inside accumulator, serialized // as a base-10 X128 value. // - error: Non-nil when the pool or position key cannot be found. func GetPositionFeeGrowthInside0LastX128(poolPath, key string) (string, error) { return getImplementation().GetPositionFeeGrowthInside0LastX128(poolPath, key) } // GetPositionFeeGrowthInside1LastX128 returns the last recorded fee growth inside for token1. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - string: Position's last token1 fee-growth-inside accumulator, serialized // as a base-10 X128 value. // - error: Non-nil when the pool or position key cannot be found. func GetPositionFeeGrowthInside1LastX128(poolPath, key string) (string, error) { return getImplementation().GetPositionFeeGrowthInside1LastX128(poolPath, key) } // GetPositionFeeGrowthInsideLastX128 returns the last recorded fee growth inside for both tokens. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - string: Position's last token0 fee-growth-inside accumulator, serialized // as a base-10 X128 value. // - string: Position's last token1 fee-growth-inside accumulator, serialized // as a base-10 X128 value. // - error: Non-nil when the pool or position key cannot be found. func GetPositionFeeGrowthInsideLastX128(poolPath, key string) (string, string, error) { return getImplementation().GetPositionFeeGrowthInsideLastX128(poolPath, key) } // GetPoolPositions returns a read-only view of a pool's positions, keyed by // position key. nil is returned when the pool does not exist. // Parameters: // - poolPath: Canonical pool path identifying the pool whose positions are read. // // Returns: // - *rotree.ReadOnlyTree: Read-only tree keyed by position key; nil when the // pool does not exist. func GetPoolPositions(poolPath string) *rotree.ReadOnlyTree { return getImplementation().GetPoolPositions(poolPath) } // GetPositionLiquidity returns the liquidity of a position. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - string: Position liquidity, serialized as a base-10 unsigned integer. // - error: Non-nil when the pool or position key cannot be found. func GetPositionLiquidity(poolPath, key string) (string, error) { return getImplementation().GetPositionLiquidity(poolPath, key) } // GetPositionTokensOwed0 returns the amount of token0 owed to a position. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - int64: Amount of token0 owed to the position, in token base units. // - error: Non-nil when the pool or position key cannot be found. func GetPositionTokensOwed0(poolPath, key string) (int64, error) { return getImplementation().GetPositionTokensOwed0(poolPath, key) } // GetPositionTokensOwed1 returns the amount of token1 owed to a position. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - int64: Amount of token1 owed to the position, in token base units. // - error: Non-nil when the pool or position key cannot be found. func GetPositionTokensOwed1(poolPath, key string) (int64, error) { return getImplementation().GetPositionTokensOwed1(poolPath, key) } // GetPositionTokensOwedInfos returns the amount of tokens owed for both tokens. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the position. // - key: Position key identifying the position within the pool. // // Returns: // - int64: Amount of token0 owed to the position, in token base units. // - int64: Amount of token1 owed to the position, in token base units. // - error: Non-nil when the pool or position key cannot be found. func GetPositionTokensOwed(poolPath, key string) (int64, int64, error) { tokensOwed0, err := getImplementation().GetPositionTokensOwed0(poolPath, key) if err != nil { return 0, 0, err } tokensOwed1, err := getImplementation().GetPositionTokensOwed1(poolPath, key) if err != nil { return 0, 0, err } return tokensOwed0, tokensOwed1, nil } // GetProtocolFeesToken0 returns accumulated protocol fees for token0. // Parameters: // - poolPath: Canonical pool path identifying the pool whose fees are read. // // Returns: // - int64: Accumulated protocol fee for token0, in token base units. // - error: Non-nil when poolPath does not identify an existing pool. func GetProtocolFeesToken0(poolPath string) (int64, error) { return getImplementation().GetProtocolFeesToken0(poolPath) } // GetProtocolFeesToken1 returns accumulated protocol fees for token1. // Parameters: // - poolPath: Canonical pool path identifying the pool whose fees are read. // // Returns: // - int64: Accumulated protocol fee for token1, in token base units. // - error: Non-nil when poolPath does not identify an existing pool. func GetProtocolFeesToken1(poolPath string) (int64, error) { return getImplementation().GetProtocolFeesToken1(poolPath) } // GetProtocolFeesTokens returns the accumulated protocol fees for both tokens. // Parameters: // - poolPath: Canonical pool path identifying the pool whose fees are read. // // Returns: // - int64: Accumulated protocol fee for token0, in token base units. // - int64: Accumulated protocol fee for token1, in token base units. // - error: Non-nil when poolPath does not identify an existing pool. func GetProtocolFeesTokens(poolPath string) (int64, int64, error) { fees0, err := getImplementation().GetProtocolFeesToken0(poolPath) if err != nil { return 0, 0, err } fees1, err := getImplementation().GetProtocolFeesToken1(poolPath) if err != nil { return 0, 0, err } return fees0, fees1, nil } // GetSlot0FeeProtocol returns the protocol fee rate from slot0. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - uint8: Packed protocol-fee denominator configuration from slot0; token0 // occupies the low nibble and token1 the high nibble. // - error: Non-nil when poolPath does not identify an existing pool. func GetSlot0FeeProtocol(poolPath string) (uint8, error) { return getImplementation().GetSlot0FeeProtocol(poolPath) } // GetSlot0SqrtPriceX96 returns the current sqrt price from slot0. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - string: Current square-root price in Q64.96 fixed-point form, serialized // as a base-10 unsigned integer. // - error: Non-nil when poolPath does not identify an existing pool. func GetSlot0SqrtPriceX96(poolPath string) (string, error) { sqrtPriceX96, err := getImplementation().GetSlot0SqrtPriceX96(poolPath) if err != nil { return "", err } return sqrtPriceX96.ToString(), nil } // GetSlot0Tick returns the current tick from slot0. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - int32: Current active tick index stored in slot0. // - error: Non-nil when poolPath does not identify an existing pool. func GetSlot0Tick(poolPath string) (int32, error) { return getImplementation().GetSlot0Tick(poolPath) } // GetSlot0Unlocked reports whether the pool is currently unlocked. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - bool: True when the pool is not holding its reentrancy lock; false when // the pool is currently locked. // - error: Non-nil when poolPath does not identify an existing pool. func GetSlot0Unlocked(poolPath string) (bool, error) { return getImplementation().GetSlot0Unlocked(poolPath) } // GetSlot0 returns a safe copy of the pool's slot0 (sqrt price, tick, // protocol fee, lock state, and oracle cursor/capacity metadata). // The clone happens here, at the proxy boundary, so every implementation // version can return its internal Slot0 by value without each one having to // remember to defend against callers mutating the shared sqrtPriceX96. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - Slot0: Copy of the pool's slot0 state, including price, tick, protocol // fee, lock, and observation metadata; panics if the pool is missing. func GetSlot0(poolPath string) Slot0 { slot0 := getImplementation().GetSlot0(poolPath) return slot0.Clone() } // GetObservationAt returns a safe copy of the observation stored at index. // An in-range but uninitialized slot returns the zero observation, matching // Uniswap V3's fixed observation-array getter. // // ref: uniswap v3 IUniswapV3PoolState.observations(uint256 index) // Parameters: // - poolPath: Canonical pool path identifying the pool containing the observation. // - index: Zero-based observation-array index; values at or above the fixed // cardinality bound return an error. // // Returns: // - Observation: Observation at index, or the zero observation when the slot // is in range but has never been written. // - error: Non-nil when the pool is missing or index is out of range. func GetObservationAt(poolPath string, index uint16) (Observation, error) { observation, err := getImplementation().GetObservationAt(poolPath, index) if err != nil { return Observation{}, err } return observation, nil } // Observe returns the tick and seconds-per-liquidity cumulatives for each // requested lookback, matching Uniswap V3's observe(uint32[] secondsAgos). // Parameters: // - poolPath: Canonical pool path whose oracle history is queried. // - secondsAgos: Lookback intervals in seconds; one cumulative pair is returned // for each value, in the same order. // // Returns: // - []int64: Tick cumulative values corresponding to each requested lookback. // - []string: Seconds-per-liquidity cumulative X128 values, serialized as // base-10 strings, corresponding to each lookback. // - error: Non-nil when pool or observation data cannot be read. func Observe(poolPath string, secondsAgos []uint32) ([]int64, []string, error) { tickCumulatives, secondsPerLiquidityCumulativeX128s, err := getImplementation().Observe( poolPath, cloneUint32Slice(secondsAgos), ) if err != nil { return nil, nil, err } return cloneInt64Slice(tickCumulatives), cloneStringSlice(secondsPerLiquidityCumulativeX128s), nil } // SnapshotCumulativesInside returns the tick, seconds-per-liquidity, and // seconds cumulatives accrued while the pool price was inside // [tickLower, tickUpper). Both boundary ticks must be initialized. // // Snapshots are only comparable across an interval during which a position in // the range existed, matching Uniswap V3's snapshotCumulativesInside. // Parameters: // - poolPath: Canonical pool path whose oracle snapshot is queried. // - tickLower: Lower inclusive tick boundary of the position range. // - tickUpper: Upper exclusive tick boundary of the position range; both // boundary ticks must be initialized. // // Returns: // - int64: Tick cumulative accrued while the price was inside the range. // - string: Seconds-per-liquidity-inside X128 accumulator, serialized as a // base-10 string. // - uint32: Seconds accrued while the price was inside the range. // - error: Non-nil when the pool, observations, or initialized boundaries are // unavailable. func SnapshotCumulativesInside( poolPath string, tickLower int32, tickUpper int32, ) (int64, string, uint32, error) { tickCumulativeInside, secondsPerLiquidityInsideX128, secondsInside, err := getImplementation().SnapshotCumulativesInside( poolPath, tickLower, tickUpper, ) if err != nil { return 0, "", 0, err } return tickCumulativeInside, secondsPerLiquidityInsideX128.ToString(), secondsInside, nil } // GetTickCumulativeOutside returns the tick cumulative value outside a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose outside tick cumulative is requested. // // Returns: // - int64: Tick cumulative recorded outside tick. // - error: Non-nil when the pool or tick is not found. func GetTickCumulativeOutside(poolPath string, tick int32) (int64, error) { return getImplementation().GetTickCumulativeOutside(poolPath, tick) } // GetTickFeeGrowthOutside0X128 returns fee growth outside for token0 at a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose outside fee growth is requested. // // Returns: // - string: Token0 fee growth outside tick, serialized as a base-10 X128 // accumulator. // - error: Non-nil when the pool or tick is not found. func GetTickFeeGrowthOutside0X128(poolPath string, tick int32) (string, error) { return getImplementation().GetTickFeeGrowthOutside0X128(poolPath, tick) } // GetTickFeeGrowthOutside1X128 returns fee growth outside for token1 at a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose outside fee growth is requested. // // Returns: // - string: Token1 fee growth outside tick, serialized as a base-10 X128 // accumulator. // - error: Non-nil when the pool or tick is not found. func GetTickFeeGrowthOutside1X128(poolPath string, tick int32) (string, error) { return getImplementation().GetTickFeeGrowthOutside1X128(poolPath, tick) } // GetTickFeeGrowthOutsideX128 returns fee growth outside for both tokens at a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose outside fee growth is requested. // // Returns: // - string: Token0 fee growth outside tick, serialized as a base-10 X128 // accumulator. // - string: Token1 fee growth outside tick, serialized as a base-10 X128 // accumulator. // - error: Non-nil when the pool or tick is not found. func GetTickFeeGrowthOutsideX128(poolPath string, tick int32) (string, string, error) { return getImplementation().GetTickFeeGrowthOutsideX128(poolPath, tick) } // GetTickInitialized returns whether a tick is initialized. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose initialization flag is requested. // // Returns: // - bool: True when the tick has initialized liquidity and outside accumulators. // - error: Non-nil when the pool or tick is not found. func GetTickInitialized(poolPath string, tick int32) (bool, error) { return getImplementation().GetTickInitialized(poolPath, tick) } // GetInitializedTicksInRange returns initialized ticks within the given range. // Parameters: // - poolPath: Canonical pool path whose ticks are enumerated. // - tickLower: Lower inclusive tick bound for enumeration. // - tickUpper: Upper bound for enumeration; ticks are selected through the // pool's range iterator. // // Returns: // - []int32: Tick indices that are initialized in the requested range. // - error: Non-nil when poolPath does not identify an existing pool. func GetInitializedTicksInRange(poolPath string, tickLower, tickUpper int32) ([]int32, error) { ticks, err := getImplementation().GetInitializedTicksInRange(poolPath, tickLower, tickUpper) if err != nil { return nil, err } return cloneInt32Slice(ticks), nil } // GetTickLiquidityGross returns the total liquidity that references a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose gross liquidity is requested. // // Returns: // - string: Gross liquidity referencing the tick, serialized as a base-10 // unsigned integer. // - error: Non-nil when the pool or tick is not found. func GetTickLiquidityGross(poolPath string, tick int32) (string, error) { return getImplementation().GetTickLiquidityGross(poolPath, tick) } // GetTickLiquidityNet returns the net liquidity change at a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose net liquidity change is requested. // // Returns: // - string: Signed net liquidity change at the tick, serialized as a base-10 // integer. // - error: Non-nil when the pool or tick is not found. func GetTickLiquidityNet(poolPath string, tick int32) (string, error) { return getImplementation().GetTickLiquidityNet(poolPath, tick) } // GetTickSecondsOutside returns seconds spent outside a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose outside time is requested. // // Returns: // - uint32: Seconds accumulated outside the tick. // - error: Non-nil when the pool or tick is not found. func GetTickSecondsOutside(poolPath string, tick int32) (uint32, error) { return getImplementation().GetTickSecondsOutside(poolPath, tick) } // GetTickSecondsPerLiquidityOutsideX128 returns seconds per liquidity outside a tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose outside accumulator is requested. // // Returns: // - string: Seconds-per-liquidity outside tick in X128 precision, serialized // as a base-10 unsigned integer. // - error: Non-nil when the pool or tick is not found. func GetTickSecondsPerLiquidityOutsideX128(poolPath string, tick int32) (string, error) { return getImplementation().GetTickSecondsPerLiquidityOutsideX128(poolPath, tick) } // GetTickSpacing returns the tick spacing of the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool whose spacing is read. // // Returns: // - int32: Tick spacing associated with the pool's fee tier. // - error: Non-nil when poolPath does not identify an existing pool. func GetTickSpacing(poolPath string) (int32, error) { return getImplementation().GetTickSpacing(poolPath) } // GetToken0Path returns the path of token0 in the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - string: Canonical token0 contract path stored by the pool. // - error: Non-nil when poolPath does not identify an existing pool. func GetToken0Path(poolPath string) (string, error) { return getImplementation().GetToken0Path(poolPath) } // GetToken1Path returns the path of token1 in the pool. // Parameters: // - poolPath: Canonical pool path identifying the pool to query. // // Returns: // - string: Canonical token1 contract path stored by the pool. // - error: Non-nil when poolPath does not identify an existing pool. func GetToken1Path(poolPath string) (string, error) { return getImplementation().GetToken1Path(poolPath) } // GetWithdrawalFee returns the current withdrawal fee rate. // Returns: // - uint64: Withdrawal fee rate in basis points; zero means no withdrawal // fee is charged. func GetWithdrawalFee() uint64 { return getImplementation().GetWithdrawalFee() } // OracleConsult returns the arithmetic mean tick and harmonic mean liquidity // over a lookback, following Uniswap V3 OracleLibrary's consult convention. // Parameters: // - poolPath: Canonical pool path whose oracle data is queried. // - secondsAgo: Lookback duration in seconds used to compute the time-weighted // values. // // Returns: // - int32: Arithmetic mean tick over the requested lookback. // - string: Harmonic mean liquidity over the lookback, serialized as a base-10 // unsigned integer. // - error: Non-nil when the pool or required observation history is unavailable. func OracleConsult(poolPath string, secondsAgo uint32) (int32, string, error) { tick, liquidity, err := getImplementation().OracleConsult(poolPath, secondsAgo) if err != nil { return 0, "", err } return tick, liquidity.ToString(), nil } // Structure getters // GetTickInfo returns the tick info for a given tick. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the tick. // - tick: Tick index whose complete state is requested. // // Returns: // - TickInfo: Safe copy of the requested tick's liquidity and oracle/fee // accumulator fields. // - error: Non-nil when the pool or tick is not found. func GetTickInfo(poolPath string, tick int32) (TickInfo, error) { tickInfo, err := getImplementation().GetTickInfo(poolPath, tick) if err != nil { return TickInfo{}, err } return tickInfo.Clone(), nil } // GetTickBitmaps returns the tick bitmap for a given word position. // Parameters: // - poolPath: Canonical pool path identifying the pool containing the bitmap. // - wordPos: Signed bitmap word position to read. // // Returns: // - string: Bitmap word serialized as a base-10 unsigned integer. // - error: Non-nil when the pool or bitmap word is not found. func GetTickBitmaps(poolPath string, wordPos int16) (string, error) { return getImplementation().GetTickBitmaps(poolPath, wordPos) } // GetPendingProtocolFees returns the pending protocol fee amount per token path. // Returns: // - map[string]int64: Copy of pending protocol fees keyed by token contract path, // with amounts in token base units. func GetPendingProtocolFees() map[string]int64 { return cloneStringInt64Map(getImplementation().GetPendingProtocolFees()) }
- #8getter_utils.gno
- #9package pool func cloneInt32Slice(src []int32) []int32 { if src == nil { return nil } cloned := make([]int32, len(src)) copy(cloned, src) return cloned } func cloneInt64Slice(src []int64) []int64 { if src == nil { return nil } cloned := make([]int64, len(src)) copy(cloned, src) return cloned } func cloneStringSlice(src []string) []string { if src == nil { return nil } cloned := make([]string, len(src)) copy(cloned, src) return cloned } func cloneUint32Slice(src []uint32) []uint32 { if src == nil { return nil } cloned := make([]uint32, len(src)) copy(cloned, src) return cloned } func cloneFeeAmountTickSpacings(src map[uint32]int32) map[uint32]int32 { if src == nil { return nil } cloned := make(map[uint32]int32, len(src)) for fee, spacing := range src { cloned[fee] = spacing } return cloned } func cloneStringInt64Map(src map[string]int64) map[string]int64 { if src == nil { return nil } cloned := make(map[string]int64, len(src)) for k, v := range src { cloned[k] = v } return cloned } // NewDefaultPositionInfo returns the zero-accounting position state used for a new pool position. // // Returns: // - info: Position info with zero liquidity, zero fee-growth checkpoints, and no tokens owed. func NewDefaultPositionInfo() PositionInfo { return PositionInfo{ liquidity: "0", feeGrowthInside0LastX128: "0", feeGrowthInside1LastX128: "0", tokensOwed0: 0, tokensOwed1: 0, } }
- #10gnomod.toml
- #11module = "gno.land/r/gnoswap/pool" gno = "0.9"
- #12oracle.gno
- #13package pool import ( "gno.land/p/gnoswap/utils/v1" "gno.land/p/nt/bptree/v0" ) type Observation struct { blockTimestamp int64 // timestamp of the observation tickCumulative int64 // cumulative tick up to this timestamp secondsPerLiquidityCumulativeX128 string // cumulative seconds per liquidity initialized bool // whether this observation has been initialized } // Observation getter methods. // BlockTimestamp returns the observation's block timestamp. // // Returns: // - blockTimestamp: Timestamp at which this observation was recorded. func (o Observation) BlockTimestamp() int64 { return o.blockTimestamp } // TickCumulative returns the signed cumulative tick recorded by the observation. // // Returns: // - tickCumulative: Cumulative tick value through the observation timestamp. func (o Observation) TickCumulative() int64 { return o.tickCumulative } // SecondsPerLiquidityCumulativeX128 returns the Q128-encoded cumulative // seconds-per-liquidity value recorded by the observation. // // Returns: // - secondsPerLiquidityCumulativeX128: Decimal string containing the cumulative value scaled by 2^128. func (o Observation) SecondsPerLiquidityCumulativeX128() string { return o.secondsPerLiquidityCumulativeX128 } // Initialized reports whether the observation contains initialized oracle data. // // Returns: // - initialized: True when the observation slot has been initialized; false for an empty slot. func (o Observation) Initialized() bool { return o.initialized } // MakeObservation constructs an observation from its stored oracle accumulators. // // Parameters: // - blockTimestamp: Timestamp associated with the observation. // - tickCumulative: Signed cumulative tick at blockTimestamp. // - secondsPerLiquidityCumulativeX128: Decimal Q128-scaled cumulative seconds-per-liquidity value. // - initialized: Whether this observation slot is initialized and usable. // // Returns: // - observation: Observation containing the supplied timestamp, accumulators, and initialization flag. func MakeObservation( blockTimestamp int64, tickCumulative int64, secondsPerLiquidityCumulativeX128 string, initialized bool, ) Observation { return Observation{ blockTimestamp: blockTimestamp, tickCumulative: tickCumulative, secondsPerLiquidityCumulativeX128: secondsPerLiquidityCumulativeX128, initialized: initialized, } } // DefaultObservation returns the zero, uninitialized observation used for an empty slot. // // Returns: // - observation: Observation with zero timestamp and accumulators and Initialized false. func DefaultObservation() Observation { return MakeObservation( 0, 0, "0", false, ) } // ObservationTree is a pool-local oracle ring buffer keyed by index. // It owns the B+tree key encoding so oracle code works with uint16 indices. type ObservationTree struct { tree *bptree.BPTree } // NewObservationTree creates an empty pool-local observation tree keyed by uint16 index. // // Returns: // - tree: Initialized observation tree ready for Get, Set, and Has operations. func NewObservationTree() *ObservationTree { return &ObservationTree{tree: bptree.NewBPTreeN(32)} } // Get looks up an observation by its circular-buffer index. // // Parameters: // - index: uint16 observation slot to read. // // Returns: // - observation: Stored observation when the tree contains a value of the expected type; otherwise the zero observation. // - ok: True when a stored value was found and decoded as an Observation; false for nil/uninitialized trees, missing slots, or a type mismatch. func (t *ObservationTree) Get(index uint16) (Observation, bool) { if t == nil || t.tree == nil { return Observation{}, false } value := t.tree.Get(utils.EncodeUint16(index)) if value == nil { return Observation{}, false } observation, ok := value.(Observation) return observation, ok } // Set stores an observation at a circular-buffer index. // // Parameters: // - index: uint16 observation slot to write. // - observation: Observation value to associate with index. func (t *ObservationTree) Set(index uint16, observation Observation) { if t == nil || t.tree == nil { panic("observation tree is not initialized") } t.tree.Set(utils.EncodeUint16(index), observation) } // Has reports whether an observation slot exists in the tree. // // Parameters: // - index: uint16 observation slot to test. // // Returns: // - exists: True when the initialized tree contains index; false for nil/uninitialized trees or an absent slot. func (t *ObservationTree) Has(index uint16) bool { if t == nil || t.tree == nil { return false } return t.tree.Has(utils.EncodeUint16(index)) } // NewObservationsTree creates the top-level B+tree that indexes pool observation trees by pool path. // // Returns: // - tree: Empty B+tree suitable for storing pool-path observation trees. func NewObservationsTree() *bptree.BPTree { return bptree.NewBPTreeN(32) } // NewPoolObservationsTree creates a circular observation buffer with slot zero initialized. // // Parameters: // - currentTime: Timestamp assigned to the initial observation at index zero. // // Returns: // - tree: Observation tree containing an initialized zero-index observation with zero cumulative values. func NewPoolObservationsTree(currentTime int64) *ObservationTree { observations := NewObservationTree() observations.Set(0, MakeObservation( currentTime, 0, "0", true, )) return observations }
- #14pool.gno
- #15package pool import ( bptree "gno.land/p/nt/bptree/v0" ufmt "gno.land/p/nt/ufmt/v0" u256 "gno.land/p/gnoswap/uint256/v1" ) // Pool describes a single pool's state. // A pool is identified with a unique key (token0, token1, fee), where token0 < token1. type Pool struct { // token0/token1 path of the pool token0Path string token1Path string fee uint32 // fee tier of the pool tickSpacing int32 // spacing between ticks slot0 Slot0 balances TokenPair // balances of the pool protocolFees TokenPair feeGrowthGlobal0X128 *u256.Uint // uint256 feeGrowthGlobal1X128 *u256.Uint // uint256 liquidity *u256.Uint // total amount of active liquidity in the pool (within current tick range) ticks *bptree.BPTree // tick(int32) -> TickInfo tickBitmaps map[int16]string // tick(wordPos)(int16) -> bitMap(tickWord ^ mask)(string) positions *bptree.BPTree // maps encoded lower/upper tick pairs to aggregate pool accounting } // Pool Getters methods // PoolPath returns the canonical pool path derived from token0, token1, and the fee tier. // // Returns: // - string: pool identifier assembled from the pool's token paths and fee. func (p *Pool) PoolPath() string { return GetPoolPath(p.token0Path, p.token1Path, p.fee) } // Token0Path returns the path of the pool's token0 asset. // // Returns: // - string: token0 path stored in the pool. func (p *Pool) Token0Path() string { return p.token0Path } // Token1Path returns the path of the pool's token1 asset. // // Returns: // - string: token1 path stored in the pool. func (p *Pool) Token1Path() string { return p.token1Path } // Fee returns the pool's fee tier. // // Returns: // - uint32: fee tier used by swaps in this pool. func (p *Pool) Fee() uint32 { return p.fee } // Balances returns the pool's current token balances. // // Returns: // - TokenPair: token0 and token1 balances tracked by the pool. func (p *Pool) Balances() TokenPair { return p.balances } // BalanceToken0 returns the pool's current token0 balance. // // Returns: // - int64: token0 balance available in the pool. func (p *Pool) BalanceToken0() int64 { return p.balances.token0 } // BalanceToken1 returns the pool's current token1 balance. // // Returns: // - int64: token1 balance available in the pool. func (p *Pool) BalanceToken1() int64 { return p.balances.token1 } // TickSpacing returns the permitted spacing between initialized ticks. // // Returns: // - int32: tick spacing configured for the pool. func (p *Pool) TickSpacing() int32 { return p.tickSpacing } // Slot0 returns the pool's current price, tick, lock, and oracle cursor state. // // Returns: // - Slot0: current slot0 state, including the price and observation metadata. func (p *Pool) Slot0() Slot0 { return p.slot0 } // Slot0SqrtPriceX96 returns the current square-root price in Q96 fixed-point form. // // Returns: // - *u256.Uint: stored sqrt(token1/token0) price scaled by 2^96. func (p *Pool) Slot0SqrtPriceX96() *u256.Uint { return p.slot0.sqrtPriceX96 } // Slot0Tick returns the current pool tick. // // Returns: // - int32: tick corresponding to the current pool price. func (p *Pool) Slot0Tick() int32 { return p.slot0.tick } // Slot0FeeProtocol returns the packed protocol-fee denominators from slot0. // // Returns: // - uint8: packed token0/token1 protocol-fee denominator configuration. func (p *Pool) Slot0FeeProtocol() uint8 { return p.slot0.feeProtocol } // Slot0Unlocked reports whether the pool is currently available for reentrant operations. // // Returns: // - bool: true when the pool is unlocked; false while its swap lock is held. func (p *Pool) Slot0Unlocked() bool { return p.slot0.unlocked } // FeeGrowthGlobal0X128 returns cumulative token0 fee growth per unit of liquidity. // // Returns: // - *u256.Uint: token0 fee-growth accumulator scaled by 2^128. func (p *Pool) FeeGrowthGlobal0X128() *u256.Uint { return p.feeGrowthGlobal0X128 } // FeeGrowthGlobal1X128 returns cumulative token1 fee growth per unit of liquidity. // // Returns: // - *u256.Uint: token1 fee-growth accumulator scaled by 2^128. func (p *Pool) FeeGrowthGlobal1X128() *u256.Uint { return p.feeGrowthGlobal1X128 } // ProtocolFees returns protocol fees accrued in both pool tokens. // // Returns: // - TokenPair: token0 and token1 protocol-fee balances. func (p *Pool) ProtocolFees() TokenPair { return p.protocolFees } // ProtocolFeesToken0 returns protocol fees accrued in token0. // // Returns: // - int64: token0 amount reserved as protocol fees. func (p *Pool) ProtocolFeesToken0() int64 { return p.protocolFees.token0 } // ProtocolFeesToken1 returns protocol fees accrued in token1. // // Returns: // - int64: token1 amount reserved as protocol fees. func (p *Pool) ProtocolFeesToken1() int64 { return p.protocolFees.token1 } // Liquidity returns the pool's active liquidity for the current tick range. // // Returns: // - *u256.Uint: active liquidity amount. func (p *Pool) Liquidity() *u256.Uint { return p.liquidity } // Ticks returns the tree containing tick state keyed by encoded tick. // // Returns: // - *bptree.BPTree: pool tick storage tree. func (p *Pool) Ticks() *bptree.BPTree { return p.ticks } // TickBitmaps returns the map of initialized-tick bitmap words. // // Returns: // - map[int16]string: bitmap words keyed by signed word position. func (p *Pool) TickBitmaps() map[int16]string { return p.tickBitmaps } // Positions returns the tree containing aggregate position state by range key. // // Returns: // - *bptree.BPTree: pool position storage tree. func (p *Pool) Positions() *bptree.BPTree { return p.positions } // GetPosition returns the position information for key. // // Parameters: // - key: encoded lower/upper tick-range key to look up. // // Returns: // - PositionInfo: stored aggregate position for the key. // - error: non-nil when the key is absent or contains a value of the wrong type. func (p *Pool) GetPosition(key string) (PositionInfo, error) { iPositionInfo := p.positions.Get(key) if iPositionInfo == nil { return PositionInfo{}, ufmt.Errorf("position %s not found", key) } positionInfo, ok := iPositionInfo.(PositionInfo) if !ok { return PositionInfo{}, ufmt.Errorf("position %s has invalid type", key) } return positionInfo, nil } // Pool Setters methods // SetToken0Path stores the path of the pool's token0 asset. // // Parameters: // - token0Path: token0 asset path to store. func (p *Pool) SetToken0Path(token0Path string) { p.token0Path = token0Path } // SetToken1Path stores the path of the pool's token1 asset. // // Parameters: // - token1Path: token1 asset path to store. func (p *Pool) SetToken1Path(token1Path string) { p.token1Path = token1Path } // SetFee stores the pool's fee tier. // // Parameters: // - fee: fee tier to use for swaps in this pool. func (p *Pool) SetFee(fee uint32) { p.fee = fee } // SetBalances replaces the pool's tracked balances for both tokens. // // Parameters: // - balances: token0 and token1 balances to store. func (p *Pool) SetBalances(balances TokenPair) { p.balances = balances } // SetBalanceToken0 updates the pool's tracked token0 balance. // // Parameters: // - token0: new token0 balance. func (p *Pool) SetBalanceToken0(token0 int64) { p.balances.token0 = token0 } // SetBalanceToken1 updates the pool's tracked token1 balance. // // Parameters: // - token1: new token1 balance. func (p *Pool) SetBalanceToken1(token1 int64) { p.balances.token1 = token1 } // SetTickSpacing stores the permitted spacing between initialized ticks. // // Parameters: // - tickSpacing: tick spacing to configure for the pool. func (p *Pool) SetTickSpacing(tickSpacing int32) { p.tickSpacing = tickSpacing } // SetSlot0 replaces the pool's current price, tick, lock, and oracle cursor state. // // Parameters: // - slot0: slot0 state to store. func (p *Pool) SetSlot0(slot0 Slot0) { p.slot0 = slot0 } // SetFeeGrowthGlobal0X128 stores the token0 fee-growth accumulator. // // Parameters: // - feeGrowthGlobal0X128: token0 fee growth per liquidity unit, scaled by 2^128. func (p *Pool) SetFeeGrowthGlobal0X128(feeGrowthGlobal0X128 *u256.Uint) { p.feeGrowthGlobal0X128 = u256.Zero().Set(feeGrowthGlobal0X128) } // SetFeeGrowthGlobal1X128 stores the token1 fee-growth accumulator. // // Parameters: // - feeGrowthGlobal1X128: token1 fee growth per liquidity unit, scaled by 2^128. func (p *Pool) SetFeeGrowthGlobal1X128(feeGrowthGlobal1X128 *u256.Uint) { p.feeGrowthGlobal1X128 = u256.Zero().Set(feeGrowthGlobal1X128) } // SetProtocolFees replaces the pool's accrued protocol fees for both tokens. // // Parameters: // - protocolFees: token0 and token1 protocol-fee balances to store. func (p *Pool) SetProtocolFees(protocolFees TokenPair) { p.protocolFees = protocolFees } // SetProtocolFeesToken0 updates the pool's accrued token0 protocol fees. // // Parameters: // - token0: new token0 protocol-fee balance. func (p *Pool) SetProtocolFeesToken0(token0 int64) { p.protocolFees.token0 = token0 } // SetProtocolFeesToken1 updates the pool's accrued token1 protocol fees. // // Parameters: // - token1: new token1 protocol-fee balance. func (p *Pool) SetProtocolFeesToken1(token1 int64) { p.protocolFees.token1 = token1 } // SetLiquidity stores the pool's active liquidity for the current tick range. // // Parameters: // - liquidity: active liquidity amount to store. func (p *Pool) SetLiquidity(liquidity *u256.Uint) { p.liquidity = u256.Zero().Set(liquidity) } // SetTicks replaces the tree containing pool tick state. // // Parameters: // - ticks: tick storage tree to use. func (p *Pool) SetTicks(ticks *bptree.BPTree) { p.ticks = ticks } // SetTickBitmap stores one initialized-tick bitmap word. // // Parameters: // - wordPos: signed bitmap word position. // - tickBitmap: encoded bitmap bits for that word. func (p *Pool) SetTickBitmap(wordPos int16, tickBitmap string) { p.tickBitmaps[wordPos] = tickBitmap } // DeleteTickBitmap deletes the tick bitmap for the given word position. // // Parameters: // - wordPos: signed bitmap word position to remove. func (p *Pool) DeleteTickBitmap(wordPos int16) { delete(p.tickBitmaps, wordPos) } // SetTickBitmaps replaces the map of initialized-tick bitmap words. // // Parameters: // - tickBitmaps: bitmap words keyed by signed word position. func (p *Pool) SetTickBitmaps(tickBitmaps map[int16]string) { p.tickBitmaps = tickBitmaps } // SetPositions replaces the tree containing aggregate position state. // // Parameters: // - positions: position storage tree to use. func (p *Pool) SetPositions(positions *bptree.BPTree) { p.positions = positions } // SetPosition stores position information under an encoded range key. // // Parameters: // - posKey: encoded lower/upper tick-range key. // - positionInfo: aggregate position state to store. func (p *Pool) SetPosition(posKey string, positionInfo PositionInfo) { p.positions.Set(posKey, positionInfo) } // HasTick reports whether a tick entry exists in the pool's tick tree. // // Parameters: // - tick: tick index to check. // // Returns: // - bool: true when the encoded tick is present; false otherwise. func (p *Pool) HasTick(tick int32) bool { tickKey := EncodeTickKey(tick) return p.ticks.Has(tickKey) } // GetTick returns the state stored for a tick. // // Parameters: // - tick: tick index to look up. // // Returns: // - TickInfo: stored tick state. // - error: non-nil when the tick is absent; a present value of the wrong type causes a panic. func (p *Pool) GetTick(tick int32) (TickInfo, error) { tickKey := EncodeTickKey(tick) iTickInfo := p.ticks.Get(tickKey) if iTickInfo == nil { return TickInfo{}, ufmt.Errorf("tick %d not found", tick) } tickInfo, ok := iTickInfo.(TickInfo) if !ok { panic(ufmt.Sprintf("failed to cast tickInfo to TickInfo: %T", iTickInfo)) } return tickInfo, nil } // SetTick stores tick state under its encoded tick key. // // Parameters: // - tick: tick index to store. // - tickInfo: tick state associated with the index. func (p *Pool) SetTick(tick int32, tickInfo TickInfo) { tickKey := EncodeTickKey(tick) p.ticks.Set(tickKey, tickInfo) } // DeleteTick removes the tick state for the given tick index. // // Parameters: // - tick: tick index to remove. func (p *Pool) DeleteTick(tick int32) { tickKey := EncodeTickKey(tick) p.ticks.Remove(tickKey) } // IterateTicks visits pool ticks in the inclusive range [startTick, endTick]. // Iteration stops when the callback returns true. // // Parameters: // - startTick: first tick index in the inclusive range. // - endTick: last tick index in the inclusive range. // - fn: callback receiving each decoded tick and its state; return true to stop iteration. func (p *Pool) IterateTicks(startTick int32, endTick int32, fn func(tick int32, tickInfo TickInfo) bool) { startTickKey := EncodeTickKey(startTick) endTickKey := EncodeTickKey(endTick + 1) // endTick inclusive p.ticks.Iterate(startTickKey, endTickKey, func(key string, value any) bool { tick := DecodeTickKey(key) tickInfo, ok := value.(TickInfo) if !ok { return false } return fn(tick, tickInfo) }) } // Clone copies a pool's own fields and leaves its collections nil: ticks, // tickBitmaps, and positions are not copied. Oracle observations are stored // separately from Pool. // // The copy is shallow because the read-only pool view clones every entry a // caller reads, so copying the collections would walk the whole tick tree for // each entry on a page. Read them through their own lookups instead: // GetTickInfo / GetInitializedTicksInRange for ticks, GetTickBitmaps for the // bitmaps, GetPoolPositions for positions, and GetSlot0 / GetObservationAt for // oracle metadata and entries. A caller that needs a working copy of a // collection -- DrySwap is the only one -- assembles it from those getters. // // Returns: // - *Pool: shallow pool copy with scalar state and cloned numeric values; nil when the receiver is nil. func (p *Pool) Clone() *Pool { if p == nil { return nil } return &Pool{ token0Path: p.token0Path, token1Path: p.token1Path, fee: p.fee, tickSpacing: p.tickSpacing, slot0: p.slot0.Clone(), balances: p.balances, protocolFees: p.protocolFees, feeGrowthGlobal0X128: p.feeGrowthGlobal0X128.Clone(), feeGrowthGlobal1X128: p.feeGrowthGlobal1X128.Clone(), liquidity: p.liquidity.Clone(), ticks: nil, tickBitmaps: nil, positions: nil, } } // NewPool constructs a pool with the supplied token pair, fee, price, and tick configuration. // // Parameters: // - token0Path: path of the pool's token0 asset. // - token1Path: path of the pool's token1 asset. // - fee: fee tier used by swaps. // - sqrtPriceX96: initial sqrt(token1/token0) price scaled by 2^96. // - tickSpacing: spacing between initialized ticks. // - tick: initial current tick. // - slot0FeeProtocol: packed protocol-fee denominator configuration for slot0. // // Returns: // - *Pool: initialized pool with empty balances, fee growth, ticks, bitmaps, and positions. func NewPool( token0Path string, token1Path string, fee uint32, sqrtPriceX96 *u256.Uint, tickSpacing int32, tick int32, slot0FeeProtocol uint8, ) *Pool { slot0 := NewSlot0(sqrtPriceX96, tick, slot0FeeProtocol, true) return &Pool{ token0Path: token0Path, token1Path: token1Path, balances: NewTokenPair(), fee: fee, tickSpacing: tickSpacing, slot0: slot0, feeGrowthGlobal0X128: u256.Zero(), feeGrowthGlobal1X128: u256.Zero(), protocolFees: NewTokenPair(), liquidity: u256.Zero(), ticks: bptree.NewBPTreeN(32), tickBitmaps: make(map[int16]string), positions: bptree.NewBPTreeN(16), } } // NewPoolsTree creates the BPTree used to store pools by pool path. // // Returns: // - *bptree.BPTree: empty pool storage tree with fanout 32. func NewPoolsTree() *bptree.BPTree { return bptree.NewBPTreeN(32) } // NewPoolTicksTree creates a BPTree for storing pool tick info (fanout 32), // owned by the pool domain realm so leaf-slot writes are not readonly tainted. // // Returns: // - *bptree.BPTree: empty tick storage tree with fanout 32. func NewPoolTicksTree() *bptree.BPTree { return bptree.NewBPTreeN(32) } // NewPoolPositionsTree creates a BPTree for storing pool position info (fanout 16), // owned by the pool domain realm so leaf-slot writes are not readonly tainted. // // Returns: // - *bptree.BPTree: empty position storage tree with fanout 16. func NewPoolPositionsTree() *bptree.BPTree { return bptree.NewBPTreeN(16) } type TokenPair struct { token0, token1 int64 } // NewTokenPair creates a token-pair balance value initialized to zero. // // Returns: // - TokenPair: zero token0 and token1 balances. func NewTokenPair() TokenPair { return TokenPair{ token0: 0, token1: 0, } } // Token0 returns the token0 amount in the pair. // // Returns: // - int64: stored token0 amount. func (p *TokenPair) Token0() int64 { return p.token0 } // Token1 returns the token1 amount in the pair. // // Returns: // - int64: stored token1 amount. func (p *TokenPair) Token1() int64 { return p.token1 } // SetToken0 updates the token0 amount in the pair. // // Parameters: // - token0: token0 amount to store. func (p *TokenPair) SetToken0(token0 int64) { p.token0 = token0 } // SetToken1 updates the token1 amount in the pair. // // Parameters: // - token1: token1 amount to store. func (p *TokenPair) SetToken1(token1 int64) { p.token1 = token1 } // Slot0 mirrors Uniswap V3's slot0(): current price/tick/protocol-fee/lock // state and the oracle cursor/capacity metadata. type Slot0 struct { sqrtPriceX96 *u256.Uint // current price of the pool as a sqrt(token1/token0) Q96 value tick int32 // current tick of the pool, i.e according to the last tick transition that was run feeProtocol uint8 // packed protocol-fee denominators: token0 low nibble, token1 high nibble unlocked bool // whether the pool is currently locked to reentrancy observationIndex uint16 // the index of the most-recently written observation observationCardinality uint16 // the current maximum number of observations stored observationCardinalityNext uint16 // the next maximum number of observations to store } // SqrtPriceX96 returns a copy of the current sqrt price in Q96 fixed-point form. // // Returns: // - *u256.Uint: cloned sqrt(token1/token0) price scaled by 2^96. func (s *Slot0) SqrtPriceX96() *u256.Uint { return s.sqrtPriceX96.Clone() } // Tick returns the current pool tick recorded in slot0. // // Returns: // - int32: current tick. func (s *Slot0) Tick() int32 { return s.tick } // FeeProtocol returns the packed protocol-fee denominators in slot0. // // Returns: // - uint8: packed token0/token1 protocol-fee denominator configuration. func (s *Slot0) FeeProtocol() uint8 { return s.feeProtocol } // Unlocked reports whether the pool's reentrancy lock is open. // // Returns: // - bool: true when unlocked and false while the pool is locked. func (s *Slot0) Unlocked() bool { return s.unlocked } // ObservationIndex returns the index of the most recently written observation. // // Returns: // - uint16: current observation ring-buffer index. func (s *Slot0) ObservationIndex() uint16 { return s.observationIndex } // ObservationCardinality returns the number of observation slots currently available. // // Returns: // - uint16: current observation capacity. func (s *Slot0) ObservationCardinality() uint16 { return s.observationCardinality } // ObservationCardinalityNext returns the requested future observation capacity. // // Returns: // - uint16: next observation capacity to use when the ring grows. func (s *Slot0) ObservationCardinalityNext() uint16 { return s.observationCardinalityNext } // Clone returns a value-type copy of Slot0 that shares no mutable state with // the original, so callers cannot reach back into pool internals through the // returned sqrtPriceX96 pointer. // // Returns: // - Slot0: value copy with a cloned sqrt-price pointer. func (s *Slot0) Clone() Slot0 { return Slot0{ sqrtPriceX96: s.sqrtPriceX96.Clone(), tick: s.tick, feeProtocol: s.feeProtocol, unlocked: s.unlocked, observationIndex: s.observationIndex, observationCardinality: s.observationCardinality, observationCardinalityNext: s.observationCardinalityNext, } } // SetSqrtPriceX96 updates the slot0 sqrt price. // // Parameters: // - sqrtPriceX96: sqrt(token1/token0) price scaled by 2^96; it is cloned before storage. func (s *Slot0) SetSqrtPriceX96(sqrtPriceX96 *u256.Uint) { s.sqrtPriceX96 = sqrtPriceX96.Clone() } // SetTick updates the current slot0 tick. // // Parameters: // - tick: current pool tick to store. func (s *Slot0) SetTick(tick int32) { s.tick = tick } // SetFeeProtocol updates the packed slot0 protocol-fee denominators. // // Parameters: // - feeProtocol: packed token0/token1 protocol-fee denominator configuration. func (s *Slot0) SetFeeProtocol(feeProtocol uint8) { s.feeProtocol = feeProtocol } // SetUnlocked updates the slot0 reentrancy-lock state. // // Parameters: // - unlocked: true to mark the pool unlocked, false to mark it locked. func (s *Slot0) SetUnlocked(unlocked bool) { s.unlocked = unlocked } // SetObservationIndex updates the index of the most recently written observation. // // Parameters: // - observationIndex: observation ring-buffer index to store. func (s *Slot0) SetObservationIndex(observationIndex uint16) { s.observationIndex = observationIndex } // SetObservationCardinality updates the current observation capacity. // // Parameters: // - observationCardinality: current number of observation slots available. func (s *Slot0) SetObservationCardinality(observationCardinality uint16) { s.observationCardinality = observationCardinality } // SetObservationCardinalityNext updates the requested future observation capacity. // // Parameters: // - observationCardinalityNext: next capacity to use when the ring grows. func (s *Slot0) SetObservationCardinalityNext(observationCardinalityNext uint16) { s.observationCardinalityNext = observationCardinalityNext } // NewSlot0 constructs slot0 with the supplied price, tick, protocol fee, and lock state. // Observation metadata starts with index zero and cardinality one. // // Parameters: // - sqrtPriceX96: initial sqrt(token1/token0) price scaled by 2^96. // - tick: initial current pool tick. // - feeProtocol: packed token0/token1 protocol-fee denominator configuration. // - unlocked: initial reentrancy-lock state. // // Returns: // - Slot0: initialized slot0 value with one observation slot. func NewSlot0( sqrtPriceX96 *u256.Uint, tick int32, feeProtocol uint8, unlocked bool, ) Slot0 { return Slot0{ sqrtPriceX96: sqrtPriceX96.Clone(), tick: tick, feeProtocol: feeProtocol, unlocked: unlocked, observationIndex: 0, observationCardinality: 1, observationCardinalityNext: 1, } } // TickInfo stores information about a specific tick in the pool. // TIcks represent discrete price points that can be used as boundaries for positions. type TickInfo struct { liquidityGross string // total position liquidity that references this tick liquidityNet string // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left) // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) // only has relative meaning, not absolute — the value depends on when the tick is initialized feeGrowthOutside0X128 string feeGrowthOutside1X128 string tickCumulativeOutside int64 // cumulative tick value on the other side of the tick // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) // only has relative meaning, not absolute — the value depends on when the tick is initialized secondsPerLiquidityOutsideX128 string // the seconds spent on the other side of the tick (relative to the current tick) // only has relative meaning, not absolute — the value depends on when the tick is initialized secondsOutside uint32 initialized bool // whether the tick is initialized } // TickInfo Getters methods // LiquidityGross returns total position liquidity referencing this tick. // // Returns: // - string: gross liquidity encoded as a decimal string. func (t *TickInfo) LiquidityGross() string { return t.liquidityGross } // LiquidityNet returns net liquidity applied when this tick is crossed. // // Returns: // - string: signed net liquidity encoded as a decimal string. func (t *TickInfo) LiquidityNet() string { return t.liquidityNet } // FeeGrowthOutside0X128 returns token0 fee growth on the side of this tick opposite the current tick. // // Returns: // - string: token0 outside fee-growth accumulator scaled by 2^128. func (t *TickInfo) FeeGrowthOutside0X128() string { return t.feeGrowthOutside0X128 } // FeeGrowthOutside1X128 returns token1 fee growth on the side of this tick opposite the current tick. // // Returns: // - string: token1 outside fee-growth accumulator scaled by 2^128. func (t *TickInfo) FeeGrowthOutside1X128() string { return t.feeGrowthOutside1X128 } // SecondsPerLiquidityOutsideX128 returns seconds per liquidity outside this tick. // // Returns: // - string: outside seconds-per-liquidity accumulator scaled by 2^128. func (t *TickInfo) SecondsPerLiquidityOutsideX128() string { return t.secondsPerLiquidityOutsideX128 } // SecondsOutside returns the time spent on the side of this tick opposite the current tick. // // Returns: // - uint32: seconds accumulated outside the tick. func (t *TickInfo) SecondsOutside() uint32 { return t.secondsOutside } // Initialized reports whether this tick has active initialized state. // // Returns: // - bool: true when the tick is initialized; false otherwise. func (t *TickInfo) Initialized() bool { return t.initialized } // TickCumulativeOutside returns cumulative tick value on the side opposite the current tick. // // Returns: // - int64: outside cumulative tick value. func (t *TickInfo) TickCumulativeOutside() int64 { return t.tickCumulativeOutside } // TickInfo Setters methods // SetLiquidityGross stores total position liquidity referencing this tick. // // Parameters: // - liquidityGross: gross liquidity encoded as a decimal string. func (t *TickInfo) SetLiquidityGross(liquidityGross string) { t.liquidityGross = liquidityGross } // SetLiquidityNet stores the net liquidity change applied when crossing this tick. // // Parameters: // - liquidityNet: signed net liquidity encoded as a decimal string. func (t *TickInfo) SetLiquidityNet(liquidityNet string) { t.liquidityNet = liquidityNet } // SetFeeGrowthOutside0X128 stores token0 outside fee growth for this tick. // // Parameters: // - feeGrowthOutside0X128: token0 outside fee growth scaled by 2^128, encoded as a decimal string. func (t *TickInfo) SetFeeGrowthOutside0X128(feeGrowthOutside0X128 string) { t.feeGrowthOutside0X128 = feeGrowthOutside0X128 } // SetFeeGrowthOutside1X128 stores token1 outside fee growth for this tick. // // Parameters: // - feeGrowthOutside1X128: token1 outside fee growth scaled by 2^128, encoded as a decimal string. func (t *TickInfo) SetFeeGrowthOutside1X128(feeGrowthOutside1X128 string) { t.feeGrowthOutside1X128 = feeGrowthOutside1X128 } // SetSecondsPerLiquidityOutsideX128 stores outside seconds-per-liquidity growth. // // Parameters: // - secondsPerLiquidityOutsideX128: outside seconds per liquidity scaled by 2^128, encoded as a decimal string. func (t *TickInfo) SetSecondsPerLiquidityOutsideX128(secondsPerLiquidityOutsideX128 string) { t.secondsPerLiquidityOutsideX128 = secondsPerLiquidityOutsideX128 } // SetSecondsOutside stores the seconds accumulated outside this tick. // // Parameters: // - secondsOutside: elapsed seconds outside the tick. func (t *TickInfo) SetSecondsOutside(secondsOutside uint32) { t.secondsOutside = secondsOutside } // SetInitialized updates whether this tick is initialized. // // Parameters: // - initialized: true to mark the tick initialized, false otherwise. func (t *TickInfo) SetInitialized(initialized bool) { t.initialized = initialized } // SetTickCumulativeOutside stores cumulative tick value outside this tick. // // Parameters: // - tickCumulativeOutside: cumulative tick value on the opposite side. func (t *TickInfo) SetTickCumulativeOutside(tickCumulativeOutside int64) { t.tickCumulativeOutside = tickCumulativeOutside } // Clone returns an independent value copy of this tick's stored state. // // Returns: // - TickInfo: copied liquidity, fee-growth, time, and initialization fields. func (t *TickInfo) Clone() TickInfo { return TickInfo{ feeGrowthOutside0X128: t.feeGrowthOutside0X128, feeGrowthOutside1X128: t.feeGrowthOutside1X128, liquidityGross: t.liquidityGross, liquidityNet: t.liquidityNet, tickCumulativeOutside: t.tickCumulativeOutside, secondsPerLiquidityOutsideX128: t.secondsPerLiquidityOutsideX128, secondsOutside: t.secondsOutside, initialized: t.initialized, } } // NewTickInfo creates an uninitialized tick state with zero accumulators. // // Returns: // - TickInfo: default tick state with all numeric fields zero and Initialized false. func NewTickInfo() TickInfo { return TickInfo{ liquidityGross: "0", liquidityNet: "0", feeGrowthOutside0X128: "0", feeGrowthOutside1X128: "0", secondsPerLiquidityOutsideX128: "0", secondsOutside: 0, initialized: false, tickCumulativeOutside: 0, } } // PositionInfo stores aggregate liquidity, fee-growth checkpoints, and tokens // owed for one pool-scoped lower/upper tick key. type PositionInfo struct { liquidity string // aggregate liquidity for this tick-range key feeGrowthInside0LastX128 string // fee growth per unit of liquidity for token0 as of last update feeGrowthInside1LastX128 string // fee growth per unit of liquidity for token1 as of last update // accumulated token0 amount waiting to be collected (principal or swap fee) tokensOwed0 int64 // accumulated token1 amount waiting to be collected (principal or swap fee) tokensOwed1 int64 } // Liquidity returns aggregate liquidity for this position range. // // Returns: // - string: position liquidity encoded as a decimal string. func (p *PositionInfo) Liquidity() string { return p.liquidity } // FeeGrowthInside0LastX128 returns the token0 fee-growth checkpoint for this position. // // Returns: // - string: token0 inside fee-growth checkpoint scaled by 2^128. func (p *PositionInfo) FeeGrowthInside0LastX128() string { return p.feeGrowthInside0LastX128 } // FeeGrowthInside1LastX128 returns the token1 fee-growth checkpoint for this position. // // Returns: // - string: token1 inside fee-growth checkpoint scaled by 2^128. func (p *PositionInfo) FeeGrowthInside1LastX128() string { return p.feeGrowthInside1LastX128 } // TokensOwed0 returns token0 accumulated for this position. // // Returns: // - int64: token0 principal or fee amount awaiting collection. func (p *PositionInfo) TokensOwed0() int64 { return p.tokensOwed0 } // TokensOwed1 returns token1 accumulated for this position. // // Returns: // - int64: token1 principal or fee amount awaiting collection. func (p *PositionInfo) TokensOwed1() int64 { return p.tokensOwed1 } // SetLiquidity updates aggregate liquidity for this position range. // // Parameters: // - liquidity: position liquidity encoded as a decimal string. func (p *PositionInfo) SetLiquidity(liquidity string) { p.liquidity = liquidity } // SetFeeGrowthInside0LastX128 updates the token0 fee-growth checkpoint. // // Parameters: // - feeGrowthInside0LastX128: token0 inside fee-growth checkpoint scaled by 2^128, encoded as a decimal string. func (p *PositionInfo) SetFeeGrowthInside0LastX128(feeGrowthInside0LastX128 string) { p.feeGrowthInside0LastX128 = feeGrowthInside0LastX128 } // SetFeeGrowthInside1LastX128 updates the token1 fee-growth checkpoint. // // Parameters: // - feeGrowthInside1LastX128: token1 inside fee-growth checkpoint scaled by 2^128, encoded as a decimal string. func (p *PositionInfo) SetFeeGrowthInside1LastX128(feeGrowthInside1LastX128 string) { p.feeGrowthInside1LastX128 = feeGrowthInside1LastX128 } // SetTokensOwed0 updates token0 accumulated for this position. // // Parameters: // - tokensOwed0: token0 principal or fee amount awaiting collection. func (p *PositionInfo) SetTokensOwed0(tokensOwed0 int64) { p.tokensOwed0 = tokensOwed0 } // SetTokensOwed1 updates token1 accumulated for this position. // // Parameters: // - tokensOwed1: token1 principal or fee amount awaiting collection. func (p *PositionInfo) SetTokensOwed1(tokensOwed1 int64) { p.tokensOwed1 = tokensOwed1 } // NewPositionInfo creates a zeroed position state for a tick range. // // Returns: // - PositionInfo: position with zero liquidity, fee-growth checkpoints, and owed tokens. func NewPositionInfo() PositionInfo { return PositionInfo{ liquidity: "0", feeGrowthInside0LastX128: "0", feeGrowthInside1LastX128: "0", tokensOwed0: 0, tokensOwed1: 0, } } // NewDefaultFeeAmountTickSpacing returns the default tick spacing for each supported fee tier. // // Returns: // - map[uint32]int32: fee-tier to tick-spacing mapping for 100, 500, 3000, and 10000 tiers. func NewDefaultFeeAmountTickSpacing() map[uint32]int32 { return map[uint32]int32{ 100: 1, 500: 10, 3000: 60, 10000: 200, } }
- #16proxy.gno
- #17package pool // CreatePool creates a new liquidity pool for a token pair. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Token contract path supplied for token0; the pool orders the // pair canonically rather than preserving the supplied order. // - token1Path: Token contract path supplied for token1; must form a distinct // registered pair with token0Path. // - fee: Fee tier for the pool; determines its tick spacing and swap fee. // - sqrtPriceX96: Initial square-root price in Q64.96 fixed-point form; it is // inverted when the supplied token order is reversed and must be in bounds. // // Halt check: reverts while the Pool halt scope is active. func CreatePool( cur realm, token0Path string, token1Path string, fee uint32, sqrtPriceX96 string, ) { getImplementation().CreatePool( 0, cur, token0Path, token1Path, fee, sqrtPriceX96, ) } // SetPoolCreationFee sets the pool creation fee. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - fee: New pool creation fee, in the configured native-token base units. // // Halt check: reverts while the Pool halt scope is active. func SetPoolCreationFee(cur realm, fee int64) { getImplementation().SetPoolCreationFee(0, cur, fee) } // IncreaseObservationCardinalityNext increases the observation cardinality for a pool. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - cardinalityNext: Requested maximum number of oracle observations retained // for the pool. // // Halt check: reverts while the Pool halt scope is active. func IncreaseObservationCardinalityNext( cur realm, token0Path string, token1Path string, fee uint32, cardinalityNext uint16, ) { getImplementation().IncreaseObservationCardinalityNext( 0, cur, token0Path, token1Path, fee, cardinalityNext, ) } // Mint adds liquidity to a position. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - tickLower: Lower inclusive tick boundary of the position range. // - tickUpper: Upper exclusive tick boundary of the position range. // - liquidityAmount: Decimal liquidity amount to add to the position. // - positionCaller: Position-contract address whose position receives the // liquidity. // // Returns: // - amount0: Token0 amount required for the liquidity addition, as a decimal // string. // - amount1: Token1 amount required for the liquidity addition, as a decimal // string. // // Halt check: reverts while the Pool halt scope is active. func Mint( cur realm, token0Path string, token1Path string, fee uint32, tickLower int32, tickUpper int32, liquidityAmount string, positionCaller address, ) (string, string) { return getImplementation().Mint( 0, cur, token0Path, token1Path, fee, tickLower, tickUpper, liquidityAmount, positionCaller, ) } // Burn removes liquidity from a position. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - tickLower: Lower inclusive tick boundary of the position range. // - tickUpper: Upper exclusive tick boundary of the position range. // - liquidityAmount: Decimal liquidity amount to remove from the position. // - positionCaller: Position-contract address whose position is decreased. // // Returns: // - amount0: Token0 principal credited to the position, as a decimal string; // the pool operation leaves it owed until a subsequent collection. // - amount1: Token1 principal credited to the position, as a decimal string; // the pool operation leaves it owed until a subsequent collection. // // Halt check: reverts while the Withdraw halt scope is active. func Burn( cur realm, token0Path string, token1Path string, fee uint32, tickLower int32, tickUpper int32, liquidityAmount string, positionCaller address, ) (string, string) { return getImplementation().Burn( 0, cur, token0Path, token1Path, fee, tickLower, tickUpper, liquidityAmount, positionCaller, ) } // CollectSwapFee pays accrued swap fees for a position out to recipient, // net of the withdrawal fee. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - recipient: Non-zero address receiving the collected token amounts after // withdrawal-fee deduction. // - tickLower: Lower inclusive tick boundary of the position range. // - tickUpper: Upper exclusive tick boundary of the position range. // - amount0Requested: Decimal token0 amount requested; the configured maximum // value requests all token0 fees owed. // - amount1Requested: Decimal token1 amount requested; the configured maximum // value requests all token1 fees owed. // // Returns: // - amount0: Collected token0 amount before withdrawal-fee deduction, as a // decimal string. // - amount1: Collected token1 amount before withdrawal-fee deduction, as a // decimal string. // - fee0: Withdrawal fee withheld from token0, as a decimal string. // - fee1: Withdrawal fee withheld from token1, as a decimal string. // // Halt check: reverts while the Withdraw halt scope is active. func CollectSwapFee( cur realm, token0Path string, token1Path string, fee uint32, recipient address, tickLower int32, tickUpper int32, amount0Requested string, amount1Requested string, ) (amount0, amount1, fee0, fee1 string) { return getImplementation().CollectSwapFee( 0, cur, token0Path, token1Path, fee, recipient, tickLower, tickUpper, amount0Requested, amount1Requested, ) } // Collect pays tokens owed to a position out to recipient in full. No // withdrawal fee is charged on this path; see CollectSwapFee for the // fee-bearing path. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - recipient: Non-zero address receiving the owed token amounts. // - tickLower: Lower inclusive tick boundary of the position range. // - tickUpper: Upper exclusive tick boundary of the position range. // - amount0Requested: Decimal token0 amount requested from the position's // accrued principal. // - amount1Requested: Decimal token1 amount requested from the position's // accrued principal. // // Returns: // - amount0: Token0 principal transferred to recipient, as a decimal string. // - amount1: Token1 principal transferred to recipient, as a decimal string. // // Halt check: reverts while the Withdraw halt scope is active. func Collect( cur realm, token0Path string, token1Path string, fee uint32, recipient address, tickLower int32, tickUpper int32, amount0Requested string, amount1Requested string, ) (string, string) { return getImplementation().Collect( 0, cur, token0Path, token1Path, fee, recipient, tickLower, tickUpper, amount0Requested, amount1Requested, ) } // SetWithdrawalFee sets the withdrawal fee rate. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - fee: Withdrawal fee in basis points; zero disables the fee. // // Halt check: reverts while the Pool halt scope is active. func SetWithdrawalFee(cur realm, fee uint64) { getImplementation().SetWithdrawalFee(0, cur, fee) } // Swap executes a token swap in the pool. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - recipient: Address receiving output tokens. // - zeroForOne: True to sell token0 for token1; false to sell token1 for // token0. // - amountSpecified: Decimal signed amount; positive requests exact input and // negative requests exact output. // - sqrtPriceLimitX96: Price boundary in Q64.96 fixed-point form at which the // swap must stop. // - swapCallback: Callback invoked with the current realm, signed token // deltas, and a callback marker; it must settle the input token and return a // non-nil error to abort settlement. // // Returns: // - amount0: Signed token0 delta for the swap, as a decimal string. // - amount1: Signed token1 delta for the swap, as a decimal string. // // Halt check: reverts while the Pool halt scope is active. func Swap( cur realm, token0Path string, token1Path string, fee uint32, recipient address, zeroForOne bool, amountSpecified string, sqrtPriceLimitX96 string, swapCallback func(cur realm, amount0Delta, amount1Delta int64, callbackMarker *CallbackMarker) error, ) (string, string) { return getImplementation().Swap( 0, cur, token0Path, token1Path, fee, recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, swapCallback, ) } // DrySwap simulates a swap without executing it, returning the expected output. // // This is a read-only operation that does not modify pool state. // Used by router for multi-hop swap simulations and by clients for price quotes. // // Parameters: // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - zeroForOne: True to sell token0 for token1; false to sell token1 for // token0. // - amountSpecified: Decimal signed amount; positive requests exact input and // negative requests exact output. // - sqrtPriceLimitX96: Price boundary in Q64.96 fixed-point form at which the // simulation must stop. // // Returns: // - amount0: Simulated signed token0 delta, as a decimal string; "0" on an // unsuccessful simulation. // - amount1: Simulated signed token1 delta, as a decimal string; "0" on an // unsuccessful simulation. // - error: Nil when the simulation succeeds; non-nil when validation, // computation, or pool-balance checks fail. func DrySwap( token0Path string, token1Path string, fee uint32, zeroForOne bool, amountSpecified string, sqrtPriceLimitX96 string, ) (string, string, error) { return getImplementation().DrySwap(token0Path, token1Path, fee, zeroForOne, amountSpecified, sqrtPriceLimitX96) } // SetSwapEndHook sets the hook to be called at the end of a swap. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - hook: Callback invoked with the current realm and pool path after swap // settlement; its error is propagated to abort the swap. func SetSwapEndHook(cur realm, hook func(cur realm, poolPath string) error) { getImplementation().SetSwapEndHook(0, cur, hook) } // SetSwapStartHook sets the hook to be called at the start of a swap. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - hook: Callback invoked with the current realm, pool path, and swap // timestamp before swap computation. func SetSwapStartHook(cur realm, hook func(cur realm, poolPath string, timestamp int64)) { getImplementation().SetSwapStartHook(0, cur, hook) } // SetTickCrossHook sets the hook to be called when a tick is crossed during a swap. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - hook: Callback invoked with the current realm, pool path, crossed tick // index, swap direction, and block timestamp. func SetTickCrossHook(cur realm, hook func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64)) { getImplementation().SetTickCrossHook(0, cur, hook) } // CollectProtocol collects protocol fees from a pool. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - token0Path: Path of the first token in the pool pair. // - token1Path: Path of the second token in the pool pair. // - fee: Fee tier identifying the pool. // - recipient: Address receiving the collected protocol fees. // - amount0Requested: Decimal token0 protocol-fee amount requested; collection // is capped at the available amount. // - amount1Requested: Decimal token1 protocol-fee amount requested; collection // is capped at the available amount. // // Returns: // - amount0: Token0 protocol fee transferred to recipient, as a decimal string. // - amount1: Token1 protocol fee transferred to recipient, as a decimal string. // // Halt check: reverts while the Withdraw halt scope is active. func CollectProtocol( cur realm, token0Path string, token1Path string, fee uint32, recipient address, amount0Requested string, amount1Requested string, ) (string, string) { return getImplementation().CollectProtocol( 0, cur, token0Path, token1Path, fee, recipient, amount0Requested, amount1Requested, ) } // SetFeeProtocol sets the protocol fee rates for a pool. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this // realm. // - feeProtocol0: Token0 protocol-fee denominator; zero disables it, while // non-zero supported values route the corresponding fraction to protocol. // - feeProtocol1: Token1 protocol-fee denominator; zero disables it, while // non-zero supported values route the corresponding fraction to protocol. // // Halt check: reverts while the Pool halt scope is active. func SetFeeProtocol(cur realm, feeProtocol0, feeProtocol1 uint8) { getImplementation().SetFeeProtocol(0, cur, feeProtocol0, feeProtocol1) }
- #18render.gno
- #19package pool // Render delegates web rendering to the active implementation. func Render(path string) string { return getImplementation().Render(path) }
- #20state.gno
- #21package pool import ( "errors" "gno.land/p/gnoswap/store/v1" "gno.land/p/gnoswap/version_manager/v1" // initialize rbac roles _ "gno.land/r/gnoswap/rbac/v1" ) var ( domainPath string currentAddress address // kvStore is the core storage instance for the pool domain. // All pool implementations share this single storage instance, // ensuring data consistency across version upgrades. kvStore store.KVStore // versionManager is the version manager for the pool domain. // It manages the registration and switching of pool implementations. versionManager version_manager.VersionManager // implementation is the currently active pool implementation. // This pointer is switched during upgrades to point to different versions (v1, v2, etc.). // The proxy layer routes all calls to this implementation. implementation IPool ) // init initializes the pool domain state. // This function is called when the pool domain contract is first deployed. func init(cur realm) { domainPath = cur.PkgPath() currentAddress = cur.Address() // Create a new KV store instance for this domain kvStore = store.NewKVStore(currentAddress) // Initialize the initializers map to store implementation registration functions versionManager = version_manager.NewVersionManager( domainPath, kvStore, initializeDomainStore, ) implementation = nil } func initializeDomainStore(_ int, rlm realm, kvStore store.KVStore) any { return NewPoolStore(kvStore) } // getImplementation returns the currently active pool implementation. // This function is used by all proxy functions to route calls to the active implementation. // If no implementation is set, it panics to prevent invalid state. func getImplementation() IPool { if implementation == nil { panic("implementation is not initialized") } return implementation } func updateImplementation() error { result := versionManager.GetCurrentImplementation() if result == nil { return errors.New("implementation is not initialized") } impl, ok := result.(IPool) if !ok { return errors.New("impl is not an IPool") } implementation = impl return nil }
- #22store.gno
- #23package pool import ( "errors" "gno.land/p/gnoswap/store/v1" bptree "gno.land/p/nt/bptree/v0" ufmt "gno.land/p/nt/ufmt/v0" ) // StoreKey defines the keys used for storing pool data in the KV store. // These keys are prefixed with the domain address to ensure namespace isolation. type StoreKey string // Returns: // - keyText: the textual store key represented by s. func (s StoreKey) String() string { return string(s) } const ( // Pool data storage keys StoreKeyPools StoreKey = "pools" // Map containing all pools StoreKeyObservations StoreKey = "observations" // poolPath -> observation B+tree StoreKeyFeeAmountTickSpacing StoreKey = "feeAmountTickSpacing" // Fee tier to tick spacing mapping StoreKeySlot0FeeProtocol StoreKey = "slot0FeeProtocol" // Protocol fee denominator(s) // Protocol fee storage keys StoreKeyPoolCreationFee StoreKey = "poolCreationFee" // Pool creation fee amount StoreKeyPendingProtocolFees StoreKey = "pendingProtocolFees" // tokenPath -> amount held locally for protocol_fee StoreKeyWithdrawalFeeBPS StoreKey = "withdrawalFeeBPS" // Withdrawal fee in basis points StoreKeyUnlocked StoreKey = "unlocked" // Global pool reentrancy lock // Swap hook storage keys StoreKeySwapStartHook StoreKey = "swapStartHook" // Swap start hook function StoreKeySwapEndHook StoreKey = "swapEndHook" // Swap end hook function StoreKeyTickCrossHook StoreKey = "tickCrossHook" // Tick cross hook function ) // poolStore implements the IPoolStore interface for pool domain storage. // It provides type-safe access to pool data stored in the underlying KV store. type poolStore struct { kvStore store.KVStore } // Returns: // - exists: true when the pool collection key is present in the KV store. func (s *poolStore) HasPools() bool { return s.kvStore.Has(StoreKeyPools.String()) } // GetPools retrieves the map containing all pool data. // This is the main data structure that stores all pool instances. // Returns: // - pools: the B+tree containing all persisted pool instances; panics if the // KV store cannot read it or the stored value is nil. func (s *poolStore) GetPools() *bptree.BPTree { pools, err := s.kvStore.GetBPTree(StoreKeyPools.String()) if err != nil { panic(err) } if pools == nil { panic("pools is nil") } return pools } // SetPools stores the map containing all pool data. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - pools: non-nil B+tree containing the pool instances to persist. // // Returns: // - err: nil when the pool tree is stored; ErrSpoofedRealm for a non-current // realm, or the underlying KV-store write error. func (s *poolStore) SetPools(_ int, rlm realm, pools *bptree.BPTree) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } if pools == nil { panic("pools is nil") } return s.kvStore.Set(0, rlm, StoreKeyPools.String(), pools) } // Returns: // - exists: true when the observations collection key is present in the KV store. func (s *poolStore) HasObservations() bool { return s.kvStore.Has(StoreKeyObservations.String()) } // Returns: // - observations: the B+tree containing persisted observation trees; panics if // the KV store cannot read it or the stored value is nil. func (s *poolStore) GetObservations() *bptree.BPTree { observations, err := s.kvStore.GetBPTree(StoreKeyObservations.String()) if err != nil { panic(err) } if observations == nil { panic("observations is nil") } return observations } // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - observations: non-nil B+tree containing the observation data to persist. // // Returns: // - err: nil when the observation tree is stored; ErrSpoofedRealm for a // non-current realm, or the underlying KV-store write error. func (s *poolStore) SetObservations(_ int, rlm realm, observations *bptree.BPTree) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } if observations == nil { panic("observations is nil") } return s.kvStore.Set(0, rlm, StoreKeyObservations.String(), observations) } // Returns: // - exists: true when the fee-tier/tick-spacing mapping key is present in the // KV store. func (s *poolStore) HasFeeAmountTickSpacing() bool { return s.kvStore.Has(StoreKeyFeeAmountTickSpacing.String()) } // GetFeeAmountTickSpacing retrieves the mapping between fee amounts and tick spacing. // This mapping determines the tick spacing for each supported fee tier. // Returns: // - feeAmountTickSpacing: a copy of the fee amount to tick-spacing mapping; // panics if the KV value cannot be read, has the wrong type, or is nil. func (s *poolStore) GetFeeAmountTickSpacing() map[uint32]int32 { result, err := s.kvStore.Get(StoreKeyFeeAmountTickSpacing.String()) if err != nil { panic(err) } feeAmountTickSpacing, ok := result.(map[uint32]int32) if !ok { panic(ufmt.Sprintf("failed to cast result to map[uint32]int32: %T", result)) } if feeAmountTickSpacing == nil { panic("feeAmountTickSpacing is nil") } return cloneFeeAmountTickSpacings(feeAmountTickSpacing) } // SetFeeAmountTickSpacing stores the mapping between fee amounts and tick spacing. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - feeAmountTickSpacing: non-nil mapping from fee tiers to their required // int32 tick spacing values. // // Returns: // - err: nil when the mapping is stored; ErrSpoofedRealm for a non-current // realm, or the underlying KV-store write error. func (s *poolStore) SetFeeAmountTickSpacing(_ int, rlm realm, feeAmountTickSpacing map[uint32]int32) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } if feeAmountTickSpacing == nil { panic("feeAmountTickSpacing is nil") } return s.kvStore.Set(0, rlm, StoreKeyFeeAmountTickSpacing.String(), feeAmountTickSpacing) } // Returns: // - exists: true when the slot0 protocol-fee configuration key is present in // the KV store. func (s *poolStore) HasSlot0FeeProtocol() bool { return s.kvStore.Has(StoreKeySlot0FeeProtocol.String()) } // GetSlot0FeeProtocol retrieves the protocol fee denominator(s) for slot0. // Returns: // - slot0FeeProtocol: packed protocol-fee denominator configuration for // slot0; panics if the KV value cannot be read or has the wrong type. func (s *poolStore) GetSlot0FeeProtocol() uint8 { result, err := s.kvStore.Get(StoreKeySlot0FeeProtocol.String()) if err != nil { panic(err) } slot0FeeProtocol, ok := result.(uint8) if !ok { panic(ufmt.Sprintf("failed to cast result to uint8: %T", result)) } return slot0FeeProtocol } // SetSlot0FeeProtocol stores the protocol fee denominator(s) for slot0. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - slot0FeeProtocol: packed uint8 protocol-fee denominators for token0 and // token1. // // Returns: // - err: nil when the protocol-fee configuration is stored; ErrSpoofedRealm // for a non-current realm, or the underlying KV-store write error. func (s *poolStore) SetSlot0FeeProtocol(_ int, rlm realm, slot0FeeProtocol uint8) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeySlot0FeeProtocol.String(), slot0FeeProtocol) } // Returns: // - exists: true when the pool-creation-fee key is present in the KV store. func (s *poolStore) HasPoolCreationFee() bool { return s.kvStore.Has(StoreKeyPoolCreationFee.String()) } // GetPoolCreationFee retrieves the pool creation fee amount. // Returns: // - poolCreationFee: configured pool-creation charge in the chain's smallest // currency unit; panics if the KV value cannot be read or has the wrong type. func (s *poolStore) GetPoolCreationFee() int64 { result, err := s.kvStore.Get(StoreKeyPoolCreationFee.String()) if err != nil { panic(err) } poolCreationFee, ok := result.(int64) if !ok { panic(ufmt.Sprintf("failed to cast result to int64: %T", result)) } return poolCreationFee } // SetPoolCreationFee stores the pool creation fee amount. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - poolCreationFee: pool-creation charge in the chain's smallest currency // unit. // // Returns: // - err: nil when the fee is stored; ErrSpoofedRealm for a non-current realm, // or the underlying KV-store write error. func (s *poolStore) SetPoolCreationFee(_ int, rlm realm, poolCreationFee int64) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyPoolCreationFee.String(), poolCreationFee) } // Returns: // - exists: true when the pending protocol-fees key is present in the KV store. func (s *poolStore) HasPendingProtocolFees() bool { return s.kvStore.Has(StoreKeyPendingProtocolFees.String()) } // Returns: // - pendingProtocolFees: mapping from token path to pending protocol-fee // amount in the chain's smallest currency unit; panics if the KV value // cannot be read or has the wrong type. func (s *poolStore) GetPendingProtocolFees() map[string]int64 { result, err := s.kvStore.Get(StoreKeyPendingProtocolFees.String()) if err != nil { panic(err) } pendingProtocolFees, ok := result.(map[string]int64) if !ok { panic(ufmt.Sprintf("failed to cast result to map[string]int64: %T", result)) } return pendingProtocolFees } // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - pendingProtocolFees: token-path-to-amount mapping; amounts are in the // chain's smallest currency unit and are copied before storage. // // Returns: // - err: nil when the copied mapping is stored; ErrSpoofedRealm for a // non-current realm, or the underlying KV-store write error. func (s *poolStore) SetPendingProtocolFees(_ int, rlm realm, pendingProtocolFees map[string]int64) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } // The map is copied here so it is allocated by, and therefore mutable from, this realm. owned := make(map[string]int64, len(pendingProtocolFees)) for tokenPath, amount := range pendingProtocolFees { owned[tokenPath] = amount } return s.kvStore.Set(0, rlm, StoreKeyPendingProtocolFees.String(), owned) } // Parameters: // - tokenPath: token contract path used as the pending-fee map key. // // Returns: // - amount: pending protocol-fee amount for tokenPath in the chain's smallest // currency unit, or zero when no entry exists. func (s *poolStore) GetPendingProtocolFee(tokenPath string) int64 { return s.GetPendingProtocolFees()[tokenPath] } // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - tokenPath: token contract path identifying the pending-fee entry. // - amount: pending protocol-fee amount to assign for tokenPath, in the // chain's smallest currency unit. // // Returns: // - err: nil after the map entry is updated; ErrSpoofedRealm for a non-current // realm, or store.ErrWritePermissionDenied for an unauthorized code realm. func (s *poolStore) SetPendingProtocolFee(_ int, rlm realm, tokenPath string, amount int64) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } if rlm.IsCode() && !s.kvStore.IsWriteAuthorized(rlm.Address()) { return errors.New(store.ErrWritePermissionDenied) } s.GetPendingProtocolFees()[tokenPath] = amount return nil } // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - tokenPath: token contract path identifying the pending-fee entry to delete. // // Returns: // - err: nil after the map entry is removed; ErrSpoofedRealm for a non-current // realm, or store.ErrWritePermissionDenied for an unauthorized code realm. func (s *poolStore) RemovePendingProtocolFee(_ int, rlm realm, tokenPath string) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } if rlm.IsCode() && !s.kvStore.IsWriteAuthorized(rlm.Address()) { return errors.New(store.ErrWritePermissionDenied) } delete(s.GetPendingProtocolFees(), tokenPath) return nil } // Returns: // - exists: true when the withdrawal-fee key is present in the KV store. func (s *poolStore) HasWithdrawalFeeBPS() bool { return s.kvStore.Has(StoreKeyWithdrawalFeeBPS.String()) } // GetWithdrawalFeeBPS retrieves the withdrawal fee in basis points. // Returns: // - withdrawalFeeBPS: withdrawal fee expressed in basis points (1/100 of a // percent); panics if the KV value cannot be read or has the wrong type. func (s *poolStore) GetWithdrawalFeeBPS() uint64 { result, err := s.kvStore.Get(StoreKeyWithdrawalFeeBPS.String()) if err != nil { panic(err) } withdrawalFeeBPS, ok := result.(uint64) if !ok { panic(ufmt.Sprintf("failed to cast result to uint64: %T", result)) } return withdrawalFeeBPS } // SetWithdrawalFeeBPS stores the withdrawal fee in basis points. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - withdrawalFeeBPS: withdrawal fee in basis points, where 100 basis points // equals one percent. // // Returns: // - err: nil when the fee is stored; ErrSpoofedRealm for a non-current realm, // or the underlying KV-store write error. func (s *poolStore) SetWithdrawalFeeBPS(_ int, rlm realm, withdrawalFeeBPS uint64) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyWithdrawalFeeBPS.String(), withdrawalFeeBPS) } // Returns: // - exists: true when the global unlocked-state key is present in the KV store. func (s *poolStore) HasUnlocked() bool { return s.kvStore.Has(StoreKeyUnlocked.String()) } // Returns: // - unlocked: the persisted global reentrancy-lock state; panics if the KV // value cannot be read or has the wrong type. func (s *poolStore) GetUnlocked() bool { result, err := s.kvStore.Get(StoreKeyUnlocked.String()) if err != nil { panic(err) } unlocked, ok := result.(bool) if !ok { panic(ufmt.Sprintf("failed to cast result to bool: %T", result)) } return unlocked } // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - unlocked: true when pool operations may proceed without the global lock. // // Returns: // - err: nil when the lock state is stored; ErrSpoofedRealm for a non-current // realm, or the underlying KV-store write error. func (s *poolStore) SetUnlocked(_ int, rlm realm, unlocked bool) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyUnlocked.String(), unlocked) } // HasSwapStartHook checks if the swap start hook is set. // Returns: // - exists: true when a swap-start hook key is present in the KV store. func (s *poolStore) HasSwapStartHook() bool { return s.kvStore.Has(StoreKeySwapStartHook.String()) } // GetSwapStartHook retrieves the swap start hook function. // Returns: // - swapStartHook: stored callback invoked at swap start with the current // realm, pool path, and block timestamp; panics if the KV value cannot be // read or has the wrong function type. func (s *poolStore) GetSwapStartHook() func(cur realm, poolPath string, timestamp int64) { result, err := s.kvStore.Get(StoreKeySwapStartHook.String()) if err != nil { panic(err) } swapStartHook, ok := result.(func(cur realm, poolPath string, timestamp int64)) if !ok { panic(ufmt.Sprintf("failed to cast result to func(poolPath string, timestamp int64): %T", result)) } return swapStartHook } // SetSwapStartHook stores the swap start hook function. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - swapStartHook: callback receiving the current realm, pool path, and // block timestamp when a swap starts. // // Returns: // - err: nil when the callback is stored; ErrSpoofedRealm for a non-current // realm, or the underlying KV-store write error. func (s *poolStore) SetSwapStartHook(_ int, rlm realm, swapStartHook func(cur realm, poolPath string, timestamp int64)) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeySwapStartHook.String(), swapStartHook) } // HasSwapEndHook checks if the swap end hook is set. // Returns: // - exists: true when a swap-end hook key is present in the KV store. func (s *poolStore) HasSwapEndHook() bool { return s.kvStore.Has(StoreKeySwapEndHook.String()) } // GetSwapEndHook retrieves the swap end hook function. // Returns: // - swapEndHook: stored callback receiving the current realm and pool path at // swap end and returning an error; panics if the KV value cannot be read or // has the wrong function type. func (s *poolStore) GetSwapEndHook() func(cur realm, poolPath string) error { result, err := s.kvStore.Get(StoreKeySwapEndHook.String()) if err != nil { panic(err) } swapEndHook, ok := result.(func(cur realm, poolPath string) error) if !ok { panic(ufmt.Sprintf("failed to cast result to func(poolPath string): %T", result)) } return swapEndHook } // SetSwapEndHook stores the swap end hook function. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - swapEndHook: callback receiving the current realm and pool path at swap // end and returning any hook error. // // Returns: // - err: nil when the callback is stored; ErrSpoofedRealm for a non-current // realm, or the underlying KV-store write error. func (s *poolStore) SetSwapEndHook(_ int, rlm realm, swapEndHook func(cur realm, poolPath string) error) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeySwapEndHook.String(), swapEndHook) } // HasTickCrossHook checks if the tick cross hook is set. // Returns: // - exists: true when a tick-cross hook key is present in the KV store. func (s *poolStore) HasTickCrossHook() bool { return s.kvStore.Has(StoreKeyTickCrossHook.String()) } // GetTickCrossHook retrieves the tick cross hook function. // Returns: // - tickCrossHook: stored callback receiving the current realm, pool path, // crossed tick, swap direction, and block timestamp; panics if the KV value // cannot be read or has the wrong function type. func (s *poolStore) GetTickCrossHook() func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64) { result, err := s.kvStore.Get(StoreKeyTickCrossHook.String()) if err != nil { panic(err) } tickCrossHook, ok := result.(func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64)) if !ok { panic(ufmt.Sprintf("failed to cast result to func(poolPath string, tickId int32, zeroForOne bool, timestamp int64): %T", result)) } return tickCrossHook } // SetTickCrossHook stores the tick cross hook function. // Parameters: // - _: leading realm-call discriminator; callers pass 0. // - rlm: propagated realm context; it must be the current realm or the method // returns ErrSpoofedRealm before writing. // - tickCrossHook: callback receiving the current realm, pool path, crossed // tick ID, swap direction, and block timestamp. // // Returns: // - err: nil when the callback is stored; ErrSpoofedRealm for a non-current // realm, or the underlying KV-store write error. func (s *poolStore) SetTickCrossHook(_ int, rlm realm, tickCrossHook func(cur realm, poolPath string, tickId int32, zeroForOne bool, timestamp int64)) error { if !rlm.IsCurrent() { return errors.New(ErrSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyTickCrossHook.String(), tickCrossHook) } // NewPoolStore creates a new pool store instance with the provided KV store. // This function is used by the upgrade system to create storage instances for each implementation. // Parameters: // - kvStore: KV store used to persist and retrieve pool-domain state. // // Returns: // - poolStore: an IPoolStore implementation backed by kvStore. func NewPoolStore(kvStore store.KVStore) IPoolStore { return &poolStore{ kvStore: kvStore, } }
- #24types.gno
Result log
msg:0,success:true,log:,events:[]