### Example Call to Initialize Oracle Array - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md This snippet shows an example of how the `initialize` function is called within another contract, specifically from the `initialize` method of `UniswapV3Pool.sol`. It passes the observation storage array (`observations`) and the current block timestamp (`_blockTimestamp()`) to set up the initial oracle state. ```Solidity (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); ``` -------------------------------- ### Initialize Swap Parameters - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/en/PoolLibrary.md Initializes key variables for the swap execution, including the starting slot0 state, swap direction (`zeroForOne`), remaining specified amount, calculated amount, and the initial price, tick, and liquidity from the current state. ```Solidity Slot0 slot0Start = self.slot0; bool zeroForOne = params.zeroForOne; uint256 protocolFee = zeroForOne ? slot0Start.protocolFee().getZeroForOneFee() : slot0Start.protocolFee().getOneForZeroFee(); // the amount remaining to be swapped in/out of the input/output asset. initially set to the amountSpecified int256 amountSpecifiedRemaining = params.amountSpecified; // the amount swapped out/in of the output/input asset. initially set to 0 int256 amountCalculated = 0; // initialize to the current sqrt(price) result.sqrtPriceX96 = slot0Start.sqrtPriceX96(); // initialize to the current tick result.tick = slot0Start.tick(); // initialize to the current liquidity result.liquidity = self.liquidity; ``` -------------------------------- ### Initialize Swap Parameters - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/zh/PoolLibrary.md Initializes variables required for the swap process, including the starting slot state, swap direction, remaining amount to swap, calculated amount, and initial price, tick, and liquidity from the pool's state. ```Solidity Slot0 slot0Start = self.slot0; bool zeroForOne = params.zeroForOne; uint256 protocolFee = zeroForOne ? slot0Start.protocolFee().getZeroForOneFee() : slot0Start.protocolFee().getOneForZeroFee(); // the amount remaining to be swapped in/out of the input/output asset. initially set to the amountSpecified int256 amountSpecifiedRemaining = params.amountSpecified; // the amount swapped out/in of the output/input asset. initially set to 0 int256 amountCalculated = 0; // initialize to the current sqrt(price) result.sqrtPriceX96 = slot0Start.sqrtPriceX96(); // initialize to the current tick result.tick = slot0Start.tick(); // initialize to the current liquidity result.liquidity = self.liquidity; ``` -------------------------------- ### Deploying Contract with Salt (Modern Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README.md Shows the modern Solidity syntax for deploying a contract using the `create2` opcode by providing a `salt` parameter directly to the `new` keyword. This syntax was not available during the initial development of Uniswap V2. ```Solidity pair = new UniswapV2Pair{salt: salt}(); ``` -------------------------------- ### Reading Uint24 from Bytes (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README_zh.md Reads a 3-byte uint24 value from a byte array starting at a specified index. It uses assembly to load 32 bytes from the calculated position (skipping the ABI length prefix and adding the start offset) and relies on the uint24 type casting to truncate the loaded value to the lowest 3 bytes. ```Solidity function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { require(_start + 3 >= _start, 'toUint24_overflow'); require(_bytes.length >= _start + 3, 'toUint24_outOfBounds'); uint24 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) } return tempUint; } ``` -------------------------------- ### Swapping Exact Tokens for Tokens (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README.md This function implements the scenario where a user wants to swap a specified amount of an input token (`amountIn`) for at least a minimum amount of an output token (`amountOutMin`) along a given path. It first calculates the expected output amounts using `UniswapV2Library.getAmountsOut`, verifies the minimum output requirement, transfers the initial input tokens to the first trading pair, and then calls the internal `_swap` function to execute the trades along the path. ```Solidity function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } ``` -------------------------------- ### Calculating Tick Bitmap Position in Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md This private pure function calculates the word position (wordPos) and bit position (bitPos) within a bitmap for a given tick. It uses bitwise right shift (>> 8) to get the word index and the modulo operator (% 256) to get the bit index within the word. This is used to map a tick to a specific bit in a bitmap for tracking initialization state. ```Solidity /// @notice Computes the position in the mapping where the initialized bit for a tick lives /// @param tick The tick for which to compute the position /// @return wordPos The key in the mapping containing the word in which the bit is stored /// @return bitPos The bit position in the word where the flag is stored function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { wordPos = int16(tick >> 8); bitPos = uint8(tick % 256); } ``` -------------------------------- ### Initializing Pool Manager Contract (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/en/PoolManager.md The `initialize` function sets up a new liquidity pool. It validates the provided `PoolKey` parameters, including tick spacing, currency order, and hooks address. It calculates the initial LP fee, calls `beforeInitialize` and `afterInitialize` hooks, and emits an `Initialize` event. ```Solidity /// @inheritdoc IPoolManager 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); if (key.tickSpacing < MIN_TICK_SPACING) TickSpacingTooSmall.selector.revertWith(key.tickSpacing); if (key.currency0 >= key.currency1) { CurrenciesOutOfOrderOrEqual.selector.revertWith( Currency.unwrap(key.currency0), Currency.unwrap(key.currency1) ); } if (!key.hooks.isValidHookAddress(key.fee)) Hooks.HookAddressNotValid.selector.revertWith(address(key.hooks)); uint24 lpFee = key.fee.getInitialLPFee(); key.hooks.beforeInitialize(key, sqrtPriceX96); PoolId id = key.toId(); tick = _pools[id].initialize(sqrtPriceX96, lpFee); // event is emitted before the afterInitialize call to ensure events are always emitted in order // emit all details of a pool key. poolkeys are not saved in storage and must always be provided by the caller // the key's fee may be a static fee or a sentinel to denote a dynamic fee. emit Initialize(id, key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks, sqrtPriceX96, tick); key.hooks.afterInitialize(key, sqrtPriceX96, tick); } ``` -------------------------------- ### Solidity Binary Search Loop and Midpoint Check Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md Initializes the binary search loop, calculates the midpoint index `i`, retrieves the `beforeOrAt` observation, and checks if it's initialized. If not, the left boundary is adjusted to continue searching in the more recent half. ```Solidity uint256 i; while (true) { i = (l + r) / 2; beforeOrAt = self[i % cardinality]; // we've landed on an uninitialized tick, keep searching higher (more recently) if (!beforeOrAt.initialized) { l = i + 1; continue; } ``` -------------------------------- ### Get Uniswap V3 Pool Address - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README_zh.md Retrieves the address of an initialized Uniswap V3 pool based on the factory address, token addresses (token0, token1), and the fee tier. ```Solidity PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}); pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); ``` -------------------------------- ### Executing Uniswap V3 Swap Steps (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md This code snippet outlines the main loop for the Uniswap v3 swap execution. It iterates as long as there is remaining input/output amount and the price limit has not been reached. Inside the loop, it prepares `StepComputations`, finds the next initialized tick within the current word, adjusts the tick to stay within min/max bounds, calculates the price at the next tick, and prepares to compute swap values for the step. ```Solidity // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) { StepComputations memory step; step.sqrtPriceStartX96 = state.sqrtPriceX96; (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( state.tick, tickSpacing, zeroForOne ); // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds if (step.tickNext < TickMath.MIN_TICK) { step.tickNext = TickMath.MIN_TICK; } else if (step.tickNext > TickMath.MAX_TICK) { step.tickNext = TickMath.MAX_TICK; } // get the price for the next tick step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted } ``` -------------------------------- ### BalanceDeltaLibrary.amount0 Function Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/en/BalanceDelta.md Extracts the amount0 value (upper 128 bits) from a BalanceDelta variable. It uses assembly's arithmetic right shift (sar) to get the signed value from the upper bits. ```Solidity function amount0(BalanceDelta balanceDelta) internal pure returns (int128 _amount0) { assembly ("memory-safe") { _amount0 := sar(128, balanceDelta) } } ``` -------------------------------- ### Get full credit _getFullCredit in DeltaResolver (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/en/DeltaResolver.md Obtains the full credit owed to the current contract (positive delta) from the PoolManager for a specific currency. It reverts if the delta is negative and returns the value as a uint256. ```Solidity /// @notice Obtain the full credit owed to this contract (positive delta) /// @param currency Currency to get the delta for /// @return amount The amount owed to this contract as a uint256 function _getFullCredit(Currency currency) internal view returns (uint256 amount) { int256 _amount = poolManager.currencyDelta(address(this), currency); // If the amount is negative, it should be settled not taken. if (_amount < 0) revert DeltaNotPositive(currency); amount = uint256(_amount); } ``` -------------------------------- ### Swap Exact Input for Minimum Output (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README_zh.md Executes a token swap where the input amount is fixed and the output amount must meet a minimum threshold. Calculates the output amounts along the path using `getAmountsOut` and initiates the swap process via the internal `_swap` function. ```Solidity function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } ``` -------------------------------- ### Swap Tokens for Exact Output (Uniswap V2 Router, Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README.md Implements the scenario where a user wants to receive an exact amount of output tokens, exchanging a maximum input amount. It uses `UniswapV2Library.getAmountsIn` to calculate the minimum required input tokens, checks against `amountInMax`, transfers the input tokens to the pair, and performs the swap via `_swap`. Requires the `ensure` modifier for deadline checks. ```Solidity function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } ``` -------------------------------- ### Get full debt _getFullDebt in DeltaResolver (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/en/DeltaResolver.md Obtains the full amount owed by the current contract (negative delta) from the PoolManager for a specific currency. It reverts if the delta is positive and returns the absolute value as a uint256. ```Solidity /// @notice Obtain the full amount owed by this contract (negative delta) /// @param currency Currency to get the delta for /// @return amount The amount owed by this contract as a uint256 function _getFullDebt(Currency currency) internal view returns (uint256 amount) { int256 _amount = poolManager.currencyDelta(address(this), currency); // If the amount is positive, it should be taken not settled. if (_amount > 0) revert DeltaNotNegative(currency); // Casting is safe due to limits on the total supply of a pool amount = uint256(-_amount); } ``` -------------------------------- ### Getting Full Credit from PoolManager in DeltaResolver (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/zh/DeltaResolver.md Retrieves the total credit (positive delta) available to the current contract from the `PoolManager` for a specific currency. It returns the amount as a `uint256` and reverts if the delta is negative, indicating it should be settled, not taken. ```Solidity /// @notice Obtain the full credit owed to this contract (positive delta) /// @param currency Currency to get the delta for /// @return amount The amount owed to this contract as a uint256 function _getFullCredit(Currency currency) internal view returns (uint256 amount) { int256 _amount = poolManager.currencyDelta(address(this), currency); // If the amount is negative, it should be settled not taken. if (_amount < 0) revert DeltaNotPositive(currency); amount = uint256(_amount); } ``` -------------------------------- ### Getting Full Debt from PoolManager in DeltaResolver (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/zh/DeltaResolver.md Retrieves the total debt (negative delta) owed by the current contract to the `PoolManager` for a specific currency. It returns the absolute value as a `uint256` and reverts if the delta is positive, indicating it should be taken, not settled. ```Solidity /// @notice Obtain the full amount owed by this contract (negative delta) /// @param currency Currency to get the delta for /// @return amount The amount owed by this contract as a uint256 function _getFullDebt(Currency currency) internal view returns (uint256 amount) { int256 _amount = poolManager.currencyDelta(address(this), currency); // If the amount is positive, it should be taken not settled. if (_amount > 0) revert DeltaNotNegative(currency); // Casting is safe due to limits on the total supply of a pool amount = uint256(-_amount); } ``` -------------------------------- ### Calling Deploy Function to Create Pool (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md Initiates the deployment of the actual Uniswap V3 pool contract by calling the internal `deploy` function, passing the factory address, sorted token addresses, fee tier, and calculated tick spacing. ```Solidity pool = deploy(address(this), token0, token1, fee, tickSpacing); ``` -------------------------------- ### Calculating Output Amounts for a Path (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README.md Calculates the amounts of tokens received when swapping a given input amount through a sequence of trading pairs defined by a path. It performs chained calculations using the getAmountOut function for each pair in the path. ```Solidity // performs chained getAmountOut calculations on any number of pairs function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) { require(path.length >= 2, 'UniswapV2Library: INVALID_PATH'); amounts = new uint[](path.length); amounts[0] = amountIn; for (uint i; i < path.length - 1; i++) { (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]); amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); } } ``` -------------------------------- ### Getting Currency Delta (getDelta) - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/en/CurrencyDeltaLibrary.md Retrieves the current transient storage delta value for a specific currency and target address. It first computes the storage slot using _computeSlot and then reads the value from that slot using the tload assembly instruction. ```Solidity function getDelta(Currency currency, address target) internal view returns (int256 delta) { bytes32 hashSlot = _computeSlot(target, currency); assembly ("memory-safe") { delta := tload(hashSlot) } } ``` -------------------------------- ### Calculating Required Input Amounts for a Path (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README.md Calculates the amounts of tokens required at each step of a path to achieve a specific output amount of the final token. It performs chained calculations iteratively using the getAmountIn function, working backward from the desired output. ```Solidity // performs chained getAmountIn calculations on any number of pairs function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) { require(path.length >= 2, 'UniswapV2Library: INVALID_PATH'); amounts = new uint[](path.length); amounts[amounts.length - 1] = amountOut; for (uint i = path.length - 1; i > 0; i--) { (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]); amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); } } ``` -------------------------------- ### Reading Address from Bytes (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README_zh.md Reads a 20-byte address from a byte array starting at a specified index. It accounts for the ABI-defined length prefix of the bytes type and uses assembly to efficiently load and right-shift the relevant 32 bytes to extract the 20-byte address. ```Solidity function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_start + 20 >= _start, 'toAddress_overflow'); require(_bytes.length >= _start + 20, 'toAddress_outOfBounds'); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } ``` -------------------------------- ### _swapExactInputSingle function in Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/en/V4Router.md This function executes a single-hop exact input token swap. It determines the input token based on the `zeroForOne` flag, handles the `OPEN_DELTA` case by using the contract's full credit, performs the swap, and verifies that the output amount is not less than the specified minimum. ```Solidity function _swapExactInputSingle(IV4Router.ExactInputSingleParams calldata params) private { uint128 amountIn = params.amountIn; if (amountIn == ActionConstants.OPEN_DELTA) { amountIn = _getFullCredit(params.zeroForOne ? params.poolKey.currency0 : params.poolKey.currency1).toUint128(); } uint128 amountOut = _swap(params.poolKey, params.zeroForOne, -int256(uint256(amountIn)), params.hookData).toUint128(); if (amountOut < params.amountOutMinimum) revert V4TooLittleReceived(params.amountOutMinimum, amountOut); } ``` -------------------------------- ### Get Transient Storage Delta in Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/zh/CurrencyDeltaLibrary.md Retrieves the current delta balance stored in transient storage for a specific currency and target address. It first computes the storage slot using `_computeSlot` and then uses the `tload` assembly instruction to read the value from that slot. ```Solidity function getDelta(Currency currency, address target) internal view returns (int256 delta) { bytes32 hashSlot = _computeSlot(target, currency); assembly ("memory-safe") { delta := tload(hashSlot) } } ``` -------------------------------- ### Get First Pool Segment from Path (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README.md This internal pure function extracts the first segment of a bytes encoded swap path, which corresponds to the first pool. It returns the initial 43 characters of the path. ```Solidity /// @notice Gets the segment corresponding to the first pool in the path /// @param path The bytes encoded swap path /// @return The segment containing all data necessary to target the first pool in the path function getFirstPool(bytes memory path) internal pure returns (bytes memory) { return path.slice(0, POP_OFFSET); } ``` -------------------------------- ### Calling beforeInitialize Hook (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/zh/HooksLibrary.md Calls the `beforeInitialize` method on the specified Hooks contract using `callHook` if the contract possesses the `BEFORE_INITIALIZE_FLAG` permission. It encodes the necessary parameters (`msg.sender`, `key`, `sqrtPriceX96`) for the call. ```Solidity /// @notice calls beforeInitialize hook if permissioned and validates return value function beforeInitialize(IHooks self, PoolKey memory key, uint160 sqrtPriceX96) internal noSelfCall(self) { if (self.hasPermission(BEFORE_INITIALIZE_FLAG)) { self.callHook(abi.encodeCall(IHooks.beforeInitialize, (msg.sender, key, sqrtPriceX96))); } } ``` -------------------------------- ### Initializing a Uniswap V3 Pool (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md Initializes a newly created Uniswap V3 pool contract. Requires the pool's `sqrtPriceX96` to be zero, indicating it hasn't been initialized yet. Calculates the initial tick based on the provided `sqrtPriceX96`, initializes the observation array, and sets the initial values for the `slot0` state variable, including price, tick, observation details, protocol fee, and unlocked status. Emits an `Initialize` event. ```Solidity /// @inheritdoc IUniswapV3PoolActions /// @dev not locked because it initializes unlocked function initialize(uint160 sqrtPriceX96) external override { require(slot0.sqrtPriceX96 == 0, 'AI'); int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); slot0 = Slot0({ sqrtPriceX96: sqrtPriceX96, tick: tick, observationIndex: 0, observationCardinality: cardinality, observationCardinalityNext: cardinalityNext, feeProtocol: 0, unlocked: true }); emit Initialize(sqrtPriceX96, tick); } ``` -------------------------------- ### Creating and Initializing Uniswap V3 Pool - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README.md This function creates and initializes a Uniswap v3 pool if it doesn't exist, or initializes it if it exists but hasn't been initialized yet. It first checks for an existing pool using the factory contract based on token pair and fee. If no pool is found, it creates one and initializes it with the provided sqrtPriceX96. If a pool exists, it checks its initialization status via slot0 and initializes it if necessary. ```Solidity /// @inheritdoc IPoolInitializer function createAndInitializePoolIfNecessary( address token0, address token1, uint24 fee, uint160 sqrtPriceX96 ) external payable override returns (address pool) { require(token0 < token1); pool = IUniswapV3Factory(factory).getPool(token0, token1, fee); if (pool == address(0)) { pool = IUniswapV3Factory(factory).createPool(token0, token1, fee); IUniswapV3Pool(pool).initialize(sqrtPriceX96); } else { (uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0(); if (sqrtPriceX96Existing == 0) { IUniswapV3Pool(pool).initialize(sqrtPriceX96); } else { // pool already exists and is initialized } } } ``` -------------------------------- ### Fetching Time-Weighted Average Tick (Part 1) (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README_zh.md Initializes the `consult` function which fetches the time-weighted average tick from a Uniswap V3 pool over a specified period. It requires the period to be non-zero and sets up an array `secondAgos` to query cumulative ticks at the start and end of the period. ```Solidity /// @notice Fetches time-weighted average tick using Uniswap V3 oracle /// @param pool Address of Uniswap V3 pool that we want to observe /// @param period Number of seconds in the past to start calculating time-weighted average /// @return timeWeightedAverageTick The time-weighted average tick from (block.timestamp - period) to block.timestamp function consult(address pool, uint32 period) internal view returns (int24 timeWeightedAverageTick) { require(period != 0, 'BP'); uint32[] memory secondAgos = new uint32[](2); secondAgos[0] = period; secondAgos[1] = 0; ``` -------------------------------- ### Fetching Time-Weighted Average Tick (Part 2) (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README_zh.md Calls the `observe` function on the target Uniswap V3 pool contract using the prepared `secondAgos` array. This retrieves the cumulative tick values at the start and end of the observation period, and the difference between them represents the total tick movement over the period. ```Solidity (int56[] memory tickCumulatives, ) = IUniswapV3Pool(pool).observe(secondAgos); int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; ``` -------------------------------- ### Calculate First Fractional Log2 Bit (Solidity Assembly) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md Performs the first iteration of the binary logarithm fractional part algorithm using assembly. It squares `r` (Q129.127), checks if the result is >= 2, and if so, adds the value corresponding to the first binary fractional bit (2^-1) to `log_2` (in Q192.64 format) using a bitwise OR operation. `r` is then updated for the next iteration. ```Solidity assembly { // According to step 1, calculate r^2, shifting right by 127 places because both rs are Q129.127 r := shr(127, mul(r, r)) // Since 1 <= r^2 < 4, only 2 places are needed to represent the integer part of r^2, // therefore, counting from the right, the 129th and 128th places represent the integer part of r^2, // shifting right by 128 places, only leaving the 129th place, // if this value is 1, then it indicates r >= 2; if 0, then r < 2 let f := shr(128, r) // If f == 1, then log_2 += Q192.64's 1/2 log_2 := or(log_2, shl(63, f)) // According to step 2 (i.e., formula 1.4), if r >= 2 (i.e., f == 1), then r /= 2; otherwise, no action, i.e., step 3 r := shr(f, r) } ``` -------------------------------- ### Initializing Pool with PositionManager (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/en/PositionManager.md Initializes a liquidity pool by calling the underlying PoolManager's initialize method. This sets the initial price (sqrtPriceX96) and returns the initial tick. ```Solidity /// @inheritdoc IPoolInitializer_v4 function initializePool(PoolKey calldata key, uint160 sqrtPriceX96) external payable returns (int24) { try poolManager.initialize(key, sqrtPriceX96) returns (int24 tick) { return tick; } catch { return type(int24).max; } } ``` -------------------------------- ### Skipping First Token and Fee in Uniswap V3 Swap Path Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README_zh.md Returns the remaining portion of an encoded Uniswap V3 swap path after removing the segment corresponding to the first token and its associated fee. This effectively advances the path pointer to the start of the second pool's data. ```Solidity /// @notice Skips a token + fee element from the buffer and returns the remainder /// @param path The swap path /// @return The remaining token + fee elements in the path function skipToken(bytes memory path) internal pure returns (bytes memory) { return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); } ``` -------------------------------- ### Clearing or Taking Token Balance - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-periphery/en/PositionManager.md Allows abandoning a positive token balance up to a specified maximum amount (`amountMax`). If the balance (`delta`) is zero, it returns. If `delta` is less than or equal to `amountMax`, it calls `poolManager.clear`. Otherwise, it withdraws the balance to the caller using `_take`. Uses `_getFullCredit` to get the balance. ```Solidity /// @dev integrators may elect to forfeit positive deltas with clear /// if the forfeit amount exceeds the user-specified max, the amount is taken instead /// if there is no credit, no call is made. function _clearOrTake(Currency currency, uint256 amountMax) internal { uint256 delta = _getFullCredit(currency); if (delta == 0) return; // forfeit the delta if its less than or equal to the user-specified limit if (delta <= amountMax) { poolManager.clear(currency, delta); } else { _take(currency, msgSender(), delta); } } ``` -------------------------------- ### Initializing Art Gobblers VRGDA Parameters in Solidity Source: https://github.com/adshao/publications/blob/master/artgobblers/vrgda/README_zh.md This Solidity constructor snippet from the Art Gobblers project demonstrates how the VRGDA parameters (target price, price decay, max mintable supply, and time scale) are defined and passed to the `LogisticVRGDA` base contract during the contract deployment. ```Solidity constructor( // Mint config: bytes32 _merkleRoot, uint256 _mintStart, // Addresses: Goo _goo, Pages _pages, address _team, address _community, RandProvider _randProvider, // URIs: string memory _baseUri, string memory _unrevealedUri ) GobblersERC721("Art Gobblers", "GOBBLER") Owned(msg.sender) LogisticVRGDA( 69.42e18, // Target price. 0.31e18, // Price decay percent. // Max gobblers mintable via VRGDA. toWadUnsafe(MAX_MINTABLE), 0.0023e18 // Time scale. ) ``` -------------------------------- ### Swap Exact Output Multi-step - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README.md Executes a multi-step swap across multiple pools to achieve a specified output amount with minimum input. It uses the `exactOutputInternal` function for the first step, with subsequent steps handled via callbacks. The final input amount is checked against a maximum limit. ```Solidity /// @inheritdoc ISwapRouter function exactOutput(ExactOutputParams calldata params) external payable override checkDeadline(params.deadline) returns (uint256 amountIn) { // it's okay that the payer is fixed to msg.sender here, as they're only paying for the "final" exact output // swap, which happens first, and subsequent swaps are paid for within nested callback frames exactOutputInternal( params.amountOut, params.recipient, 0, SwapCallbackData({path: params.path, payer: msg.sender}) ); amountIn = amountInCached; require(amountIn <= params.amountInMaximum, 'Too much requested'); amountInCached = DEFAULT_AMOUNT_IN_CACHED; } ``` -------------------------------- ### Getting Tick Spacing for Fee Tier (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md Retrieves the required tick spacing value from the `feeAmountTickSpacing` mapping based on the specified fee tier. Tick spacing is crucial for determining valid tick initialization points and impacts liquidity distribution and gas costs. ```Solidity int24 tickSpacing = feeAmountTickSpacing[fee]; ``` -------------------------------- ### LTE Search Logic in nextInitializedTickWithinOneWord (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README_zh.md This snippet shows the logic within `nextInitializedTickWithinOneWord` when searching for an initialized tick less than or equal to the starting tick (`lte = true`). It calculates the word and bit position, creates a mask to isolate bits at or below the current position, and applies it to the word's bitmap value. ```Solidity if (lte) { (int16 wordPos, uint8 bitPos) = position(compressed); // all the 1s at or to the right of the current bitPos uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); uint256 masked = self[wordPos] & mask; } ``` -------------------------------- ### Add Liquidity for New Position (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README.md This snippet shows the initial step of the `mint` function, where it calls the internal `addLiquidity` method. This call calculates the actual liquidity provided, the amounts of token0 and token1 consumed, and identifies the relevant pool based on the provided parameters. ```Solidity /// @inheritdoc INonfungiblePositionManager function mint(MintParams calldata params) external payable override checkDeadline(params.deadline) returns ( uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1 ) { IUniswapV3Pool pool; (liquidity, amount0, amount1, pool) = addLiquidity( AddLiquidityParams({ token0: params.token0, token1: params.token1, fee: params.fee, recipient: address(this), tickLower: params.tickLower, tickUpper: params.tickUpper, amount0Desired: params.amount0Desired, amount1Desired: params.amount1Desired, amount0Min: params.amount0Min, amount1Min: params.amount1Min }) ); ``` -------------------------------- ### Implementing Uniswap V3 Swap Callback (Initial) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README.md Implements the `IUniswapV3SwapCallback.uniswapV3SwapCallback` interface. This function is called by the Uniswap V3 pool during a swap. It decodes the callback data to get swap path information and verifies the callback source. It requires that at least one of `amount0Delta` or `amount1Delta` is positive. ```Solidity /// @inheritdoc IUniswapV3SwapCallback function uniswapV3SwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata _data ) external override { require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData)); (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool(); CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); } ``` -------------------------------- ### Swap Maximum Input for Exact Output (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v2-contracts/README_zh.md Executes a token swap where the output amount is fixed and the input amount must not exceed a maximum threshold. Calculates the required input amounts along the path using `getAmountsIn` and initiates the swap process via the internal `_swap` function. ```Solidity function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } ``` -------------------------------- ### computeSwapStep Function Signature and Initialization (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README.md Defines the computeSwapStep function with its required parameters (current price, target price, liquidity, remaining amount, fee) and specifies its return values (next price, amount in, amount out, fee amount). It initializes boolean flags `zeroForOne` and `exactIn` based on the relationship between current and target prices and the sign of the remaining amount. ```Solidity function computeSwapStep( uint160 sqrtRatioCurrentX96, uint160 sqrtRatioTargetX96, uint128 liquidity, int256 amountRemaining, uint24 feePips ) internal pure returns ( uint160 sqrtRatioNextX96, uint256 amountIn, uint256 amountOut, uint256 feeAmount ) { bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; bool exactIn = amountRemaining >= 0; } ``` -------------------------------- ### Greater Than Search Logic in nextInitializedTickWithinOneWord (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README_zh.md This snippet shows the logic within `nextInitializedTickWithinOneWord` when searching for an initialized tick greater than the starting tick (`lte = false`). It calculates the word and bit position for the *next* compressed tick, creates a mask to isolate bits at or above that position, and applies it to the word's bitmap value. ```Solidity else { // start from the word of the next tick, since the current tick state doesn't matter (int16 wordPos, uint8 bitPos) = position(compressed + 1); // all the 1s at or to the left of the bitPos uint256 mask = ~((1 << bitPos) - 1); uint256 masked = self[wordPos] & mask; // if there are no initialized ticks to the left of the current tick, return leftmost in the word initialized = masked != 0; // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick next = initialized ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; } ``` -------------------------------- ### Calling afterModifyLiquidity Hook in Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v4-contracts/v4-core/en/HooksLibrary.md This internal Solidity function handles post-liquidity modification hooks. It checks for `AFTER_ADD_LIQUIDITY_FLAG` or `AFTER_REMOVE_LIQUIDITY_FLAG` based on `liquidityDelta`. If permissioned, it calls the corresponding hook method using `callHookWithReturnDelta`. If the hook has the `_RETURNS_DELTA_FLAG` permission, it parses the returned `hookDelta` and subtracts it from the original `delta` to get `callerDelta`. ```Solidity /// @notice calls afterModifyLiquidity hook if permissioned and validates return value function afterModifyLiquidity( IHooks self, PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) internal returns (BalanceDelta callerDelta, BalanceDelta hookDelta) { if (msg.sender == address(self)) return (delta, BalanceDeltaLibrary.ZERO_DELTA); callerDelta = delta; if (params.liquidityDelta > 0) { if (self.hasPermission(AFTER_ADD_LIQUIDITY_FLAG)) { hookDelta = BalanceDelta.wrap( self.callHookWithReturnDelta( abi.encodeCall( IHooks.afterAddLiquidity, (msg.sender, key, params, delta, feesAccrued, hookData) ), self.hasPermission(AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG) ) ); callerDelta = callerDelta - hookDelta; } } else { if (self.hasPermission(AFTER_REMOVE_LIQUIDITY_FLAG)) { hookDelta = BalanceDelta.wrap( self.callHookWithReturnDelta( abi.encodeCall( IHooks.afterRemoveLiquidity, (msg.sender, key, params, delta, feesAccrued, hookData) ), self.hasPermission(AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG) ) ); callerDelta = callerDelta - hookDelta; } } } ``` -------------------------------- ### Uniswap V3 Swap Function Initialization - Solidity Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts/README_zh.md Initializes the state variables and performs prerequisite checks for the Uniswap V3 `swap` function. It verifies the input `amountSpecified` and `sqrtPriceLimitX96` against current conditions and limits. It then sets up the `Slot0`, `SwapCache`, and `SwapState` structs containing essential data like current price, tick, liquidity, timestamps, and fee information. ```Solidity /// @inheritdoc IUniswapV3PoolActions function swap( address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data ) external override noDelegateCall returns (int256 amount0, int256 amount1) { require(amountSpecified != 0, 'AS'); Slot0 memory slot0Start = slot0; require(slot0Start.unlocked, 'LOK'); require( zeroForOne ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, 'SPL' ); slot0.unlocked = false; SwapCache memory cache = SwapCache({ liquidityStart: liquidity, blockTimestamp: _blockTimestamp(), feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), secondsPerLiquidityCumulativeX128: 0, tickCumulative: 0, computedLatestObservation: false }); bool exactInput = amountSpecified > 0; SwapState memory state = SwapState({ amountSpecifiedRemaining: amountSpecified, amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, protocolFee: 0, liquidity: cache.liquidityStart }); ``` -------------------------------- ### Read Address from Bytes Array (Solidity) Source: https://github.com/adshao/publications/blob/master/uniswap/dive-into-uniswap-v3-contracts-2/README.md This internal pure function reads a 20-byte address from a specified starting index within a bytes array. It uses assembly to efficiently load data, accounting for the ABI length prefix and shifting the loaded 32 bytes to extract the 20-byte address. ```Solidity function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_start + 20 >= _start, 'toAddress_overflow'); require(_bytes.length >= _start + 20, 'toAddress_outOfBounds'); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } ```