Degen Code

Degen Code

Share this post

Degen Code
Degen Code
Uniswap V4 — Part VI: State View
Copy link
Facebook
Email
Notes
More

Uniswap V4 — Part VI: State View

Let's Just Add A Few More Libraries, What Could Go Wrong?

Feb 27, 2025
∙ Paid
5

Share this post

Degen Code
Degen Code
Uniswap V4 — Part VI: State View
Copy link
Facebook
Email
Notes
More
Share

The Factory and Pool pattern of Uniswap V3 is familiar, and we’ve become used to calling “getter” functions to retrieve state data for a given token pair.

However Uniswap V4’s PoolManager contract takes a different approach in the name of efficiency, adding a lot of complexity to gain flexibility and lower gas costs.

The primary place this has occurred is the pool initialization step. The PoolManager, playing the role of factory, no longer maintains a mapping of token pairs. Instead, it emits an Initialized event with the relevant pool parameters, and places the responsibility for recording and providing these parameters on the user.

As a direct consequence, PoolManager has no equivalent to V2’s getPair or V3’s getPool functions. And it’s no surprise either, because V4 allows pools of any fee and tick spacing, plus the option to customize the behavior of state changing functions using hooks. Multiple pools can be created with the same token pair, but different sets of fee/spacing/hook options. The only true restriction on pool creation is that two pools cannot be created with the same Pool ID, which is deterministic based on the options already described. So we must live with the fact that multiple pools will exist inside the PoolManager at a given time, and devise methods to track them.

Please read Part II if you need more information on the design of PoolManager.

Uniswap V4 — Part II: Pool Manager

Uniswap V4 — Part II: Pool Manager

BowTiedDevil
·
Feb 5
Read full story

Pool State

PoolManager.sol defines an internal mapping of PoolIds to the Pool.State struct:

/// @title PoolManager
/// @notice Holds the state for all pools
contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claims, Extsload, Exttload {
    using SafeCast for *;
    using Pool for *;
    using Hooks for IHooks;
    using CurrencyDelta for Currency;
    using LPFeeLibrary for uint24;
    using CurrencyReserves for Currency;
    using CustomRevert for bytes4;

    int24 private constant MAX_TICK_SPACING = TickMath.MAX_TICK_SPACING;

    int24 private constant MIN_TICK_SPACING = TickMath.MIN_TICK_SPACING;

    mapping(PoolId id => Pool.State) internal _pools;

    [...]

}

The Pool.State struct is defined in Pool.sol:

/// @notice The state of a pool
/// @dev Note that feeGrowthGlobal can be artificially inflated
/// For pools with a single liquidity position, actors can donate to 
/// themselves to freely inflate feeGrowthGlobal
/// atomically donating and collecting fees in the same unlockCallback 
/// may make the inflated value more extreme
struct State {
    Slot0 slot0;
    uint256 feeGrowthGlobal0X128;
    uint256 feeGrowthGlobal1X128;
    uint128 liquidity;
    mapping(int24 tick => TickInfo) ticks;
    mapping(int16 wordPos => uint256) tickBitmap;
    mapping(bytes32 positionKey => Position.State) positions;
}

The Uniswap V3 Pool Contract has dedicated getters for retrieving slot0 and liquidity values.

UniswapV3 — Pool Contract

UniswapV3 — Pool Contract

BowTiedDevil
·
September 16, 2022
Read full story

V3 also includes a special TickLens library for retrieving tick mappings and liquidity positions at those ticks.

UniswapV3 — TickLens Contract

UniswapV3 — TickLens Contract

BowTiedDevil
·
September 27, 2022
Read full story

External Storage Access

PoolManager throws all that out the window, instead leaning heavily on a more generic approach that allows generic external storage access. From the list of inherited features, covered in Part II:

  • An implementation in the spirit of EIP-2330 that would allow external contracts to retrieve storage values from a given slot using a lightweight external method, from Extsload.sol

  • The same EIP-2330 access implementation described above but for transient storage, from Exttload.sol

Storage state on EVM is not private, and you can retrieve the storage state at a given 32-byte address at any time. I covered this in an article on eth_call, going into great detail on hashing and retrieving storage values for balances and approvals held in mappings.

Avoiding Trap Tokens With eth_call

Avoiding Trap Tokens With eth_call

BowTiedDevil
·
September 1, 2022
Read full story

However generalized storage access is not yet available on EVM, and all storage reads/writes are specific to the contract executing the calls.

You can read more about EVM storage in Part IV in the Low Level EVM series:

Low Level EVM — Part IV: Storage

Low Level EVM — Part IV: Storage

BowTiedDevil
·
Jan 29
Read full story

The Extsload and Exttload contracts expose the read-only opcodes (SLOAD, TLOAD) to external callers. The functionality of these contracts are available at PoolManager, which inherits from them.

State Library

Having a generic interface to look up arbitrary storage values is useful, but not exactly friendly.

Luckily for us, several useful functions in StateLibrary.sol facilitate direct lookup of the appropriate values for a given pool. These functions perform the necessary translation of PoolId to a given storage slot, retrieve the value, decode it, and return it to the caller. Nice!

This post is for paid subscribers

Already a paid subscriber? Sign in
© 2025 BowTiedDevil
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share

Copy link
Facebook
Email
Notes
More