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 9ce49d105..3bbacd1d4 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,28 @@ library StateLibrary { // index of Position.Info mapping in Pool.State: mapping(bytes32 => Position.Info) positions; uint256 public constant POSITIONS_OFFSET = 6; + struct PoolStateView { + Slot0 slot0; + uint256 feeGrowthGlobal0X128; + uint256 feeGrowthGlobal1X128; + uint128 liquidity; + } + + /** + * @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 state The state of the pool. + */ + function getPoolState(IPoolManager manager, PoolId poolId) internal view returns (PoolStateView memory state) { + bytes32 stateSlot = _getPoolStateSlot(poolId); + bytes memory data = manager.extsload(stateSlot, 4); + assembly { + state := add(data, 32) + } + } + /** * @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..0bf7c222b 100644 --- a/test/libraries/StateLibrary.t.sol +++ b/test/libraries/StateLibrary.t.sol @@ -37,6 +37,36 @@ 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); + + StateLibrary.PoolStateView memory state = StateLibrary.getPoolState(manager, poolId); + snapLastCall("extsload getPoolState"); + + assertEq(state.slot0.sqrtPriceX96(), 78680104762184586858280382455); + assertEq(state.slot0.tick(), -139); + assertEq(state.slot0.protocolFee(), 0); + assertEq(state.slot0.lpFee(), 3000); + + uint256 liquidity = StateLibrary.getLiquidity(manager, poolId); + assertEq(state.liquidity, liquidity); + + (uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1) = StateLibrary.getFeeGrowthGlobals(manager, poolId); + assertEq(state.feeGrowthGlobal0X128, feeGrowthGlobal0); + assertEq(state.feeGrowthGlobal1X128, feeGrowthGlobal1); + } + function test_getSlot0() public { // create liquidity modifyLiquidityRouter.modifyLiquidity( @@ -50,15 +80,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 {