AlgebraPoolDeployer

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/AlgebraPool.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;
pragma abicoder v1;

import './base/AlgebraPoolBase.sol';
import './base/ReentrancyGuard.sol';
import './base/Positions.sol';
import './base/SwapCalculation.sol';
import './base/ReservesManager.sol';
import './base/TickStructure.sol';

import './libraries/FullMath.sol';
import './libraries/Constants.sol';
import './libraries/SafeCast.sol';
import './libraries/TickMath.sol';
import './libraries/LiquidityMath.sol';
import './libraries/Plugins.sol';

import './interfaces/plugin/IAlgebraPlugin.sol';
import './interfaces/IAlgebraFactory.sol';

/// @title Algebra concentrated liquidity pool
/// @notice This contract is responsible for liquidity positions, swaps and flashloans
/// @dev Version: Algebra Integral 1.2.1
contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positions, SwapCalculation, ReservesManager {
  using SafeCast for uint256;
  using SafeCast for uint128;
  using Plugins for uint8;
  using Plugins for bytes4;

  /// @inheritdoc IAlgebraPoolActions
  function initialize(uint160 initialPrice) external override {
    int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); // getTickAtSqrtRatio checks validity of initialPrice inside
    if (globalState.price != 0) revert alreadyInitialized(); // after initialization, the price can never become zero
    globalState.price = initialPrice;
    globalState.tick = tick;
    emit Initialize(initialPrice, tick);

    if (plugin != address(0)) {
      IAlgebraPlugin(plugin).beforeInitialize(msg.sender, initialPrice).shouldReturn(IAlgebraPlugin.beforeInitialize.selector);
    }

    (uint16 _communityFee, int24 _tickSpacing, uint16 _fee) = _getDefaultConfiguration();

    _setFee(_fee);
    _setTickSpacing(_tickSpacing);
    if (_communityFee != 0 && communityVault == address(0)) revert invalidNewCommunityFee(); // the pool should not accumulate a community fee without a vault
    _setCommunityFee(_communityFee);

    if (globalState.pluginConfig.hasFlag(Plugins.AFTER_INIT_FLAG)) {
      IAlgebraPlugin(plugin).afterInitialize(msg.sender, initialPrice, tick).shouldReturn(IAlgebraPlugin.afterInitialize.selector);
    }
  }

  /// @inheritdoc IAlgebraPoolActions
  function mint(
    address leftoversRecipient,
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 liquidityDesired,
    bytes calldata data
  ) external override onlyValidTicks(bottomTick, topTick) returns (uint256 amount0, uint256 amount1, uint128 liquidityActual) {
    if (liquidityDesired == 0) revert zeroLiquidityDesired();

    _beforeModifyPos(recipient, bottomTick, topTick, liquidityDesired.toInt128(), data);
    _lock();

    {
      // scope to prevent stack too deep
      int24 currentTick = globalState.tick;
      uint160 currentPrice = globalState.price;
      if (currentPrice == 0) revert notInitialized();

      unchecked {
        int24 _tickSpacing = tickSpacing;
        if (bottomTick % _tickSpacing | topTick % _tickSpacing != 0) revert tickIsNotSpaced();
      }

      (amount0, amount1, ) = LiquidityMath.getAmountsForLiquidity(bottomTick, topTick, liquidityDesired.toInt128(), currentTick, currentPrice);
    }

    (uint256 receivedAmount0, uint256 receivedAmount1) = _updateReserves();
    _mintCallback(amount0, amount1, data); // IAlgebraMintCallback.algebraMintCallback to msg.sender

    receivedAmount0 = amount0 == 0 ? 0 : _balanceToken0() - receivedAmount0;
    receivedAmount1 = amount1 == 0 ? 0 : _balanceToken1() - receivedAmount1;

    if (receivedAmount0 < amount0) {
      liquidityActual = uint128(FullMath.mulDiv(uint256(liquidityDesired), receivedAmount0, amount0));
    } else {
      liquidityActual = liquidityDesired;
    }
    if (receivedAmount1 < amount1) {
      uint128 liquidityForRA1 = uint128(FullMath.mulDiv(uint256(liquidityDesired), receivedAmount1, amount1));
      if (liquidityForRA1 < liquidityActual) liquidityActual = liquidityForRA1;
    }
    if (liquidityActual == 0) revert zeroLiquidityActual();

    // scope to prevent "stack too deep"
    {
      Position storage _position = getOrCreatePosition(recipient, bottomTick, topTick);
      (amount0, amount1) = _updatePositionTicksAndFees(_position, bottomTick, topTick, liquidityActual.toInt128());
    }

    unchecked {
      // return leftovers
      if (amount0 > 0) {
        if (receivedAmount0 > amount0) _transfer(token0, leftoversRecipient, receivedAmount0 - amount0);
        else assert(receivedAmount0 == amount0); // must always be true
      }
      if (amount1 > 0) {
        if (receivedAmount1 > amount1) _transfer(token1, leftoversRecipient, receivedAmount1 - amount1);
        else assert(receivedAmount1 == amount1); // must always be true
      }
    }

    _changeReserves(int256(amount0), int256(amount1), 0, 0, 0, 0);
    emit Mint(msg.sender, recipient, bottomTick, topTick, liquidityActual, amount0, amount1);

    _unlock();
    _afterModifyPos(recipient, bottomTick, topTick, liquidityActual.toInt128(), amount0, amount1, data);
  }

  /// @inheritdoc IAlgebraPoolActions
  function burn(
    int24 bottomTick,
    int24 topTick,
    uint128 amount,
    bytes calldata data
  ) external override onlyValidTicks(bottomTick, topTick) returns (uint256 amount0, uint256 amount1) {
    if (amount > uint128(type(int128).max)) revert arithmeticError();

    int128 liquidityDelta = -int128(amount);

    uint24 pluginFee = _beforeModifyPos(msg.sender, bottomTick, topTick, liquidityDelta, data);
    _lock();

    _updateReserves();
    {
      Position storage position = getOrCreatePosition(msg.sender, bottomTick, topTick);

      (amount0, amount1) = _updatePositionTicksAndFees(position, bottomTick, topTick, liquidityDelta);

      if (pluginFee > 0) {
        uint256 deltaPluginFeePending0;
        uint256 deltaPluginFeePending1;

        if (amount0 > 0) {
          deltaPluginFeePending0 = FullMath.mulDiv(amount0, pluginFee, Constants.FEE_DENOMINATOR);
          amount0 -= deltaPluginFeePending0;
        }
        if (amount1 > 0) {
          deltaPluginFeePending1 = FullMath.mulDiv(amount1, pluginFee, Constants.FEE_DENOMINATOR);
          amount1 -= deltaPluginFeePending1;
        }

        _changeReserves(0, 0, 0, 0, deltaPluginFeePending0, deltaPluginFeePending1);
      }

      if (amount0 | amount1 != 0) {
        // since we do not support tokens whose total supply can exceed uint128, these casts are safe
        // and, theoretically, unchecked cast prevents a complete blocking of burn
        (position.fees0, position.fees1) = (position.fees0 + uint128(amount0), position.fees1 + uint128(amount1));
      }
    }

    if (amount | amount0 | amount1 != 0) emit Burn(msg.sender, bottomTick, topTick, amount, amount0, amount1, pluginFee);

    _unlock();
    _afterModifyPos(msg.sender, bottomTick, topTick, liquidityDelta, amount0, amount1, data);
  }

  function _isPlugin() internal view returns (bool) {
    return msg.sender == plugin;
  }

  function _beforeModifyPos(
    address owner,
    int24 bottomTick,
    int24 topTick,
    int128 liquidityDelta,
    bytes calldata data
  ) internal returns (uint24 pluginFee) {
    if (globalState.pluginConfig.hasFlag(Plugins.BEFORE_POSITION_MODIFY_FLAG)) {
      if (_isPlugin()) return 0;
      bytes4 selector;
      (selector, pluginFee) = IAlgebraPlugin(plugin).beforeModifyPosition(msg.sender, owner, bottomTick, topTick, liquidityDelta, data);
      if (pluginFee >= 1e6) revert incorrectPluginFee();
      selector.shouldReturn(IAlgebraPlugin.beforeModifyPosition.selector);
    }
  }

  function _afterModifyPos(address owner, int24 bTick, int24 tTick, int128 deltaL, uint256 amount0, uint256 amount1, bytes calldata data) internal {
    if (_isPlugin()) return;
    if (globalState.pluginConfig.hasFlag(Plugins.AFTER_POSITION_MODIFY_FLAG)) {
      IAlgebraPlugin(plugin).afterModifyPosition(msg.sender, owner, bTick, tTick, deltaL, amount0, amount1, data).shouldReturn(
        IAlgebraPlugin.afterModifyPosition.selector
      );
    }
  }

  /// @inheritdoc IAlgebraPoolActions
  function collect(
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 amount0Requested,
    uint128 amount1Requested
  ) external override returns (uint128 amount0, uint128 amount1) {
    _lock();
    // we don't check tick range validity, because if ticks are incorrect, the position will be empty
    Position storage position = getOrCreatePosition(msg.sender, bottomTick, topTick);
    (uint128 positionFees0, uint128 positionFees1) = (position.fees0, position.fees1);

    if (amount0Requested > positionFees0) amount0Requested = positionFees0;
    if (amount1Requested > positionFees1) amount1Requested = positionFees1;

    if (amount0Requested | amount1Requested != 0) {
      // use one if since fees0 and fees1 are tightly packed
      (amount0, amount1) = (amount0Requested, amount1Requested);

      unchecked {
        // single SSTORE
        (position.fees0, position.fees1) = (positionFees0 - amount0, positionFees1 - amount1);

        if (amount0 > 0) _transfer(token0, recipient, amount0);
        if (amount1 > 0) _transfer(token1, recipient, amount1);
        _changeReserves(-int256(uint256(amount0)), -int256(uint256(amount1)), 0, 0, 0, 0);
      }
      emit Collect(msg.sender, recipient, bottomTick, topTick, amount0, amount1);
    }
    _unlock();
  }

  struct SwapEventParams {
    uint160 currentPrice;
    int24 currentTick;
    uint128 currentLiquidity;
  }

  /// @inheritdoc IAlgebraPoolActions
  function swap(
    address recipient,
    bool zeroToOne,
    int256 amountRequired,
    uint160 limitSqrtPrice,
    bytes calldata data
  ) external override returns (int256 amount0, int256 amount1) {
    (uint24 overrideFee, uint24 pluginFee) = _beforeSwap(recipient, zeroToOne, amountRequired, limitSqrtPrice, false, data);
    _lock();

    {
      // scope to prevent "stack too deep"
      SwapEventParams memory eventParams;
      FeesAmount memory fees;
      (amount0, amount1, eventParams.currentPrice, eventParams.currentTick, eventParams.currentLiquidity, fees) = _calculateSwap(
        overrideFee,
        pluginFee,
        zeroToOne,
        amountRequired,
        limitSqrtPrice
      );
      (uint256 balance0Before, uint256 balance1Before) = _updateReserves();
      if (zeroToOne) {
        unchecked {
          if (amount1 < 0) _transfer(token1, recipient, uint256(-amount1)); // amount1 cannot be > 0
        }
        _swapCallback(amount0, amount1, data); // callback to get tokens from the msg.sender
        if (balance0Before + uint256(amount0) > _balanceToken0()) revert insufficientInputAmount();
        _changeReserves(amount0, amount1, fees.communityFeeAmount, 0, fees.pluginFeeAmount, 0); // reflect reserve change and pay communityFee
      } else {
        unchecked {
          if (amount0 < 0) _transfer(token0, recipient, uint256(-amount0)); // amount0 cannot be > 0
        }
        _swapCallback(amount0, amount1, data); // callback to get tokens from the msg.sender
        if (balance1Before + uint256(amount1) > _balanceToken1()) revert insufficientInputAmount();
        _changeReserves(amount0, amount1, 0, fees.communityFeeAmount, 0, fees.pluginFeeAmount); // reflect reserve change and pay communityFee
      }

      _emitSwapEvent(
        recipient,
        amount0,
        amount1,
        eventParams.currentPrice,
        eventParams.currentLiquidity,
        eventParams.currentTick,
        overrideFee,
        pluginFee
      );
    }

    _unlock();
    _afterSwap(recipient, zeroToOne, amountRequired, limitSqrtPrice, amount0, amount1, data);
  }

  /// @inheritdoc IAlgebraPoolActions
  function swapWithPaymentInAdvance(
    address leftoversRecipient,
    address recipient,
    bool zeroToOne,
    int256 amountToSell,
    uint160 limitSqrtPrice,
    bytes calldata data
  ) external override returns (int256 amount0, int256 amount1) {
    if (amountToSell < 0) revert invalidAmountRequired(); // we support only exactInput here

    _lock();
    // firstly we are getting tokens from the original caller of the transaction
    // since the pool can get less/more tokens then expected, _amountToSell_ can be changed
    {
      // scope to prevent "stack too deep"
      int256 amountReceived;
      if (zeroToOne) {
        uint256 balanceBefore = _balanceToken0();
        _swapCallback(amountToSell, 0, data); // callback to get tokens from the msg.sender
        uint256 balanceAfter = _balanceToken0();
        amountReceived = (balanceAfter - balanceBefore).toInt256();
        _changeReserves(amountReceived, 0, 0, 0, 0, 0);
      } else {
        uint256 balanceBefore = _balanceToken1();
        _swapCallback(0, amountToSell, data); // callback to get tokens from the msg.sender
        uint256 balanceAfter = _balanceToken1();
        amountReceived = (balanceAfter - balanceBefore).toInt256();
        _changeReserves(0, amountReceived, 0, 0, 0, 0);
      }
      if (amountReceived != amountToSell) amountToSell = amountReceived;
    }
    if (amountToSell == 0) revert insufficientInputAmount();

    _unlock();
    (uint24 overrideFee, uint24 pluginFee) = _beforeSwap(recipient, zeroToOne, amountToSell, limitSqrtPrice, true, data);
    _lock();

    _updateReserves();

    SwapEventParams memory eventParams;
    FeesAmount memory fees;
    (amount0, amount1, eventParams.currentPrice, eventParams.currentTick, eventParams.currentLiquidity, fees) = _calculateSwap(
      overrideFee,
      pluginFee,
      zeroToOne,
      amountToSell,
      limitSqrtPrice
    );

    unchecked {
      // transfer to the recipient
      if (zeroToOne) {
        if (amount1 < 0) _transfer(token1, recipient, uint256(-amount1)); // amount1 cannot be > 0
        uint256 leftover = uint256(amountToSell - amount0); // return the leftovers
        if (leftover != 0) _transfer(token0, leftoversRecipient, leftover);
        _changeReserves(-leftover.toInt256(), amount1, fees.communityFeeAmount, 0, fees.pluginFeeAmount, 0); // reflect reserve change and pay communityFee
      } else {
        if (amount0 < 0) _transfer(token0, recipient, uint256(-amount0)); // amount0 cannot be > 0
        uint256 leftover = uint256(amountToSell - amount1); // return the leftovers
        if (leftover != 0) _transfer(token1, leftoversRecipient, leftover);
        _changeReserves(amount0, -leftover.toInt256(), 0, fees.communityFeeAmount, 0, fees.pluginFeeAmount); // reflect reserve change and pay communityFee
      }
    }

    _emitSwapEvent(
      recipient,
      amount0,
      amount1,
      eventParams.currentPrice,
      eventParams.currentLiquidity,
      eventParams.currentTick,
      overrideFee,
      pluginFee
    );

    _unlock();
    _afterSwap(recipient, zeroToOne, amountToSell, limitSqrtPrice, amount0, amount1, data);
  }

  /// @dev internal function to reduce bytecode size
  function _emitSwapEvent(
    address recipient,
    int256 amount0,
    int256 amount1,
    uint160 newPrice,
    uint128 newLiquidity,
    int24 newTick,
    uint24 overrideFee,
    uint24 pluginFee
  ) private {
    emit Swap(msg.sender, recipient, amount0, amount1, newPrice, newLiquidity, newTick, overrideFee, pluginFee);
  }

  function _beforeSwap(
    address recipient,
    bool zto,
    int256 amount,
    uint160 limitPrice,
    bool payInAdvance,
    bytes calldata data
  ) internal returns (uint24 overrideFee, uint24 pluginFee) {
    uint8 pluginConfig = globalState.pluginConfig;
    if (pluginConfig.hasFlag(Plugins.BEFORE_SWAP_FLAG)) {
      if (_isPlugin()) return (0, 0);
      bytes4 selector;
      (selector, overrideFee, pluginFee) = IAlgebraPlugin(plugin).beforeSwap(msg.sender, recipient, zto, amount, limitPrice, payInAdvance, data);
      if (!pluginConfig.hasFlag(Plugins.DYNAMIC_FEE) && (overrideFee > 0 || pluginFee > 0)) revert dynamicFeeDisabled();
      // we will check that fee is less than denominator inside the swap calculation
      selector.shouldReturn(IAlgebraPlugin.beforeSwap.selector);
    }
  }

  function _afterSwap(address recipient, bool zto, int256 amount, uint160 limitPrice, int256 amount0, int256 amount1, bytes calldata data) internal {
    if (globalState.pluginConfig.hasFlag(Plugins.AFTER_SWAP_FLAG)) {
      if (_isPlugin()) return;
      IAlgebraPlugin(plugin).afterSwap(msg.sender, recipient, zto, amount, limitPrice, amount0, amount1, data).shouldReturn(
        IAlgebraPlugin.afterSwap.selector
      );
    }
  }

  /// @inheritdoc IAlgebraPoolActions
  function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external override {
    if (globalState.pluginConfig.hasFlag(Plugins.BEFORE_FLASH_FLAG)) {
      IAlgebraPlugin(plugin).beforeFlash(msg.sender, recipient, amount0, amount1, data).shouldReturn(IAlgebraPlugin.beforeFlash.selector);
    }
    _lock();

    uint256 paid0;
    uint256 paid1;
    {
      (uint256 balance0Before, uint256 balance1Before) = _updateReserves();
      uint256 fee0;
      if (amount0 > 0) {
        fee0 = FullMath.mulDivRoundingUp(amount0, Constants.FLASH_FEE, Constants.FEE_DENOMINATOR);
        _transfer(token0, recipient, amount0);
      }
      uint256 fee1;
      if (amount1 > 0) {
        fee1 = FullMath.mulDivRoundingUp(amount1, Constants.FLASH_FEE, Constants.FEE_DENOMINATOR);
        _transfer(token1, recipient, amount1);
      }

      _flashCallback(fee0, fee1, data); // IAlgebraFlashCallback.algebraFlashCallback to msg.sender

      paid0 = _balanceToken0();
      if (balance0Before + fee0 > paid0) revert flashInsufficientPaid0();
      paid1 = _balanceToken1();
      if (balance1Before + fee1 > paid1) revert flashInsufficientPaid1();

      unchecked {
        paid0 -= balance0Before;
        paid1 -= balance1Before;
      }

      uint256 _communityFee = globalState.communityFee;
      if (_communityFee > 0) {
        uint256 communityFee0;
        if (paid0 > 0) communityFee0 = FullMath.mulDiv(paid0, _communityFee, Constants.COMMUNITY_FEE_DENOMINATOR);
        uint256 communityFee1;
        if (paid1 > 0) communityFee1 = FullMath.mulDiv(paid1, _communityFee, Constants.COMMUNITY_FEE_DENOMINATOR);

        _changeReserves(int256(communityFee0), int256(communityFee1), communityFee0, communityFee1, 0, 0);
      }
      emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);
    }

    _unlock();
    if (globalState.pluginConfig.hasFlag(Plugins.AFTER_FLASH_FLAG)) {
      IAlgebraPlugin(plugin).afterFlash(msg.sender, recipient, amount0, amount1, paid0, paid1, data).shouldReturn(IAlgebraPlugin.afterFlash.selector);
    }
  }

  /// @dev using function to save bytecode
  function _checkIfAdministrator() private view {
    if (!IAlgebraFactory(factory).hasRoleOrOwner(Constants.POOLS_ADMINISTRATOR_ROLE, msg.sender)) revert notAllowed();
  }

  // permissioned actions use reentrancy lock to prevent call from callback (to keep the correct order of events, etc.)

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setCommunityFee(uint16 newCommunityFee) external override onlyUnlocked {
    _checkIfAdministrator();
    if (
      newCommunityFee > Constants.MAX_COMMUNITY_FEE ||
      newCommunityFee == globalState.communityFee ||
      (newCommunityFee != 0 && communityVault == address(0))
    ) revert invalidNewCommunityFee();
    _setCommunityFee(newCommunityFee);
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setTickSpacing(int24 newTickSpacing) external override onlyUnlocked {
    _checkIfAdministrator();
    if (newTickSpacing <= 0 || newTickSpacing > Constants.MAX_TICK_SPACING || tickSpacing == newTickSpacing) revert invalidNewTickSpacing();
    _setTickSpacing(newTickSpacing);
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setPlugin(address newPluginAddress) external override onlyUnlocked {
    _checkIfAdministrator();
    _setPluginConfig(0);
    _setPlugin(newPluginAddress);
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setPluginConfig(uint8 newConfig) external override onlyUnlocked {
    address _plugin = plugin;
    if (_plugin == address(0)) revert pluginIsNotConnected(); // it is not allowed to set plugin config without plugin

    if (msg.sender != _plugin) _checkIfAdministrator();

    _setPluginConfig(newConfig);
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setCommunityVault(address newCommunityVault) external override onlyUnlocked {
    // factory is allowed to set initial vault
    if (msg.sender != factory) _checkIfAdministrator();
    if (newCommunityVault == address(0) && globalState.communityFee != 0) _setCommunityFee(0); // the pool should not accumulate a community fee without a vault
    _setCommunityFeeVault(newCommunityVault); // accumulated but not yet sent to the vault community fees once will be sent to the `newCommunityVault` address
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setFee(uint16 newFee) external override {
    _checkIfAdministrator();
    bool isDynamicFeeEnabled = globalState.pluginConfig.hasFlag(Plugins.DYNAMIC_FEE);
    if (!globalState.unlocked) revert locked(); // cheaper to check lock here
    if (isDynamicFeeEnabled) revert dynamicFeeActive();
    _setFee(newFee);
  }

  /// @dev using function to save bytecode
  function _checkIfPlugin() private view {
    if (msg.sender != plugin) revert notAllowed();
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function sync() external override {
    _checkIfPlugin();
    _lock();
    _updateReserves();
    _unlock();
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function skim() external override {
    _checkIfPlugin();
    _lock();
    _skimReserves(msg.sender);
    _unlock();
  }
}
"
    },
    "contracts/AlgebraPoolDeployer.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;
pragma abicoder v1;

import './interfaces/IAlgebraPoolDeployer.sol';

import './AlgebraPool.sol';

/// @title Algebra pool deployer
/// @notice Is used by AlgebraFactory to deploy pools
/// @dev Version: Algebra Integral 1.2.1
contract AlgebraPoolDeployer is IAlgebraPoolDeployer {
  /// @dev two storage slots for dense cache packing
  bytes32 private cache0;
  bytes32 private cache1;

  address private immutable factory;

  constructor(address _factory) {
    require(_factory != address(0));
    factory = _factory;
  }

  /// @inheritdoc IAlgebraPoolDeployer
  function getDeployParameters() external view override returns (address _plugin, address _factory, address _token0, address _token1) {
    (_plugin, _token0, _token1) = _readFromCache();
    _factory = factory;
  }

  /// @inheritdoc IAlgebraPoolDeployer
  function deploy(address plugin, address token0, address token1, address deployer) external override returns (address pool) {
    require(msg.sender == factory);

    _writeToCache(plugin, token0, token1);
    bytes memory _encodedParams;
    if (deployer == address(0)) {
      _encodedParams = abi.encode(token0, token1);
    } else {
      _encodedParams = abi.encode(deployer, token0, token1);
    }
    pool = address(new AlgebraPool{salt: keccak256(_encodedParams)}());
    (cache0, cache1) = (bytes32(0), bytes32(0));
  }

  /// @notice densely packs three addresses into two storage slots
  function _writeToCache(address plugin, address token0, address token1) private {
    assembly {
      // cache0 = [plugin, token0[0, 96]], cache1 = [token0[0, 64], 0-s x32 , token1]
      token0 := and(token0, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // clean higher bits, just in case
      sstore(cache0.slot, or(shr(64, token0), shl(96, plugin)))
      sstore(cache1.slot, or(shl(160, token0), and(token1, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)))
    }
  }

  /// @notice reads three densely packed addresses from two storage slots
  function _readFromCache() private view returns (address plugin, address token0, address token1) {
    (bytes32 _cache0, bytes32 _cache1) = (cache0, cache1);
    assembly {
      plugin := shr(96, _cache0)
      token0 := or(shl(64, and(_cache0, 0xFFFFFFFFFFFFFFFFFFFFFFFF)), shr(160, _cache1))
      token1 := and(_cache1, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
    }
  }
}
"
    },
    "contracts/base/AlgebraPoolBase.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../interfaces/callback/IAlgebraSwapCallback.sol';
import '../interfaces/callback/IAlgebraMintCallback.sol';
import '../interfaces/callback/IAlgebraFlashCallback.sol';
import '../interfaces/plugin/IAlgebraDynamicFeePlugin.sol';
import '../interfaces/IAlgebraPool.sol';
import '../interfaces/IAlgebraFactory.sol';
import '../interfaces/IAlgebraPoolDeployer.sol';
import '../interfaces/IERC20Minimal.sol';

import '../libraries/TickManagement.sol';
import '../libraries/SafeTransfer.sol';
import '../libraries/Constants.sol';
import '../libraries/Plugins.sol';

import './common/Timestamp.sol';

/// @title Algebra pool base abstract contract
/// @notice Contains state variables, immutables and common internal functions
/// @dev Decoupling into a separate abstract contract simplifies testing
abstract contract AlgebraPoolBase is IAlgebraPool, Timestamp {
  using TickManagement for mapping(int24 => TickManagement.Tick);

  /// @notice The struct with important state values of pool
  /// @dev fits into one storage slot
  /// @param price The square root of the current price in Q64.96 format
  /// @param tick The current tick (price(tick) <= current price). May not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick boundary
  /// @param lastFee The current (last known) fee in hundredths of a bip, i.e. 1e-6 (so 100 is 0.01%). May be obsolete if using dynamic fee plugin
  /// @param pluginConfig The current plugin config as bitmap. Each bit is responsible for enabling/disabling the hooks, the last bit turns on/off dynamic fees logic
  /// @param communityFee The community fee represented as a percent of all collected fee in thousandths, i.e. 1e-3 (so 100 is 10%)
  /// @param unlocked  Reentrancy lock flag, true if the pool currently is unlocked, otherwise - false
  struct GlobalState {
    uint160 price;
    int24 tick;
    uint16 lastFee;
    uint8 pluginConfig;
    uint16 communityFee;
    bool unlocked;
  }

  /// @inheritdoc IAlgebraPoolImmutables
  uint128 public constant override maxLiquidityPerTick = Constants.MAX_LIQUIDITY_PER_TICK;
  /// @inheritdoc IAlgebraPoolImmutables
  address public immutable override factory;
  /// @inheritdoc IAlgebraPoolImmutables
  address public immutable override token0;
  /// @inheritdoc IAlgebraPoolImmutables
  address public immutable override token1;

  // ! IMPORTANT security note: the pool state can be manipulated
  // ! external contracts using this data must prevent read-only reentrancy

  /// @inheritdoc IAlgebraPoolState
  uint256 public override totalFeeGrowth0Token;
  /// @inheritdoc IAlgebraPoolState
  uint256 public override totalFeeGrowth1Token;

  /// @inheritdoc IAlgebraPoolState
  GlobalState public override globalState;

  /// @inheritdoc IAlgebraPoolState
  mapping(int24 => TickManagement.Tick) public override ticks;

  /// @dev The amounts of token0 and token1 that will be sent to the vault
  uint104 internal communityFeePending0;
  uint104 internal communityFeePending1;
  /// @inheritdoc IAlgebraPoolState
  uint32 public override lastFeeTransferTimestamp;

  uint104 internal pluginFeePending0;
  uint104 internal pluginFeePending1;

  /// @inheritdoc IAlgebraPoolState
  address public override plugin;

  /// @inheritdoc IAlgebraPoolState
  address public override communityVault;

  /// @inheritdoc IAlgebraPoolState
  mapping(int16 => uint256) public override tickTable;

  /// @inheritdoc IAlgebraPoolState
  int24 public override nextTickGlobal;
  /// @inheritdoc IAlgebraPoolState
  int24 public override prevTickGlobal;
  /// @inheritdoc IAlgebraPoolState
  uint128 public override liquidity;
  /// @inheritdoc IAlgebraPoolState
  int24 public override tickSpacing;
  // shares one slot with TickStructure.tickTreeRoot

  /// @notice Check that the lower and upper ticks do not violate the boundaries of allowed ticks and are specified in the correct order
  modifier onlyValidTicks(int24 bottomTick, int24 topTick) {
    TickManagement.checkTickRangeValidity(bottomTick, topTick);
    _;
  }

  constructor() {
    address _plugin;
    (_plugin, factory, token0, token1) = _getDeployParameters();
    (prevTickGlobal, nextTickGlobal) = (TickMath.MIN_TICK, TickMath.MAX_TICK);
    globalState.unlocked = true;
    if (_plugin != address(0)) {
      _setPlugin(_plugin);
    }
  }

  /// @inheritdoc IAlgebraPoolState
  /// @dev safe from read-only reentrancy getter function
  function safelyGetStateOfAMM()
    external
    view
    override
    returns (uint160 sqrtPrice, int24 tick, uint16 lastFee, uint8 pluginConfig, uint128 activeLiquidity, int24 nextTick, int24 previousTick)
  {
    sqrtPrice = globalState.price;
    tick = globalState.tick;
    lastFee = globalState.lastFee;
    pluginConfig = globalState.pluginConfig;
    bool unlocked = globalState.unlocked;
    if (!unlocked) revert IAlgebraPoolErrors.locked();

    activeLiquidity = liquidity;
    nextTick = nextTickGlobal;
    previousTick = prevTickGlobal;
  }

  /// @inheritdoc IAlgebraPoolState
  function isUnlocked() external view override returns (bool unlocked) {
    return globalState.unlocked;
  }

  /// @inheritdoc IAlgebraPoolState
  function getCommunityFeePending() external view override returns (uint128, uint128) {
    return (communityFeePending0, communityFeePending1);
  }

  function getPluginFeePending() external view override returns (uint128, uint128) {
    return (pluginFeePending0, pluginFeePending1);
  }

  /// @inheritdoc IAlgebraPoolState
  function fee() external view override returns (uint16 currentFee) {
    currentFee = globalState.lastFee;
    uint8 pluginConfig = globalState.pluginConfig;

    if (Plugins.hasFlag(pluginConfig, Plugins.DYNAMIC_FEE)) return IAlgebraDynamicFeePlugin(plugin).getCurrentFee();
  }

  /// @dev Gets the parameter values ​​for creating the pool. They are not passed in the constructor to make it easier to use create2 opcode
  /// Can be overridden in tests
  function _getDeployParameters() internal virtual returns (address, address, address, address) {
    return IAlgebraPoolDeployer(msg.sender).getDeployParameters();
  }

  /// @dev Gets the default settings for pool initialization. Can be overridden in tests
  function _getDefaultConfiguration() internal virtual returns (uint16, int24, uint16) {
    return IAlgebraFactory(factory).defaultConfigurationForPool();
  }

  // The main external calls that are used by the pool. Can be overridden in tests

  function _balanceToken0() internal view virtual returns (uint256) {
    return IERC20Minimal(token0).balanceOf(address(this));
  }

  function _balanceToken1() internal view virtual returns (uint256) {
    return IERC20Minimal(token1).balanceOf(address(this));
  }

  function _transfer(address token, address to, uint256 amount) internal virtual {
    SafeTransfer.safeTransfer(token, to, amount);
  }

  // These 'callback' functions are wrappers over the callbacks that the pool calls on the msg.sender
  // These methods can be overridden in tests

  /// @dev Using function to save bytecode
  function _swapCallback(int256 amount0, int256 amount1, bytes calldata data) internal virtual {
    IAlgebraSwapCallback(msg.sender).algebraSwapCallback(amount0, amount1, data);
  }

  function _mintCallback(uint256 amount0, uint256 amount1, bytes calldata data) internal virtual {
    IAlgebraMintCallback(msg.sender).algebraMintCallback(amount0, amount1, data);
  }

  function _flashCallback(uint256 fee0, uint256 fee1, bytes calldata data) internal virtual {
    IAlgebraFlashCallback(msg.sender).algebraFlashCallback(fee0, fee1, data);
  }

  // This virtual function is implemented in TickStructure and used in Positions
  /// @dev Add or remove a pair of ticks to the corresponding data structure
  function _addOrRemoveTicks(int24 bottomTick, int24 topTick, bool toggleBottom, bool toggleTop, int24 currentTick, bool remove) internal virtual;

  function _setCommunityFee(uint16 _communityFee) internal {
    globalState.communityFee = _communityFee;
    emit CommunityFee(_communityFee);
  }

  function _setCommunityFeeVault(address _communityFeeVault) internal {
    communityVault = _communityFeeVault;
    emit CommunityVault(_communityFeeVault);
  }

  function _setFee(uint16 _fee) internal {
    globalState.lastFee = _fee;
    emit Fee(_fee);
  }

  function _setTickSpacing(int24 _tickSpacing) internal {
    tickSpacing = _tickSpacing;
    emit TickSpacing(_tickSpacing);
  }

  function _setPlugin(address _plugin) internal {
    plugin = _plugin;
    emit Plugin(_plugin);
  }

  function _setPluginConfig(uint8 _pluginConfig) internal {
    globalState.pluginConfig = _pluginConfig;
    emit PluginConfig(_pluginConfig);
  }
}
"
    },
    "contracts/base/common/Timestamp.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title Abstract contract with modified blockTimestamp functionality
/// @notice Allows the pool and other contracts to get a timestamp truncated to 32 bits
/// @dev Can be overridden in tests to make testing easier
abstract contract Timestamp {
  /// @dev This function is created for testing by overriding it.
  /// @return A timestamp converted to uint32
  function _blockTimestamp() internal view virtual returns (uint32) {
    return uint32(block.timestamp); // truncation is desired
  }
}
"
    },
    "contracts/base/Positions.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../libraries/LiquidityMath.sol';
import '../libraries/TickManagement.sol';
import './AlgebraPoolBase.sol';

/// @title Algebra positions abstract contract
/// @notice Contains the logic of recalculation and change of liquidity positions
/// @dev Relies on method _addOrRemoveTicks, which is implemented in TickStructure
abstract contract Positions is AlgebraPoolBase {
  using TickManagement for mapping(int24 => TickManagement.Tick);

  struct Position {
    uint256 liquidity; // The amount of liquidity concentrated in the range
    uint256 innerFeeGrowth0Token; // The last updated fee growth per unit of liquidity
    uint256 innerFeeGrowth1Token;
    uint128 fees0; // The amount of token0 owed to a LP
    uint128 fees1; // The amount of token1 owed to a LP
  }

  /// @inheritdoc IAlgebraPoolState
  mapping(bytes32 => Position) public override positions;

  /// @notice This function fetches certain position object
  /// @param owner The address owing the position
  /// @param bottomTick The position's bottom tick
  /// @param topTick The position's top tick
  /// @return position The Position object
  function getOrCreatePosition(address owner, int24 bottomTick, int24 topTick) internal view returns (Position storage) {
    bytes32 key;
    assembly {
      key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF))
    }
    return positions[key];
  }

  /// @dev Updates position's ticks and its fees
  /// @return amount0 The abs amount of token0 that corresponds to liquidityDelta
  /// @return amount1 The abs amount of token1 that corresponds to liquidityDelta
  function _updatePositionTicksAndFees(
    Position storage position,
    int24 bottomTick,
    int24 topTick,
    int128 liquidityDelta
  ) internal returns (uint256 amount0, uint256 amount1) {
    (uint160 currentPrice, int24 currentTick) = (globalState.price, globalState.tick);

    bool toggledBottom;
    bool toggledTop;
    {
      // scope to prevent "stack too deep"
      (uint256 _totalFeeGrowth0, uint256 _totalFeeGrowth1) = (totalFeeGrowth0Token, totalFeeGrowth1Token);
      if (liquidityDelta != 0) {
        toggledBottom = ticks.update(bottomTick, currentTick, liquidityDelta, _totalFeeGrowth0, _totalFeeGrowth1, false); // isTopTick: false
        toggledTop = ticks.update(topTick, currentTick, liquidityDelta, _totalFeeGrowth0, _totalFeeGrowth1, true); // isTopTick: true
      }

      (uint256 feeGrowth0, uint256 feeGrowth1) = ticks.getInnerFeeGrowth(bottomTick, topTick, currentTick, _totalFeeGrowth0, _totalFeeGrowth1);
      _recalculatePosition(position, liquidityDelta, feeGrowth0, feeGrowth1);
    }

    if (liquidityDelta != 0) {
      // if liquidityDelta is negative and the tick was toggled, it means that it should not be initialized anymore, so we delete it
      if (toggledBottom || toggledTop) {
        _addOrRemoveTicks(bottomTick, topTick, toggledBottom, toggledTop, currentTick, liquidityDelta < 0);
      }

      int128 globalLiquidityDelta;
      (amount0, amount1, globalLiquidityDelta) = LiquidityMath.getAmountsForLiquidity(bottomTick, topTick, liquidityDelta, currentTick, currentPrice);
      if (globalLiquidityDelta != 0) liquidity = LiquidityMath.addDelta(liquidity, liquidityDelta); // update global liquidity
    }
  }

  /// @notice Increases amounts of tokens owed to owner of the position
  /// @param position The position object to operate with
  /// @param liquidityDelta The amount on which to increase\decrease the liquidity
  /// @param innerFeeGrowth0Token Total fee token0 fee growth per liquidity between position's lower and upper ticks
  /// @param innerFeeGrowth1Token Total fee token1 fee growth per liquidity between position's lower and upper ticks
  function _recalculatePosition(
    Position storage position,
    int128 liquidityDelta,
    uint256 innerFeeGrowth0Token,
    uint256 innerFeeGrowth1Token
  ) internal {
    uint128 liquidityBefore = uint128(position.liquidity);

    if (liquidityDelta == 0) {
      if (liquidityBefore == 0) return; // Do not recalculate the empty ranges
    } else {
      // change position liquidity
      position.liquidity = LiquidityMath.addDelta(liquidityBefore, liquidityDelta);
    }

    unchecked {
      // update the position
      (uint256 lastInnerFeeGrowth0Token, uint256 lastInnerFeeGrowth1Token) = (position.innerFeeGrowth0Token, position.innerFeeGrowth1Token);
      uint128 fees0;
      if (lastInnerFeeGrowth0Token != innerFeeGrowth0Token) {
        position.innerFeeGrowth0Token = innerFeeGrowth0Token;
        fees0 = uint128(FullMath.mulDiv(innerFeeGrowth0Token - lastInnerFeeGrowth0Token, liquidityBefore, Constants.Q128));
      }
      uint128 fees1;
      if (lastInnerFeeGrowth1Token != innerFeeGrowth1Token) {
        position.innerFeeGrowth1Token = innerFeeGrowth1Token;
        fees1 = uint128(FullMath.mulDiv(innerFeeGrowth1Token - lastInnerFeeGrowth1Token, liquidityBefore, Constants.Q128));
      }

      // To avoid overflow owner has to collect fee before it
      if (fees0 | fees1 != 0) {
        position.fees0 += fees0;
        position.fees1 += fees1;
      }
    }
  }
}
"
    },
    "contracts/base/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import './AlgebraPoolBase.sol';

/// @title Algebra reentrancy protection
/// @notice Provides a modifier that protects against reentrancy
abstract contract ReentrancyGuard is AlgebraPoolBase {
  /// @notice checks that reentrancy lock is unlocked
  modifier onlyUnlocked() {
    _checkUnlocked();
    _;
  }

  /// @dev using private function to save bytecode
  function _checkUnlocked() internal view {
    if (!globalState.unlocked) revert IAlgebraPoolErrors.locked();
  }

  /// @dev using private function to save bytecode
  function _lock() internal {
    if (!globalState.unlocked) revert IAlgebraPoolErrors.locked();
    globalState.unlocked = false;
  }

  /// @dev using private function to save bytecode
  function _unlock() internal {
    globalState.unlocked = true;
  }
}
"
    },
    "contracts/base/ReservesManager.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../libraries/SafeCast.sol';
import '../libraries/Plugins.sol';
import './AlgebraPoolBase.sol';
import '../interfaces/plugin/IAlgebraPlugin.sol';
import '../interfaces/pool/IAlgebraPoolErrors.sol';
/// @title Algebra reserves management abstract contract
/// @notice Encapsulates logic for tracking and changing pool reserves
/// @dev The reserve mechanism allows the pool to keep track of unexpected increases in balances
abstract contract ReservesManager is AlgebraPoolBase {
  using Plugins for bytes4;
  using SafeCast for uint256;

  /// @dev The tracked token0 and token1 reserves of pool
  uint128 internal reserve0;
  uint128 internal reserve1;

  /// @inheritdoc IAlgebraPoolState
  function getReserves() external view returns (uint128, uint128) {
    return (reserve0, reserve1);
  }

  /// @dev updates reserves data and distributes excess in the form of fee to liquidity providers.
  /// If any of the balances is greater than uint128, the excess is sent to the communityVault
  function _updateReserves() internal returns (uint256 balance0, uint256 balance1) {
    (balance0, balance1) = (_balanceToken0(), _balanceToken1());
    // we do not support tokens with totalSupply > type(uint128).max, so any excess will be sent to communityVault
    // this situation can only occur if the tokens are sent directly to the pool from outside
    // **such excessive tokens will be burned if there is no communityVault connected**
    if (balance0 > type(uint128).max || balance1 > type(uint128).max) {
      unchecked {
        address _communityVault = communityVault;
        if (balance0 > type(uint128).max) {
          _transfer(token0, _communityVault, balance0 - type(uint128).max);
          balance0 = type(uint128).max;
        }
        if (balance1 > type(uint128).max) {
          _transfer(token1, _communityVault, balance1 - type(uint128).max);
          balance1 = type(uint128).max;
        }
      }
    }

    uint128 _liquidity = liquidity;
    if (_liquidity == 0) return (balance0, balance1);

    (uint128 _reserve0, uint128 _reserve1) = (reserve0, reserve1);
    (bool hasExcessToken0, bool hasExcessToken1) = (balance0 > _reserve0, balance1 > _reserve1);
    if (hasExcessToken0 || hasExcessToken1) {
      unchecked {
        if (hasExcessToken0) totalFeeGrowth0Token += FullMath.mulDiv(balance0 - _reserve0, Constants.Q128, _liquidity);
        if (hasExcessToken1) totalFeeGrowth1Token += FullMath.mulDiv(balance1 - _reserve1, Constants.Q128, _liquidity);
        emit ExcessTokens(balance0 - _reserve0, balance1 - _reserve1);
        (reserve0, reserve1) = (uint128(balance0), uint128(balance1));
      }
    }
  }

  /// @notice Forces reserves to match balances. Excess of tokens will be sent to `receiver`
  function _skimReserves(address receiver) internal {
    (uint256 balance0, uint256 balance1) = (_balanceToken0(), _balanceToken1());
    (uint128 _reserve0, uint128 _reserve1) = (reserve0, reserve1);
    if (balance0 > _reserve0 || balance1 > _reserve1) {
      if (balance0 > _reserve0) _transfer(token0, receiver, balance0 - _reserve0);
      if (balance1 > _reserve1) _transfer(token1, receiver, balance1 - _reserve1);
      emit Skim(receiver, balance0 - _reserve0, balance1 - _reserve1);
    }
  }

  /// @notice Accrues fees and transfers them to `recipient`
  /// @dev If we transfer fees, writes zeros to the storage slot specified by the slot argument
  /// If we do not transfer fees, returns actual pendingFees
  function _accrueAndTransferFees(
    uint256 fee0,
    uint256 fee1,
    uint256 lastTimestamp,
    bytes32 receiverSlot,
    bytes32 feePendingSlot
  ) internal returns (uint104, uint104, uint256, uint256) {
    if (fee0 | fee1 != 0) {
      uint256 feePending0;
      uint256 feePending1;
      assembly {
        // Load the storage slot specified by the slot argument
        let sl := sload(feePendingSlot)
        // Extract the uint104 value
        feePending0 := and(sl, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF)
        // Shift right by 104 bits and extract the uint104 value
        feePending1 := and(shr(104, sl), 0xFFFFFFFFFFFFFFFFFFFFFFFFFF)
      }
      feePending0 += fee0;
      feePending1 += fee1;

      if (
        _blockTimestamp() - lastTimestamp >= Constants.FEE_TRANSFER_FREQUENCY || feePending0 > type(uint104).max || feePending1 > type(uint104).max
      ) {
        // use sload from slot (like pointer dereference) to avoid gas
        address recipient;
        assembly {
          recipient := sload(receiverSlot)
        }
        (uint256 feeSent0, uint256 feeSent1) = _transferFees(feePending0, feePending1, recipient);
        // use sload from slot (like pointer dereference) to avoid gas
        // override `lastFeeTransferTimestamp` with zeros is OK
        // because we will update it later
        assembly {
          sstore(feePendingSlot, 0)
        }
        // sent fees return 0 pending and sent fees
        return (0, 0, feeSent0, feeSent1);
      } else {
        // didn't send fees return pending fees and 0 sent
        return (uint104(feePending0), uint104(feePending1), 0, 0);
      }
    } else {
      if (_blockTimestamp() - lastTimestamp >= Constants.FEE_TRANSFER_FREQUENCY) {
        uint256 feePending0;
        uint256 feePending1;
        assembly {
          // Load the storage slot specified by the slot argument
          let sl := sload(feePendingSlot)
          // Extract the uint104 value
          feePending0 := and(sl, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF)
          // Shift right by 104 bits and extract the uint104 value
          feePending1 := and(shr(104, sl), 0xFFFFFFFFFFFFFFFFFFFFFFFFFF)
        }

        if (feePending0 | feePending1 != 0) {
          address recipient;
          // use sload from slot (like pointer dereference) to avoid gas
          assembly {
            recipient := sload(receiverSlot)
          }
          (uint256 feeSent0, uint256 feeSent1) = _transferFees(feePending0, feePending1, recipient);
          // use sload from slot (like pointer dereference) to avoid gas
          assembly {
            sstore(feePendingSlot, 0)
          }
          // sent fees return 0 pending and sent fees
          return (0, 0, feeSent0, feeSent1);
        }
      }
      // didn't either sent fees or increased pending
      return (0, 0, 0, 0);
    }
  }

  function _transferFees(uint256 feePending0, uint256 feePending1, address feesRecipient) private returns (uint256, uint256) {
    uint256 feeSent0;
    uint256 feeSent1;

    if (feePending0 > 0) {
      _transfer(token0, feesRecipient, feePending0);
      feeSent0 = feePending0;
    }
    if (feePending1 > 0) {
      _transfer(token1, feesRecipient, feePending1);
      feeSent1 = feePending1;
    }

    return (feeSent0, feeSent1);
  }

  /// @notice Applies deltas to reserves and pays communityFees
  /// @dev Community fee is sent to the vault at a specified frequency or when variables communityFeePending{0,1} overflow
  /// @param deltaR0 Amount of token0 to add/subtract to/from reserve0, must not exceed uint128
  /// @param deltaR1 Amount of token1 to add/subtract to/from reserve1, must not exceed uint128
  /// @param communityFee0 Amount of token0 to pay as communityFee, must not exceed uint128
  /// @param communityFee1 Amount of token1 to pay as communityFee, must not exceed uint128
  function _changeReserves(
    int256 deltaR0,
    int256 deltaR1,
    uint256 communityFee0,
    uint256 communityFee1,
    uint256 pluginFee0,
    uint256 pluginFee1
  ) internal {
    if (communityFee0 > 0 || communityFee1 > 0 || pluginFee0 > 0 || pluginFee1 > 0) {
      bytes32 feePendingSlot;
      bytes32 feeRecipientSlot;
      uint32 lastTimestamp = lastFeeTransferTimestamp;
      bool feeSent;

      assembly {
        feePendingSlot := communityFeePending0.slot
        feeRecipientSlot := communityVault.slot
      }
      // pass feeRecipientSlot to avoid redundant sload of an address
      (uint104 feePending0, uint104 feePending1, uint256 feeSent0, uint256 feeSent1) = _accrueAndTransferFees(
        communityFee0,
        communityFee1,
        lastTimestamp,
        feeRecipientSlot,
        feePendingSlot
      );
      if (feeSent0 | feeSent1 != 0) {
        // sent fees so decrease deltas
        (deltaR0, deltaR1) = (deltaR0 - feeSent0.toInt256(), deltaR1 - feeSent1.toInt256());
        feeSent = true;
      } else {
        // update pending if we accrued fees
        if (feePending0 | feePending1 != 0) (communityFeePending0, communityFeePending1) = (feePending0, feePending1);
      }

      assembly {
        feePendingSlot := pluginFeePending0.slot
        feeRecipientSlot := plugin.slot
      }
      // pass feeRecipientSlot to avoid redundant sload of an address
      (feePending0, feePending1, feeSent0, feeSent1) = _accrueAndTransferFees(
        pluginFee0,
        pluginFee1,
        lastTimestamp,
        feeRecipientSlot,
        feePendingSlot
      );
      if (feeSent0 | feeSent1 != 0) {
        // sent fees so decrease deltas
        (deltaR0, deltaR1) = (deltaR0 - feeSent0.toInt256(), deltaR1 - feeSent1.toInt256());
        feeSent = true;

        // notify plugin about sent fees
        IAlgebraPlugin(plugin).handlePluginFee(feeSent0, feeSent1).shouldReturn(IAlgebraPlugin.handlePluginFee.selector);
      } else {
        // update pending if we accrued fees
        if (feePending0 | feePending1 != 0) (pluginFeePending0, pluginFeePending1) = (feePending0, feePending1);
      }

      if (feeSent) lastFeeTransferTimestamp = _blockTimestamp();
    }

    if (deltaR0 | deltaR1 == 0) return;
    (uint256 _reserve0, uint256 _reserve1) = (reserve0, reserve1);
    if (deltaR0 != 0) _reserve0 = (uint256(int256(_reserve0) + deltaR0)).toUint128();
    if (deltaR1 != 0) _reserve1 = (uint256(int256(_reserve1) + deltaR1)).toUint128();
    (reserve0, reserve1) = (uint128(_reserve0), uint128(_reserve1));
  }
}
"
    },
    "contracts/base/SwapCalculation.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../libraries/PriceMovementMath.sol';
import '../libraries/LowGasSafeMath.sol';
import '../libraries/SafeCast.sol';
import './AlgebraPoolBase.sol';

/// @title Algebra swap calculation abstract contract
/// @notice Contains _calculateSwap encapsulating internal logic of swaps
abstract contract SwapCalculation is AlgebraPoolBase {
  using TickManagement for mapping(int24 => TickManagement.Tick);
  using SafeCast for uint256;
  using LowGasSafeMath for uint256;
  using LowGasSafeMath for int256;

  struct SwapCalculationCache {
    uint256 communityFee; // The community fee of the selling token, uint256 to minimize casts
    bool crossedAnyTick; //  If we have already crossed at least one active tick
    int256 amountRequiredInitial; // The initial value of the exact input\output amount
    int256 amountCalculated; // The additive amount of total output\input calculated through the swap
    uint256 totalFeeGrowthInput; // The initial totalFeeGrowth + the fee growth during a swap
    uint256 totalFeeGrowthOutput; // The initial totalFeeGrowth for output token, should not change during swap
    bool exactInput; // Whether the exact input or output is specified
    uint24 fee; // The current fee value in hundredths of a bip, i.e. 1e-6
    int24 prevInitializedTick; // The previous initialized tick in linked list
    int24 nextInitializedTick; // The next initialized tick in linked list
    uint24 pluginFee;
  }

  struct PriceMovementCache {
    uint256 stepSqrtPrice; // The Q64.96 sqrt of the price at the start of the step, uint256 to minimize casts
    uint256 nextTickPrice; // The Q64.96 sqrt of the price calculated from the _nextTick_, uint256 to minimize casts
    uint256 input; // The additive amount of tokens that have been provided
    uint256 output; // The additive amount of token that have been withdrawn
    uint256 feeAmount; // The total amount of fee earned within a current step
  }

  struct FeesAmount {
    uint256 communityFeeAmount;
    uint256 pluginFeeAmount;
  }

  function _calculateSwap(
    uint24 overrideFee,
    uint24 pluginFee,
    bool zeroToOne,
    int256 amountRequired,
    uint160 limitSqrtPrice
  ) internal returns (int256 amount0, int256 amount1, uint160 currentPrice, int24 currentTick, uint128 currentLiquidity, FeesAmount memory fees) {
    if (amountRequired == 0) revert zeroAmountRequired();
    if (amountRequired == type(int256).min) revert invalidAmountRequired(); // to avoid problems when changing sign

    SwapCalculationCache memory cache;
    (cache.amountRequiredInitial, cache.exactInput, cache.pluginFee) = (amountRequired, amountRequired > 0, pluginFee);

    // load from one storage slot
    (currentLiquidity, cache.prevInitializedTick, cache.nextInitializedTick) = (liquidity, prevTickGlobal, nextTickGlobal);

    // load from one storage slot too
    (currentPrice, currentTick, cache.fee, cache.communityFee) = (globalState.price, globalState.tick, globalState.lastFee, globalState.communityFee);
    if (currentPrice == 0) revert notInitialized();
    if (overrideFee != 0) {
      cache.fee = overrideFee + pluginFee;
      if (cache.fee >= 1e6) revert incorrectPluginFee();
    } else {
      if (pluginFee != 0) {
        cache.fee += pluginFee;
        if (cache.fee >= 1e6) revert incorrectPluginFee();
      }
    }

    if (zeroToOne) {
      if (limitSqrtPrice >= currentPrice || limitSqrtPrice <= TickMath.MIN_SQRT_RATIO) revert invalidLimitSqrtPrice();
      cache.totalFeeGrowthInput = totalFeeGrowth0Token;
    } else {
      if (limitSqrtPrice <= currentPrice || limitSqrtPrice >= TickMath.MAX_SQRT_RATIO) revert invalidLimitSqrtPrice();
      cache.totalFeeGrowthInput = totalFeeGrowth1Token;
    }

    PriceMovementCache memory step;
    unchecked {
      // swap until there is remaining input or output tokens or we reach the price limit
      do {
        int24 nextTick = zeroToOne ? cache.prevInitializedTick : cache.nextInitializedTick;
        step.stepSqrtPrice = currentPrice;
        step.nextTickPrice = TickMath.getSqrtRatioAtTick(nextTick);

        (currentPrice, step.input, step.output, step.feeAmount) = PriceMovementMath.movePriceTowardsTarget(
          zeroToOne, // if zeroToOne then the price is moving down
          currentPrice,
          (zeroToOne == (step.nextTickPrice < limitSqrtPrice)) // move the price to the nearest of the next tick and the limit price
            ? limitSqrtPrice
            : uint160(step.nextTickPrice), // cast is safe
          currentLiquidity,
          amountRequired,
          cache.fee
        );

        if (cache.exactInput) {
          amountRequired -= (step.input + step.feeAmount).toInt256(); // decrease remaining input amount
          cache.amountCalculated = cache.amountCalculated.sub(step.output.toInt256()); // decrease calculated output amount
        } else {
          amountRequired += step.output.toInt256(); // increase remaining output amount (since its negative)
          cache.amountCalculated = cache.amountCalculated.add((step.input + step.feeAmount).toInt256()); // increase calculated input amount
        }

        if (cache.communityFee > 0) {
          uint256 delta = (step.feeAmount.mul(cache.communityFee)) / Constants.COMMUNITY_FEE_DENOMINATOR;
          step.feeAmount -= delta;
          fees.communityFeeAmount += delta;
        }

        if (cache.pluginFee > 0 && cache.fee > 0) {
          uint256 delta = FullMath.mulDiv(step.feeAmount, cache.pluginFee, cache.fee);
          step.feeAmount -= delta;
          fees.pluginFeeAmount += delta;
        }

        if (currentLiquidity > 0) cache.totalFeeGrowthInput += FullMath.mulDiv(step.feeAmount, Constants.Q128, currentLiquidity);

        // min or max tick can not be crossed due to limitSqrtPrice check
        if (currentPrice == step.nextTickPrice) {
          // crossing tick
          if (!cache.crossedAnyTick) {
            cache.crossedAnyTick = true;
            cache.totalFeeGrowthOutput = zeroToOne ? totalFeeGrowth1Token : totalFeeGrowth0Token;
          }

          int128 liquidityDelta;
          if (zeroToOne) {
            (liquidityDelta, cache.prevInitializedTick, ) = ticks.cross(nextTick, cache.totalFeeGrowthInput, cache.totalFeeGrowthOutput);
            liquidityDelta = -liquidityDelta;
            (currentTick, cache.nextInitializedTick) = (nextTick - 1, nextTick);
          } else {
            (liquidityDelta, , cache.nextInitializedTick) = ticks.cross(nextTick, cache.totalFeeGrowthOutput, cache.totalFeeGrowthInput);
            (currentTick, cache.prevInitializedTick) = (nextTick, nextTick);
          }
          currentLiquidity = LiquidityMath.addDelta(currentLiquidity, liquidityDelta);
        } else if (currentPrice != step.stepSqrtPrice) {
          currentTick = TickMath.getTickAtSqrtRatio(currentPrice); // the price has changed but hasn't reached the target
          break; // since the price hasn't reached the target, amountRequired should be 0
        }
      } while (amountRequired != 0 && currentPrice != limitSqrtPrice); // check stop condition

      int256 amountSpent = cache.amountRequiredInitial - amountRequired; // spent amount could be less than initially specified (e.g. reached limit)
      (amount0, amount1) = zeroToOne == cache.exactInput ? (amountSpent, cache.amountCalculated) : (cache.amountCalculated, amountSpent);
    }

    (globalState.price, globalState.tick) = (currentPrice, currentTick);

    if (cache.crossedAnyTick) {
      (liquidity, prevTickGlobal, nextTickGlobal) = (currentLiquidity, cache.prevInitializedTick, cache.nextInitializedTick);
    }
    if (zeroToOne) {
      totalFeeGrowth0Token = cache.totalFeeGrowthInput;
    } else {
      totalFeeGrowth1Token = cache.totalFeeGrowthInput;
    }
  }
}
"
    },
    "contracts/base/TickStructure.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../libraries/TickManagement.sol';
import '../libraries/TickTree.sol';
import './AlgebraPoolBase.sol';

/// @title Algebra tick structure abstract contract
/// @notice Encapsulates the logic of interaction with the data structure with ticks
/// @dev Ticks are stored as a doubly linked list. A three-level bitmap tree is used to search through the list
abstract contract TickStructure is AlgebraPoolBase {
  using TickManagement for mapping(int24 => TickManagement.Tick);
  using TickTree for mapping(int16 => uint256);

  /// @inheritdoc IAlgebraPoolState
  uint32 public override tickTreeRoot; // The root bitmap of search tree
  /// @inheritdoc IAlgebraPoolState
  mapping(int16 => uint256) public override tickTreeSecondLayer; // The second layer of search tree

  // the leaves of the tree are stored in `tickTable`

  constructor() {
    ticks.initTickState();
  }

  /// @notice Used to add or remove a tick from a doubly linked list and search tree
  /// @param tick The tick being removed or added now
  /// @param currentTick The current global tick in the pool
  /// @param oldTickTreeRoot The current tick tree root
  /// @param prevInitializedTick Previous active tick before `currentTick`
  /// @param nextInitializedTick Next active tick after `currentTick`
  /// @param remove Remove or add the tick
  /// @return New previous active tick before `currentTick` if changed
  /// @return New next active tick after `currentTick` if changed
  /// @return New tick tree root if changed
  function _addOrRemoveTick(
    int24 tick,
    int24 currentTick,
    uint32 oldTickTreeRoot,
    int24 prevInitializedTick,
    int24 nextInitializedTick,
    bool remove
  ) internal returns (int24, int24, uint32) {
    if (remove) {
      (int24 prevTick, int24 nextTick) = ticks.removeTick(tick);
      if (prevInitializedTick == tick) prevInitializedTick = prevTick;
      else if (nextInitializedTick == tick) nextInitializedTick = nextTick;
    } else {
      int24 prevTick;
      int24 nextTick;
      if (prevInitializedTick < tick && nextInitializedTick > tick) {
        (prevTick, nextTick) = (prevInitializedTick, nextInitializedTick); // we know next and prev ticks
        if (tick > currentTick) nextInitializedTick = tick;
        else prevInitializedTick = tick;
      } else {
        nextTick = tickTable.getNextTick(tickTreeSecondLayer, oldTickTreeRoot, tick);
        prevTick = ticks[nextTick].prevTick;
      }
      ticks.insertTick(tick, prevTick, nextTick);
    }

    uint32 newTickTreeRoot = tickTable.toggleTick(tickTreeSecondLayer, oldTickTreeRoot, tick);
    return (prevInitializedTick, nextInitializedTick, newTickTreeRoot);
  }

  /// @notice Used to add or remove a pair of ticks from a doubly linked list and search tree
  /// @param bottomTick The bottom tick being removed or added now
  /// @param topTick The top tick being removed or added now
  /// @param toggleBottom Should bottom tick be changed or not
  /// @param toggleTop Should top tick be changed or not
  /// @param currentTick The current global tick in the pool
  /// @param remove Remove or add the ticks
  function _addOrRemoveTicks(int24 bottomTick, int24 topTick, bool toggleBottom, bool toggleTop, int24 currentTick, bool remove) internal override {
    (int24 prevInitializedTick, int24 nextInitializedTick, uint32 oldTickTreeRoot) = (prevTickGlobal, nextTickGlobal, tickTreeRoot);
    (int24 newPrevTick, int24 newNextTick, uint32 newTreeRoot) = (prevInitializedTick, nextInitializedTick, oldTickTreeRoot);
    if (toggleBottom) {
      (newPrevTick, newNextTick, newTreeRoot) = _addOrRemoveTick(bottomTick, currentTick, newTreeRoot, newPrevTick, newNextTick, remove);
    }
    if (toggleTop) {
      (newPrevTick, newNextTick, newTreeRoot) = _addOrRemoveTick(topTick, currentTick, newTreeRoot, newPrevTick, newNextTick, remove);
    }
    if (prevInitializedTick != newPrevTick || nextInitializedTick != newNextTick || newTreeRoot != oldTickTreeRoot) {
      (prevTickGlobal, nextTickGlobal, tickTreeRoot) = (newPrevTick, newNextTick, newTreeRoot);
    }
  }
}
"
    },
    "contracts/interfaces/callback/IAlgebraFlashCallback.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IAlgebraPoolActions#flash
/// @notice Any contract that calls IAlgebraPoolActions#flash must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraFlashCallback {
  /// @notice Called to `msg.sender` after transferring to the recipient from IAlgebraPool#flash.
  /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts.
  /// The caller of this method _must_ be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
  /// @param fee0 The fee amount in token0 due to the pool by the end of the flash
  /// @param fee1 The fee amount in token1 due to the pool by the end of the flash
  /// @param data Any data passed through by the caller via the IAlgebraPoolActions#flash call
  function algebraFlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external;
}
"
    },
    "contracts/interfaces/callback/IAlgebraMintCallback.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IAlgebraPoolActions#mint
/// @notice Any contract that calls IAlgebraPoolActions#mint must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraMintCallback {
  /// @notice Called to `msg.sender` after minting liquidity to a position from IAlgebraPool#mint.
  /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity.
  /// The caller of th

Tags:
ERC20, Multisig, Mintable, Burnable, Swap, Liquidity, Upgradeable, Multi-Signature, Factory|addr:0x42ac1bef3f25c29bbe5e06ef5df3d00ead7cf20f|verified:true|block:23739978|tx:0x94f1aa12428da0d48c27d8972131c9d9ccfbdf5206ce832f5cbdb1b0c39a86a7|first_check:1762435123

Submitted on: 2025-11-06 14:18:44

Comments

Log in to comment.

No comments yet.