diff --git a/.forge-snapshots/extsload getPoolState.snap b/.forge-snapshots/extsload getPoolState.snap new file mode 100644 index 000000000..3d3f9551e --- /dev/null +++ b/.forge-snapshots/extsload getPoolState.snap @@ -0,0 +1 @@ +973 \ No newline at end of file diff --git a/src/libraries/StateLibrary.sol b/src/libraries/StateLibrary.sol index 8e91f3f29..f095a521d 100644 --- a/src/libraries/StateLibrary.sol +++ b/src/libraries/StateLibrary.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import {PoolId} from "../types/PoolId.sol"; +import {Slot0} from "../types/Slot0.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {Currency} from "../types/Currency.sol"; import {Position} from "./Position.sol"; @@ -30,6 +31,31 @@ library StateLibrary { // index of Position.Info mapping in Pool.State: mapping(bytes32 => Position.Info) positions; uint256 public constant POSITIONS_OFFSET = 6; + /** + * @notice Get the global state of a pool. + * @dev Corresponds to pools[poolId] + * @param manager The pool manager contract. + * @param poolId The ID of the pool. + * @return slot0 The slot0 of the pool. + * @return feeGrowthGlobal0X128 The global fee growth for token0. + * @return feeGrowthGlobal1X128 The global fee growth for token1. + * @return liquidity The liquidity of the pool. + */ + function getPoolState(IPoolManager manager, PoolId poolId) + internal + view + returns (Slot0 slot0, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128, uint128 liquidity) + { + bytes32 stateSlot = _getPoolStateSlot(poolId); + bytes memory data = manager.extsload(stateSlot, 4); + assembly { + slot0 := mload(add(data, 32)) + feeGrowthGlobal0X128 := mload(add(data, 64)) + feeGrowthGlobal1X128 := mload(add(data, 96)) + liquidity := mload(add(data, 128)) + } + } + /** * @notice Get Slot0 of the pool: sqrtPriceX96, tick, protocolFee, lpFee * @dev Corresponds to pools[poolId].slot0 diff --git a/test/libraries/StateLibrary.t.sol b/test/libraries/StateLibrary.t.sol index dd5c6861b..354deaaa2 100644 --- a/test/libraries/StateLibrary.t.sol +++ b/test/libraries/StateLibrary.t.sol @@ -8,6 +8,7 @@ import {Hooks} from "../../src/libraries/Hooks.sol"; import {TickMath} from "../../src/libraries/TickMath.sol"; import {IPoolManager} from "../../src/interfaces/IPoolManager.sol"; import {PoolKey} from "../../src/types/PoolKey.sol"; +import {Slot0} from "../../src/types/Slot0.sol"; import {BalanceDelta} from "../../src/types/BalanceDelta.sol"; import {PoolId, PoolIdLibrary} from "../../src/types/PoolId.sol"; import {CurrencyLibrary, Currency} from "../../src/types/Currency.sol"; @@ -37,6 +38,38 @@ contract StateLibraryTest is Test, Deployers, Fuzzers, GasSnapshot { manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); } + function test_getPoolState() public { + // create liquidity + modifyLiquidityRouter.modifyLiquidity( + key, IPoolManager.ModifyLiquidityParams(-60, 60, 10_000 ether, 0), ZERO_BYTES + ); + + modifyLiquidityRouter.modifyLiquidity( + key, IPoolManager.ModifyLiquidityParams(-600, 600, 10_000 ether, 0), ZERO_BYTES + ); + + // swap to create fees, crossing a tick + uint256 swapAmount = 100 ether; + swap(key, true, -int256(swapAmount), ZERO_BYTES); + + (Slot0 slot0, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128, uint128 liquidity) = + StateLibrary.getPoolState(manager, poolId); + snapLastCall("extsload getPoolState"); + + assertEq(slot0.sqrtPriceX96(), 78680104762184586858280382455); + assertEq(slot0.tick(), -139); + assertEq(slot0.protocolFee(), 0); + assertEq(slot0.lpFee(), 3000); + + uint256 liquidityExpected = StateLibrary.getLiquidity(manager, poolId); + assertEq(liquidity, liquidityExpected); + + (uint256 feeGrowthGlobal0Expected, uint256 feeGrowthGlobal1Expected) = + StateLibrary.getFeeGrowthGlobals(manager, poolId); + assertEq(feeGrowthGlobal0X128, feeGrowthGlobal0Expected); + assertEq(feeGrowthGlobal1X128, feeGrowthGlobal1Expected); + } + function test_getSlot0() public { // create liquidity modifyLiquidityRouter.modifyLiquidity( @@ -50,15 +83,15 @@ contract StateLibraryTest is Test, Deployers, Fuzzers, GasSnapshot { // swap to create fees, crossing a tick uint256 swapAmount = 100 ether; swap(key, true, -int256(swapAmount), ZERO_BYTES); - (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 swapFee) = StateLibrary.getSlot0(manager, poolId); + + (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee) = StateLibrary.getSlot0(manager, poolId); snapLastCall("extsload getSlot0"); - assertEq(tick, -139); // magic number verified against a native getter assertEq(sqrtPriceX96, 78680104762184586858280382455); assertEq(tick, -139); assertEq(protocolFee, 0); // tested in protocol fee tests - assertEq(swapFee, 3000); + assertEq(lpFee, 3000); } function test_getTickLiquidity() public {