Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
3718b3e
docs: improve NatSpec and clarify internal comments
Dark-Brain07 Jan 24, 2026
74b2254
docs: add comprehensive NatSpec documentation to FixedPoint128.sol
Dark-Brain07 Jan 25, 2026
64bdd68
docs: enhance NatSpec documentation for FixedPoint96.sol with Q96 for…
Dark-Brain07 Jan 25, 2026
f49ce02
docs: add detailed NatSpec and inline assembly comments to LiquidityM…
Dark-Brain07 Jan 25, 2026
f4c0c17
docs: add comprehensive warning documentation and inline comments to …
Dark-Brain07 Jan 25, 2026
320708c
docs: add comprehensive NatSpec documentation to BeforeSwapDelta.sol
Dark-Brain07 Jan 25, 2026
f6ca3ed
docs: enhance Lock.sol with detailed NatSpec and assembly comments
Dark-Brain07 Jan 25, 2026
040c946
docs: add comprehensive NatSpec documentation to ParseBytes.sol
Dark-Brain07 Jan 25, 2026
83db1c8
docs: add comprehensive NatSpec documentation to BalanceDelta.sol
Dark-Brain07 Jan 25, 2026
fc5d913
docs: enhance SafeCast.sol with detailed NatSpec and fix missing retu…
Dark-Brain07 Jan 25, 2026
73546f9
test: add comprehensive tests for FixedPoint96 and FixedPoint128 libr…
Dark-Brain07 Jan 25, 2026
5bc56da
docs: enhance PoolId.sol with comprehensive NatSpec documentation
Dark-Brain07 Jan 25, 2026
be9c5b3
docs: add comprehensive NatSpec documentation to CurrencyReserves.sol
Dark-Brain07 Jan 25, 2026
ed44928
docs: add comprehensive NatSpec documentation to Currency.sol type an…
Dark-Brain07 Jan 25, 2026
285e0c0
docs: enhance Slot0.sol with comprehensive NatSpec documentation
Dark-Brain07 Jan 25, 2026
2bd9963
docs: add comprehensive NatSpec documentation to PoolOperation.sol op…
Dark-Brain07 Jan 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/PoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim
}

/// @inheritdoc IPoolManager
/// @dev Events are emitted before the afterInitialize hook to ensure event ordering.
/// @dev This function initializes the pool state and parameters.
function initialize(PoolKey memory key, uint160 sqrtPriceX96) external noDelegateCall returns (int24 tick) {
// see TickBitmap.sol for overflow conditions that can arise from tick spacing being too large
if (key.tickSpacing > MAX_TICK_SPACING) TickSpacingTooLarge.selector.revertWith(key.tickSpacing);
Expand Down Expand Up @@ -142,6 +144,8 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim
}

/// @inheritdoc IPoolManager
/// @dev Events are emitted before the afterModifyLiquidity hook to ensure event ordering.
/// @dev Reverts if the pool is locked.
function modifyLiquidity(PoolKey memory key, ModifyLiquidityParams memory params, bytes calldata hookData)
external
onlyWhenUnlocked
Expand Down Expand Up @@ -184,6 +188,8 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim
}

/// @inheritdoc IPoolManager
/// @dev Events are emitted before the afterSwap hook to ensure event ordering.
/// @dev Reverts if the pool is locked.
function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData)
external
onlyWhenUnlocked
Expand Down Expand Up @@ -253,6 +259,8 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim
}

/// @inheritdoc IPoolManager
/// @dev Events are emitted before the afterDonate hook to ensure event ordering.
/// @dev Reverts if the pool is locked.
function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData)
external
onlyWhenUnlocked
Expand Down
37 changes: 30 additions & 7 deletions src/libraries/BitMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,37 @@
pragma solidity ^0.8.0;

/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
/// @notice This library provides functionality for computing bit properties of an unsigned integer
/// @dev Provides gas-efficient methods for finding the most and least significant bits in a uint256.
/// These operations are fundamental for tick bitmap navigation in Uniswap v4's concentrated liquidity.
/// Both functions use optimized algorithms with De Bruijn sequences for O(1) bit manipulation.
/// @author Solady (https://github.com/Vectorized/solady/blob/8200a70e8dc2a77ecb074fc2e99a2a0d36547522/src/utils/LibBit.sol)
library BitMath {
/// @notice Returns the index of the most significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @dev Uses a binary search approach combined with De Bruijn sequences for efficiency.
/// The algorithm first narrows down which 128/64/32/16/8-bit section contains the MSB,
/// then uses a De Bruijn lookup for the final precision.
/// @param x the value for which to compute the most significant bit, must be greater than 0
/// @return r the index of the most significant bit
/// @return r the index of the most significant bit (0-255)
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);

assembly ("memory-safe") {
// Binary search: check if MSB is in upper half of remaining bits
// lt(0xFFFF..., x) returns 1 if x > threshold, meaning MSB is in upper half
// shl(7, ...) sets bit 7 (value 128) if MSB is in upper 128 bits
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
// Check upper 64 bits of remaining section
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
// Check upper 32 bits of remaining section
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
// Check upper 16 bits of remaining section
r := or(r, shl(4, lt(0xffff, shr(r, x))))
// Check upper 8 bits of remaining section
r := or(r, shl(3, lt(0xff, shr(r, x))))
// Final 8-bit precision: use De Bruijn-like lookup table for remaining bits
// The magic numbers encode a compact lookup that maps bit patterns to bit indices
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020500060203020504000106050205030304010505030400000000))
Expand All @@ -26,21 +41,29 @@ library BitMath {

/// @notice Returns the index of the least significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @dev First isolates the LSB using two's complement (x & -x), then uses De Bruijn multiplication
/// to efficiently compute the bit index. This technique maps each power of 2 to a unique
/// index in the lookup table.
/// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
/// @param x the value for which to compute the least significant bit, must be greater than 0
/// @return r the index of the least significant bit
/// @return r the index of the least significant bit (0-255)
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);

assembly ("memory-safe") {
// Isolate the least significant bit.
// Isolate the least significant bit: x & (-x) = x & (two's complement of x)
// This leaves only the rightmost 1 bit set
x := and(x, sub(0, x))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/

// De Bruijn multiplication technique for upper 3 bits (determines which 32-bit section)
// The magic constant maps each power of 2 to a unique 3-bit section index
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.

// De Bruijn lookup for lower 5 bits (precise position within the 32-bit section)
// 0xd76453e0 is the De Bruijn constant, lookup table maps to exact bit position
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
Expand Down
38 changes: 32 additions & 6 deletions src/libraries/CurrencyDelta.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,65 @@ pragma solidity ^0.8.24;

import {Currency} from "../types/Currency.sol";

/// @title a library to store callers' currency deltas in transient storage
/// @dev this library implements the equivalent of a mapping, as transient storage can only be accessed in assembly
/// @title CurrencyDelta
/// @notice A library to store callers' currency deltas in transient storage
/// @dev This library implements the equivalent of a mapping, as transient storage can only be accessed in assembly.
/// Currency deltas track the balance changes for each (account, currency) pair during an unlock callback.
/// Positive deltas mean the account is owed tokens by the pool, negative means they owe tokens to the pool.
/// All deltas must be settled (reach zero) before the unlock completes.
/// @custom:security Transient storage is automatically cleared at the end of each transaction.
library CurrencyDelta {
/// @notice calculates which storage slot a delta should be stored in for a given account and currency
/// @notice Calculates the transient storage slot for a given account and currency delta
/// @dev Uses keccak256(target || currency) to derive a unique slot for each (account, currency) pair.
/// This prevents collisions between different accounts and currencies.
/// @param target The address whose delta is being tracked
/// @param currency The currency for which the delta is being tracked
/// @return hashSlot The computed storage slot
function _computeSlot(address target, Currency currency) internal pure returns (bytes32 hashSlot) {
assembly ("memory-safe") {
// Store target address in first 32 bytes (right-padded), masking to 160 bits
mstore(0, and(target, 0xffffffffffffffffffffffffffffffffffffffff))
// Store currency address in second 32 bytes, masking to 160 bits
mstore(32, and(currency, 0xffffffffffffffffffffffffffffffffffffffff))
// Hash both values together to get unique slot
hashSlot := keccak256(0, 64)
}
}

/// @notice Gets the current delta for a currency and account
/// @dev Reads from transient storage at the computed slot
/// @param currency The currency to check
/// @param target The account to check
/// @return delta The current delta (positive = owed to account, negative = owed by account)
function getDelta(Currency currency, address target) internal view returns (int256 delta) {
bytes32 hashSlot = _computeSlot(target, currency);
assembly ("memory-safe") {
// Load delta from transient storage
delta := tload(hashSlot)
}
}

/// @notice applies a new currency delta for a given account and currency
/// @return previous The prior value
/// @return next The modified result
/// @notice Applies a new currency delta for a given account and currency
/// @dev Adds the delta to the existing value in transient storage
/// @param currency The currency being modified
/// @param target The account whose delta is being modified
/// @param delta The amount to add to the current delta (can be negative)
/// @return previous The prior delta value
/// @return next The new delta value after applying the change
function applyDelta(Currency currency, address target, int128 delta)
internal
returns (int256 previous, int256 next)
{
bytes32 hashSlot = _computeSlot(target, currency);

assembly ("memory-safe") {
// Load current delta
previous := tload(hashSlot)
}
// Compute new delta (addition happens outside assembly for clarity)
next = previous + delta;
assembly ("memory-safe") {
// Store updated delta
tstore(hashSlot, next)
}
}
Expand Down
50 changes: 48 additions & 2 deletions src/libraries/CurrencyReserves.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,81 @@ pragma solidity ^0.8.24;
import {Currency} from "../types/Currency.sol";
import {CustomRevert} from "./CustomRevert.sol";

/// @title CurrencyReserves
/// @author Uniswap Labs
/// @notice Library for managing synced currency and reserve tracking using EIP-1153 transient storage
/// @dev This library provides transient storage (TSTORE/TLOAD) operations for tracking
/// the currently synced currency and its reserves during a transaction. Transient storage
/// is automatically cleared at the end of each transaction, making it ideal for
/// temporary state that only needs to persist within a single transaction.
///
/// The library uses namespaced storage slots calculated as `keccak256(name) - 1` to avoid
/// collisions with other storage variables.
///
/// Key concepts:
/// - Synced currency: The currency being tracked for balance verification
/// - Reserves: The expected balance of the synced currency
/// - These values are used to verify that balance changes match expected deltas
library CurrencyReserves {
using CustomRevert for bytes4;

/// bytes32(uint256(keccak256("ReservesOf")) - 1)
/// @dev The transient storage slot holding the reserves of the synced currency.
/// @dev Calculated as `bytes32(uint256(keccak256("ReservesOf")) - 1)` to effectively
/// namespace the slot and avoid collisions with other storage variables.
bytes32 constant RESERVES_OF_SLOT = 0x1e0745a7db1623981f0b2a5d4232364c00787266eb75ad546f190e6cebe9bd95;
/// bytes32(uint256(keccak256("Currency")) - 1)

/// @dev The transient storage slot holding the currently synced currency address.
/// @dev Calculated as `bytes32(uint256(keccak256("Currency")) - 1)` to effectively
/// namespace the slot and avoid collisions with other storage variables.
bytes32 constant CURRENCY_SLOT = 0x27e098c505d44ec3574004bca052aabf76bd35004c182099d8c575fb238593b9;

/// @notice Retrieves the currently synced currency from transient storage
/// @dev Uses TLOAD opcode (EIP-1153) to read from transient storage.
/// The value is automatically cleared at the end of the transaction.
/// @return currency The Currency type representing the synced currency address
function getSyncedCurrency() internal view returns (Currency currency) {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// TLOAD reads from transient storage at CURRENCY_SLOT
currency := tload(CURRENCY_SLOT)
}
}

/// @notice Resets the synced currency to address(0) in transient storage
/// @dev Uses TSTORE opcode (EIP-1153) to clear the currency slot.
/// This is typically called after completing sync verification to clean up state.
function resetCurrency() internal {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// TSTORE writes 0 to clear the transient storage slot
tstore(CURRENCY_SLOT, 0)
}
}

/// @notice Atomically sets both the synced currency and its reserves in transient storage
/// @dev Uses TSTORE opcode (EIP-1153) to write to transient storage.
/// The currency address is masked to 160 bits to ensure proper address formatting.
/// Both values are set in a single function call to maintain consistency.
/// @param currency The currency to sync (will be masked to 160 bits)
/// @param value The reserve amount to store for the synced currency
function syncCurrencyAndReserves(Currency currency, uint256 value) internal {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// Mask currency to 160 bits (address size) and store in CURRENCY_SLOT
tstore(CURRENCY_SLOT, and(currency, 0xffffffffffffffffffffffffffffffffffffffff))
// Store the reserve value in RESERVES_OF_SLOT
tstore(RESERVES_OF_SLOT, value)
}
}

/// @notice Retrieves the current reserves of the synced currency from transient storage
/// @dev Uses TLOAD opcode (EIP-1153) to read from transient storage.
/// This value represents the expected/tracked balance of the synced currency.
/// @return value The reserve amount stored for the synced currency
function getSyncedReserves() internal view returns (uint256 value) {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// TLOAD reads from transient storage at RESERVES_OF_SLOT
value := tload(RESERVES_OF_SLOT)
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/libraries/FixedPoint128.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@
pragma solidity ^0.8.0;

/// @title FixedPoint128
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @notice A library for handling binary fixed point numbers in Q128 format
/// @dev This library provides the Q128 constant used for fixed-point arithmetic with 128 fractional bits.
/// The Q128 format represents numbers as: value = rawValue / 2^128
/// This is primarily used in Uniswap v4 for tracking fee growth per unit of liquidity,
/// where high precision is required to accurately accumulate fees over many transactions.
/// See https://en.wikipedia.org/wiki/Q_(number_format) for more information on Q number formats.
library FixedPoint128 {
/// @notice The Q128 constant representing 2^128
/// @dev Used as the denominator in Q128 fixed-point calculations.
/// Q128 = 2^128 = 340282366920938463463374607431768211456
/// In hex: 0x100000000000000000000000000000000 (1 followed by 32 zeros)
/// @dev Example usage: To convert a Q128 value to a regular number, divide by Q128
/// realValue = q128Value / Q128
uint256 internal constant Q128 = 0x100000000000000000000000000000000;
}
18 changes: 16 additions & 2 deletions src/libraries/FixedPoint96.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@
pragma solidity ^0.8.0;

/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
/// @notice A library for handling binary fixed point numbers in Q96 format
/// @dev This library provides constants for fixed-point arithmetic with 96 fractional bits.
/// The Q96 format represents numbers as: value = rawValue / 2^96
/// This format is central to Uniswap v4's price representation, where prices are stored as
/// sqrtPriceX96 = sqrt(price) * 2^96. This allows for efficient price calculations while
/// maintaining high precision across a wide range of token price ratios.
/// See https://en.wikipedia.org/wiki/Q_(number_format) for more information on Q number formats.
library FixedPoint96 {
/// @notice The number of fractional bits in a Q96 fixed-point number
/// @dev 96 bits of resolution provides approximately 29 decimal digits of precision
uint8 internal constant RESOLUTION = 96;

/// @notice The Q96 constant representing 2^96
/// @dev Used as the denominator in Q96 fixed-point calculations.
/// Q96 = 2^96 = 79228162514264337593543950336
/// In hex: 0x1000000000000000000000000 (1 followed by 24 zeros)
/// @dev Example: sqrtPriceX96 uses this format, so to get the actual sqrt price:
/// sqrtPrice = sqrtPriceX96 / Q96
uint256 internal constant Q96 = 0x1000000000000000000000000;
}
20 changes: 17 additions & 3 deletions src/libraries/LiquidityMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,30 @@
pragma solidity ^0.8.0;

/// @title Math library for liquidity
/// @notice Provides utility functions for liquidity calculations in Uniswap v4
/// @dev This library handles safe arithmetic operations for liquidity amounts,
/// which are represented as uint128 values. The primary function addDelta
/// allows both adding and subtracting liquidity using a signed delta value.
library LiquidityMath {
/// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows
/// @param x The liquidity before change
/// @param y The delta by which liquidity should be changed
/// @return z The liquidity delta
/// @dev This function safely handles both positive and negative deltas:
/// - Positive delta: adds liquidity (minting)
/// - Negative delta: removes liquidity (burning)
/// Uses assembly for gas optimization and to handle edge cases with int128.min
/// @param x The current liquidity before the change (uint128)
/// @param y The delta by which liquidity should be changed (int128, can be negative)
/// @return z The new liquidity after applying the delta
/// @custom:error SafeCastOverflow Reverts if the result would overflow uint128 or underflow below 0
function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {
assembly ("memory-safe") {
// Mask x to 128 bits and sign-extend y from 128 bits to 256 bits
// signextend(15, y) extends the sign bit of byte 15 (the 128th bit) across the upper bits
z := add(and(x, 0xffffffffffffffffffffffffffffffff), signextend(15, y))
// Check if result exceeds uint128 max by checking if any bits above position 128 are set
// If shr(128, z) is non-zero, the result overflowed uint128 bounds
if shr(128, z) {
// revert SafeCastOverflow()
// Selector: 0x93dafdf1 = bytes4(keccak256("SafeCastOverflow()"))
mstore(0, 0x93dafdf1)
revert(0x1c, 0x04)
}
Expand Down
Loading