MON Price: $0.018848 (+2.88%)

Contract

0x38A5C36FA8c8c9E4649b51FCD61810B14e7ce047

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Default Fee ...377459852025-11-24 19:09:2360 days ago1764011363IN
Atlantis: Algebra Default Plugin Factory
0 MON0.00615916102
Set Default Fee ...370700692025-11-21 15:50:4063 days ago1763740240IN
Atlantis: Algebra Default Plugin Factory
0 MON0.00615672102
Set Limit Order ...355486482025-11-14 13:12:1671 days ago1763125936IN
Atlantis: Algebra Default Plugin Factory
0 MON0.0072473102
Set Security Reg...355486352025-11-14 13:12:1171 days ago1763125931IN
Atlantis: Algebra Default Plugin Factory
0 MON0.00724965102
Set Farming Addr...355486312025-11-14 13:12:0971 days ago1763125929IN
Atlantis: Algebra Default Plugin Factory
0 MON0.00727627102

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
507962782026-01-24 11:32:353 hrs ago1769254355
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
489196742026-01-15 18:15:118 days ago1768500911
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
487931162026-01-15 4:00:499 days ago1768449649
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
487265122026-01-14 20:30:599 days ago1768422659
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
484435582026-01-13 12:54:4411 days ago1768308884
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
478450302026-01-10 18:15:5713 days ago1768068957
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
473554622026-01-08 11:42:4516 days ago1767872565
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
464674182026-01-04 8:38:2320 days ago1767515903
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
457480832026-01-01 0:34:0023 days ago1767227640
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
452449442025-12-29 16:33:5825 days ago1767026038
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
449750192025-12-28 10:25:1627 days ago1766917516
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
418191782025-12-13 18:26:3141 days ago1765650391
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
417626412025-12-13 12:07:2042 days ago1765627640
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
416997092025-12-13 5:06:0842 days ago1765602368
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
416799982025-12-13 2:54:1342 days ago1765594453
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
414734052025-12-12 3:51:1043 days ago1765511470
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
411332302025-12-10 13:49:3045 days ago1765374570
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
408930962025-12-09 11:01:2146 days ago1765278081
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
405960532025-12-08 1:49:1847 days ago1765158558
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
400559212025-12-05 13:18:1850 days ago1764940698
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
400546512025-12-05 13:09:4850 days ago1764940188
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
400545372025-12-05 13:09:0250 days ago1764940142
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
400512672025-12-05 12:47:0750 days ago1764938827
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
400399512025-12-05 11:31:2150 days ago1764934281
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
400389762025-12-05 11:24:4950 days ago1764933889
Atlantis: Algebra Default Plugin Factory
 Contract Creation0 MON
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AlgebraDefaultPluginFactory

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 0 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import './interfaces/IAlgebraDefaultPluginFactory.sol';
import '@cryptoalgebra/dynamic-fee-plugin/contracts/libraries/AdaptiveFee.sol';
import './AlgebraDefaultPlugin.sol';

/// @title Algebra Integral 1.2.2 default plugin factory
/// @notice This contract creates Algebra adaptive fee plugins for Algebra liquidity pools
/// @dev This plugin factory can only be used for Algebra default pools
contract AlgebraDefaultPluginFactory is IAlgebraDefaultPluginFactory {
  /// @inheritdoc IAlgebraDefaultPluginFactory
  bytes32 public constant override ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR = keccak256('ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR');

  /// @inheritdoc IBasePluginFactory
  address public immutable override algebraFactory;

  /// @inheritdoc IDynamicFeePluginFactory
  AlgebraFeeConfiguration public override defaultFeeConfiguration; // values of constants for sigmoids in fee calculation formula

  /// @inheritdoc IFarmingPluginFactory
  address public override farmingAddress;

  /// @inheritdoc ISecurityPluginFactory
  address public override securityRegistry;

  address public limitOrderManager;

  /// @inheritdoc IBasePluginFactory
  mapping(address poolAddress => address pluginAddress) public override pluginByPool;

  modifier onlyAdministrator() {
    require(IAlgebraFactory(algebraFactory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR, msg.sender), 'Only administrator');
    _;
  }

  constructor(address _algebraFactory) {
    algebraFactory = _algebraFactory;
    defaultFeeConfiguration = AdaptiveFee.initialFeeConfiguration();
    emit DefaultFeeConfiguration(defaultFeeConfiguration);
  }

  /// @inheritdoc IAlgebraPluginFactory
  function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata) external override returns (address) {
    require(msg.sender == algebraFactory);
    return _createPlugin(pool);
  }

  /// @inheritdoc IAlgebraPluginFactory
  function afterCreatePoolHook(address, address, address) external view override {
    require(msg.sender == algebraFactory);
  }

  /// @inheritdoc IBasePluginFactory
  function createPluginForExistingPool(address token0, address token1) external override returns (address) {
    IAlgebraFactory factory = IAlgebraFactory(algebraFactory);
    require(factory.hasRoleOrOwner(factory.POOLS_ADMINISTRATOR_ROLE(), msg.sender));

    address pool = factory.poolByPair(token0, token1);
    require(pool != address(0), 'Pool not exist');

    return _createPlugin(pool);
  }

  function _createPlugin(address pool) internal returns (address) {
    require(pluginByPool[pool] == address(0), 'Already created');
    IDynamicFeeManager volatilityOracle = new AlgebraDefaultPlugin(pool, algebraFactory, address(this), defaultFeeConfiguration, securityRegistry, limitOrderManager);
    pluginByPool[pool] = address(volatilityOracle);
    return address(volatilityOracle);
  }

  /// @inheritdoc IDynamicFeePluginFactory
  function setDefaultFeeConfiguration(AlgebraFeeConfiguration calldata newConfig) external override onlyAdministrator {
    AdaptiveFee.validateFeeConfiguration(newConfig);
    defaultFeeConfiguration = newConfig;
    emit DefaultFeeConfiguration(newConfig);
  }

  /// @inheritdoc IFarmingPluginFactory
  function setFarmingAddress(address newFarmingAddress) external override onlyAdministrator {
    require(farmingAddress != newFarmingAddress);
    farmingAddress = newFarmingAddress;
    emit FarmingAddress(newFarmingAddress);
  }

  /// @inheritdoc ISecurityPluginFactory
  function setSecurityRegistry(address newSecurityRegistry) external override onlyAdministrator {
    securityRegistry = newSecurityRegistry;
    emit SecurityRegistry(newSecurityRegistry);
  }

  /// @inheritdoc ILimitOrderPluginFactory
  function setLimitOrderManager(address newLimitOrderManager) external override onlyAdministrator {
    limitOrderManager = newLimitOrderManager;
    emit LimitOrderManager(newLimitOrderManager);
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/SafeTransfer.sol';

import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolState.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';

import './interfaces/IAbstractPlugin.sol';

/// @title Algebra Integral 1.2.1 plugin base
/// @notice This contract simplifies development process of plugins by providing base functionality
abstract contract AbstractPlugin is IAbstractPlugin, Timestamp {
  using Plugins for uint8;

  /// @dev The role can be granted in AlgebraFactory
  bytes32 public constant ALGEBRA_BASE_PLUGIN_MANAGER = keccak256('ALGEBRA_BASE_PLUGIN_MANAGER');

  uint8 public defaultPluginConfig = 0;
  string[] public activeModules;

  address public immutable pool;
  address internal immutable pluginFactory;

  modifier onlyPool() {
    _checkIfFromPool();
    _;
  }

  constructor(address _pool, address _pluginFactory) {
    (pool, pluginFactory) = (_pool, _pluginFactory);
  }

  function _checkIfFromPool() internal view {
    require(msg.sender == pool, 'Only pool can call this');
  }

  function _authorize() internal view virtual;

  function getActiveModuleNames() external view override returns (string[] memory moduleNames) {
    moduleNames = new string[](activeModules.length);
    for (uint256 i = 0; i < activeModules.length; i++) {
      moduleNames[i] = activeModules[i];
    }
  }

  function _getPoolState() internal view returns (uint160 price, int24 tick, uint16 fee, uint8 pluginConfig) {
    (price, tick, fee, pluginConfig, , ) = IAlgebraPoolState(pool).globalState();
  }

  function _getPluginInPool() internal view returns (address plugin) {
    return IAlgebraPool(pool).plugin();
  }

  /// @inheritdoc IAbstractPlugin
  function collectPluginFee(address token, uint256 amount, address recipient) external virtual override {
    _authorize();
    SafeTransfer.safeTransfer(token, recipient, amount);
  }

  /// @inheritdoc IAlgebraPlugin
  function handlePluginFee(uint256, uint256) external virtual view override onlyPool returns (bytes4) {
    return IAlgebraPlugin.handlePluginFee.selector;
  }

  // ###### HOOKS ######

  function beforeInitialize(address, uint160) external virtual override onlyPool returns (bytes4) {
    return IAlgebraPlugin.beforeInitialize.selector;
  }

  function afterInitialize(address, uint160, int24) external virtual override onlyPool returns (bytes4) {
    return IAlgebraPlugin.afterInitialize.selector;
  }

  function beforeModifyPosition(address, address, int24, int24, int128, bytes calldata) external virtual override onlyPool returns (bytes4, uint24) {
    return (IAlgebraPlugin.beforeModifyPosition.selector, 0);
  }

  function afterModifyPosition(
    address,
    address,
    int24,
    int24,
    int128,
    uint256,
    uint256,
    bytes calldata
  ) external virtual override onlyPool returns (bytes4) {
    return IAlgebraPlugin.afterModifyPosition.selector;
  }

  function beforeSwap(
    address,
    address,
    bool,
    int256,
    uint160,
    bool,
    bytes calldata
  ) external virtual override onlyPool returns (bytes4, uint24, uint24) {
    return (IAlgebraPlugin.beforeSwap.selector, 0, 0);
  }

  function afterSwap(address, address, bool, int256, uint160, int256, int256, bytes calldata) external virtual override onlyPool returns (bytes4) {
    return IAlgebraPlugin.afterSwap.selector;
  }

  function beforeFlash(address, address, uint256, uint256, bytes calldata) external virtual override onlyPool returns (bytes4) {
    return IAlgebraPlugin.beforeFlash.selector;
  }

  function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external virtual override onlyPool returns (bytes4) {
    return IAlgebraPlugin.afterFlash.selector;
  }

  function _updatePluginConfigInPool(uint8 newPluginConfig) internal {
    (, , , uint8 currentPluginConfig) = _getPoolState();
    if (currentPluginConfig != newPluginConfig) {
      IAlgebraPool(pool).setPluginConfig(newPluginConfig);
    }
  }

  function _disablePluginFlags(uint8 config) internal {
    (, , , uint8 currentPluginConfig) = _getPoolState();
    uint8 newPluginConfig = currentPluginConfig & ~config;
    if (currentPluginConfig != newPluginConfig) {
      IAlgebraPool(pool).setPluginConfig(newPluginConfig);
    }
  }

  function _enablePluginFlags(uint8 config) internal {
    (, , , uint8 currentPluginConfig) = _getPoolState();
    uint8 newPluginConfig = currentPluginConfig | config;
    if (currentPluginConfig != newPluginConfig) {
      IAlgebraPool(pool).setPluginConfig(newPluginConfig);
    }
  }
}

File 3 of 67 : BaseAbstractPlugin.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import './AbstractPlugin.sol';

/// @title Algebra's internal Integral 1.2.1 abstract plugin for `Base` plugins
/// @notice This contract inherits AbstractPlugin
abstract contract BaseAbstractPlugin is AbstractPlugin {
  address internal immutable factory;

  constructor(address _pool, address _factory, address _pluginFactory) AbstractPlugin(_pool, _pluginFactory) {
    factory = _factory;
  }

  function _authorize() internal view virtual override {
    require(IAlgebraFactory(factory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_MANAGER, msg.sender));
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPlugin.sol';

/// @title The interface for the BasePlugin
interface IAbstractPlugin is IAlgebraPlugin {
  /// @notice Claim plugin fee
  /// @param token The token address
  /// @param amount Amount of tokens
  /// @param recipient Recipient address
  function collectPluginFee(address token, uint256 amount, address recipient) external;

  /// @notice Get all active module names
  /// @return moduleNames Array of active module names
  function getActiveModuleNames() external view returns (string[] memory moduleNames);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPluginFactory.sol';

/// @title The interface for the BasePluginFactory
interface IBasePluginFactory is IAlgebraPluginFactory {

  /// @notice Returns the address of AlgebraFactory
  /// @return The AlgebraFactory contract address
  function algebraFactory() external view returns (address);

  /// @notice Returns address of plugin created for given AlgebraPool
  /// @param pool The address of AlgebraPool
  /// @return The address of corresponding plugin
  function pluginByPool(address pool) external view returns (address);

  /// @notice Create plugin for already existing pool
  /// @param token0 The address of first token in pool
  /// @param token1 The address of second token in pool
  /// @return The address of created plugin
  function createPluginForExistingPool(address token0, address token1) external returns (address);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/abstract-plugin/contracts/BaseAbstractPlugin.sol';
import './interfaces/IAlmPlugin.sol';
import './interfaces/IRebalanceManager.sol';

abstract contract AlmPlugin is BaseAbstractPlugin, IAlmPlugin {
  /// @inheritdoc	IAlmPlugin
  address public rebalanceManager;

  /// @inheritdoc	IAlmPlugin
  uint32 public slowTwapPeriod;
  
  /// @inheritdoc	IAlmPlugin
  uint32 public fastTwapPeriod;

  constructor(){
    defaultPluginConfig = defaultPluginConfig | uint8(Plugins.AFTER_SWAP_FLAG);
    activeModules.push("ALM Plugin");
  }

  /// @inheritdoc	IAlmPlugin
  function initializeALM(address _rebalanceManager, uint32 _slowTwapPeriod, uint32 _fastTwapPeriod) external {
    _authorize();
    require(_rebalanceManager != address(0), '_rebalanceManager must be non zero address');
    require(_slowTwapPeriod >= _fastTwapPeriod, '_slowTwapPeriod must be >= _fastTwapPeriod');
    rebalanceManager = _rebalanceManager;
    slowTwapPeriod = _slowTwapPeriod;
    fastTwapPeriod = _fastTwapPeriod;
  }

  /// @inheritdoc	IAlmPlugin
  function setSlowTwapPeriod(uint32 _slowTwapPeriod) external {
    _authorize();
    require(_slowTwapPeriod >= fastTwapPeriod, '_slowTwapPeriod must be >= fastTwapPeriod');
    slowTwapPeriod = _slowTwapPeriod;
  }

  /// @inheritdoc	IAlmPlugin
  function setFastTwapPeriod(uint32 _fastTwapPeriod) external {
    _authorize();
    require(_fastTwapPeriod <= slowTwapPeriod, '_fastTwapPeriod must be <= slowTwapPeriod');
    fastTwapPeriod = _fastTwapPeriod;
  }

  /// @inheritdoc	IAlmPlugin
  function setRebalanceManager(address _rebalanceManager) external {
    _authorize();
    rebalanceManager = _rebalanceManager;
  }

  function _obtainTWAPAndRebalance(
    int24 currentTick,
    int24 slowTwapTick,
    int24 fastTwapTick,
    uint32 lastBlockTimestamp
  ) internal {
    IRebalanceManager(rebalanceManager).obtainTWAPAndRebalance(currentTick, slowTwapTick, fastTwapTick, lastBlockTimestamp);
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IAbstractPlugin.sol';

interface IAlmPlugin is IAbstractPlugin{
	/// @notice Initializing ALM plugin
	/// @param _rebalanceManager address of rebalance manager
	/// @param _slowTwapPeriod period in seconds to get slow TWAP
	/// @param _fastTwapPeriod period in seconds to get fast TWAP
	function initializeALM(address _rebalanceManager, uint32 _slowTwapPeriod, uint32 _fastTwapPeriod) external;

	/// @notice Set slow TWAP period
	/// @param _slowTwapPeriod period in seconds to get slow TWAP
	function setSlowTwapPeriod(uint32 _slowTwapPeriod) external;

	/// @notice Set slow TWAP period
	/// @param _fastTwapPeriod period in seconds to get fast TWAP
	function setFastTwapPeriod(uint32 _fastTwapPeriod) external;

	/// @notice Set rebalance manager
	/// @param _rebalanceManager address of rebalance manager
	function setRebalanceManager(address _rebalanceManager) external;

	/// @notice Returns address of rebalance manager
	/// @return Address of rebalance manager
	function rebalanceManager() external view returns (address);

	/// @notice Returns time interval in seconds of slow TWAP period
	/// @return Time interval in seconds of slow TWAP period
	function slowTwapPeriod() external view returns (uint32);

	/// @notice Returns time interval in seconds of fast TWAP period
	/// @return Time interval in seconds of fast TWAP period
	function fastTwapPeriod() external view returns (uint32);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface IRebalanceManager {
	event SetPriceChangeThreshold(uint16 priceChangeThreshold);
	event SetPercentages(uint16 baseLowPct, uint16 baseHighPct, uint16 limitReservePct);
	event SetTriggers(uint16 simulate, uint16 normalThreshold, uint16 underInventoryThreshold, uint16 overInventoryThreshold);
	event SetDtrDelta(uint16 dtrDelta);
	event SetHighVolatility(uint16 highVolatility);
	event SetSomeVolatility(uint16 someVolatility);
	event SetExtremeVolatility(uint16 extremeVolatility);
	event SetDepositTokenUnusedThreshold(uint16 depositTokenUnusedThreshold);
	event SetMinTimeBetweenRebalances(uint32 minTimeBetweenRebalances);
	event SetVault(address vault);
	event Paused();
	event Unpaused();

	function obtainTWAPAndRebalance(
		int24 currentTick,
        int24 slowTwapTick,
        int24 fastTwapTick,
        uint32 lastBlockTimestamp
	) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';

import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';

import './interfaces/IDynamicFeeManager.sol';

import './libraries/AdaptiveFee.sol';
import { AlgebraFeeConfigurationU144 } from './types/AlgebraFeeConfigurationU144.sol';
import '@cryptoalgebra/abstract-plugin/contracts/BaseAbstractPlugin.sol';

/// @title Algebra Integral 1.2.1 default plugin
/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages
abstract contract DynamicFeePlugin is BaseAbstractPlugin, IDynamicFeeManager {
  using Plugins for uint8;
  using AlgebraFeeConfigurationU144Lib for AlgebraFeeConfiguration;

  /// @dev AlgebraFeeConfiguration struct packed in uint144
  AlgebraFeeConfigurationU144 internal _feeConfig;

  constructor(AlgebraFeeConfiguration memory _config) {
    defaultPluginConfig = defaultPluginConfig | uint8(Plugins.BEFORE_SWAP_FLAG | Plugins.DYNAMIC_FEE);
    AdaptiveFee.validateFeeConfiguration(_config);

    _feeConfig = _config.pack(); // pack struct to uint144 and write in storage

    activeModules.push("Dynamic Fee Plugin");
  }

  /// @inheritdoc IDynamicFeeManager
  function feeConfig()
    external
    view
    override
    returns (uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint16 baseFee)
  {
    (alpha1, alpha2) = (_feeConfig.alpha1(), _feeConfig.alpha2());
    (beta1, beta2) = (_feeConfig.beta1(), _feeConfig.beta2());
    (gamma1, gamma2) = (_feeConfig.gamma1(), _feeConfig.gamma2());
    baseFee = _feeConfig.baseFee();
  }

  // ###### Fee manager ######

  /// @inheritdoc IDynamicFeeManager
  function changeFeeConfiguration(AlgebraFeeConfiguration calldata _config) external override {
    _authorize();
    AdaptiveFee.validateFeeConfiguration(_config);

    _feeConfig = _config.pack(); // pack struct to uint144 and write in storage
    emit FeeConfiguration(_config);
  }

  function _getCurrentFee(uint88 volatilityAverage) internal view returns (uint16 fee) {
    AlgebraFeeConfigurationU144 feeConfig_ = _feeConfig;
    if (feeConfig_.alpha1() | feeConfig_.alpha2() == 0) return feeConfig_.baseFee();

    return AdaptiveFee.getFee(volatilityAverage, feeConfig_);
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraDynamicFeePlugin.sol';
import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IAbstractPlugin.sol';
import '../types/AlgebraFeeConfiguration.sol';

/// @title The interface for the Algebra dynamic fee manager
/// @dev This contract calculates adaptive fee
interface IDynamicFeeManager is IAlgebraDynamicFeePlugin, IAbstractPlugin {
  /// @notice Emitted when the fee configuration is changed
  /// @param feeConfig The structure with dynamic fee parameters
  /// @dev See the AdaptiveFee struct for more details
  event FeeConfiguration(AlgebraFeeConfiguration feeConfig);

  /// @notice Current dynamic fee configuration
  /// @dev See the AdaptiveFee struct for more details
  function feeConfig() external view returns (uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint16 baseFee);

  /// @notice Changes fee configuration for the pool
  function changeFeeConfiguration(AlgebraFeeConfiguration calldata feeConfig) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import '../types/AlgebraFeeConfiguration.sol';

/// @title The interface for the Algebra dynamic fee plugin factory
interface IDynamicFeePluginFactory {
  /// @notice Emitted when the default fee configuration is changed
  /// @param newConfig The structure with dynamic fee parameters
  /// @dev See the AdaptiveFee library for more details
  event DefaultFeeConfiguration(AlgebraFeeConfiguration newConfig);

  /// @notice Current default dynamic fee configuration
  /// @dev See the AdaptiveFee struct for more details about params.
  /// This value is set by default in new plugins
  function defaultFeeConfiguration()
    external
    view
    returns (uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint16 baseFee);

  /// @notice Changes initial fee configuration for new pools
  /// @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
  /// alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max and gammas must be > 0
  /// @param newConfig new default fee configuration. See the #AdaptiveFee.sol library for details
  function setDefaultFeeConfiguration(AlgebraFeeConfiguration calldata newConfig) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Constants.sol';
import '../types/AlgebraFeeConfiguration.sol';
import '../types/AlgebraFeeConfigurationU144.sol';

/// @title AdaptiveFee
/// @notice Calculates fee based on combination of sigmoids
library AdaptiveFee {
  uint16 internal constant INITIAL_MIN_FEE = 0.01e4; // 0.01%

  /// @notice Returns default initial fee configuration
  function initialFeeConfiguration() internal pure returns (AlgebraFeeConfiguration memory) {
    return
      AlgebraFeeConfiguration({
        alpha1: 3000 - INITIAL_MIN_FEE, // max value of the first sigmoid in hundredths of a bip, i.e. 1e-6
        alpha2: 15000 - 3000, // max value of the second sigmoid in hundredths of a bip, i.e. 1e-6
        beta1: 360, // shift along the x-axis (volatility) for the first sigmoid
        beta2: 60000, // shift along the x-axis (volatility) for the second sigmoid
        gamma1: 59, // horizontal stretch factor for the first sigmoid
        gamma2: 8500, // horizontal stretch factor for the second sigmoid
        baseFee: INITIAL_MIN_FEE // in hundredths of a bip, i.e. 1e-6
      });
  }

  /// @notice Validates fee configuration.
  /// @dev Maximum fee value capped by baseFee + alpha1 + alpha2 must be <= type(uint16).max
  /// gammas must be > 0
  function validateFeeConfiguration(AlgebraFeeConfiguration memory _config) internal pure {
    require(uint256(_config.alpha1) + uint256(_config.alpha2) + uint256(_config.baseFee) <= type(uint16).max, 'Max fee exceeded');
    require(_config.gamma1 != 0 && _config.gamma2 != 0, 'Gammas must be > 0');
  }

  /// @notice Calculates fee based on formula:
  /// baseFee + sigmoid1(volatility) + sigmoid2(volatility)
  /// maximum value capped by baseFee + alpha1 + alpha2
  function getFee(uint88 volatility, AlgebraFeeConfigurationU144 config) internal pure returns (uint16 fee) {
    unchecked {
      volatility /= 15; // normalize for 15 sec interval
      uint256 sumOfSigmoids = sigmoid(volatility, config.gamma1(), config.alpha1(), config.beta1()) +
        sigmoid(volatility, config.gamma2(), config.alpha2(), config.beta2());

      uint256 result = uint256(config.baseFee()) + sumOfSigmoids;
      assert(result <= type(uint16).max); // should always be true

      return uint16(result); // safe since alpha1 + alpha2 + baseFee _must_ be <= type(uint16).max
    }
  }

  /// @notice calculates α / (1 + e^( (β-x) / γ))
  /// that is a sigmoid with a maximum value of α, x-shifted by β, and stretched by γ
  /// @dev returns uint256 for fuzzy testing. Guaranteed that the result is not greater than alpha
  function sigmoid(uint256 x, uint16 g, uint16 alpha, uint256 beta) internal pure returns (uint256 res) {
    unchecked {
      if (x > beta) {
        x = x - beta;
        if (x >= 6 * uint256(g)) return alpha; // so x < 19 bits
        uint256 g4 = uint256(g) ** 4; // < 64 bits (4*16)
        uint256 ex = expXg4(x, g, g4); // < 155 bits
        res = (alpha * ex) / (g4 + ex); // in worst case: (16 + 155 bits) / 155 bits
        // so res <= alpha
      } else {
        x = beta - x;
        if (x >= 6 * uint256(g)) return 0; // so x < 19 bits
        uint256 g4 = uint256(g) ** 4; // < 64 bits (4*16)
        uint256 ex = g4 + expXg4(x, g, g4); // < 156 bits
        res = (alpha * g4) / ex; // in worst case: (16 + 128 bits) / 156 bits
        // g8 <= ex, so res <= alpha
      }
    }
  }

  /// @notice calculates e^(x/g) * g^4 in a series, since (around zero):
  /// e^x = 1 + x + x^2/2 + ... + x^n/n! + ...
  /// e^(x/g) = 1 + x/g + x^2/(2*g^2) + ... + x^(n)/(g^n * n!) + ...
  /// @dev has good accuracy only if x/g < 6
  function expXg4(uint256 x, uint16 g, uint256 gHighestDegree) internal pure returns (uint256 res) {
    uint256 closestValue; // nearest 'table' value of e^(x/g), multiplied by 10^20
    assembly {
      let xdg := div(x, g)
      switch xdg
      case 0 {
        closestValue := 100000000000000000000 // 1
      }
      case 1 {
        closestValue := 271828182845904523536 // ~= e
      }
      case 2 {
        closestValue := 738905609893065022723 // ~= e^2
      }
      case 3 {
        closestValue := 2008553692318766774092 // ~= e^3
      }
      case 4 {
        closestValue := 5459815003314423907811 // ~= e^4
      }
      default {
        closestValue := 14841315910257660342111 // ~= e^5
      }

      x := mod(x, g)
    }

    unchecked {
      if (x >= g / 2) {
        // (x - closestValue) >= 0.5, so closestValue := closestValue * e^0.5
        x -= g / 2;
        closestValue = (closestValue * 164872127070012814684) / 1e20;
      }

      // After calculating the closestValue x/g is <= 0.5, so that the series in the neighborhood of zero converges with sufficient speed
      uint256 xLowestDegree = x;
      res = gHighestDegree; // g**4, res < 64 bits

      gHighestDegree /= g; // g**3
      res += xLowestDegree * gHighestDegree; // g**4 + x*g**3, res < 68

      gHighestDegree /= g; // g**2
      xLowestDegree *= x; // x**2
      // g**4 + x * g**3 + (x**2 * g**2) / 2, res < 71
      res += (xLowestDegree * gHighestDegree) / 2;

      gHighestDegree /= g; // g
      xLowestDegree *= x; // x**3
      // g^4 + x * g^3 + (x^2 * g^2)/2 + x^3(g*4 + x)/24, res < 73
      res += (xLowestDegree * g * 4 + xLowestDegree * x) / 24;

      // res = g^4 * (1 + x/g + x^2/(2*g^2) + x^3/(6*g^3) + x^4/(24*g^4)) * closestValue / 10^20, closestValue < 75 bits, res < 155
      res = (res * closestValue) / (1e20);
    }
  }
}

File 13 of 67 : AlgebraFeeConfiguration.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @notice coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
/// @dev alpha1 + alpha2 + baseFee must be <= type(uint16).max
struct AlgebraFeeConfiguration {
  uint16 alpha1; // max value of the first sigmoid
  uint16 alpha2; // max value of the second sigmoid
  uint32 beta1; // shift along the x-axis for the first sigmoid
  uint32 beta2; // shift along the x-axis for the second sigmoid
  uint16 gamma1; // horizontal stretch factor for the first sigmoid
  uint16 gamma2; // horizontal stretch factor for the second sigmoid
  uint16 baseFee; // minimum possible fee
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import './AlgebraFeeConfiguration.sol';

type AlgebraFeeConfigurationU144 is uint144;
using AlgebraFeeConfigurationU144Lib for AlgebraFeeConfigurationU144 global;

/// @title AdaptiveFee packed configuration library
/// @notice Used to interact with uint144-packed fee config
/// @dev Structs are not packed in storage with neighboring values, but uint144 can be packed
library AlgebraFeeConfigurationU144Lib {
  uint256 private constant UINT16_MASK = 0xFFFF;
  uint256 private constant UINT32_MASK = 0xFFFFFFFF;

  // alpha1 offset is 0
  uint256 private constant ALPHA2_OFFSET = 16;
  uint256 private constant BETA1_OFFSET = 32;
  uint256 private constant BETA2_OFFSET = 64;
  uint256 private constant GAMMA1_OFFSET = 96;
  uint256 private constant GAMMA2_OFFSET = 112;
  uint256 private constant BASE_FEE_OFFSET = 128;

  function pack(AlgebraFeeConfiguration memory config) internal pure returns (AlgebraFeeConfigurationU144) {
    uint144 _config = uint144(
      (uint256(config.baseFee) << BASE_FEE_OFFSET) |
        (uint256(config.gamma2) << GAMMA2_OFFSET) |
        (uint256(config.gamma1) << GAMMA1_OFFSET) |
        (uint256(config.beta2) << BETA2_OFFSET) |
        (uint256(config.beta1) << BETA1_OFFSET) |
        (uint256(config.alpha2) << ALPHA2_OFFSET) |
        uint256(config.alpha1)
    );

    return AlgebraFeeConfigurationU144.wrap(_config);
  }

  function alpha1(AlgebraFeeConfigurationU144 config) internal pure returns (uint16 _alpha1) {
    assembly {
      _alpha1 := and(UINT16_MASK, config)
    }
  }

  function alpha2(AlgebraFeeConfigurationU144 config) internal pure returns (uint16 _alpha2) {
    assembly {
      _alpha2 := and(UINT16_MASK, shr(ALPHA2_OFFSET, config))
    }
  }

  function beta1(AlgebraFeeConfigurationU144 config) internal pure returns (uint32 _beta1) {
    assembly {
      _beta1 := and(UINT32_MASK, shr(BETA1_OFFSET, config))
    }
  }

  function beta2(AlgebraFeeConfigurationU144 config) internal pure returns (uint32 _beta2) {
    assembly {
      _beta2 := and(UINT32_MASK, shr(BETA2_OFFSET, config))
    }
  }

  function gamma1(AlgebraFeeConfigurationU144 config) internal pure returns (uint16 _gamma1) {
    assembly {
      _gamma1 := and(UINT16_MASK, shr(GAMMA1_OFFSET, config))
    }
  }

  function gamma2(AlgebraFeeConfigurationU144 config) internal pure returns (uint16 _gamma2) {
    assembly {
      _gamma2 := and(UINT16_MASK, shr(GAMMA2_OFFSET, config))
    }
  }

  function baseFee(AlgebraFeeConfigurationU144 config) internal pure returns (uint16 _baseFee) {
    assembly {
      _baseFee := and(UINT16_MASK, shr(BASE_FEE_OFFSET, config))
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';

import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';

import './interfaces/IFarmingPluginFactory.sol';
import './interfaces/IAlgebraVirtualPool.sol';
import './interfaces/IFarmingPlugin.sol';

import {BaseAbstractPlugin} from '@cryptoalgebra/abstract-plugin/contracts/BaseAbstractPlugin.sol';

/// @title Algebra Integral 1.2.1 default plugin
/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages
abstract contract FarmingProxyPlugin is BaseAbstractPlugin, IFarmingPlugin {
  using Plugins for uint8;

  /// @inheritdoc IFarmingPlugin
  address public override incentive;

  /// @dev the address which connected the last incentive. Needed so that he can disconnect it
  address private _lastIncentiveOwner;

  constructor() {
    defaultPluginConfig = defaultPluginConfig | uint8(Plugins.AFTER_SWAP_FLAG);
    
    activeModules.push("Farming Proxy Plugin");
  }

  /// @inheritdoc IFarmingPlugin
  function setIncentive(address newIncentive) external override {
    bool toConnect = newIncentive != address(0);
    bool accessAllowed;
    if (toConnect) {
      accessAllowed = msg.sender == IFarmingPluginFactory(pluginFactory).farmingAddress();
    } else {
      // we allow the one who connected the incentive to disconnect it,
      // even if he no longer has the rights to connect incentives
      if (_lastIncentiveOwner != address(0)) accessAllowed = msg.sender == _lastIncentiveOwner;
      if (!accessAllowed) accessAllowed = msg.sender == IFarmingPluginFactory(pluginFactory).farmingAddress();
    }
    require(accessAllowed, 'Not allowed to set incentive');

    bool isPluginConnected = _getPluginInPool() == address(this);
    if (toConnect) require(isPluginConnected, 'Plugin not attached');

    address currentIncentive = incentive;
    require(currentIncentive != newIncentive, 'Already active');
    if (toConnect) require(currentIncentive == address(0), 'Has active incentive');

    incentive = newIncentive;
    emit Incentive(newIncentive);

    if (toConnect) {
      _lastIncentiveOwner = msg.sender; // write creator of this incentive
    } else {
      _lastIncentiveOwner = address(0);
    }

    if (isPluginConnected) {
      _enablePluginFlags(defaultPluginConfig);
    }
  }

  /// @inheritdoc IFarmingPlugin
  function isIncentiveConnected(address targetIncentive) external view override returns (bool) {
    if (incentive != targetIncentive) return false;
    if (_getPluginInPool() != address(this)) return false;
    (, , , uint8 pluginConfig) = _getPoolState();
    if (!pluginConfig.hasFlag(Plugins.AFTER_SWAP_FLAG)) return false;

    return true;
  }

  function _updateVirtualPoolTick(bool zeroToOne) internal {
    address _incentive = incentive;
    if (_incentive != address(0)) {
      (, int24 tick, , ) = _getPoolState();
      IAlgebraVirtualPool(_incentive).crossTo(tick, zeroToOne);
    }
  }

  function getPool() external view override returns (address) {
    return pool;
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the virtual pool
/// @dev Used to calculate active liquidity in farmings
interface IAlgebraVirtualPool {
  /// @dev This function is called by the main pool if an initialized ticks are crossed by swap.
  /// If any one of crossed ticks is also initialized in a virtual pool it should be crossed too
  /// @param targetTick The target tick up to which we need to cross all active ticks
  /// @param zeroToOne Swap direction
  function crossTo(int24 targetTick, bool zeroToOne) external returns (bool success);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IAbstractPlugin.sol';

/// @title The interface for the Algebra farming plugin
/// @dev This contract used for virtual pools in farms
interface IFarmingPlugin is IAbstractPlugin {
  /// @notice Emitted when new activeIncentive is set
  /// @param newIncentive The address of the new incentive
  event Incentive(address newIncentive);

  /// @notice Connects or disconnects an incentive.
  /// @dev Only farming can connect incentives.
  /// The one who connected it and the current farming has the right to disconnect the incentive.
  /// @param newIncentive The address associated with the incentive or zero address
  function setIncentive(address newIncentive) external;

  /// @notice Checks if the incentive is connected to pool
  /// @dev Returns false if the plugin has a different incentive set, the plugin is not connected to the pool,
  /// or the plugin configuration is incorrect.
  /// @param targetIncentive The address of the incentive to be checked
  /// @return Indicates whether the target incentive is active
  function isIncentiveConnected(address targetIncentive) external view returns (bool);

  /// @notice Returns the address of active incentive
  /// @dev if there is no active incentive at the moment, incentiveAddress would be equal to address(0)
  /// @return  The address associated with the current active incentive
  function incentive() external view returns (address);

  /// @notice Returns the address of the pool the plugin is created for
  /// @return address of the pool
  function getPool() external view returns (address);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the PluginFactory which deploys FarmingProxyPlugin
/// @dev This contract contains function that must be implemented by the FarmingProxyPlugin' PluginFactory
interface IFarmingPluginFactory {
  /// @notice Returns current farming address
  /// @return The farming contract address
  function farmingAddress() external view returns (address);

  /// @dev updates farmings manager address on the factory
  /// @param newFarmingAddress The new tokenomics contract address
  function setFarmingAddress(address newFarmingAddress) external;

  /// @notice Emitted when the farming address is changed
  /// @param newFarmingAddress The farming address after the address was changed
  event FarmingAddress(address newFarmingAddress);
}

File 19 of 67 : Timestamp.sol
// 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
  }
}

// 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 this method _must_ be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
  /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity
  /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity
  /// @param data Any data passed through by the caller via the IAlgebraPoolActions#mint call
  function algebraMintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import './plugin/IAlgebraPluginFactory.sol';
import './vault/IAlgebraVaultFactory.sol';

/// @title The interface for the Algebra Factory
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraFactory {
  /// @notice Emitted when a process of ownership renounce is started
  /// @param timestamp The timestamp of event
  /// @param finishTimestamp The timestamp when ownership renounce will be possible to finish
  event RenounceOwnershipStart(uint256 timestamp, uint256 finishTimestamp);

  /// @notice Emitted when a process of ownership renounce cancelled
  /// @param timestamp The timestamp of event
  event RenounceOwnershipStop(uint256 timestamp);

  /// @notice Emitted when a process of ownership renounce finished
  /// @param timestamp The timestamp of ownership renouncement
  event RenounceOwnershipFinish(uint256 timestamp);

  /// @notice Emitted when a pool is created
  /// @param token0 The first token of the pool by address sort order
  /// @param token1 The second token of the pool by address sort order
  /// @param pool The address of the created pool
  event Pool(address indexed token0, address indexed token1, address pool);

  /// @notice Emitted when a pool is created
  /// @param deployer The corresponding custom deployer contract
  /// @param token0 The first token of the pool by address sort order
  /// @param token1 The second token of the pool by address sort order
  /// @param pool The address of the created pool
  event CustomPool(address indexed deployer, address indexed token0, address indexed token1, address pool);

  /// @notice Emitted when the default community fee is changed
  /// @param newDefaultCommunityFee The new default community fee value
  event DefaultCommunityFee(uint16 newDefaultCommunityFee);

  /// @notice Emitted when the default tickspacing is changed
  /// @param newDefaultTickspacing The new default tickspacing value
  event DefaultTickspacing(int24 newDefaultTickspacing);

  /// @notice Emitted when the default fee is changed
  /// @param newDefaultFee The new default fee value
  event DefaultFee(uint16 newDefaultFee);

  /// @notice Emitted when the defaultPluginFactory address is changed
  /// @param defaultPluginFactoryAddress The new defaultPluginFactory address
  event DefaultPluginFactory(address defaultPluginFactoryAddress);

  /// @notice Emitted when the vaultFactory address is changed
  /// @param newVaultFactory The new vaultFactory address
  event VaultFactory(address newVaultFactory);

  /// @notice role that can change communityFee and tickspacing in pools
  /// @return The hash corresponding to this role
  function POOLS_ADMINISTRATOR_ROLE() external view returns (bytes32);

  /// @notice role that can call `createCustomPool` function
  /// @return The hash corresponding to this role
  function CUSTOM_POOL_DEPLOYER() external view returns (bytes32);

  /// @notice Returns `true` if `account` has been granted `role` or `account` is owner.
  /// @param role The hash corresponding to the role
  /// @param account The address for which the role is checked
  /// @return bool Whether the address has this role or the owner role or not
  function hasRoleOrOwner(bytes32 role, address account) external view returns (bool);

  /// @notice Returns the current owner of the factory
  /// @dev Can be changed by the current owner via transferOwnership(address newOwner)
  /// @return The address of the factory owner
  function owner() external view returns (address);

  /// @notice Returns the current poolDeployerAddress
  /// @return The address of the poolDeployer
  function poolDeployer() external view returns (address);

  /// @notice Returns the default community fee
  /// @return Fee which will be set at the creation of the pool
  function defaultCommunityFee() external view returns (uint16);

  /// @notice Returns the default fee
  /// @return Fee which will be set at the creation of the pool
  function defaultFee() external view returns (uint16);

  /// @notice Returns the default tickspacing
  /// @return Tickspacing which will be set at the creation of the pool
  function defaultTickspacing() external view returns (int24);

  /// @notice Return the current pluginFactory address
  /// @dev This contract is used to automatically set a plugin address in new liquidity pools
  /// @return Algebra plugin factory
  function defaultPluginFactory() external view returns (IAlgebraPluginFactory);

  /// @notice Return the current vaultFactory address
  /// @dev This contract is used to automatically set a vault address in new liquidity pools
  /// @return Algebra vault factory
  function vaultFactory() external view returns (IAlgebraVaultFactory);

  /// @notice Returns the default communityFee, tickspacing, fee and communityFeeVault for pool
  /// @return communityFee which will be set at the creation of the pool
  /// @return tickSpacing which will be set at the creation of the pool
  /// @return fee which will be set at the creation of the pool
  function defaultConfigurationForPool() external view returns (uint16 communityFee, int24 tickSpacing, uint16 fee);

  /// @notice Deterministically computes the pool address given the token0 and token1
  /// @dev The method does not check if such a pool has been created
  /// @param token0 first token
  /// @param token1 second token
  /// @return pool The contract address of the Algebra pool
  function computePoolAddress(address token0, address token1) external view returns (address pool);

  /// @notice Deterministically computes the custom pool address given the customDeployer, token0 and token1
  /// @dev The method does not check if such a pool has been created
  /// @param customDeployer the address of custom plugin deployer
  /// @param token0 first token
  /// @param token1 second token
  /// @return customPool The contract address of the Algebra pool
  function computeCustomPoolAddress(address customDeployer, address token0, address token1) external view returns (address customPool);

  /// @notice Returns the pool address for a given pair of tokens, or address 0 if it does not exist
  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
  /// @param tokenA The contract address of either token0 or token1
  /// @param tokenB The contract address of the other token
  /// @return pool The pool address
  function poolByPair(address tokenA, address tokenB) external view returns (address pool);

  /// @notice Returns the custom pool address for a customDeployer and a given pair of tokens, or address 0 if it does not exist
  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
  /// @param customDeployer The address of custom plugin deployer
  /// @param tokenA The contract address of either token0 or token1
  /// @param tokenB The contract address of the other token
  /// @return customPool The pool address
  function customPoolByPair(address customDeployer, address tokenA, address tokenB) external view returns (address customPool);

  /// @notice returns keccak256 of AlgebraPool init bytecode.
  /// @dev the hash value changes with any change in the pool bytecode
  /// @return Keccak256 hash of AlgebraPool contract init bytecode
  function POOL_INIT_CODE_HASH() external view returns (bytes32);

  /// @return timestamp The timestamp of the beginning of the renounceOwnership process
  function renounceOwnershipStartTimestamp() external view returns (uint256 timestamp);

  /// @notice Creates a pool for the given two tokens
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @param data Data for plugin creation
  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
  /// The call will revert if the pool already exists or the token arguments are invalid.
  /// @return pool The address of the newly created pool
  function createPool(address tokenA, address tokenB, bytes calldata data) external returns (address pool);

  /// @notice Creates a custom pool for the given two tokens using `deployer` contract
  /// @param deployer The address of plugin deployer, also used for custom pool address calculation
  /// @param creator The initiator of custom pool creation
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @param data The additional data bytes
  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
  /// The call will revert if the pool already exists or the token arguments are invalid.
  /// @return customPool The address of the newly created custom pool
  function createCustomPool(
    address deployer,
    address creator,
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external returns (address customPool);

  /// @dev updates default community fee for new pools
  /// @param newDefaultCommunityFee The new community fee, _must_ be <= MAX_COMMUNITY_FEE
  function setDefaultCommunityFee(uint16 newDefaultCommunityFee) external;

  /// @dev updates default fee for new pools
  /// @param newDefaultFee The new  fee, _must_ be <= MAX_DEFAULT_FEE
  function setDefaultFee(uint16 newDefaultFee) external;

  /// @dev updates default tickspacing for new pools
  /// @param newDefaultTickspacing The new tickspacing, _must_ be <= MAX_TICK_SPACING and >= MIN_TICK_SPACING
  function setDefaultTickspacing(int24 newDefaultTickspacing) external;

  /// @dev updates pluginFactory address
  /// @param newDefaultPluginFactory address of new plugin factory
  function setDefaultPluginFactory(address newDefaultPluginFactory) external;

  /// @dev updates vaultFactory address
  /// @param newVaultFactory address of new vault factory
  function setVaultFactory(address newVaultFactory) external;

  /// @notice Starts process of renounceOwnership. After that, a certain period
  /// of time must pass before the ownership renounce can be completed.
  function startRenounceOwnership() external;

  /// @notice Stops process of renounceOwnership and removes timer.
  function stopRenounceOwnership() external;
}

File 22 of 67 : IAlgebraPool.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;

import './pool/IAlgebraPoolImmutables.sol';
import './pool/IAlgebraPoolState.sol';
import './pool/IAlgebraPoolActions.sol';
import './pool/IAlgebraPoolPermissionedActions.sol';
import './pool/IAlgebraPoolEvents.sol';
import './pool/IAlgebraPoolErrors.sol';

/// @title The interface for a Algebra Pool
/// @dev The pool interface is broken up into many smaller pieces.
/// This interface includes custom error definitions and cannot be used in older versions of Solidity.
/// For older versions of Solidity use #IAlgebraPoolLegacy
/// Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPool is
  IAlgebraPoolImmutables,
  IAlgebraPoolState,
  IAlgebraPoolActions,
  IAlgebraPoolPermissionedActions,
  IAlgebraPoolEvents,
  IAlgebraPoolErrors
{
  // used only for combining interfaces
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Minimal ERC20 interface for Algebra
/// @notice Contains a subset of the full ERC20 interface that is used in Algebra
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IERC20Minimal {
  /// @notice Returns the balance of a token
  /// @param account The account for which to look up the number of tokens it has, i.e. its balance
  /// @return The number of tokens held by the account
  function balanceOf(address account) external view returns (uint256);

  /// @notice Transfers the amount of token from the `msg.sender` to the recipient
  /// @param recipient The account that will receive the amount transferred
  /// @param amount The number of tokens to send from the sender to the recipient
  /// @return Returns true for a successful transfer, false for an unsuccessful transfer
  function transfer(address recipient, uint256 amount) external returns (bool);

  /// @notice Returns the current allowance given to a spender by an owner
  /// @param owner The account of the token owner
  /// @param spender The account of the token spender
  /// @return The current allowance granted by `owner` to `spender`
  function allowance(address owner, address spender) external view returns (uint256);

  /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
  /// @param spender The account which will be allowed to spend a given amount of the owners tokens
  /// @param amount The amount of tokens allowed to be used by `spender`
  /// @return Returns true for a successful approval, false for unsuccessful
  function approve(address spender, uint256 amount) external returns (bool);

  /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
  /// @param sender The account from which the transfer will be initiated
  /// @param recipient The recipient of the transfer
  /// @param amount The amount of the transfer
  /// @return Returns true for a successful transfer, false for unsuccessful
  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

  /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
  /// @param from The account from which the tokens were sent, i.e. the balance decreased
  /// @param to The account to which the tokens were sent, i.e. the balance increased
  /// @param value The amount of tokens that were transferred
  event Transfer(address indexed from, address indexed to, uint256 value);

  /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
  /// @param owner The account that approved spending of its tokens
  /// @param spender The account for which the spending allowance was modified
  /// @param value The new allowance from the owner to the spender
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 24 of 67 : IAlgebraDynamicFeePlugin.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Algebra plugin with dynamic fee logic
/// @dev A plugin with a dynamic fee must implement this interface so that the current fee can be known through the pool
/// If the dynamic fee logic does not allow the fee to be calculated without additional data, the method should revert with the appropriate message
interface IAlgebraDynamicFeePlugin {
  /// @notice Returns fee from plugin
  /// @return fee The pool fee value in hundredths of a bip, i.e. 1e-6
  function getCurrentFee() external view returns (uint16 fee);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The Algebra plugin interface
/// @dev The plugin will be called by the pool using hook methods depending on the current pool settings
interface IAlgebraPlugin {
  /// @notice Returns plugin config
  /// @return config Each bit of the config is responsible for enabling/disabling the hooks.
  /// The last bit indicates whether the plugin contains dynamic fees logic
  function defaultPluginConfig() external view returns (uint8);

  /// @notice Handle plugin fee transfer on plugin contract
  /// @param pluginFee0 Fee0 amount transferred to plugin
  /// @param pluginFee1 Fee1 amount transferred to plugin
  /// @return bytes4 The function selector
  function handlePluginFee(uint256 pluginFee0, uint256 pluginFee1) external returns (bytes4);

  /// @notice The hook called before the state of a pool is initialized
  /// @param sender The initial msg.sender for the initialize call
  /// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
  /// @return bytes4 The function selector for the hook
  function beforeInitialize(address sender, uint160 sqrtPriceX96) external returns (bytes4);

  /// @notice The hook called after the state of a pool is initialized
  /// @param sender The initial msg.sender for the initialize call
  /// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
  /// @param tick The current tick after the state of a pool is initialized
  /// @return bytes4 The function selector for the hook
  function afterInitialize(address sender, uint160 sqrtPriceX96, int24 tick) external returns (bytes4);

  /// @notice The hook called before a position is modified
  /// @param sender The initial msg.sender for the modify position call
  /// @param recipient Address to which the liquidity will be assigned in case of a mint or
  /// to which tokens will be sent in case of a burn
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param desiredLiquidityDelta The desired amount of liquidity to mint/burn
  /// @param data Data that passed through the callback
  /// @return selector The function selector for the hook
  function beforeModifyPosition(
    address sender,
    address recipient,
    int24 bottomTick,
    int24 topTick,
    int128 desiredLiquidityDelta,
    bytes calldata data
  ) external returns (bytes4 selector, uint24 pluginFee);

  /// @notice The hook called after a position is modified
  /// @param sender The initial msg.sender for the modify position call
  /// @param recipient Address to which the liquidity will be assigned in case of a mint or
  /// to which tokens will be sent in case of a burn
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param desiredLiquidityDelta The desired amount of liquidity to mint/burn
  /// @param amount0 The amount of token0 sent to the recipient or was paid to mint
  /// @param amount1 The amount of token0 sent to the recipient or was paid to mint
  /// @param data Data that passed through the callback
  /// @return bytes4 The function selector for the hook
  function afterModifyPosition(
    address sender,
    address recipient,
    int24 bottomTick,
    int24 topTick,
    int128 desiredLiquidityDelta,
    uint256 amount0,
    uint256 amount1,
    bytes calldata data
  ) external returns (bytes4);

  /// @notice The hook called before a swap
  /// @param sender The initial msg.sender for the swap call
  /// @param recipient The address to receive the output of the swap
  /// @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
  /// @param amountRequired The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
  /// @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
  /// @param withPaymentInAdvance The flag indicating whether the `swapWithPaymentInAdvance` method was called
  /// @param data Data that passed through the callback
  /// @return selector The function selector for the hook
  function beforeSwap(
    address sender,
    address recipient,
    bool zeroToOne,
    int256 amountRequired,
    uint160 limitSqrtPrice,
    bool withPaymentInAdvance,
    bytes calldata data
  ) external returns (bytes4 selector, uint24 feeOverride, uint24 pluginFee);

  /// @notice The hook called after a swap
  /// @param sender The initial msg.sender for the swap call
  /// @param recipient The address to receive the output of the swap
  /// @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
  /// @param amountRequired The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
  /// @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
  /// @param amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
  /// @param amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
  /// @param data Data that passed through the callback
  /// @return bytes4 The function selector for the hook
  function afterSwap(
    address sender,
    address recipient,
    bool zeroToOne,
    int256 amountRequired,
    uint160 limitSqrtPrice,
    int256 amount0,
    int256 amount1,
    bytes calldata data
  ) external returns (bytes4);

  /// @notice The hook called before flash
  /// @param sender The initial msg.sender for the flash call
  /// @param recipient The address which will receive the token0 and token1 amounts
  /// @param amount0 The amount of token0 being requested for flash
  /// @param amount1 The amount of token1 being requested for flash
  /// @param data Data that passed through the callback
  /// @return bytes4 The function selector for the hook
  function beforeFlash(address sender, address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external returns (bytes4);

  /// @notice The hook called after flash
  /// @param sender The initial msg.sender for the flash call
  /// @param recipient The address which will receive the token0 and token1 amounts
  /// @param amount0 The amount of token0 being requested for flash
  /// @param amount1 The amount of token1 being requested for flash
  /// @param paid0 The amount of token0 being paid for flash
  /// @param paid1 The amount of token1 being paid for flash
  /// @param data Data that passed through the callback
  /// @return bytes4 The function selector for the hook
  function afterFlash(
    address sender,
    address recipient,
    uint256 amount0,
    uint256 amount1,
    uint256 paid0,
    uint256 paid1,
    bytes calldata data
  ) external returns (bytes4);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title An interface for a contract that is capable of deploying Algebra plugins
/// @dev Such a factory can be used for automatic plugin creation for new pools.
/// Also a factory be used as an entry point for custom (additional) pools creation
interface IAlgebraPluginFactory {
  /// @notice Deploys new plugin contract for pool
  /// @param pool The address of the new pool
  /// @param creator The address that initiated the pool creation
  /// @param deployer The address of new plugin deployer contract (0 if not used)
  /// @param token0 First token of the pool
  /// @param token1 Second token of the pool
  /// @return New plugin address
  function beforeCreatePoolHook(
    address pool,
    address creator,
    address deployer,
    address token0,
    address token1,
    bytes calldata data
  ) external returns (address);

  /// @notice Called after the pool is created
  /// @param plugin The plugin address
  /// @param pool The address of the new pool
  /// @param deployer The address of new plugin deployer contract (0 if not used)
  function afterCreatePoolHook(address plugin, address pool, address deployer) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissionless pool actions
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolActions {
  /// @notice Sets the initial price for the pool
  /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
  /// @dev Initialization should be done in one transaction with pool creation to avoid front-running
  /// @param initialPrice The initial sqrt price of the pool as a Q64.96
  function initialize(uint160 initialPrice) external;

  /// @notice Adds liquidity for the given recipient/bottomTick/topTick position
  /// @dev The caller of this method receives a callback in the form of IAlgebraMintCallback#algebraMintCallback
  /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
  /// on bottomTick, topTick, the amount of liquidity, and the current price.
  /// @param leftoversRecipient The address which will receive potential surplus of paid tokens
  /// @param recipient The address for which the liquidity will be created
  /// @param bottomTick The lower tick of the position in which to add liquidity
  /// @param topTick The upper tick of the position in which to add liquidity
  /// @param liquidityDesired The desired amount of liquidity to mint
  /// @param data Any data that should be passed through to the callback
  /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
  /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
  /// @return liquidityActual The actual minted amount of liquidity
  function mint(
    address leftoversRecipient,
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 liquidityDesired,
    bytes calldata data
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityActual);

  /// @notice Collects tokens owed to a position
  /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
  /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
  /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
  /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
  /// @param recipient The address which should receive the fees collected
  /// @param bottomTick The lower tick of the position for which to collect fees
  /// @param topTick The upper tick of the position for which to collect fees
  /// @param amount0Requested How much token0 should be withdrawn from the fees owed
  /// @param amount1Requested How much token1 should be withdrawn from the fees owed
  /// @return amount0 The amount of fees collected in token0
  /// @return amount1 The amount of fees collected in token1
  function collect(
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 amount0Requested,
    uint128 amount1Requested
  ) external returns (uint128 amount0, uint128 amount1);

  /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
  /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
  /// @dev Fees must be collected separately via a call to #collect
  /// @param bottomTick The lower tick of the position for which to burn liquidity
  /// @param topTick The upper tick of the position for which to burn liquidity
  /// @param amount How much liquidity to burn
  /// @param data Any data that should be passed through to the plugin
  /// @return amount0 The amount of token0 sent to the recipient
  /// @return amount1 The amount of token1 sent to the recipient
  function burn(int24 bottomTick, int24 topTick, uint128 amount, bytes calldata data) external returns (uint256 amount0, uint256 amount1);

  /// @notice Swap token0 for token1, or token1 for token0
  /// @dev The caller of this method receives a callback in the form of IAlgebraSwapCallback#algebraSwapCallback
  /// @param recipient The address to receive the output of the swap
  /// @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
  /// @param amountRequired The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
  /// @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
  /// @param data Any data to be passed through to the callback. If using the Router it should contain SwapRouter#SwapCallbackData
  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
  function swap(
    address recipient,
    bool zeroToOne,
    int256 amountRequired,
    uint160 limitSqrtPrice,
    bytes calldata data
  ) external returns (int256 amount0, int256 amount1);

  /// @notice Swap token0 for token1, or token1 for token0 with prepayment
  /// @dev The caller of this method receives a callback in the form of IAlgebraSwapCallback#algebraSwapCallback
  /// caller must send tokens in callback before swap calculation
  /// the actually sent amount of tokens is used for further calculations
  /// @param leftoversRecipient The address which will receive potential surplus of paid tokens
  /// @param recipient The address to receive the output of the swap
  /// @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
  /// @param amountToSell The amount of the swap, only positive (exact input) amount allowed
  /// @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
  /// @param data Any data to be passed through to the callback. If using the Router it should contain SwapRouter#SwapCallbackData
  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
  function swapWithPaymentInAdvance(
    address leftoversRecipient,
    address recipient,
    bool zeroToOne,
    int256 amountToSell,
    uint160 limitSqrtPrice,
    bytes calldata data
  ) external returns (int256 amount0, int256 amount1);

  /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
  /// @dev The caller of this method receives a callback in the form of IAlgebraFlashCallback#algebraFlashCallback
  /// @dev All excess tokens paid in the callback are distributed to currently in-range liquidity providers as an additional fee.
  /// If there are no in-range liquidity providers, the fee will be transferred to the first active provider in the future
  /// @param recipient The address which will receive the token0 and token1 amounts
  /// @param amount0 The amount of token0 to send
  /// @param amount1 The amount of token1 to send
  /// @param data Any data to be passed through to the callback
  function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;
}

File 28 of 67 : IAlgebraPoolErrors.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;

/// @title Errors emitted by a pool
/// @notice Contains custom errors emitted by the pool
/// @dev Custom errors are separated from the common pool interface for compatibility with older versions of Solidity
interface IAlgebraPoolErrors {
  // ####  pool errors  ####

  /// @notice Emitted by the reentrancy guard
  error locked();

  /// @notice Emitted if arithmetic error occurred
  error arithmeticError();

  /// @notice Emitted if an attempt is made to initialize the pool twice
  error alreadyInitialized();

  /// @notice Emitted if an attempt is made to mint or swap in uninitialized pool
  error notInitialized();

  /// @notice Emitted if 0 is passed as amountRequired to swap function
  error zeroAmountRequired();

  /// @notice Emitted if invalid amount is passed as amountRequired to swap function
  error invalidAmountRequired();

  /// @notice Emitted if plugin fee param greater than fee/override fee
  error incorrectPluginFee();

  /// @notice Emitted if the pool received fewer tokens than it should have
  error insufficientInputAmount();

  /// @notice Emitted if there was an attempt to mint zero liquidity
  error zeroLiquidityDesired();
  /// @notice Emitted if actual amount of liquidity is zero (due to insufficient amount of tokens received)
  error zeroLiquidityActual();

  /// @notice Emitted if the pool received fewer tokens0 after flash than it should have
  error flashInsufficientPaid0();
  /// @notice Emitted if the pool received fewer tokens1 after flash than it should have
  error flashInsufficientPaid1();

  /// @notice Emitted if limitSqrtPrice param is incorrect
  error invalidLimitSqrtPrice();

  /// @notice Tick must be divisible by tickspacing
  error tickIsNotSpaced();

  /// @notice Emitted if a method is called that is accessible only to the factory owner or dedicated role
  error notAllowed();

  /// @notice Emitted if new tick spacing exceeds max allowed value
  error invalidNewTickSpacing();
  /// @notice Emitted if new community fee exceeds max allowed value
  error invalidNewCommunityFee();

  /// @notice Emitted if an attempt is made to manually change the fee value, but dynamic fee is enabled
  error dynamicFeeActive();
  /// @notice Emitted if an attempt is made by plugin to change the fee value, but dynamic fee is disabled
  error dynamicFeeDisabled();
  /// @notice Emitted if an attempt is made to change the plugin configuration, but the plugin is not connected
  error pluginIsNotConnected();
  /// @notice Emitted if a plugin returns invalid selector after hook call
  /// @param expectedSelector The expected selector
  error invalidHookResponse(bytes4 expectedSelector);

  // ####  LiquidityMath errors  ####

  /// @notice Emitted if liquidity underflows
  error liquiditySub();
  /// @notice Emitted if liquidity overflows
  error liquidityAdd();

  // ####  TickManagement errors  ####

  /// @notice Emitted if the topTick param not greater then the bottomTick param
  error topTickLowerOrEqBottomTick();
  /// @notice Emitted if the bottomTick param is lower than min allowed value
  error bottomTickLowerThanMIN();
  /// @notice Emitted if the topTick param is greater than max allowed value
  error topTickAboveMAX();
  /// @notice Emitted if the liquidity value associated with the tick exceeds MAX_LIQUIDITY_PER_TICK
  error liquidityOverflow();
  /// @notice Emitted if an attempt is made to interact with an uninitialized tick
  error tickIsNotInitialized();
  /// @notice Emitted if there is an attempt to insert a new tick into the list of ticks with incorrect indexes of the previous and next ticks
  error tickInvalidLinks();

  // ####  SafeTransfer errors  ####

  /// @notice Emitted if token transfer failed internally
  error transferFailed();

  // ####  TickMath errors  ####

  /// @notice Emitted if tick is greater than the maximum or less than the minimum allowed value
  error tickOutOfRange();
  /// @notice Emitted if price is greater than the maximum or less than the minimum allowed value
  error priceOutOfRange();
}

File 29 of 67 : IAlgebraPoolEvents.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Events emitted by a pool
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolEvents {
  /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
  /// @dev Mint/Burn/Swaps cannot be emitted by the pool before Initialize
  /// @param price The initial sqrt price of the pool, as a Q64.96
  /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
  event Initialize(uint160 price, int24 tick);

  /// @notice Emitted when liquidity is minted for a given position
  /// @param sender The address that minted the liquidity
  /// @param owner The owner of the position and recipient of any minted liquidity
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param liquidityAmount The amount of liquidity minted to the position range
  /// @param amount0 How much token0 was required for the minted liquidity
  /// @param amount1 How much token1 was required for the minted liquidity
  event Mint(
    address sender,
    address indexed owner,
    int24 indexed bottomTick,
    int24 indexed topTick,
    uint128 liquidityAmount,
    uint256 amount0,
    uint256 amount1
  );

  /// @notice Emitted when fees are collected by the owner of a position
  /// @param owner The owner of the position for which fees are collected
  /// @param recipient The address that received fees
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param amount0 The amount of token0 fees collected
  /// @param amount1 The amount of token1 fees collected
  event Collect(address indexed owner, address recipient, int24 indexed bottomTick, int24 indexed topTick, uint128 amount0, uint128 amount1);

  /// @notice Emitted when a position's liquidity is removed
  /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
  /// @param owner The owner of the position for which liquidity is removed
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param liquidityAmount The amount of liquidity to remove
  /// @param amount0 The amount of token0 withdrawn
  /// @param amount1 The amount of token1 withdrawn
  event Burn(
    address indexed owner,
    int24 indexed bottomTick,
    int24 indexed topTick,
    uint128 liquidityAmount,
    uint256 amount0,
    uint256 amount1
  );

  /// @notice Emitted when a plugin fee is applied during a burn
  /// @param owner The owner of the position
  /// @param pluginFee The fee to be sent to the plugin
  event BurnFee(address indexed owner, uint24 pluginFee); 

  /// @notice Emitted by the pool for any swaps between token0 and token1
  /// @param sender The address that initiated the swap call, and that received the callback
  /// @param recipient The address that received the output of the swap
  /// @param amount0 The delta of the token0 balance of the pool
  /// @param amount1 The delta of the token1 balance of the pool
  /// @param price The sqrt(price) of the pool after the swap, as a Q64.96
  /// @param liquidity The liquidity of the pool after the swap
  /// @param tick The log base 1.0001 of price of the pool after the swap

  event Swap(
    address indexed sender,
    address indexed recipient,
    int256 amount0,
    int256 amount1,
    uint160 price,
    uint128 liquidity,
    int24 tick
  );

  /// @notice Emitted by the pool after any swaps 
  /// @param sender The address that initiated the swap 
  /// @param overrideFee The fee to be applied to the trade
  /// @param pluginFee The fee to be sent to the plugin
  event SwapFee(address indexed sender, uint24 overrideFee, uint24 pluginFee);

  /// @notice Emitted by the pool for any flashes of token0/token1
  /// @param sender The address that initiated the swap call, and that received the callback
  /// @param recipient The address that received the tokens from flash
  /// @param amount0 The amount of token0 that was flashed
  /// @param amount1 The amount of token1 that was flashed
  /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
  /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
  event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1);

  /// @notice Emitted when the pool has higher balances than expected.
  /// Any excess of tokens will be distributed between liquidity providers as fee.
  /// @dev Fees after flash also will trigger this event due to mechanics of flash.
  /// @param amount0 The excess of token0
  /// @param amount1 The excess of token1
  event ExcessTokens(uint256 amount0, uint256 amount1);

  /// @notice Emitted when the community fee is changed by the pool
  /// @param communityFeeNew The updated value of the community fee in thousandths (1e-3)
  event CommunityFee(uint16 communityFeeNew);

  /// @notice Emitted when the tick spacing changes
  /// @param newTickSpacing The updated value of the new tick spacing
  event TickSpacing(int24 newTickSpacing);

  /// @notice Emitted when the plugin address changes
  /// @param newPluginAddress New plugin address
  event Plugin(address newPluginAddress);

  /// @notice Emitted when the plugin config changes
  /// @param newPluginConfig New plugin config
  event PluginConfig(uint8 newPluginConfig);

  /// @notice Emitted when the fee changes inside the pool
  /// @param fee The current fee in hundredths of a bip, i.e. 1e-6
  event Fee(uint16 fee);

  /// @notice Emitted when the community vault address changes
  /// @param newCommunityVault New community vault
  event CommunityVault(address newCommunityVault);

  /// @notice Emitted when the plugin does skim the excess of tokens
  /// @param to THe receiver of tokens (plugin)
  /// @param amount0 The amount of token0
  /// @param amount1 The amount of token1
  event Skim(address indexed to, uint256 amount0, uint256 amount1);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolImmutables {
  /// @notice The Algebra factory contract, which must adhere to the IAlgebraFactory interface
  /// @return The contract address
  function factory() external view returns (address);

  /// @notice The first of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token0() external view returns (address);

  /// @notice The second of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token1() external view returns (address);

  /// @notice The maximum amount of position liquidity that can use any tick in the range
  /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
  /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
  /// @return The max amount of liquidity per tick
  function maxLiquidityPerTick() external view returns (uint128);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by permissioned addresses
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolPermissionedActions {
  /// @notice Set the community's % share of the fees. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newCommunityFee The new community fee percent in thousandths (1e-3)
  function setCommunityFee(uint16 newCommunityFee) external;

  /// @notice Set the new tick spacing values. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newTickSpacing The new tick spacing value
  function setTickSpacing(int24 newTickSpacing) external;

  /// @notice Set the new plugin address. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newPluginAddress The new plugin address
  function setPlugin(address newPluginAddress) external;

  /// @notice Set new plugin config. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newConfig In the new configuration of the plugin,
  /// each bit of which is responsible for a particular hook.
  function setPluginConfig(uint8 newConfig) external;

  /// @notice Set new community fee vault address. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @dev Community fee vault receives collected community fees.
  /// **accumulated but not yet sent to the vault community fees once will be sent to the `newCommunityVault` address**
  /// @param newCommunityVault The address of new community fee vault
  function setCommunityVault(address newCommunityVault) external;

  /// @notice Set new pool fee. Can be called by owner if dynamic fee is disabled.
  /// Called by the plugin if dynamic fee is enabled
  /// @param newFee The new fee value
  function setFee(uint16 newFee) external;

  /// @notice Forces balances to match reserves. Excessive tokens will be distributed between active LPs
  /// @dev Only plugin can call this function
  function sync() external;

  /// @notice Forces balances to match reserves. Excessive tokens will be sent to msg.sender
  /// @dev Only plugin can call this function
  function skim() external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that can change
/// @dev Important security note: when using this data by external contracts, it is necessary to take into account the possibility
/// of manipulation (including read-only reentrancy).
/// This interface is based on the UniswapV3 interface, credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolState {
  /// @notice Safely get most important state values of Algebra Integral AMM
  /// @dev Several values exposed as a single method to save gas when accessed externally.
  /// **Important security note: this method checks reentrancy lock and should be preferred in most cases**.
  /// @return sqrtPrice The current price of the pool as a sqrt(dToken1/dToken0) Q64.96 value
  /// @return tick The current global tick of the pool. May not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick boundary
  /// @return lastFee The current (last known) pool fee value in hundredths of a bip, i.e. 1e-6 (so '100' is '0.01%'). May be obsolete if using dynamic fee plugin
  /// @return 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
  /// @return activeLiquidity  The currently in-range liquidity available to the pool
  /// @return nextTick The next initialized tick after current global tick
  /// @return previousTick The previous initialized tick before (or at) current global tick
  function safelyGetStateOfAMM()
    external
    view
    returns (uint160 sqrtPrice, int24 tick, uint16 lastFee, uint8 pluginConfig, uint128 activeLiquidity, int24 nextTick, int24 previousTick);

  /// @notice Allows to easily get current reentrancy lock status
  /// @dev can be used to prevent read-only reentrancy.
  /// This method just returns `globalState.unlocked` value
  /// @return unlocked Reentrancy lock flag, true if the pool currently is unlocked, otherwise - false
  function isUnlocked() external view returns (bool unlocked);

  // ! IMPORTANT security note: the pool state can be manipulated.
  // ! The following methods do not check reentrancy lock themselves.

  /// @notice The globalState structure in the pool stores many values but requires only one slot
  /// and is exposed as a single method to save gas when accessed externally.
  /// @dev **important security note: caller should check `unlocked` flag to prevent read-only reentrancy**
  /// @return price The current price of the pool as a sqrt(dToken1/dToken0) Q64.96 value
  /// @return tick The current tick of the pool, i.e. according to the last tick transition that was run
  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick boundary
  /// @return lastFee The current (last known) pool fee value in hundredths of a bip, i.e. 1e-6 (so '100' is '0.01%'). May be obsolete if using dynamic fee plugin
  /// @return 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
  /// @return communityFee The community fee represented as a percent of all collected fee in thousandths, i.e. 1e-3 (so 100 is 10%)
  /// @return unlocked Reentrancy lock flag, true if the pool currently is unlocked, otherwise - false
  function globalState() external view returns (uint160 price, int24 tick, uint16 lastFee, uint8 pluginConfig, uint16 communityFee, bool unlocked);

  /// @notice Look up information about a specific tick in the pool
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @param tick The tick to look up
  /// @return liquidityTotal The total amount of position liquidity that uses the pool either as tick lower or tick upper
  /// @return liquidityDelta How much liquidity changes when the pool price crosses the tick
  /// @return prevTick The previous tick in tick list
  /// @return nextTick The next tick in tick list
  /// @return outerFeeGrowth0Token The fee growth on the other side of the tick from the current tick in token0
  /// @return outerFeeGrowth1Token The fee growth on the other side of the tick from the current tick in token1
  /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
  /// a specific position.
  function ticks(
    int24 tick
  )
    external
    view
    returns (
      uint256 liquidityTotal,
      int128 liquidityDelta,
      int24 prevTick,
      int24 nextTick,
      uint256 outerFeeGrowth0Token,
      uint256 outerFeeGrowth1Token
    );

  /// @notice The timestamp of the last sending of tokens to vault/plugin
  /// @return The timestamp truncated to 32 bits
  function lastFeeTransferTimestamp() external view returns (uint32);

  /// @notice The amounts of token0 and token1 that will be sent to the vault
  /// @dev Will be sent FEE_TRANSFER_FREQUENCY after communityFeeLastTimestamp
  /// @return communityFeePending0 The amount of token0 that will be sent to the vault
  /// @return communityFeePending1 The amount of token1 that will be sent to the vault
  function getCommunityFeePending() external view returns (uint128 communityFeePending0, uint128 communityFeePending1);

  /// @notice The amounts of token0 and token1 that will be sent to the plugin
  /// @dev Will be sent FEE_TRANSFER_FREQUENCY after feeLastTransferTimestamp
  /// @return pluginFeePending0 The amount of token0 that will be sent to the plugin
  /// @return pluginFeePending1 The amount of token1 that will be sent to the plugin
  function getPluginFeePending() external view returns (uint128 pluginFeePending0, uint128 pluginFeePending1);

  /// @notice Returns the address of currently used plugin
  /// @dev The plugin is subject to change
  /// @return pluginAddress The address of currently used plugin
  function plugin() external view returns (address pluginAddress);

  /// @notice The contract to which community fees are transferred
  /// @return communityVaultAddress The communityVault address
  function communityVault() external view returns (address communityVaultAddress);

  /// @notice Returns 256 packed tick initialized boolean values. See TickTree for more information
  /// @param wordPosition Index of 256-bits word with ticks
  /// @return The 256-bits word with packed ticks info
  function tickTable(int16 wordPosition) external view returns (uint256);

  /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  /// @return The fee growth accumulator for token0
  function totalFeeGrowth0Token() external view returns (uint256);

  /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  /// @return The fee growth accumulator for token1
  function totalFeeGrowth1Token() external view returns (uint256);

  /// @notice The current pool fee value
  /// @dev In case dynamic fee is enabled in the pool, this method will call the plugin to get the current fee.
  /// If the plugin implements complex fee logic, this method may return an incorrect value or revert.
  /// In this case, see the plugin implementation and related documentation.
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return currentFee The current pool fee value in hundredths of a bip, i.e. 1e-6
  function fee() external view returns (uint16 currentFee);

  /// @notice The tracked token0 and token1 reserves of pool
  /// @dev If at any time the real balance is larger, the excess will be transferred to liquidity providers as additional fee.
  /// If the balance exceeds uint128, the excess will be sent to the communityVault.
  /// @return reserve0 The last known reserve of token0
  /// @return reserve1 The last known reserve of token1
  function getReserves() external view returns (uint128 reserve0, uint128 reserve1);

  /// @notice Returns the information about a position by the position's key
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @param key The position's key is a packed concatenation of the owner address, bottomTick and topTick indexes
  /// @return liquidity The amount of liquidity in the position
  /// @return innerFeeGrowth0Token Fee growth of token0 inside the tick range as of the last mint/burn/poke
  /// @return innerFeeGrowth1Token Fee growth of token1 inside the tick range as of the last mint/burn/poke
  /// @return fees0 The computed amount of token0 owed to the position as of the last mint/burn/poke
  /// @return fees1 The computed amount of token1 owed to the position as of the last mint/burn/poke
  function positions(
    bytes32 key
  ) external view returns (uint256 liquidity, uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token, uint128 fees0, uint128 fees1);

  /// @notice The currently in range liquidity available to the pool
  /// @dev This value has no relationship to the total liquidity across all ticks.
  /// Returned value cannot exceed type(uint128).max
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The current in range liquidity
  function liquidity() external view returns (uint128);

  /// @notice The current tick spacing
  /// @dev Ticks can only be initialized by new mints at multiples of this value
  /// e.g.: a tickSpacing of 60 means ticks can be initialized every 60th tick, i.e., ..., -120, -60, 0, 60, 120, ...
  /// However, tickspacing can be changed after the ticks have been initialized.
  /// This value is an int24 to avoid casting even though it is always positive.
  /// @return The current tick spacing
  function tickSpacing() external view returns (int24);

  /// @notice The previous initialized tick before (or at) current global tick
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The previous initialized tick
  function prevTickGlobal() external view returns (int24);

  /// @notice The next initialized tick after current global tick
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The next initialized tick
  function nextTickGlobal() external view returns (int24);

  /// @notice The root of tick search tree
  /// @dev Each bit corresponds to one node in the second layer of tick tree: '1' if node has at least one active bit.
  /// **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The root of tick search tree as bitmap
  function tickTreeRoot() external view returns (uint32);

  /// @notice The second layer of tick search tree
  /// @dev Each bit in node corresponds to one node in the leafs layer (`tickTable`) of tick tree: '1' if leaf has at least one active bit.
  /// **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The node of tick search tree second layer
  function tickTreeSecondLayer(int16) external view returns (uint256);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Algebra Vault Factory
/// @notice This contract can be used for automatic vaults creation
/// @dev Version: Algebra Integral
interface IAlgebraVaultFactory {
  /// @notice returns address of the community fee vault for the pool
  /// @param pool the address of Algebra Integral pool
  /// @return communityFeeVault the address of community fee vault
  function getVaultForPool(address pool) external view returns (address communityFeeVault);

  /// @notice creates the community fee vault for the pool if needed
  /// @param pool the address of Algebra Integral pool
  /// @return communityFeeVault the address of community fee vault
  function createVaultForPool(
    address pool,
    address creator,
    address deployer,
    address token0,
    address token1
  ) external returns (address communityFeeVault);
}

File 34 of 67 : Constants.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0 <0.9.0;

/// @title Contains common constants for Algebra contracts
/// @dev Constants moved to the library, not the base contract, to further emphasize their constant nature
library Constants {
  uint8 internal constant RESOLUTION = 96;
  uint256 internal constant Q96 = 1 << 96;
  uint256 internal constant Q128 = 1 << 128;

  uint24 internal constant FEE_DENOMINATOR = 1e6;
  uint16 internal constant FLASH_FEE = 0.01e4; // fee for flash loan in hundredths of a bip (0.01%)
  uint16 internal constant INIT_DEFAULT_FEE = 0.05e4; // init default fee value in hundredths of a bip (0.05%)
  uint16 internal constant MAX_DEFAULT_FEE = 5e4; // max default fee value in hundredths of a bip (5%)

  int24 internal constant INIT_DEFAULT_TICK_SPACING = 60;
  int24 internal constant MAX_TICK_SPACING = 500;
  int24 internal constant MIN_TICK_SPACING = 1;

  // the frequency with which the accumulated community fees are sent to the vault
  uint32 internal constant FEE_TRANSFER_FREQUENCY = 8 hours;

  // max(uint128) / (MAX_TICK - MIN_TICK)
  uint128 internal constant MAX_LIQUIDITY_PER_TICK = 191757638537527648490752896198553;

  uint16 internal constant MAX_COMMUNITY_FEE = 1e3; // 100%
  uint256 internal constant COMMUNITY_FEE_DENOMINATOR = 1e3;
  // role that can change settings in pools
  bytes32 internal constant POOLS_ADMINISTRATOR_ROLE = keccak256('POOLS_ADMINISTRATOR');
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
  /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
  /// @param a The multiplicand
  /// @param b The multiplier
  /// @param denominator The divisor
  /// @return result The 256-bit result
  /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
  function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
    unchecked {
      // 512-bit multiply [prod1 prod0] = a * b
      // Compute the product mod 2**256 and mod 2**256 - 1
      // then use the Chinese Remainder Theorem to reconstruct
      // the 512 bit result. The result is stored in two 256
      // variables such that product = prod1 * 2**256 + prod0
      uint256 prod0 = a * b; // Least significant 256 bits of the product
      uint256 prod1; // Most significant 256 bits of the product
      assembly {
        let mm := mulmod(a, b, not(0))
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
      }

      // Make sure the result is less than 2**256.
      // Also prevents denominator == 0
      require(denominator > prod1);

      // Handle non-overflow cases, 256 by 256 division
      if (prod1 == 0) {
        assembly {
          result := div(prod0, denominator)
        }
        return result;
      }

      ///////////////////////////////////////////////
      // 512 by 256 division.
      ///////////////////////////////////////////////

      // Make division exact by subtracting the remainder from [prod1 prod0]
      // Compute remainder using mulmod
      // Subtract 256 bit remainder from 512 bit number
      assembly {
        let remainder := mulmod(a, b, denominator)
        prod1 := sub(prod1, gt(remainder, prod0))
        prod0 := sub(prod0, remainder)
      }

      // Factor powers of two out of denominator
      // Compute largest power of two divisor of denominator.
      // Always >= 1.
      uint256 twos = (0 - denominator) & denominator;
      // Divide denominator by power of two
      assembly {
        denominator := div(denominator, twos)
      }

      // Divide [prod1 prod0] by the factors of two
      assembly {
        prod0 := div(prod0, twos)
      }
      // Shift in bits from prod1 into prod0. For this we need
      // to flip `twos` such that it is 2**256 / twos.
      // If twos is zero, then it becomes one
      assembly {
        twos := add(div(sub(0, twos), twos), 1)
      }
      prod0 |= prod1 * twos;

      // Invert denominator mod 2**256
      // Now that denominator is an odd number, it has an inverse
      // modulo 2**256 such that denominator * inv = 1 mod 2**256.
      // Compute the inverse by starting with a seed that is correct
      // correct for four bits. That is, denominator * inv = 1 mod 2**4
      uint256 inv = (3 * denominator) ^ 2;
      // Now use Newton-Raphson iteration to improve the precision.
      // Thanks to Hensel's lifting lemma, this also works in modular
      // arithmetic, doubling the correct bits in each step.
      inv *= 2 - denominator * inv; // inverse mod 2**8
      inv *= 2 - denominator * inv; // inverse mod 2**16
      inv *= 2 - denominator * inv; // inverse mod 2**32
      inv *= 2 - denominator * inv; // inverse mod 2**64
      inv *= 2 - denominator * inv; // inverse mod 2**128
      inv *= 2 - denominator * inv; // inverse mod 2**256

      // Because the division is now exact we can divide by multiplying
      // with the modular inverse of denominator. This will give us the
      // correct result modulo 2**256. Since the preconditions guarantee
      // that the outcome is less than 2**256, this is the final result.
      // We don't need to compute the high bits of the result and prod1
      // is no longer required.
      result = prod0 * inv;
      return result;
    }
  }

  /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
  /// @param a The multiplicand
  /// @param b The multiplier
  /// @param denominator The divisor
  /// @return result The 256-bit result
  function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
    unchecked {
      if (a == 0 || ((result = a * b) / a == b)) {
        require(denominator > 0);
        assembly {
          result := add(div(result, denominator), gt(mod(result, denominator), 0))
        }
      } else {
        result = mulDiv(a, b, denominator);
        if (mulmod(a, b, denominator) > 0) {
          require(result < type(uint256).max);
          result++;
        }
      }
    }
  }

  /// @notice Returns ceil(x / y)
  /// @dev division by 0 has unspecified behavior, and must be checked externally
  /// @param x The dividend
  /// @param y The divisor
  /// @return z The quotient, ceil(x / y)
  function unsafeDivRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
    assembly {
      z := add(div(x, y), gt(mod(x, y), 0))
    }
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4 <0.9.0;

import '../interfaces/pool/IAlgebraPoolErrors.sol';
import './TickMath.sol';
import './TokenDeltaMath.sol';

/// @title Math library for liquidity
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library LiquidityMath {
  /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows
  /// @param x The liquidity before change
  /// @param y The delta by which liquidity should be changed
  /// @return z The liquidity delta
  function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {
    unchecked {
      if (y < 0) {
        if ((z = x - uint128(-y)) >= x) revert IAlgebraPoolErrors.liquiditySub();
      } else {
        if ((z = x + uint128(y)) < x) revert IAlgebraPoolErrors.liquidityAdd();
      }
    }
  }

  function getAmountsForLiquidity(
    int24 bottomTick,
    int24 topTick,
    int128 liquidityDelta,
    int24 currentTick,
    uint160 currentPrice
  ) internal pure returns (uint256 amount0, uint256 amount1, int128 globalLiquidityDelta) {
    uint160 priceAtBottomTick = TickMath.getSqrtRatioAtTick(bottomTick);
    uint160 priceAtTopTick = TickMath.getSqrtRatioAtTick(topTick);

    int256 amount0Int;
    int256 amount1Int;
    if (currentTick < bottomTick) {
      // If current tick is less than the provided bottom one then only the token0 has to be provided
      amount0Int = TokenDeltaMath.getToken0Delta(priceAtBottomTick, priceAtTopTick, liquidityDelta);
    } else if (currentTick < topTick) {
      amount0Int = TokenDeltaMath.getToken0Delta(currentPrice, priceAtTopTick, liquidityDelta);
      amount1Int = TokenDeltaMath.getToken1Delta(priceAtBottomTick, currentPrice, liquidityDelta);
      globalLiquidityDelta = liquidityDelta;
    } else {
      // If current tick is greater than the provided top one then only the token1 has to be provided
      amount1Int = TokenDeltaMath.getToken1Delta(priceAtBottomTick, priceAtTopTick, liquidityDelta);
    }

    unchecked {
      (amount0, amount1) = liquidityDelta < 0 ? (uint256(-amount0Int), uint256(-amount1Int)) : (uint256(amount0Int), uint256(amount1Int));
    }
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4 <0.9.0;

import '../interfaces/pool/IAlgebraPoolErrors.sol';

/// @title Contains logic and constants for interacting with the plugin through hooks
/// @dev Allows pool to check which hooks are enabled, as well as control the return selector
library Plugins {
  function hasFlag(uint8 pluginConfig, uint256 flag) internal pure returns (bool res) {
    assembly {
      res := gt(and(pluginConfig, flag), 0)
    }
  }

  function shouldReturn(bytes4 selector, bytes4 expectedSelector) internal pure {
    if (selector != expectedSelector) revert IAlgebraPoolErrors.invalidHookResponse(expectedSelector);
  }

  uint256 internal constant BEFORE_SWAP_FLAG = 1;
  uint256 internal constant AFTER_SWAP_FLAG = 1 << 1;
  uint256 internal constant BEFORE_POSITION_MODIFY_FLAG = 1 << 2;
  uint256 internal constant AFTER_POSITION_MODIFY_FLAG = 1 << 3;
  uint256 internal constant BEFORE_FLASH_FLAG = 1 << 4;
  uint256 internal constant AFTER_FLASH_FLAG = 1 << 5;
  uint256 internal constant AFTER_INIT_FLAG = 1 << 6;
  uint256 internal constant DYNAMIC_FEE = 1 << 7;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0 <0.9.0;

/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library SafeCast {
  /// @notice Cast a uint256 to a uint160, revert on overflow
  /// @param y The uint256 to be downcasted
  /// @return z The downcasted integer, now type uint160
  function toUint160(uint256 y) internal pure returns (uint160 z) {
    require((z = uint160(y)) == y);
  }

  /// @notice Cast a uint256 to a uint128, revert on overflow
  /// @param y The uint256 to be downcasted
  /// @return z The downcasted integer, now type uint128
  function toUint128(uint256 y) internal pure returns (uint128 z) {
    require((z = uint128(y)) == y);
  }

  /// @notice Cast a int256 to a int128, revert on overflow or underflow
  /// @param y The int256 to be downcasted
  /// @return z The downcasted integer, now type int128
  function toInt128(int256 y) internal pure returns (int128 z) {
    require((z = int128(y)) == y);
  }

  /// @notice Cast a uint128 to a int128, revert on overflow
  /// @param y The uint128 to be downcasted
  /// @return z The downcasted integer, now type int128
  function toInt128(uint128 y) internal pure returns (int128 z) {
    require((z = int128(y)) >= 0);
  }

  /// @notice Cast a uint256 to a int256, revert on overflow
  /// @param y The uint256 to be casted
  /// @return z The casted integer, now type int256
  function toInt256(uint256 y) internal pure returns (int256 z) {
    require((z = int256(y)) >= 0);
  }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4 <0.9.0;

import '../interfaces/pool/IAlgebraPoolErrors.sol';

/// @title SafeTransfer
/// @notice Safe ERC20 transfer library that gracefully handles missing return values.
/// @dev Credit to Solmate under MIT license: https://github.com/transmissions11/solmate/blob/ed67feda67b24fdeff8ad1032360f0ee6047ba0a/src/utils/SafeTransferLib.sol
/// @dev Please note that this library does not check if the token has a code! That responsibility is delegated to the caller.
library SafeTransfer {
  /// @notice Transfers tokens to a recipient
  /// @dev Calls transfer on token contract, errors with transferFailed() if transfer fails
  /// @param token The contract address of the token which will be transferred
  /// @param to The recipient of the transfer
  /// @param amount The amount of the token to transfer
  function safeTransfer(address token, address to, uint256 amount) internal {
    bool success;
    assembly {
      let freeMemoryPointer := mload(0x40) // we will need to restore 0x40 slot
      mstore(0x00, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // "transfer(address,uint256)" selector
      mstore(0x04, and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // append cleaned "to" address
      mstore(0x24, amount)
      // now we use 0x00 - 0x44 bytes (68), freeMemoryPointer is dirty
      success := call(gas(), token, 0, 0, 0x44, 0, 0x20)
      success := and(
        // set success to true if call isn't reverted and returned exactly 1 (can't just be non-zero data) or nothing
        or(and(eq(mload(0), 1), eq(returndatasize(), 32)), iszero(returndatasize())),
        success
      )
      mstore(0x40, freeMemoryPointer) // restore the freeMemoryPointer
    }

    if (!success) revert IAlgebraPoolErrors.transferFailed();
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../interfaces/pool/IAlgebraPoolErrors.sol';

import './TickMath.sol';
import './LiquidityMath.sol';
import './Constants.sol';

/// @title Library for managing and interacting with ticks
/// @notice Contains functions for managing tick processes and relevant calculations
/// @dev Ticks are organized as a doubly linked list
library TickManagement {
  // info stored for each initialized individual tick
  struct Tick {
    uint256 liquidityTotal; // the total position liquidity that references this tick
    int128 liquidityDelta; // amount of net liquidity added (subtracted) when tick is crossed left-right (right-left),
    int24 prevTick;
    int24 nextTick;
    // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
    // only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint256 outerFeeGrowth0Token;
    uint256 outerFeeGrowth1Token;
  }

  function checkTickRangeValidity(int24 bottomTick, int24 topTick) internal pure {
    if (topTick > TickMath.MAX_TICK) revert IAlgebraPoolErrors.topTickAboveMAX();
    if (topTick <= bottomTick) revert IAlgebraPoolErrors.topTickLowerOrEqBottomTick();
    if (bottomTick < TickMath.MIN_TICK) revert IAlgebraPoolErrors.bottomTickLowerThanMIN();
  }

  /// @notice Retrieves fee growth data
  /// @param self The mapping containing all tick information for initialized ticks
  /// @param bottomTick The lower tick boundary of the position
  /// @param topTick The upper tick boundary of the position
  /// @param currentTick The current tick
  /// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0
  /// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1
  /// @return innerFeeGrowth0Token The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
  /// @return innerFeeGrowth1Token The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
  function getInnerFeeGrowth(
    mapping(int24 => Tick) storage self,
    int24 bottomTick,
    int24 topTick,
    int24 currentTick,
    uint256 totalFeeGrowth0Token,
    uint256 totalFeeGrowth1Token
  ) internal view returns (uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token) {
    Tick storage lower = self[bottomTick];
    Tick storage upper = self[topTick];

    unchecked {
      if (currentTick < topTick) {
        if (currentTick >= bottomTick) {
          innerFeeGrowth0Token = totalFeeGrowth0Token - lower.outerFeeGrowth0Token;
          innerFeeGrowth1Token = totalFeeGrowth1Token - lower.outerFeeGrowth1Token;
        } else {
          innerFeeGrowth0Token = lower.outerFeeGrowth0Token;
          innerFeeGrowth1Token = lower.outerFeeGrowth1Token;
        }
        innerFeeGrowth0Token -= upper.outerFeeGrowth0Token;
        innerFeeGrowth1Token -= upper.outerFeeGrowth1Token;
      } else {
        innerFeeGrowth0Token = upper.outerFeeGrowth0Token - lower.outerFeeGrowth0Token;
        innerFeeGrowth1Token = upper.outerFeeGrowth1Token - lower.outerFeeGrowth1Token;
      }
    }
  }

  /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa
  /// @param self The mapping containing all tick information for initialized ticks
  /// @param tick The tick that will be updated
  /// @param currentTick The current tick
  /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)
  /// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0
  /// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1
  /// @param upper True for updating a position's upper tick, or false for updating a position's lower tick
  /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa
  function update(
    mapping(int24 => Tick) storage self,
    int24 tick,
    int24 currentTick,
    int128 liquidityDelta,
    uint256 totalFeeGrowth0Token,
    uint256 totalFeeGrowth1Token,
    bool upper
  ) internal returns (bool flipped) {
    Tick storage data = self[tick];

    uint256 liquidityTotalBefore = data.liquidityTotal;
    uint256 liquidityTotalAfter = LiquidityMath.addDelta(uint128(liquidityTotalBefore), liquidityDelta);
    if (liquidityTotalAfter > Constants.MAX_LIQUIDITY_PER_TICK) revert IAlgebraPoolErrors.liquidityOverflow();

    int128 liquidityDeltaBefore = data.liquidityDelta;
    // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)
    data.liquidityDelta = upper ? int128(int256(liquidityDeltaBefore) - liquidityDelta) : int128(int256(liquidityDeltaBefore) + liquidityDelta);
    data.liquidityTotal = liquidityTotalAfter;

    flipped = (liquidityTotalAfter == 0);
    if (liquidityTotalBefore == 0) {
      flipped = !flipped;
      // by convention, we assume that all growth before a tick was initialized happened _below_ the tick
      if (tick <= currentTick) (data.outerFeeGrowth0Token, data.outerFeeGrowth1Token) = (totalFeeGrowth0Token, totalFeeGrowth1Token);
    }
  }

  /// @notice Transitions to next tick as needed by price movement
  /// @param self The mapping containing all tick information for initialized ticks
  /// @param tick The destination tick of the transition
  /// @param feeGrowth0 The all-time global fee growth, per unit of liquidity, in token0
  /// @param feeGrowth1 The all-time global fee growth, per unit of liquidity, in token1
  /// @return liquidityDelta The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)
  /// @return prevTick The previous active tick before _tick_
  /// @return nextTick The next active tick after _tick_
  function cross(
    mapping(int24 => Tick) storage self,
    int24 tick,
    uint256 feeGrowth0,
    uint256 feeGrowth1
  ) internal returns (int128 liquidityDelta, int24 prevTick, int24 nextTick) {
    Tick storage data = self[tick];
    unchecked {
      (data.outerFeeGrowth1Token, data.outerFeeGrowth0Token) = (feeGrowth1 - data.outerFeeGrowth1Token, feeGrowth0 - data.outerFeeGrowth0Token);
    }
    return (data.liquidityDelta, data.prevTick, data.nextTick);
  }

  /// @notice Used for initial setup of ticks list
  /// @param self The mapping containing all tick information for initialized ticks
  function initTickState(mapping(int24 => Tick) storage self) internal {
    (self[TickMath.MIN_TICK].prevTick, self[TickMath.MIN_TICK].nextTick) = (TickMath.MIN_TICK, TickMath.MAX_TICK);
    (self[TickMath.MAX_TICK].prevTick, self[TickMath.MAX_TICK].nextTick) = (TickMath.MIN_TICK, TickMath.MAX_TICK);
  }

  /// @notice Removes tick from the linked list
  /// @param self The mapping containing all tick information for initialized ticks
  /// @param tick The tick that will be removed
  /// @return prevTick The previous active tick before _tick_
  /// @return nextTick The next active tick after _tick_
  function removeTick(mapping(int24 => Tick) storage self, int24 tick) internal returns (int24 prevTick, int24 nextTick) {
    (prevTick, nextTick) = (self[tick].prevTick, self[tick].nextTick);
    delete self[tick];

    if (tick == TickMath.MIN_TICK || tick == TickMath.MAX_TICK) {
      // MIN_TICK and MAX_TICK cannot be removed from tick list
      (self[tick].prevTick, self[tick].nextTick) = (prevTick, nextTick);
    } else {
      if (prevTick == nextTick) revert IAlgebraPoolErrors.tickIsNotInitialized();
      self[prevTick].nextTick = nextTick;
      self[nextTick].prevTick = prevTick;
    }
    return (prevTick, nextTick);
  }

  /// @notice Adds tick to the linked list
  /// @param self The mapping containing all tick information for initialized ticks
  /// @param tick The tick that will be inserted
  /// @param prevTick The previous active tick before _tick_
  /// @param nextTick The next active tick after _tick_
  function insertTick(mapping(int24 => Tick) storage self, int24 tick, int24 prevTick, int24 nextTick) internal {
    if (tick == TickMath.MIN_TICK || tick == TickMath.MAX_TICK) return;
    if (!(prevTick < tick && nextTick > tick)) revert IAlgebraPoolErrors.tickInvalidLinks();
    (self[tick].prevTick, self[tick].nextTick) = (prevTick, nextTick);

    self[prevTick].nextTick = tick;
    self[nextTick].prevTick = tick;
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4 <0.9.0;

import '../interfaces/pool/IAlgebraPoolErrors.sol';

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library TickMath {
  /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
  int24 internal constant MIN_TICK = -887272;
  /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
  int24 internal constant MAX_TICK = -MIN_TICK;

  /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
  uint160 internal constant MIN_SQRT_RATIO = 4295128739;
  /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
  uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

  /// @notice Calculates sqrt(1.0001^tick) * 2^96
  /// @dev Throws if |tick| > max tick
  /// @param tick The input tick for the above formula
  /// @return price A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
  /// at the given tick
  function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 price) {
    unchecked {
      // get abs value
      int24 absTickMask = tick >> (24 - 1);
      uint256 absTick = uint24((tick + absTickMask) ^ absTickMask);
      if (absTick > uint24(MAX_TICK)) revert IAlgebraPoolErrors.tickOutOfRange();

      uint256 ratio = 0x100000000000000000000000000000000;
      if (absTick & 0x1 != 0) ratio = 0xfffcb933bd6fad37aa2d162d1a594001;
      if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
      if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
      if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
      if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
      if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
      if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
      if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
      if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
      if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
      if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
      if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
      if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
      if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
      if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
      if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
      if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
      if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
      if (absTick >= 0x40000) {
        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
      }

      if (tick > 0) {
        assembly {
          ratio := div(not(0), ratio)
        }
      }

      // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
      // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
      // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
      price = uint160((ratio + 0xFFFFFFFF) >> 32);
    }
  }

  /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
  /// @dev Throws in case price < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
  /// ever return.
  /// @param price The sqrt ratio for which to compute the tick as a Q64.96
  /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
  function getTickAtSqrtRatio(uint160 price) internal pure returns (int24 tick) {
    unchecked {
      // second inequality must be >= because the price can never reach the price at the max tick
      if (price < MIN_SQRT_RATIO || price >= MAX_SQRT_RATIO) revert IAlgebraPoolErrors.priceOutOfRange();
      uint256 ratio = uint256(price) << 32;

      uint256 r = ratio;
      uint256 msb;

      assembly {
        let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(5, gt(r, 0xFFFFFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(4, gt(r, 0xFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(3, gt(r, 0xFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(2, gt(r, 0xF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(1, gt(r, 0x3))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := gt(r, 0x1)
        msb := or(msb, f)
      }

      if (msb >= 128) r = ratio >> (msb - 127);
      else r = ratio << (127 - msb);

      int256 log_2 = (int256(msb) - 128) << 64;

      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(63, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(62, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(61, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(60, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(59, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(58, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(57, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(56, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(55, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(54, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(53, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(52, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(51, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(50, f))
      }

      int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

      int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
      int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

      tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= price ? tickHi : tickLow;
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import './SafeCast.sol';
import './FullMath.sol';
import './Constants.sol';

/// @title Functions based on Q64.96 sqrt price and liquidity
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
library TokenDeltaMath {
  using SafeCast for uint256;

  /// @notice Gets the token0 delta between two prices
  /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper)
  /// @param priceLower A Q64.96 sqrt price
  /// @param priceUpper Another Q64.96 sqrt price
  /// @param liquidity The amount of usable liquidity
  /// @param roundUp Whether to round the amount up or down
  /// @return token0Delta Amount of token0 required to cover a position of size liquidity between the two passed prices
  function getToken0Delta(uint160 priceLower, uint160 priceUpper, uint128 liquidity, bool roundUp) internal pure returns (uint256 token0Delta) {
    unchecked {
      uint256 priceDelta = priceUpper - priceLower;
      require(priceDelta < priceUpper); // forbids underflow and 0 priceLower
      uint256 liquidityShifted = uint256(liquidity) << Constants.RESOLUTION;

      token0Delta = roundUp
        ? FullMath.unsafeDivRoundingUp(FullMath.mulDivRoundingUp(priceDelta, liquidityShifted, priceUpper), priceLower) // denominator always > 0
        : FullMath.mulDiv(priceDelta, liquidityShifted, priceUpper) / priceLower;
    }
  }

  /// @notice Gets the token1 delta between two prices
  /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
  /// @param priceLower A Q64.96 sqrt price
  /// @param priceUpper Another Q64.96 sqrt price
  /// @param liquidity The amount of usable liquidity
  /// @param roundUp Whether to round the amount up, or down
  /// @return token1Delta Amount of token1 required to cover a position of size liquidity between the two passed prices
  function getToken1Delta(uint160 priceLower, uint160 priceUpper, uint128 liquidity, bool roundUp) internal pure returns (uint256 token1Delta) {
    unchecked {
      require(priceUpper >= priceLower);
      uint256 priceDelta = priceUpper - priceLower;
      token1Delta = roundUp ? FullMath.mulDivRoundingUp(priceDelta, liquidity, Constants.Q96) : FullMath.mulDiv(priceDelta, liquidity, Constants.Q96);
    }
  }

  /// @notice Helper that gets signed token0 delta
  /// @param priceLower A Q64.96 sqrt price
  /// @param priceUpper Another Q64.96 sqrt price
  /// @param liquidity The change in liquidity for which to compute the token0 delta
  /// @return token0Delta Amount of token0 corresponding to the passed liquidityDelta between the two prices
  function getToken0Delta(uint160 priceLower, uint160 priceUpper, int128 liquidity) internal pure returns (int256 token0Delta) {
    unchecked {
      token0Delta = liquidity >= 0
        ? getToken0Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256()
        : -getToken0Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256();
    }
  }

  /// @notice Helper that gets signed token1 delta
  /// @param priceLower A Q64.96 sqrt price
  /// @param priceUpper Another Q64.96 sqrt price
  /// @param liquidity The change in liquidity for which to compute the token1 delta
  /// @return token1Delta Amount of token1 corresponding to the passed liquidityDelta between the two prices
  function getToken1Delta(uint160 priceLower, uint160 priceUpper, int128 liquidity) internal pure returns (int256 token1Delta) {
    unchecked {
      token1Delta = liquidity >= 0
        ? getToken1Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256()
        : -getToken1Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256();
    }
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPluginFactory.sol';

/// @title An interface for a contract that is used to deploy and manage Algebra Integral custom pools
/// @dev This contract should be called by every custom pool deployer to create new custom pools or manage existing ones.
interface IAlgebraCustomPoolEntryPoint is IAlgebraPluginFactory {
    /// @notice Returns the address of corresponding AlgebraFactory contract
    /// @return factory The address of AlgebraFactory
    function factory() external view returns (address factory);

    /// @notice Using for custom pools creation
    /// @param deployer The address of plugin deployer, also used for custom pool address calculation
    /// @param creator The initiator of custom pool creation
    /// @param tokenA One of the two tokens in the desired pool
    /// @param tokenB The other of the two tokens in the desired pool
    /// @param data The additional data bytes
    function createCustomPool(
        address deployer,
        address creator,
        address tokenA,
        address tokenB,
        bytes calldata data
    ) external returns (address customPool);

    /// @notice Changes the tick spacing value in the Algebra Integral custom pool
    /// @dev Only corresponding custom pool deployer contract can call this function
    /// @param pool The address of the Algebra Integral custom pool
    /// @param newTickSpacing The new tick spacing value
    function setTickSpacing(address pool, int24 newTickSpacing) external;

    /// @notice Changes the plugin address in the Algebra Integral custom pool
    /// @dev Only corresponding custom pool deployer contract can call this function
    /// @param pool The address of the Algebra Integral custom pool
    /// @param newPluginAddress The new plugin address
    function setPlugin(address pool, address newPluginAddress) external;

    /// @notice Changes the plugin configuration in the Algebra Integral custom pool
    /// @dev Only corresponding custom pool deployer contract can call this function
    /// @param pool The address of the Algebra Integral custom pool
    /// @param newConfig The new plugin configuration bitmap
    function setPluginConfig(address pool, uint8 newConfig) external;

    /// @notice Changes the fee value in the Algebra Integral custom pool
    /// @dev Only corresponding custom pool deployer contract can call this function.
    /// Fee can be changed manually only if pool does not have "dynamic fee" configuration
    /// @param pool The address of the Algebra Integral custom pool
    /// @param newFee The new fee value
    function setFee(address pool, uint16 newFee) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Provides functions for deriving a pool address from the poolDeployer and tokens
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-periphery
library PoolAddress {
    bytes32 internal constant POOL_INIT_CODE_HASH = 0x62441ebe4e4315cf3d49d5957f94d66b253dbabe7006f34ad7f70947e60bf15c;

    /// @notice The identifying key of the pool
    struct PoolKey {
        address deployer;
        address token0;
        address token1;
    }

    /// @notice Returns PoolKey: the ordered tokens
    /// @param deployer The custom pool deployer address
    /// @param tokenA The first token of a pool, unsorted
    /// @param tokenB The second token of a pool, unsorted
    /// @return Poolkey The pool details with ordered token0 and token1 assignments
    function getPoolKey(address deployer, address tokenA, address tokenB) internal pure returns (PoolKey memory) {
        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
        return PoolKey({deployer: deployer, token0: tokenA, token1: tokenB});
    }

    /// @notice Deterministically computes the pool address given the poolDeployer and PoolKey
    /// @param poolDeployer The Algebra poolDeployer contract address
    /// @param key The PoolKey
    /// @return pool The contract address of the Algebra pool
    function computeAddress(address poolDeployer, PoolKey memory key) internal pure returns (address pool) {
        require(key.token0 < key.token1, 'Invalid order of tokens');
        pool = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex'ff',
                            poolDeployer,
                            keccak256(
                                key.deployer == address(0)
                                    ? abi.encode(key.token0, key.token1)
                                    : abi.encode(key.deployer, key.token0, key.token1)
                            ),
                            POOL_INIT_CODE_HASH
                        )
                    )
                )
            )
        );
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/integral-periphery/contracts/libraries/PoolAddress.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraMintCallback.sol';

import '../libraries/EpochLibrary.sol';

interface ILimitOrderManager is IAlgebraMintCallback {
  error ZeroLiquidity();
  error InRange();
  error CrossedRange();
  error Filled();
  error NotFilled();
  error NotPoolManagerToken();
  error NotPlugin();
  error InsufficientLiquidity();

  event Place(address indexed owner, Epoch indexed epoch, address pool, int24 tickLower, int24 tickUpper, bool zeroForOne, uint128 liquidity);

  event Fill(Epoch indexed epoch, int24 tickLower, bool zeroForOne);

  event Kill(address indexed owner, Epoch indexed epoch, int24 tickLower, bool zeroForOne, uint128 liquidity);

  event Withdraw(address indexed owner, Epoch indexed epoch, uint128 liquidity);

  event LimitOrderTickSpacing(address indexed pool, int24 tickSpacing);

  function place(PoolAddress.PoolKey memory poolKey, int24 tickLower, bool zeroForOne, uint128 liquidity) external payable;

  function kill(
    PoolAddress.PoolKey memory poolKey,
    int24 tickLower,
    int24 tickUpper,
    uint128 liquidity,
    bool zeroForOne,
    address to
  ) external returns (uint256 amount0, uint256 amount1);

  function withdraw(Epoch epoch, address to) external returns (uint256 amount0, uint256 amount1);

  function afterSwap(address pool, bool zeroToOne, int24 tick) external;

  function setTickSpacing(address pool, int24 tickSpacing) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IAbstractPlugin.sol';

/// @title The interface for the AlgebraLimitOrderPlugin
interface ILimitOrderPlugin is IAbstractPlugin{

  function limitOrderManager() external view returns (address);

  function setLimitOrderManager(address newModule) external;

  event LimitOrderManager(address newModule);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

/// @title The interface for the ILimitOrderPluginFactory
interface ILimitOrderPluginFactory {

  event LimitOrderManager(address newLimitOrderManager);

  function setLimitOrderManager(address newLimitOrderManager) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.20;

type Epoch is uint232;

library EpochLibrary {
  function equals(Epoch a, Epoch b) internal pure returns (bool) {
    return Epoch.unwrap(a) == Epoch.unwrap(b);
  }

  function unsafeIncrement(Epoch a) internal pure returns (Epoch) {
    unchecked {
      return Epoch.wrap(Epoch.unwrap(a) + 1);
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';
import '@cryptoalgebra/abstract-plugin/contracts/BaseAbstractPlugin.sol';

import './interfaces/ILimitOrderPlugin.sol';
import './interfaces/ILimitOrderManager.sol';

/// @title Algebra Integral 1.2 security plugin
abstract contract LimitOrderPlugin is BaseAbstractPlugin, ILimitOrderPlugin {
  using Plugins for uint8;

  address public override limitOrderManager;

  constructor(address _limitOrderManager) {
    limitOrderManager = _limitOrderManager;
    defaultPluginConfig = defaultPluginConfig | uint8(Plugins.AFTER_SWAP_FLAG);
    activeModules.push("Limit Order Plugin");
  }

  function setLimitOrderManager(address module) external override {
    _authorize();

    limitOrderManager = module;
    emit LimitOrderManager(module);
  }

  function _updateLimitOrderManagerState(address pool, bool zeroToOne) internal {
    if (limitOrderManager != address(0)) {
      (, int24 tick, , ) = _getPoolState();
      ILimitOrderManager(limitOrderManager).afterSwap(pool, zeroToOne, tick);
    }
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IAbstractPlugin.sol';

interface ISecurityPlugin is IAbstractPlugin{
  function setSecurityRegistry(address registry) external;

  function getSecurityRegistry() external view returns (address);

  event SecurityRegistry(address registry);

  error PoolDisabled();
  error BurnOnly();
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

/// @title The interface for the SecurityPluginFactory
interface ISecurityPluginFactory {
  /// @notice Emitted when the security registry address is changed
  /// @param securityRegistry The security registry address after the address was changed
  event SecurityRegistry(address securityRegistry);

  /// @notice Returns current securityRegistry address
  /// @return The securityRegistry contract address
  function securityRegistry() external view returns (address);

  /// @dev updates securoty registry address on the factory
  /// @param newSecurityRegistry The new security registry contract address
  function setSecurityRegistry(address newSecurityRegistry) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface ISecurityRegistry {
  enum Status {
    ENABLED,
    BURN_ONLY,
    DISABLED
  }

  error OnlyOwner();

  event GlobalStatus(Status status);
  event PoolStatus(address pool, Status status);

  function setGlobalStatus(Status newStatus) external;
  function getPoolStatus(address pool) external returns (Status);
  function setPoolsStatus(address[] memory pools, Status[] memory newStatuses) external;

  function algebraFactory() external view returns (address);
  function GUARD() external pure returns (bytes32);
  function globalStatus() external view returns (Status);
  function isPoolStatusOverrided() external view returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';
import '@cryptoalgebra/abstract-plugin/contracts/BaseAbstractPlugin.sol';

import './interfaces/ISecurityPlugin.sol';
import './interfaces/ISecurityRegistry.sol';

/// @title Algebra Integral 1.2 security plugin
abstract contract SecurityPlugin is BaseAbstractPlugin, ISecurityPlugin {
  using Plugins for uint8;

  address internal securityRegistry;

  constructor(address _securityRegistry) {
    securityRegistry = _securityRegistry;
    defaultPluginConfig = defaultPluginConfig | uint8(Plugins.BEFORE_SWAP_FLAG | Plugins.BEFORE_FLASH_FLAG | Plugins.BEFORE_POSITION_MODIFY_FLAG);
    activeModules.push("Security Plugin");
  }

  function _checkStatus() internal {
    if(securityRegistry != address(0)){
      ISecurityRegistry.Status status = ISecurityRegistry(securityRegistry).getPoolStatus(msg.sender);
      if (status != ISecurityRegistry.Status.ENABLED) {
        if (status == ISecurityRegistry.Status.DISABLED) {
          revert PoolDisabled();
        } else {
          revert BurnOnly();
        }
      }
    }
  }

  function _checkStatusOnBurn() internal {
    if(securityRegistry != address(0)){
      ISecurityRegistry.Status status = ISecurityRegistry(securityRegistry).getPoolStatus(msg.sender);
      if (status == ISecurityRegistry.Status.DISABLED) {
        revert PoolDisabled();
      }
    }
  }

  function setSecurityRegistry(address _securityRegistry) external override {
    _authorize();
    securityRegistry = _securityRegistry;
    emit SecurityRegistry(_securityRegistry);
  }

  function getSecurityRegistry() external view override returns (address) {
    return securityRegistry;
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Algebra volatility oracle
/// @dev This contract stores timepoints and calculates statistical averages
interface IVolatilityOracle {
  /// @notice Returns data belonging to a certain timepoint
  /// @param index The index of timepoint in the array
  /// @dev There is more convenient function to fetch a timepoint: getTimepoints(). Which requires not an index but seconds
  /// @return initialized Whether the timepoint has been initialized and the values are safe to use
  /// @return blockTimestamp The timestamp of the timepoint
  /// @return tickCumulative The tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp
  /// @return volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp
  /// @return tick The tick at blockTimestamp
  /// @return averageTick Time-weighted average tick
  /// @return windowStartIndex Index of closest timepoint >= WINDOW seconds ago
  function timepoints(
    uint256 index
  )
    external
    view
    returns (
      bool initialized,
      uint32 blockTimestamp,
      int56 tickCumulative,
      uint88 volatilityCumulative,
      int24 tick,
      int24 averageTick,
      uint16 windowStartIndex
    );

  /// @notice Returns the index of the last timepoint that was written.
  /// @return index of the last timepoint written
  function timepointIndex() external view returns (uint16);

  /// @notice Initialize the plugin externally
  /// @dev This function allows to initialize the plugin if it was created after the pool was created
  function initialize() external;

  /// @notice Returns the timestamp of the last timepoint that was written.
  /// @return timestamp of the last timepoint
  function lastTimepointTimestamp() external view returns (uint32);

  /// @notice Returns information about whether oracle is initialized
  /// @return true if oracle is initialized, otherwise false
  function isInitialized() external view returns (bool);

  /// @dev Reverts if a timepoint at or before the desired timepoint timestamp does not exist.
  /// 0 may be passed as `secondsAgo' to return the current cumulative values.
  /// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values
  /// at exactly the timestamp between the two timepoints.
  /// @dev `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared because they may differ due to interpolation errors
  /// @param secondsAgo The amount of time to look back, in seconds, at which point to return a timepoint
  /// @return tickCumulative The cumulative tick since the pool was first initialized, as of `secondsAgo`
  /// @return volatilityCumulative The cumulative volatility value since the pool was first initialized, as of `secondsAgo`
  function getSingleTimepoint(uint32 secondsAgo) external view returns (int56 tickCumulative, uint88 volatilityCumulative);

  /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
  /// @dev Reverts if `secondsAgos` > oldest timepoint
  /// @dev `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared because they may differ due to interpolation errors
  /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return a timepoint
  /// @return tickCumulatives The cumulative tick since the pool was first initialized, as of each `secondsAgo`
  /// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo`
  function getTimepoints(uint32[] memory secondsAgos) external view returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives);

  /// @notice Fills uninitialized timepoints with nonzero value
  /// @dev Can be used to reduce the gas cost of future swaps
  /// @param startIndex The start index, must be not initialized
  /// @param amount of slots to fill, startIndex + amount must be <= type(uint16).max
  function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

/// @title VolatilityOracle
/// @notice Provides price and volatility data useful for a wide variety of system designs
/// @dev Instances of stored oracle data, "timepoints", are collected in the oracle array
/// Timepoints are overwritten when the full length of the timepoints array is populated.
/// The most recent timepoint is available by passing 0 to getSingleTimepoint().
library VolatilityOracle {
  /// @notice `target` timestamp is older than oldest timepoint
  error targetIsTooOld();

  /// @notice oracle is initialized already
  error volatilityOracleAlreadyInitialized();

  uint32 internal constant WINDOW = 1 days;
  uint256 private constant UINT16_MODULO = 65536;

  struct Timepoint {
    bool initialized; // whether or not the timepoint is initialized
    uint32 blockTimestamp; // the block timestamp of the timepoint
    int56 tickCumulative; // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
    uint88 volatilityCumulative; // the volatility accumulator; overflow after ~34800 years is desired :)
    int24 tick; // tick at this blockTimestamp
    int24 averageTick; // average tick at this blockTimestamp (for WINDOW seconds)
    uint16 windowStartIndex; // closest timepoint lte WINDOW seconds ago (or oldest timepoint), _should be used only from last timepoint_!
  }

  /// @notice Initialize the timepoints array by writing the first slot. Called once for the lifecycle of the timepoints array
  /// @param self The stored timepoints array
  /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32
  /// @param tick Initial tick
  function initialize(Timepoint[UINT16_MODULO] storage self, uint32 time, int24 tick) internal {
    Timepoint storage _zero = self[0];
    if (_zero.initialized) revert volatilityOracleAlreadyInitialized();
    (_zero.initialized, _zero.blockTimestamp, _zero.tick, _zero.averageTick) = (true, time, tick, tick);
  }

  /// @notice Writes a timepoint to the array
  /// @dev Writable at most once per block. `lastIndex` must be tracked externally.
  /// @param self The stored timepoints array
  /// @param lastIndex The index of the timepoint that was most recently written to the timepoints array
  /// @param blockTimestamp The timestamp of the new timepoint
  /// @param tick The active tick at the time of the new timepoint
  /// @return indexUpdated The new index of the most recently written element in the timepoints array
  /// @return oldestIndex The new index of the oldest timepoint
  function write(
    Timepoint[UINT16_MODULO] storage self,
    uint16 lastIndex,
    uint32 blockTimestamp,
    int24 tick
  ) internal returns (uint16 indexUpdated, uint16 oldestIndex) {
    Timepoint memory last = self[lastIndex];
    // early return if we've already written a timepoint this block
    if (last.blockTimestamp == blockTimestamp) return (lastIndex, 0);

    // get next index considering overflow
    unchecked {
      indexUpdated = lastIndex + 1;
    }

    // check if we have overflow in the past
    if (self[indexUpdated].initialized) oldestIndex = indexUpdated;

    (int24 avgTick, uint16 windowStartIndex) = _getAverageTickCasted(
      self,
      blockTimestamp,
      tick,
      lastIndex,
      oldestIndex,
      last.blockTimestamp,
      last.tickCumulative
    );
    unchecked {
      // overflow of indexes is desired
      if (windowStartIndex == indexUpdated) windowStartIndex++; // important, since this value can be used to narrow the search
      self[indexUpdated] = _createNewTimepoint(last, blockTimestamp, tick, avgTick, windowStartIndex);
      if (oldestIndex == indexUpdated) oldestIndex++; // previous oldest index has been overwritten
    }
  }

  /// @dev Reverts if a timepoint at or before the desired timepoint timestamp does not exist.
  /// 0 may be passed as `secondsAgo' to return the current cumulative values.
  /// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values
  /// at exactly the timestamp between the two timepoints.
  /// @dev `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared because they may differ due to interpolation errors
  /// @param self The stored timepoints array
  /// @param time The current block timestamp
  /// @param secondsAgo The amount of time to look back, in seconds, at which point to return a timepoint
  /// @param tick The current tick
  /// @param lastIndex The index of the timepoint that was most recently written to the timepoints array
  /// @param oldestIndex The index of the oldest timepoint
  /// @return targetTimepoint desired timepoint or it's interpolation
  function getSingleTimepoint(
    Timepoint[UINT16_MODULO] storage self,
    uint32 time,
    uint32 secondsAgo,
    int24 tick,
    uint16 lastIndex,
    uint16 oldestIndex
  ) internal view returns (Timepoint memory targetTimepoint) {
    unchecked {
      uint32 target = time - secondsAgo;
      (Timepoint storage beforeOrAt, Timepoint storage atOrAfter, bool samePoint, ) = _getTimepointsAt(self, time, target, lastIndex, oldestIndex);

      targetTimepoint = beforeOrAt;
      if (target == targetTimepoint.blockTimestamp) return targetTimepoint; // we're at the left boundary
      if (samePoint) {
        // if target is newer than last timepoint
        (int24 avgTick, uint16 windowStartIndex) = _getAverageTickCasted(
          self,
          target,
          tick,
          lastIndex,
          oldestIndex,
          targetTimepoint.blockTimestamp,
          targetTimepoint.tickCumulative
        );
        return _createNewTimepoint(targetTimepoint, target, tick, avgTick, windowStartIndex);
      }

      (uint32 timestampAfter, int56 tickCumulativeAfter) = (atOrAfter.blockTimestamp, atOrAfter.tickCumulative);
      if (target == timestampAfter) return atOrAfter; // we're at the right boundary

      // we're in the middle
      (uint32 timepointTimeDelta, uint32 targetDelta) = (timestampAfter - targetTimepoint.blockTimestamp, target - targetTimepoint.blockTimestamp);

      targetTimepoint.tickCumulative +=
        ((tickCumulativeAfter - targetTimepoint.tickCumulative) / int56(uint56(timepointTimeDelta))) *
        int56(uint56(targetDelta));
      targetTimepoint.volatilityCumulative +=
        ((atOrAfter.volatilityCumulative - targetTimepoint.volatilityCumulative) / timepointTimeDelta) *
        targetDelta;
    }
  }

  /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
  /// @dev Reverts if `secondsAgos` > oldest timepoint
  /// @dev `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared because they may differ due to interpolation errors
  /// @param self The stored timepoints array
  /// @param currentTime The current block.timestamp
  /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return a timepoint
  /// @param tick The current tick
  /// @param lastIndex The index of the timepoint that was most recently written to the timepoints array
  /// @return tickCumulatives The cumulative time-weighted tick since the pool was first initialized, as of each `secondsAgo`
  /// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo`
  function getTimepoints(
    Timepoint[UINT16_MODULO] storage self,
    uint32 currentTime,
    uint32[] memory secondsAgos,
    int24 tick,
    uint16 lastIndex
  ) internal view returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives) {
    uint256 secondsLength = secondsAgos.length;
    tickCumulatives = new int56[](secondsLength);
    volatilityCumulatives = new uint88[](secondsLength);

    uint16 oldestIndex = getOldestIndex(self, lastIndex);
    Timepoint memory current;
    unchecked {
      for (uint256 i; i < secondsLength; ++i) {
        current = getSingleTimepoint(self, currentTime, secondsAgos[i], tick, lastIndex, oldestIndex);
        (tickCumulatives[i], volatilityCumulatives[i]) = (current.tickCumulative, current.volatilityCumulative);
      }
    }
  }

  /// @notice Returns the index of the oldest timepoint
  /// @param self The stored timepoints array
  /// @param lastIndex The index of the timepoint that was most recently written to the timepoints array
  /// @return oldestIndex The index of the oldest timepoint
  function getOldestIndex(Timepoint[UINT16_MODULO] storage self, uint16 lastIndex) internal view returns (uint16 oldestIndex) {
    unchecked {
      uint16 nextIndex = lastIndex + 1; // considering overflow
      if (self[nextIndex].initialized) oldestIndex = nextIndex; // check if we have overflow in the past
    }
  }

  /// @notice Returns average volatility in the range from currentTime-WINDOW to currentTime
  /// @param self The stored timepoints array
  /// @param currentTime The current block.timestamp
  /// @param tick The current tick
  /// @param lastIndex The index of the timepoint that was most recently written to the timepoints array
  /// @param oldestIndex The index of the oldest timepoint
  /// @return volatilityAverage The average volatility in the recent range
  function getAverageVolatility(
    Timepoint[UINT16_MODULO] storage self,
    uint32 currentTime,
    int24 tick,
    uint16 lastIndex,
    uint16 oldestIndex
  ) internal view returns (uint88 volatilityAverage) {
    unchecked {
      Timepoint storage lastTimepoint = self[lastIndex];
      bool timeAtLastTimepoint = lastTimepoint.blockTimestamp == currentTime;
      uint88 lastCumulativeVolatility = lastTimepoint.volatilityCumulative;
      uint16 windowStartIndex = lastTimepoint.windowStartIndex; // index of timepoint before of at lastTimepoint.blockTimestamp - WINDOW

      if (!timeAtLastTimepoint) {
        lastCumulativeVolatility = _getVolatilityCumulativeAt(self, currentTime, 0, tick, lastIndex, oldestIndex);
      }

      uint32 oldestTimestamp = self[oldestIndex].blockTimestamp;
      if (_lteConsideringOverflow(oldestTimestamp, currentTime - WINDOW, currentTime)) {
        // oldest timepoint is earlier than 24 hours ago
        uint88 cumulativeVolatilityAtStart;
        if (timeAtLastTimepoint) {
          // interpolate cumulative volatility to avoid search. Since the last timepoint has _just_ been written, we know for sure
          // that the start of the window is between windowStartIndex and windowStartIndex + 1
          (oldestTimestamp, cumulativeVolatilityAtStart) = (self[windowStartIndex].blockTimestamp, self[windowStartIndex].volatilityCumulative);

          uint32 timeDeltaBetweenPoints = self[windowStartIndex + 1].blockTimestamp - oldestTimestamp;

          cumulativeVolatilityAtStart +=
            ((self[windowStartIndex + 1].volatilityCumulative - cumulativeVolatilityAtStart) * (currentTime - WINDOW - oldestTimestamp)) /
            timeDeltaBetweenPoints;
        } else {
          cumulativeVolatilityAtStart = _getVolatilityCumulativeAt(self, currentTime, WINDOW, tick, lastIndex, oldestIndex);
        }

        return ((lastCumulativeVolatility - cumulativeVolatilityAtStart) / WINDOW); // sample is big enough to ignore bias of variance
      } else if (currentTime != oldestTimestamp) {
        // recorded timepoints are not enough, so we will extrapolate
        uint88 _oldestVolatilityCumulative = self[oldestIndex].volatilityCumulative;
        uint32 unbiasedDenominator = currentTime - oldestTimestamp;
        if (unbiasedDenominator > 1) unbiasedDenominator--; // Bessel's correction for "small" sample
        return ((lastCumulativeVolatility - _oldestVolatilityCumulative) / unbiasedDenominator);
      }
    }
  }

  // ##### further functions are private to the library, but some are made internal for fuzzy testing #####

  /// @notice Transforms a previous timepoint into a new timepoint, given the passage of time and the current tick and liquidity values
  /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows
  /// @dev The function changes the structure given to the input, and does not create a new one
  /// @param last The specified timepoint to be used in creation of new timepoint
  /// @param blockTimestamp The timestamp of the new timepoint
  /// @param tick The active tick at the time of the new timepoint
  /// @param averageTick The average tick at the time of the new timepoint
  /// @param windowStartIndex The index of closest timepoint >= WINDOW seconds ago
  /// @return Timepoint The newly populated timepoint
  function _createNewTimepoint(
    Timepoint memory last,
    uint32 blockTimestamp,
    int24 tick,
    int24 averageTick,
    uint16 windowStartIndex
  ) internal pure returns (Timepoint memory) {
    unchecked {
      uint32 delta = blockTimestamp - last.blockTimestamp; // overflow is desired
      // We don't create a new structure in memory to save gas. Therefore, the function changes the old structure
      last.initialized = true;
      last.blockTimestamp = blockTimestamp;
      last.tickCumulative += int56(tick) * int56(uint56(delta));
      last.volatilityCumulative += uint88(_volatilityOnRange(int256(uint256(delta)), tick, tick, last.averageTick, averageTick)); // always fits 88 bits
      last.tick = tick;
      last.averageTick = averageTick;
      last.windowStartIndex = windowStartIndex;
      return last;
    }
  }

  /// @notice Calculates volatility between two sequential timepoints with resampling to 1 sec frequency
  /// @param dt Timedelta between timepoints, must be within uint32 range
  /// @param tick0 The tick after the left timepoint, must be within int24 range
  /// @param tick1 The tick at the right timepoint, must be within int24 range
  /// @param avgTick0 The average tick at the left timepoint, must be within int24 range
  /// @param avgTick1 The average tick at the right timepoint, must be within int24 range
  /// @return volatility The volatility between two sequential timepoints
  /// If the requirements for the parameters are met, it always fits 88 bits
  function _volatilityOnRange(int256 dt, int256 tick0, int256 tick1, int256 avgTick0, int256 avgTick1) internal pure returns (uint256 volatility) {
    // On the time interval from the previous timepoint to the current
    // we can represent tick and average tick change as two straight lines:
    // tick = k*t + b, where k and b are some constants
    // avgTick = p*t + q, where p and q are some constants
    // we want to get sum of (tick(t) - avgTick(t))^2 for every t in the interval (0; dt]
    // so: (tick(t) - avgTick(t))^2 = ((k*t + b) - (p*t + q))^2 = (k-p)^2 * t^2 + 2(k-p)(b-q)t + (b-q)^2
    // since everything except t is a constant, we need to use progressions for t and t^2:
    // sum(t) for t from 1 to dt = dt*(dt + 1)/2 = sumOfSequence
    // sum(t^2) for t from 1 to dt = dt*(dt+1)*(2dt + 1)/6 = sumOfSquares
    // so result will be: (k-p)^2 * sumOfSquares + 2(k-p)(b-q)*sumOfSequence + dt*(b-q)^2
    unchecked {
      int256 k = (tick1 - tick0) - (avgTick1 - avgTick0); // (k - p)*dt
      int256 b = (tick0 - avgTick0) * dt; // (b - q)*dt
      int256 sumOfSequence = dt * (dt + 1); // sumOfSequence * 2
      int256 sumOfSquares = sumOfSequence * (2 * dt + 1); // sumOfSquares * 6
      volatility = uint256((k ** 2 * sumOfSquares + 6 * b * k * sumOfSequence + 6 * dt * b ** 2) / (6 * dt ** 2));
    }
  }

  /// @notice Calculates average tick for WINDOW seconds at the moment of `time`
  /// @dev Guaranteed that the result is within the bounds of int24
  /// @return avgTick The average tick
  /// @return windowStartIndex The index of closest timepoint <= WINDOW seconds ago
  function _getAverageTickCasted(
    Timepoint[UINT16_MODULO] storage self,
    uint32 time,
    int24 tick,
    uint16 lastIndex,
    uint16 oldestIndex,
    uint32 lastTimestamp,
    int56 lastTickCumulative
  ) internal view returns (int24 avgTick, uint16 windowStartIndex) {
    (int256 _avgTick, uint256 _windowStartIndex) = _getAverageTick(self, time, tick, lastIndex, oldestIndex, lastTimestamp, lastTickCumulative);
    unchecked {
      (avgTick, windowStartIndex) = (int24(_avgTick), uint16(_windowStartIndex)); // overflow in uint16(_windowStartIndex) is desired
    }
  }

  /// @notice Calculates average tick for WINDOW seconds at the moment of `currentTime`
  /// @dev Guaranteed that the result is within the bounds of int24, but result is not casted
  /// @return avgTick int256 for fuzzy tests
  /// @return windowStartIndex The index of closest timepoint <= WINDOW seconds ago
  function _getAverageTick(
    Timepoint[UINT16_MODULO] storage self,
    uint32 currentTime,
    int24 tick,
    uint16 lastIndex,
    uint16 oldestIndex,
    uint32 lastTimestamp,
    int56 lastTickCumulative
  ) internal view returns (int256 avgTick, uint256 windowStartIndex) {
    (uint32 oldestTimestamp, int56 oldestTickCumulative) = (self[oldestIndex].blockTimestamp, self[oldestIndex].tickCumulative);

    unchecked {
      int56 currentTickCumulative = lastTickCumulative + int56(tick) * int56(uint56(currentTime - lastTimestamp)); // update with new data
      if (!_lteConsideringOverflow(oldestTimestamp, currentTime - WINDOW, currentTime)) {
        // if oldest is newer than WINDOW ago
        if (currentTime == oldestTimestamp) return (tick, oldestIndex);
        return ((currentTickCumulative - oldestTickCumulative) / int56(uint56(currentTime - oldestTimestamp)), oldestIndex);
      }

      if (_lteConsideringOverflow(lastTimestamp, currentTime - WINDOW, currentTime)) {
        // if last timepoint is older or equal than WINDOW ago
        return (tick, lastIndex);
      } else {
        int56 tickCumulativeAtStart;
        (tickCumulativeAtStart, windowStartIndex) = _getTickCumulativeAt(self, currentTime, WINDOW, tick, lastIndex, oldestIndex);

        //    current-WINDOW  last   current
        // _________*____________*_______*_
        //          ||||||||||||||||||||||
        avgTick = (currentTickCumulative - tickCumulativeAtStart) / int56(uint56(WINDOW));
      }
    }
  }

  /// @notice comparator for 32-bit timestamps
  /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to currentTime
  /// @param a A comparison timestamp from which to determine the relative position of `currentTime`
  /// @param b From which to determine the relative position of `currentTime`
  /// @param currentTime A timestamp truncated to 32 bits
  /// @return res Whether `a` is chronologically <= `b`
  function _lteConsideringOverflow(uint32 a, uint32 b, uint32 currentTime) internal pure returns (bool res) {
    res = a > currentTime;
    if (res == b > currentTime) res = a <= b; // if both are on the same side
  }

  /// @notice Calculates cumulative volatility at the moment of `time` - `secondsAgo`
  /// @dev More optimal than via `getSingleTimepoint`
  /// @return volatilityCumulative The cumulative volatility
  function _getVolatilityCumulativeAt(
    Timepoint[UINT16_MODULO] storage self,
    uint32 time,
    uint32 secondsAgo,
    int24 tick,
    uint16 lastIndex,
    uint16 oldestIndex
  ) internal view returns (uint88 volatilityCumulative) {
    unchecked {
      uint32 target = time - secondsAgo;
      (Timepoint storage beforeOrAt, Timepoint storage atOrAfter, bool samePoint, ) = _getTimepointsAt(self, time, target, lastIndex, oldestIndex);

      (uint32 timestampBefore, uint88 volatilityCumulativeBefore) = (beforeOrAt.blockTimestamp, beforeOrAt.volatilityCumulative);
      if (target == timestampBefore) return volatilityCumulativeBefore; // we're at the left boundary
      if (samePoint) {
        // since target != beforeOrAt.blockTimestamp, `samePoint` means that target is newer than last timepoint
        (int24 avgTick, ) = _getAverageTickCasted(self, target, tick, lastIndex, oldestIndex, timestampBefore, beforeOrAt.tickCumulative);

        return (volatilityCumulativeBefore +
          uint88(_volatilityOnRange(int256(uint256(target - timestampBefore)), tick, tick, beforeOrAt.averageTick, avgTick)));
      }

      (uint32 timestampAfter, uint88 volatilityCumulativeAfter) = (atOrAfter.blockTimestamp, atOrAfter.volatilityCumulative);
      if (target == timestampAfter) return volatilityCumulativeAfter; // we're at the right boundary

      // we're in the middle
      (uint32 timepointTimeDelta, uint32 targetDelta) = (timestampAfter - timestampBefore, target - timestampBefore);
      return volatilityCumulativeBefore + ((volatilityCumulativeAfter - volatilityCumulativeBefore) / timepointTimeDelta) * targetDelta;
    }
  }

  /// @notice Calculates cumulative tick at the moment of `time` - `secondsAgo`
  /// @dev More optimal than via `getSingleTimepoint`
  /// @return tickCumulative The cumulative tick
  /// @return indexBeforeOrAt The index of closest timepoint before or at the moment of `time` - `secondsAgo`
  function _getTickCumulativeAt(
    Timepoint[UINT16_MODULO] storage self,
    uint32 time,
    uint32 secondsAgo,
    int24 tick,
    uint16 lastIndex,
    uint16 oldestIndex
  ) internal view returns (int56 tickCumulative, uint256 indexBeforeOrAt) {
    unchecked {
      uint32 target = time - secondsAgo;
      (Timepoint storage beforeOrAt, Timepoint storage atOrAfter, bool samePoint, uint256 _indexBeforeOrAt) = _getTimepointsAt(
        self,
        time,
        target,
        lastIndex,
        oldestIndex
      );

      (uint32 timestampBefore, int56 tickCumulativeBefore) = (beforeOrAt.blockTimestamp, beforeOrAt.tickCumulative);
      if (target == timestampBefore) return (tickCumulativeBefore, _indexBeforeOrAt); // we're at the left boundary
      // since target != timestampBefore, `samePoint` means that target is newer than last timepoint
      if (samePoint) return ((tickCumulativeBefore + int56(tick) * int56(uint56(target - timestampBefore))), _indexBeforeOrAt); // if target is newer than last timepoint

      (uint32 timestampAfter, int56 tickCumulativeAfter) = (atOrAfter.blockTimestamp, atOrAfter.tickCumulative);
      if (target == timestampAfter) return (tickCumulativeAfter, uint16(_indexBeforeOrAt + 1)); // we're at the right boundary

      // we're in the middle
      (uint32 timepointTimeDelta, uint32 targetDelta) = (timestampAfter - timestampBefore, target - timestampBefore);
      return (
        tickCumulativeBefore + ((tickCumulativeAfter - tickCumulativeBefore) / int56(uint56(timepointTimeDelta))) * int56(uint56(targetDelta)),
        _indexBeforeOrAt
      );
    }
  }

  /// @notice Returns closest timepoint or timepoints to the moment of `target`
  /// @return beforeOrAt The timepoint recorded before, or at, the target
  /// @return atOrAfter The timepoint recorded at, or after, the target
  /// @return samePoint Are `beforeOrAt` and `atOrAfter` the same or not
  /// @return indexBeforeOrAt The index of closest timepoint before or at the moment of `target`
  function _getTimepointsAt(
    Timepoint[UINT16_MODULO] storage self,
    uint32 currentTime,
    uint32 target,
    uint16 lastIndex,
    uint16 oldestIndex
  ) private view returns (Timepoint storage beforeOrAt, Timepoint storage atOrAfter, bool samePoint, uint256 indexBeforeOrAt) {
    Timepoint storage lastTimepoint = self[lastIndex];
    uint32 lastTimepointTimestamp = lastTimepoint.blockTimestamp;
    uint16 windowStartIndex = lastTimepoint.windowStartIndex;

    // if target is newer than last timepoint
    if (target == currentTime || _lteConsideringOverflow(lastTimepointTimestamp, target, currentTime)) {
      return (lastTimepoint, lastTimepoint, true, lastIndex);
    }

    bool useHeuristic;
    unchecked {
      if (lastTimepointTimestamp - target <= WINDOW) {
        // We can limit the scope of the search. It is safe because when the array overflows,
        // `windowsStartIndex` cannot point to the overwritten timepoint (check at `write(...)`)
        oldestIndex = windowStartIndex;
        useHeuristic = target == currentTime - WINDOW; // heuristic will optimize search for timepoints close to `currentTime - WINDOW`
      }
      uint32 oldestTimestamp = self[oldestIndex].blockTimestamp;

      if (!_lteConsideringOverflow(oldestTimestamp, target, currentTime)) revert targetIsTooOld();
      if (oldestTimestamp == target) return (self[oldestIndex], self[oldestIndex], true, oldestIndex);

      // no need to search if we already know the answer
      if (lastIndex == oldestIndex + 1) return (self[oldestIndex], lastTimepoint, false, oldestIndex);
    }

    (beforeOrAt, atOrAfter, indexBeforeOrAt) = _binarySearch(self, currentTime, target, lastIndex, oldestIndex, useHeuristic);
    return (beforeOrAt, atOrAfter, false, indexBeforeOrAt);
  }

  /// @notice Fetches the timepoints beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.
  /// The result may be the same timepoint, or adjacent timepoints.
  /// @dev The answer must be older than the most recent timepoint and younger, or the same age as, the oldest timepoint
  /// @param self The stored timepoints array
  /// @param currentTime The current block.timestamp
  /// @param target The timestamp at which the timepoint should be
  /// @param upperIndex The index of the upper border of search range
  /// @param lowerIndex The index of the lower border of search range
  /// @param withHeuristic Use heuristic for first guess or not (optimize for targets close to `lowerIndex`)
  /// @return beforeOrAt The timepoint recorded before, or at, the target
  /// @return atOrAfter The timepoint recorded at, or after, the target
  function _binarySearch(
    Timepoint[UINT16_MODULO] storage self,
    uint32 currentTime,
    uint32 target,
    uint16 upperIndex,
    uint16 lowerIndex,
    bool withHeuristic
  ) private view returns (Timepoint storage beforeOrAt, Timepoint storage atOrAfter, uint256 indexBeforeOrAt) {
    unchecked {
      uint256 left = lowerIndex; // oldest timepoint
      uint256 right = upperIndex < lowerIndex ? upperIndex + UINT16_MODULO : upperIndex; // newest timepoint considering one index overflow
      (beforeOrAt, atOrAfter, indexBeforeOrAt) = _binarySearchInternal(self, currentTime, target, left, right, withHeuristic);
    }
  }

  function _binarySearchInternal(
    Timepoint[UINT16_MODULO] storage self,
    uint32 currentTime,
    uint32 target,
    uint256 left,
    uint256 right,
    bool withHeuristic
  ) private view returns (Timepoint storage beforeOrAt, Timepoint storage atOrAfter, uint256 indexBeforeOrAt) {
    unchecked {
      if (withHeuristic && right - left > 2) {
        indexBeforeOrAt = left + 1; // heuristic for first guess
      } else {
        indexBeforeOrAt = (left + right) >> 1; // "middle" point between the boundaries
      }
      beforeOrAt = self[uint16(indexBeforeOrAt)]; // checking the "middle" point between the boundaries
      atOrAfter = beforeOrAt; // to suppress compiler warning; will be overridden
      bool firstIteration = true;
      do {
        (bool initializedBefore, uint32 timestampBefore) = (beforeOrAt.initialized, beforeOrAt.blockTimestamp);
        if (initializedBefore) {
          if (_lteConsideringOverflow(timestampBefore, target, currentTime)) {
            // is current point before or at `target`?
            atOrAfter = self[uint16(indexBeforeOrAt + 1)]; // checking the next point after "middle"
            (bool initializedAfter, uint32 timestampAfter) = (atOrAfter.initialized, atOrAfter.blockTimestamp);
            if (initializedAfter) {
              if (_lteConsideringOverflow(target, timestampAfter, currentTime)) {
                // is the "next" point after or at `target`?
                return (beforeOrAt, atOrAfter, indexBeforeOrAt); // the only fully correct way to finish
              }
              left = indexBeforeOrAt + 1; // "next" point is before the `target`, so looking in the right half
            } else {
              // beforeOrAt is initialized and <= target, and next timepoint is uninitialized
              // should be impossible if initial boundaries and `target` are correct
              return (beforeOrAt, beforeOrAt, indexBeforeOrAt);
            }
          } else {
            right = indexBeforeOrAt - 1; // current point is after the `target`, so looking in the left half
          }
        } else {
          // we've landed on an uninitialized timepoint, keep searching higher
          // should be impossible if initial boundaries and `target` are correct
          left = indexBeforeOrAt + 1;
        }
        // use heuristic if looking in the right half after first iteration
        bool useHeuristic = firstIteration && withHeuristic && left == indexBeforeOrAt + 1;
        if (useHeuristic && right - left > 16) {
          indexBeforeOrAt = left + 8;
        } else {
          indexBeforeOrAt = (left + right) >> 1; // calculating the new "middle" point index after updating the bounds
        }
        beforeOrAt = self[uint16(indexBeforeOrAt)]; // update the "middle" point pointer
        firstIteration = false;
      } while (true);
    }
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;

import '@cryptoalgebra/integral-core/contracts/libraries/FullMath.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';
import '@cryptoalgebra/integral-periphery/contracts/libraries/PoolAddress.sol';

import '../interfaces/IVolatilityOracle.sol';

/// @title Volatility Oracle Interactions
/// @notice Provides functions to integrate with Algebra pool TWAP VolatilityOracle
library VolatilityOracleInteractions {
  /// @notice Fetches time-weighted average tick using Algebra VolatilityOracle
  /// @param oracleAddress The address of oracle
  /// @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 oracleAddress, uint32 period) internal view returns (int24 timeWeightedAverageTick) {
    require(period != 0, 'Period is zero');

    uint32[] memory secondAgos = new uint32[](2);
    secondAgos[0] = period;
    secondAgos[1] = 0;

    IVolatilityOracle oracle = IVolatilityOracle(oracleAddress);
    (int56[] memory tickCumulatives, ) = oracle.getTimepoints(secondAgos);
    int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];

    timeWeightedAverageTick = int24(tickCumulativesDelta / int56(uint56(period)));

    // Always round to negative infinity
    if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int56(uint56(period)) != 0)) timeWeightedAverageTick--;
  }

  /// @notice Given a tick and a token amount, calculates the amount of token received in exchange
  /// @param tick Tick value used to calculate the quote
  /// @param baseAmount Amount of token to be converted
  /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination
  /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination
  /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken
  function getQuoteAtTick(int24 tick, uint128 baseAmount, address baseToken, address quoteToken) internal pure returns (uint256 quoteAmount) {
    uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);

    // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself
    if (sqrtRatioX96 <= type(uint128).max) {
      uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;
      quoteAmount = baseToken < quoteToken ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) : FullMath.mulDiv(1 << 192, baseAmount, ratioX192);
    } else {
      uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64);
      quoteAmount = baseToken < quoteToken ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) : FullMath.mulDiv(1 << 128, baseAmount, ratioX128);
    }
  }

  /// @notice Fetches metadata of last available record (most recent) in oracle
  /// @param oracleAddress The address of oracle
  /// @return index The index of last available record (most recent) in oracle
  /// @return timestamp The timestamp of last available record (most recent) in oracle, truncated to uint32
  function lastTimepointMetadata(address oracleAddress) internal view returns (uint16 index, uint32 timestamp) {
    index = latestIndex(oracleAddress);
    timestamp = IVolatilityOracle(oracleAddress).lastTimepointTimestamp();
  }

  /// @notice Fetches metadata of oldest available record in oracle
  /// @param oracleAddress The address of oracle
  /// @return index The index of oldest available record in oracle
  /// @return timestamp The timestamp of oldest available record in oracle, truncated to uint32
  function oldestTimepointMetadata(address oracleAddress) internal view returns (uint16 index, uint32 timestamp) {
    uint16 lastIndex = latestIndex(oracleAddress);
    bool initialized;
    unchecked {
      // overflow is desired
      index = lastIndex + 1;
      (initialized, timestamp) = timepointMetadata(oracleAddress, index);
    }
    if (initialized) return (index, timestamp);

    (, timestamp) = timepointMetadata(oracleAddress, 0);
    return (0, timestamp);
  }

  /// @notice Gets information about whether the oracle has been initialized
  function isInitialized(address oracleAddress) internal view returns (bool result) {
    (result, ) = timepointMetadata(oracleAddress, 0);
    return result;
  }

  /// @notice Fetches the index of last available record (most recent) in oracle
  function latestIndex(address oracle) internal view returns (uint16) {
    return (IVolatilityOracle(oracle).timepointIndex());
  }

  /// @notice Fetches the metadata of record in oracle
  /// @param oracleAddress The address of oracle
  /// @param index The index of record in oracle
  /// @return initialized Whether or not the timepoint is initialized
  /// @return timestamp The timestamp of timepoint
  function timepointMetadata(address oracleAddress, uint16 index) internal view returns (bool initialized, uint32 timestamp) {
    (initialized, timestamp, , , , , ) = IVolatilityOracle(oracleAddress).timepoints(index);
  }

  /// @notice Checks if the oracle is currently connected to the pool
  /// @param oracleAddress The address of oracle
  /// @param oracleAddress The address of the pool
  /// @return connected Whether or not the oracle is connected
  function isOracleConnectedToPool(address oracleAddress, address poolAddress) internal view returns (bool connected) {
    IAlgebraPool pool = IAlgebraPool(poolAddress);
    if (oracleAddress == pool.plugin()) {
      (, , , uint8 pluginConfig, , ) = pool.globalState();
      connected = Plugins.hasFlag(pluginConfig, Plugins.BEFORE_SWAP_FLAG);
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';

import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IAbstractPlugin.sol';
import './interfaces/IVolatilityOracle.sol';

import './libraries/VolatilityOracle.sol';
import { BaseAbstractPlugin } from '@cryptoalgebra/abstract-plugin/contracts/BaseAbstractPlugin.sol';

/// @title Algebra Integral 1.2.1 VolatilityOraclePlugin plugin
/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages
abstract contract VolatilityOraclePlugin is BaseAbstractPlugin, IVolatilityOracle {
  using Plugins for uint8;

  uint256 internal constant UINT16_MODULO = 65536;
  using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO];

  /// @inheritdoc IVolatilityOracle
  VolatilityOracle.Timepoint[UINT16_MODULO] public override timepoints;

  /// @inheritdoc IVolatilityOracle
  uint16 public override timepointIndex;

  /// @inheritdoc IVolatilityOracle
  uint32 public override lastTimepointTimestamp;

  /// @inheritdoc IVolatilityOracle
  bool public override isInitialized;

  constructor() {
    defaultPluginConfig = defaultPluginConfig | uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG);
    
    activeModules.push("Volatility Oracle Plugin");
  }

  /// @inheritdoc IVolatilityOracle
  function initialize() external override {
    require(!isInitialized, 'Already initialized');
    require(_getPluginInPool() == address(this), 'Plugin not attached');
    (uint160 price, int24 tick, , ) = _getPoolState();
    require(price != 0, 'Pool is not initialized');
    _initialize_TWAP(tick);
  }

  function _initialize_TWAP(int24 tick) internal {
    uint32 time = _blockTimestamp();
    timepoints.initialize(time, tick);
    lastTimepointTimestamp = time;
    isInitialized = true;

    _enablePluginFlags(defaultPluginConfig);
  }
  // ###### Volatility and TWAP oracle ######

  /// @inheritdoc IVolatilityOracle
  function getSingleTimepoint(uint32 secondsAgo) external view override returns (int56 tickCumulative, uint88 volatilityCumulative) {
    // `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared: they may differ due to interpolation errors
    (, int24 tick, , ) = _getPoolState();
    uint16 lastTimepointIndex = timepointIndex;
    uint16 oldestIndex = timepoints.getOldestIndex(lastTimepointIndex);
    VolatilityOracle.Timepoint memory result = timepoints.getSingleTimepoint(_blockTimestamp(), secondsAgo, tick, lastTimepointIndex, oldestIndex);
    (tickCumulative, volatilityCumulative) = (result.tickCumulative, result.volatilityCumulative);
  }

  /// @inheritdoc IVolatilityOracle
  function getTimepoints(
    uint32[] memory secondsAgos
  ) external view override returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives) {
    // `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared: they may differ due to interpolation errors
    (, int24 tick, , ) = _getPoolState();
    return timepoints.getTimepoints(_blockTimestamp(), secondsAgos, tick, timepointIndex);
  }

  /// @inheritdoc IVolatilityOracle
  function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external override {
    require(!timepoints[startIndex].initialized); // if not initialized, then all subsequent ones too
    require(amount > 0 && type(uint16).max - startIndex >= amount);

    unchecked {
      for (uint256 i = startIndex; i < startIndex + amount; ++i) {
        timepoints[i].blockTimestamp = 1; // will be overwritten
      }
    }
  }

  function _writeTimepoint() internal {
    // single SLOAD
    uint16 _lastIndex = timepointIndex;
    uint32 _lastTimepointTimestamp = lastTimepointTimestamp;

    bool _isInitialized = isInitialized;
    require(_isInitialized, 'Not initialized');

    uint32 currentTimestamp = _blockTimestamp();
    if (_lastTimepointTimestamp == currentTimestamp) return;

    (, int24 tick, , ) = _getPoolState();
    (uint16 newLastIndex, ) = timepoints.write(_lastIndex, currentTimestamp, tick);

    timepointIndex = newLastIndex;
    lastTimepointTimestamp = currentTimestamp;
  }

  function _getAverageVolatilityLast() internal view returns (uint88 volatilityAverage) {
    uint32 currentTimestamp = _blockTimestamp();
    (, int24 tick, , ) = _getPoolState();

    uint16 lastTimepointIndex = timepointIndex;
    uint16 oldestIndex = timepoints.getOldestIndex(lastTimepointIndex);

    volatilityAverage = timepoints.getAverageVolatility(currentTimestamp, tick, lastTimepointIndex, oldestIndex);
  }

  function _getLastTick() internal view returns (int24 lastTick) {
    VolatilityOracle.Timepoint memory lastTimepoint = timepoints[timepointIndex];
    return lastTimepoint.tick;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-periphery/contracts/interfaces/IAlgebraCustomPoolEntryPoint.sol';
import '@cryptoalgebra/dynamic-fee-plugin/contracts/libraries/AdaptiveFee.sol';

import './interfaces/IAlgebraCustomPluginFactory.sol';
import './AlgebraDefaultPlugin.sol';

/// @title Algebra Integral 1.2.1 ALM custom plugin deployer
contract AlgebraCustomPluginFactory is IAlgebraCustomPluginFactory {
  /// @inheritdoc IAlgebraCustomPluginFactory
  bytes32 public constant override ALGEBRA_CUSTOM_PLUGIN_ADMINISTRATOR = keccak256('ALGEBRA_CUSTOM_PLUGIN_ADMINISTRATOR');

  /// @inheritdoc IAlgebraCustomPluginFactory
  address public immutable override algebraFactory;

  /// @inheritdoc IAlgebraCustomPluginFactory
  address public immutable entryPoint;

  /// @inheritdoc IDynamicFeePluginFactory
  AlgebraFeeConfiguration public override defaultFeeConfiguration; // values of constants for sigmoids in fee calculation formula

  /// @inheritdoc IFarmingPluginFactory
  address public override farmingAddress;

  /// @inheritdoc ISecurityPluginFactory
  address public override securityRegistry;

  address public limitOrderManager;

  /// @inheritdoc IAlgebraCustomPluginFactory
  mapping(address poolAddress => address pluginAddress) public override pluginByPool;

  modifier onlyAdministrator() {
    require(IAlgebraFactory(algebraFactory).hasRoleOrOwner(ALGEBRA_CUSTOM_PLUGIN_ADMINISTRATOR, msg.sender), 'Only administrator');
    _;
  }

  constructor(address _algebraFactory, address _entryPoint) {
    entryPoint = _entryPoint;
    algebraFactory = _algebraFactory;
    defaultFeeConfiguration = AdaptiveFee.initialFeeConfiguration();
  }

  /// @inheritdoc IAlgebraPluginFactory
  function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata) external override returns (address) {
    require(msg.sender == entryPoint);
    return _createPlugin(pool);
  }

  /// @inheritdoc IAlgebraPluginFactory
  function afterCreatePoolHook(address, address, address) external view override {
    require(msg.sender == entryPoint);
  }

  function _createPlugin(address pool) internal returns (address) {
    require(pluginByPool[pool] == address(0), 'Already created');
    address plugin = address(new AlgebraDefaultPlugin(pool, algebraFactory, address(this), defaultFeeConfiguration, securityRegistry, limitOrderManager));
    pluginByPool[pool] = plugin;
    return address(plugin);
  }
  
  /// @inheritdoc IAlgebraCustomPluginFactory
  function createCustomPool(address creator, address tokenA, address tokenB, bytes calldata data) external returns (address customPool) {
    return IAlgebraCustomPoolEntryPoint(entryPoint).createCustomPool(address(this), creator, tokenA, tokenB, data);
  }

  /// @inheritdoc IDynamicFeePluginFactory
  function setDefaultFeeConfiguration(AlgebraFeeConfiguration calldata newConfig) external override onlyAdministrator {
    AdaptiveFee.validateFeeConfiguration(newConfig);
    defaultFeeConfiguration = newConfig;
    emit DefaultFeeConfiguration(newConfig);
  }

  /// @inheritdoc IFarmingPluginFactory
  function setFarmingAddress(address newFarmingAddress) external override onlyAdministrator {
    require(farmingAddress != newFarmingAddress);
    farmingAddress = newFarmingAddress;
    emit FarmingAddress(newFarmingAddress);
  }

  /// @inheritdoc ISecurityPluginFactory
  function setSecurityRegistry(address newSecurityRegistry) external override onlyAdministrator {
    securityRegistry = newSecurityRegistry;
    emit SecurityRegistry(newSecurityRegistry);
  }

  /// @inheritdoc ILimitOrderPluginFactory
  function setLimitOrderManager(address newLimitOrderManager) external override onlyAdministrator {
    limitOrderManager = newLimitOrderManager;
    emit LimitOrderManager(newLimitOrderManager);
  }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';

import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPlugin.sol';

import '@cryptoalgebra/dynamic-fee-plugin/contracts/DynamicFeePlugin.sol';
import '@cryptoalgebra/farming-proxy-plugin/contracts/FarmingProxyPlugin.sol';
import '@cryptoalgebra/volatility-oracle-plugin/contracts/VolatilityOraclePlugin.sol';
import '@cryptoalgebra/alm-plugin/contracts/AlmPlugin.sol';
import '@cryptoalgebra/safety-switch-plugin/contracts/SecurityPlugin.sol';
import '@cryptoalgebra/limit-order-plugin/contracts/LimitOrderPlugin.sol';

/// @title Algebra Integral 1.2.2 adaptive fee, security and alm plugin
contract AlgebraDefaultPlugin is DynamicFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin, AlmPlugin, SecurityPlugin, LimitOrderPlugin {
  using Plugins for uint8;
  using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO];

  constructor(
    address _pool,
    address _factory,
    address _pluginFactory,
    AlgebraFeeConfiguration memory _config,
    address _securityRegistry,
    address _limitOrderManager
  ) BaseAbstractPlugin(_pool, _factory, _pluginFactory) DynamicFeePlugin(_config) SecurityPlugin(_securityRegistry) LimitOrderPlugin(_limitOrderManager) {}

  // ###### HOOKS ######

  function beforeInitialize(address, uint160) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4) {
    _updatePluginConfigInPool(defaultPluginConfig);
    return IAlgebraPlugin.beforeInitialize.selector;
  }

  function afterInitialize(address, uint160, int24 tick) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4) {
    _initialize_TWAP(tick);
    return IAlgebraPlugin.afterInitialize.selector;
  }

  /// @dev unused
  function beforeModifyPosition(address, address, int24, int24, int128 liquidity, bytes calldata) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4, uint24) {
    if (liquidity < 0) {
      _checkStatusOnBurn();
    } else {
      _checkStatus();
    }
    return (IAlgebraPlugin.beforeModifyPosition.selector, 0);
  }

  /// @dev unused
  function afterModifyPosition(address, address, int24, int24, int128, uint256, uint256, bytes calldata) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4) {
    _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config
    return IAlgebraPlugin.afterModifyPosition.selector;
  }

  function beforeSwap(address, address, bool, int256, uint160, bool, bytes calldata) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4, uint24, uint24) {
    _writeTimepoint();
    _checkStatus();
    uint88 volatilityAverage = _getAverageVolatilityLast();
    uint24 fee = _getCurrentFee(volatilityAverage);
    return (IAlgebraPlugin.beforeSwap.selector, fee, 0);
  }

  function afterSwap(address, address, bool zeroToOne, int256, uint160, int256, int256, bytes calldata) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4) {
    if (rebalanceManager != address(0) && _ableToGetTimepoints(slowTwapPeriod)){
      ( , int24 currentTick, , ) = _getPoolState();
      uint32 lastBlockTimestamp = _getLastBlockTimestamp();

      int24 slowTwapTick = _getTwapTick(slowTwapPeriod);
      int24 fastTwapTick = _getTwapTick(fastTwapPeriod);

      _obtainTWAPAndRebalance(currentTick, slowTwapTick, fastTwapTick, lastBlockTimestamp);
    }

    _updateLimitOrderManagerState(pool, zeroToOne);
    
    _updateVirtualPoolTick(zeroToOne);

    return IAlgebraPlugin.afterSwap.selector;
  }

  /// @dev unused
  function beforeFlash(address, address, uint256, uint256, bytes calldata) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4) {
    _checkStatus();
    return IAlgebraPlugin.beforeFlash.selector;
  }

  /// @dev unused
  function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external override(AbstractPlugin, IAlgebraPlugin) onlyPool returns (bytes4) {
    _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config
    return IAlgebraPlugin.afterFlash.selector;
  }

  function _getLastBlockTimestamp() private view returns (uint32 blockTimestamp) {
    VolatilityOracle.Timepoint memory lastTimepoint = timepoints[timepointIndex];
    return lastTimepoint.blockTimestamp;
  }

  function _getTwapTick(uint32 period) private view returns (int24 timeWeightedAverageTick) {
    require(period != 0, 'Period is zero');

    uint32[] memory secondAgos = new uint32[](2);
    secondAgos[0] = period;
    secondAgos[1] = 0;

    (, int24 tick, , ) = _getPoolState();
    (int56[] memory tickCumulatives, ) = timepoints.getTimepoints(_blockTimestamp(), secondAgos, tick, timepointIndex);

    int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];

    timeWeightedAverageTick = int24(tickCumulativesDelta / int56(uint56(period)));

    // Always round to negative infinity
    if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int56(uint56(period)) != 0)) timeWeightedAverageTick--;
  }

  function _ableToGetTimepoints(uint32 period) private view returns (bool) {
    uint16 lastIndex = timepoints.getOldestIndex(timepointIndex);
    uint32 oldestTimestamp = timepoints[lastIndex].blockTimestamp;

    return VolatilityOracle._lteConsideringOverflow(oldestTimestamp, _blockTimestamp() - period, _blockTimestamp());
  }

  function getCurrentFee() external view override returns (uint16 fee) {
    uint88 volatilityAverage = _getAverageVolatilityLast();
    fee = _getCurrentFee(volatilityAverage);
  }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPluginFactory.sol';
import '@cryptoalgebra/dynamic-fee-plugin/contracts/interfaces/IDynamicFeePluginFactory.sol';
import '@cryptoalgebra/farming-proxy-plugin/contracts/interfaces/IFarmingPluginFactory.sol';
import '@cryptoalgebra/safety-switch-plugin/contracts/interfaces/ISecurityPluginFactory.sol';
import '@cryptoalgebra/limit-order-plugin/contracts/interfaces/ILimitOrderPluginFactory.sol';

/// @title The interface for the IAlgebraCustomPluginFactory
interface IAlgebraCustomPluginFactory is IAlgebraPluginFactory, IDynamicFeePluginFactory, IFarmingPluginFactory, ISecurityPluginFactory, ILimitOrderPluginFactory {

  /// @notice The hash of 'ALGEBRA_CUSTOM_PLUGIN_ADMINISTRATOR' used as role
  /// @dev allows to change settings of AlgebraALMCustomPluginFactory
  function ALGEBRA_CUSTOM_PLUGIN_ADMINISTRATOR() external pure returns (bytes32);

  /// @notice Returns the address of AlgebraFactory
  /// @return The AlgebraFactory contract address
  function algebraFactory() external view returns (address);

  /// @notice Returns the address of entryPoint
  /// @return The entryPoint contract address
  function entryPoint() external view returns (address);

  /// @notice Returns address of plugin created for given AlgebraPool
  /// @param pool The address of AlgebraPool
  /// @return The address of corresponding plugin
  function pluginByPool(address pool) external view returns (address);

  /// @notice Create custom pool
  function createCustomPool(address creator, address tokenA, address tokenB, bytes calldata data) external returns (address customPool);

}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import '@cryptoalgebra/farming-proxy-plugin/contracts/interfaces/IFarmingPluginFactory.sol';
import '@cryptoalgebra/dynamic-fee-plugin/contracts/interfaces/IDynamicFeePluginFactory.sol';

import '@cryptoalgebra/abstract-plugin/contracts/interfaces/IBasePluginFactory.sol';
import '@cryptoalgebra/safety-switch-plugin/contracts/interfaces/ISecurityPluginFactory.sol';
import '@cryptoalgebra/limit-order-plugin/contracts/interfaces/ILimitOrderPluginFactory.sol';

/// @title The interface for the AlgebraDefaultPluginFactory
/// @notice This contract creates Algebra default plugins for Algebra liquidity pools
interface IAlgebraDefaultPluginFactory is IBasePluginFactory, IFarmingPluginFactory, IDynamicFeePluginFactory, ISecurityPluginFactory, ILimitOrderPluginFactory {

  /// @notice The hash of 'ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR' used as role
  /// @dev allows to change settings of AlgebraDefaultPluginFactory
  function ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR() external pure returns (bytes32);

}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Algebra base plugin V1 oracle frontend
/// @notice Provides data from oracle corresponding pool
/// @dev These functions are not very gas efficient and it is better not to use them on-chain
interface IOracleTWAP {
  /// @notice The address of the factory of plugins that are used as oracles by this contract
  function pluginFactory() external view returns (address);

  /// @notice Given a tick and a token amount, calculates the amount of token received in exchange
  /// @dev Should not be used as quote for swap
  /// @param tick Tick value used to calculate the quote
  /// @param baseAmount Amount of token to be converted
  /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination
  /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination
  /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken
  function getQuoteAtTick(int24 tick, uint128 baseAmount, address baseToken, address quoteToken) external pure returns (uint256 quoteAmount);

  /// @notice Fetches time-weighted average tick using Algebra VolatilityOracle
  /// @dev Oracle may stop receiving data from the pool (be disconnected). For that reason it is important
  /// not to rely on the absolute accuracy and availability at any time of this oracle.
  /// It is recommended to check the latest available timestamp using the `latestTimestamp` method and don't use the data if the last entry is too old
  /// @param pool The address of Algebra Integral pool
  /// @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
  /// @return isConnected Is oracle currently connected to the pool. If disconnected data can be obsolete
  function getAverageTick(address pool, uint32 period) external view returns (int24 timeWeightedAverageTick, bool isConnected);

  /// @notice Returns the last timestamp written in the oracle
  function latestTimestamp(address pool) external view returns (uint32);

  /// @notice Returns the oldest timestamp available in the oracle
  function oldestTimestamp(address pool) external view returns (uint32 _oldestTimestamp);

  /// @notice Returns the index of last record written in the oracle
  function latestIndex(address pool) external view returns (uint16);

  /// @notice Returns the index of oldest record available in the oracle
  function oldestIndex(address pool) external view returns (uint16);

  /// @notice Whether or not the oracle is connected to the liquidity pool
  /// @dev Oracle should not be used if disconnected from pool
  function isOracleConnected(address pool) external view returns (bool connected);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.20;
pragma abicoder v1;

import '../interfaces/IAlgebraDefaultPluginFactory.sol';
import '../interfaces/IOracleTWAP.sol';

import '@cryptoalgebra/volatility-oracle-plugin/contracts/interfaces/IVolatilityOracle.sol';
import '@cryptoalgebra/volatility-oracle-plugin/contracts/libraries/VolatilityOracleInteractions.sol';

/// @title Algebra Integral 1.2.1 TWAP oracle
/// @notice Provides data from oracle corresponding pool
/// @dev These functions are not very gas efficient and it is better not to use them on-chain
/// @dev Integrates with Volatility Oracle plugin
contract OracleTWAP is IOracleTWAP {
  /// @inheritdoc IOracleTWAP
  address public immutable override pluginFactory;

  constructor(address _pluginFactory) {
    pluginFactory = _pluginFactory;
  }

  /// @inheritdoc IOracleTWAP
  function getQuoteAtTick(
    int24 tick,
    uint128 baseAmount,
    address baseToken,
    address quoteToken
  ) external pure override returns (uint256 quoteAmount) {
    return VolatilityOracleInteractions.getQuoteAtTick(tick, baseAmount, baseToken, quoteToken);
  }

  /// @inheritdoc IOracleTWAP
  function getAverageTick(address pool, uint32 period) external view override returns (int24 timeWeightedAverageTick, bool isConnected) {
    address oracleAddress = _getPluginForPool(pool);
    timeWeightedAverageTick = VolatilityOracleInteractions.consult(oracleAddress, period);
    isConnected = VolatilityOracleInteractions.isOracleConnectedToPool(oracleAddress, pool);
  }

  /// @inheritdoc IOracleTWAP
  function latestTimestamp(address pool) external view override returns (uint32) {
    return IVolatilityOracle(_getPluginForPool(pool)).lastTimepointTimestamp();
  }

  /// @inheritdoc IOracleTWAP
  function oldestTimestamp(address pool) external view override returns (uint32 _oldestTimestamp) {
    address oracle = _getPluginForPool(pool);
    (, _oldestTimestamp) = VolatilityOracleInteractions.oldestTimepointMetadata(oracle);
  }

  /// @inheritdoc IOracleTWAP
  function latestIndex(address pool) external view override returns (uint16) {
    return VolatilityOracleInteractions.latestIndex(_getPluginForPool(pool));
  }

  /// @inheritdoc IOracleTWAP
  function isOracleConnected(address pool) external view override returns (bool connected) {
    connected = VolatilityOracleInteractions.isOracleConnectedToPool(_getPluginForPool(pool), pool);
  }

  /// @inheritdoc IOracleTWAP
  function oldestIndex(address pool) external view override returns (uint16 _oldestIndex) {
    address oracle = _getPluginForPool(pool);
    (_oldestIndex, ) = VolatilityOracleInteractions.oldestTimepointMetadata(oracle);
  }

  function _getPluginForPool(address pool) internal view returns (address) {
    address pluginAddress = IAlgebraDefaultPluginFactory(pluginFactory).pluginByPool(pool);
    require(pluginAddress != address(0), 'Oracle does not exist');
    return pluginAddress;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;
pragma abicoder v1;

import '@cryptoalgebra/integral-core/contracts/libraries/TickManagement.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/Constants.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol';

import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolActions.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolState.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolPermissionedActions.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolErrors.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPlugin.sol';

/// @title Mock of Algebra concentrated liquidity pool for plugins testing
contract MockPool is IAlgebraPoolActions, IAlgebraPoolPermissionedActions, IAlgebraPoolState {
  struct GlobalState {
    uint160 price; // The square root of the current price in Q64.96 format
    int24 tick; // The current tick
    uint16 fee; // The current fee in hundredths of a bip, i.e. 1e-6
    uint8 pluginConfig;
    uint16 communityFee; // The community fee represented as a percent of all collected fee in thousandths (1e-3)
    bool unlocked; // True if the contract is unlocked, otherwise - false
  }

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

  /// @inheritdoc IAlgebraPoolState
  int24 public override nextTickGlobal;
  /// @inheritdoc IAlgebraPoolState
  int24 public override prevTickGlobal;

  /// @inheritdoc IAlgebraPoolState
  uint128 public override liquidity;
  /// @inheritdoc IAlgebraPoolState
  int24 public override tickSpacing;
  /// @inheritdoc IAlgebraPoolState
  uint32 public override lastFeeTransferTimestamp;

  /// @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

  /// @inheritdoc IAlgebraPoolState
  address public override plugin;

  address public override communityVault;

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

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

  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;

  address owner;
  uint24 public overrideFee;
  uint24 public pluginFee;

  /// @inheritdoc IAlgebraPoolState
  function getCommunityFeePending() external pure override returns (uint128, uint128) {
    revert('not implemented');
  }

  /// @inheritdoc IAlgebraPoolState
  function getPluginFeePending() external pure override returns (uint128, uint128) {
    revert('not implemented');
  }

  /// @inheritdoc IAlgebraPoolState
  function fee() external pure returns (uint16) {
    revert('not implemented');
  }

  /// @inheritdoc IAlgebraPoolState
  function safelyGetStateOfAMM() external pure override returns (uint160, int24, uint16, uint8, uint128, int24, int24) {
    revert('not implemented');
  }

  constructor() {
    globalState.fee = Constants.INIT_DEFAULT_FEE;
    globalState.unlocked = true;
    owner = msg.sender;
  }

  /// @inheritdoc IAlgebraPoolState
  function getReserves() external pure override returns (uint128, uint128) {
    revert('not implemented');
  }

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

  /// @inheritdoc IAlgebraPoolActions
  function initialize(uint160 initialPrice) external override {
    int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); // getTickAtSqrtRatio checks validity of initialPrice inside

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

    tickSpacing = 60;

    uint8 pluginConfig = globalState.pluginConfig;

    globalState.price = initialPrice;
    globalState.tick = tick;

    if (pluginConfig & Plugins.AFTER_INIT_FLAG != 0) {
      IAlgebraPlugin(plugin).afterInitialize(msg.sender, initialPrice, tick);
    }
  }

  /// @inheritdoc IAlgebraPoolActions
  function mint(
    address,
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 liquidityDesired,
    bytes calldata data
  ) external override returns (uint256, uint256, uint128) {
    if (globalState.pluginConfig & Plugins.BEFORE_POSITION_MODIFY_FLAG != 0) {
      IAlgebraPlugin(plugin).beforeModifyPosition(msg.sender, recipient, bottomTick, topTick, int128(liquidityDesired), data);
    }

    if (globalState.pluginConfig & Plugins.AFTER_POSITION_MODIFY_FLAG != 0) {
      IAlgebraPlugin(plugin).afterModifyPosition(msg.sender, recipient, bottomTick, topTick, int128(liquidityDesired), 0, 0, data);
    }
    return (0, 0, 0);
  }

  /// @inheritdoc IAlgebraPoolActions
  function burn(int24 bottomTick, int24 topTick, uint128 liquidityDesired, bytes calldata data) external override returns (uint256, uint256) {
    if (globalState.pluginConfig & Plugins.BEFORE_POSITION_MODIFY_FLAG != 0) {
      IAlgebraPlugin(plugin).beforeModifyPosition(msg.sender, msg.sender, bottomTick, topTick, -int128(liquidityDesired), data);
    }

    if (globalState.pluginConfig & Plugins.AFTER_POSITION_MODIFY_FLAG != 0) {
      IAlgebraPlugin(plugin).afterModifyPosition(msg.sender, msg.sender, bottomTick, topTick, -int128(liquidityDesired), 0, 0, data);
    }
    return (0, 0);
  }

  /// @inheritdoc IAlgebraPoolActions
  function collect(address, int24, int24, uint128, uint128) external pure override returns (uint128, uint128) {
    revert('Not implemented');
  }

  /// @inheritdoc IAlgebraPoolActions
  function swap(address, bool, int256, uint160, bytes calldata) external pure override returns (int256, int256) {
    revert('Not implemented');
  }

  function swapToTick(int24 targetTick) external {
    IAlgebraPlugin _plugin = IAlgebraPlugin(plugin);
    if (globalState.pluginConfig & Plugins.BEFORE_SWAP_FLAG != 0) {
      (, overrideFee, pluginFee) = _plugin.beforeSwap(msg.sender, msg.sender, true, 0, 0, false, '');
    }

    globalState.price = TickMath.getSqrtRatioAtTick(targetTick);
    globalState.tick = targetTick;

    if (globalState.pluginConfig & Plugins.AFTER_SWAP_FLAG != 0) {
      _plugin.afterSwap(msg.sender, msg.sender, true, 0, 0, 0, 0, '');
    }
  }

  /// @inheritdoc IAlgebraPoolActions
  function swapWithPaymentInAdvance(address, address, bool, int256, uint160, bytes calldata) external pure override returns (int256, int256) {
    revert('Not implemented');
  }

  /// @inheritdoc IAlgebraPoolActions
  function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external override {
    uint8 pluginConfig = globalState.pluginConfig;
    if (pluginConfig & Plugins.BEFORE_FLASH_FLAG != 0) {
      IAlgebraPlugin(plugin).beforeFlash(msg.sender, recipient, amount0, amount1, data);
    }

    if (pluginConfig & Plugins.AFTER_FLASH_FLAG != 0) {
      IAlgebraPlugin(plugin).afterFlash(msg.sender, recipient, amount0, amount1, 0, 0, data);
    }
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setCommunityFee(uint16 newCommunityFee) external override {
    if (newCommunityFee > Constants.MAX_COMMUNITY_FEE || newCommunityFee == globalState.communityFee)
      revert IAlgebraPoolErrors.invalidNewCommunityFee();
    globalState.communityFee = newCommunityFee;
  }

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

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setPlugin(address newPluginAddress) external override {
    require(msg.sender == owner);
    plugin = newPluginAddress;
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setPluginConfig(uint8 newConfig) external override {
    require(msg.sender == owner || msg.sender == plugin);
    globalState.pluginConfig = newConfig;
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function setFee(uint16 newFee) external override {
    require(msg.sender == owner || msg.sender == plugin);
    bool isDynamicFeeEnabled = globalState.pluginConfig & uint8(Plugins.DYNAMIC_FEE) != 0;
    require(!isDynamicFeeEnabled && msg.sender == owner);

    globalState.fee = newFee;
  }

  function setCommunityVault(address newCommunityVault) external override {
    communityVault = newCommunityVault;
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function sync() external pure override {
    revert('Not implemented');
  }

  /// @inheritdoc IAlgebraPoolPermissionedActions
  function skim() external pure override {
    revert('Not implemented');
  }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.20;

import '../AlgebraDefaultPlugin.sol';

// used for testing time dependent behavior
contract MockTimeAlgebraDefaultPlugin is AlgebraDefaultPlugin {
  using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO];

  // Monday, October 5, 2020 9:00:00 AM GMT-05:00
  uint256 public time = 1601906400;

  constructor(
    address _pool,
    address _factory,
    address _pluginFactory,
    AlgebraFeeConfiguration memory _config,
    address _securityRegistry,
    address _limitOrderManager
  ) AlgebraDefaultPlugin(_pool, _factory, _pluginFactory, _config, _securityRegistry, _limitOrderManager) {}

  function advanceTime(uint256 by) external {
    unchecked {
      time += by;
    }
  }

  function _blockTimestamp() internal view override returns (uint32) {
    return uint32(time);
  }

  struct UpdateParams {
    uint32 advanceTimeBy;
    int24 tick;
  }

  function batchUpdate(UpdateParams[] calldata params) external {
    // sload everything
    uint16 _index = timepointIndex;
    uint32 _time = lastTimepointTimestamp;
    int24 _tick;
    unchecked {
      for (uint256 i; i < params.length; ++i) {
        _time += params[i].advanceTimeBy;
        _tick = params[i].tick;
        (_index, ) = timepoints.write(_index, _time, _tick);
      }
    }

    // sstore everything
    lastTimepointTimestamp = _time;
    timepointIndex = _index;
    time = _time;
  }

  function checkBlockTimestamp() external view returns (bool) {
    require(super._blockTimestamp() == uint32(block.timestamp));
    return true;
  }

  function getTimepointsWithParams(
    uint32 _time,
    uint32[] memory secondsAgos,
    int24 tick,
    uint16 lastIndex
  ) external view returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives) {
    return timepoints.getTimepoints(_time, secondsAgos, tick, lastIndex);
  }

  function getAverageVolatility(uint32 timestamp, int24 tick) public view returns (uint88 volatilityAverage) {
    uint16 index = timepointIndex;
    uint16 oldestIndex = timepoints.getOldestIndex(index);
    return timepoints.getAverageVolatility(timestamp, tick, index, oldestIndex);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '@cryptoalgebra/dynamic-fee-plugin/contracts/types/AlgebraFeeConfiguration.sol';
import '@cryptoalgebra/dynamic-fee-plugin/contracts/libraries/AdaptiveFee.sol';

import './MockTimeAlgebraDefaultPlugin.sol';
import '../interfaces/IAlgebraDefaultPluginFactory.sol';

contract MockTimeDSFactory is IAlgebraDefaultPluginFactory {
  /// @inheritdoc IAlgebraDefaultPluginFactory
  bytes32 public constant override ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR = keccak256('ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR');

  /// @inheritdoc IBasePluginFactory
  address public immutable override algebraFactory;

  /// @dev values of constants for sigmoids in fee calculation formula
  AlgebraFeeConfiguration public override defaultFeeConfiguration;

  /// @inheritdoc IBasePluginFactory
  mapping(address => address) public override pluginByPool;

  /// @inheritdoc IFarmingPluginFactory
  address public override farmingAddress;

  /// @inheritdoc ISecurityPluginFactory
  address public override securityRegistry;

  address public limitOrderManager;

  constructor(address _algebraFactory) {
    algebraFactory = _algebraFactory;
    defaultFeeConfiguration = AdaptiveFee.initialFeeConfiguration();
  }

  /// @inheritdoc IAlgebraPluginFactory
  function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata) external override returns (address) {
    return _createPlugin(pool);
  }

  /// @inheritdoc IAlgebraPluginFactory
  function afterCreatePoolHook(address, address, address) external view override {
    require(msg.sender == algebraFactory);
  }

  /// @inheritdoc IBasePluginFactory
  function createPluginForExistingPool(address token0, address token1) external override returns (address) {
    IAlgebraFactory factory = IAlgebraFactory(algebraFactory);
    require(factory.hasRoleOrOwner(factory.POOLS_ADMINISTRATOR_ROLE(), msg.sender));

    address pool = factory.poolByPair(token0, token1);
    require(pool != address(0), 'Pool not exist');

    return _createPlugin(pool);
  }

  function setPluginForPool(address pool, address plugin) external {
    pluginByPool[pool] = plugin;
  }

  function _createPlugin(address pool) internal returns (address) {
    MockTimeAlgebraDefaultPlugin volatilityOracle = new MockTimeAlgebraDefaultPlugin(pool, algebraFactory, address(this), defaultFeeConfiguration, securityRegistry, limitOrderManager);
    pluginByPool[pool] = address(volatilityOracle);
    return address(volatilityOracle);
  }

  /// @inheritdoc IDynamicFeePluginFactory
  function setDefaultFeeConfiguration(AlgebraFeeConfiguration calldata newConfig) external override {
    AdaptiveFee.validateFeeConfiguration(newConfig);
    defaultFeeConfiguration = newConfig;
    emit DefaultFeeConfiguration(newConfig);
  }

  /// @inheritdoc IFarmingPluginFactory
  function setFarmingAddress(address newFarmingAddress) external override {
    require(farmingAddress != newFarmingAddress);
    farmingAddress = newFarmingAddress;
    emit FarmingAddress(newFarmingAddress);
  }

  /// @inheritdoc ISecurityPluginFactory
  function setSecurityRegistry(address newSecurityRegistry) external override {
    securityRegistry = newSecurityRegistry;
    emit SecurityRegistry(newSecurityRegistry);
  }

  /// @inheritdoc ILimitOrderPluginFactory
  function setLimitOrderManager(address newLimitOrderManager) external override {
    limitOrderManager = newLimitOrderManager;
    emit LimitOrderManager(newLimitOrderManager);
  }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.20;
pragma abicoder v1;

import '@cryptoalgebra/integral-core/contracts/interfaces/IERC20Minimal.sol';

contract TestERC20 is IERC20Minimal {
  mapping(address => uint256) public override balanceOf;
  mapping(address => mapping(address => uint256)) public override allowance;

  constructor(uint256 amountToMint) {
    mint(msg.sender, amountToMint);
  }

  function mint(address to, uint256 amount) public {
    unchecked {
      uint256 balanceNext = balanceOf[to] + amount;
      require(balanceNext >= amount, 'overflow balance');
      balanceOf[to] = balanceNext;
    }
  }

  function transfer(address recipient, uint256 amount) external override returns (bool) {
    unchecked {
      uint256 balanceBefore = balanceOf[msg.sender];
      require(balanceBefore >= amount, 'insufficient balance');
      balanceOf[msg.sender] = balanceBefore - amount;

      uint256 balanceRecipient = balanceOf[recipient];
      require(balanceRecipient + amount >= balanceRecipient, 'recipient balance overflow');
      if (!isDeflationary) {
        balanceOf[recipient] = balanceRecipient + amount;
      } else {
        balanceOf[recipient] = balanceRecipient + (amount - (amount * 5) / 100);
      }

      emit Transfer(msg.sender, recipient, amount);
      return true;
    }
  }

  function approve(address spender, uint256 amount) external override returns (bool) {
    allowance[msg.sender][spender] = amount;
    emit Approval(msg.sender, spender, amount);
    return true;
  }

  bool isDeflationary = false;

  function setDefl() external {
    isDeflationary = true;
  }

  function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
    unchecked {
      uint256 allowanceBefore = allowance[sender][msg.sender];
      require(allowanceBefore >= amount, 'allowance insufficient');

      allowance[sender][msg.sender] = allowanceBefore - amount;

      uint256 balanceRecipient = balanceOf[recipient];
      require(balanceRecipient + amount >= balanceRecipient, 'overflow balance recipient');
      if (!isDeflationary) {
        balanceOf[recipient] = balanceRecipient + amount;
      } else {
        balanceOf[recipient] = balanceRecipient + (amount - (amount * 5) / 100);
      }
      uint256 balanceSender = balanceOf[sender];
      require(balanceSender >= amount, 'underflow balance sender');
      balanceOf[sender] = balanceSender - amount;

      emit Transfer(sender, recipient, amount);
      return true;
    }
  }
}

Settings
{
  "evmVersion": "paris",
  "optimizer": {
    "enabled": true,
    "runs": 0
  },
  "metadata": {
    "bytecodeHash": "none"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_algebraFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"alpha1","type":"uint16"},{"internalType":"uint16","name":"alpha2","type":"uint16"},{"internalType":"uint32","name":"beta1","type":"uint32"},{"internalType":"uint32","name":"beta2","type":"uint32"},{"internalType":"uint16","name":"gamma1","type":"uint16"},{"internalType":"uint16","name":"gamma2","type":"uint16"},{"internalType":"uint16","name":"baseFee","type":"uint16"}],"indexed":false,"internalType":"struct AlgebraFeeConfiguration","name":"newConfig","type":"tuple"}],"name":"DefaultFeeConfiguration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFarmingAddress","type":"address"}],"name":"FarmingAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newLimitOrderManager","type":"address"}],"name":"LimitOrderManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"securityRegistry","type":"address"}],"name":"SecurityRegistry","type":"event"},{"inputs":[],"name":"ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"afterCreatePoolHook","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"algebraFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"beforeCreatePoolHook","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"name":"createPluginForExistingPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultFeeConfiguration","outputs":[{"internalType":"uint16","name":"alpha1","type":"uint16"},{"internalType":"uint16","name":"alpha2","type":"uint16"},{"internalType":"uint32","name":"beta1","type":"uint32"},{"internalType":"uint32","name":"beta2","type":"uint32"},{"internalType":"uint16","name":"gamma1","type":"uint16"},{"internalType":"uint16","name":"gamma2","type":"uint16"},{"internalType":"uint16","name":"baseFee","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmingAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"limitOrderManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"pluginByPool","outputs":[{"internalType":"address","name":"pluginAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"securityRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"alpha1","type":"uint16"},{"internalType":"uint16","name":"alpha2","type":"uint16"},{"internalType":"uint32","name":"beta1","type":"uint32"},{"internalType":"uint32","name":"beta2","type":"uint32"},{"internalType":"uint16","name":"gamma1","type":"uint16"},{"internalType":"uint16","name":"gamma2","type":"uint16"},{"internalType":"uint16","name":"baseFee","type":"uint16"}],"internalType":"struct AlgebraFeeConfiguration","name":"newConfig","type":"tuple"}],"name":"setDefaultFeeConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFarmingAddress","type":"address"}],"name":"setFarmingAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newLimitOrderManager","type":"address"}],"name":"setLimitOrderManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSecurityRegistry","type":"address"}],"name":"setSecurityRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234801561001057600080fd5b50604051620060a6380380620060a683398101604081905261003191610240565b6001600160a01b0381166080526100466101b8565b80516000805460208401516040808601516060870151608088015160a089015160c09099015161ffff908116600160801b0261ffff60801b199a8216600160701b0261ffff60701b199383166c01000000000000000000000000029390931663ffffffff60601b1963ffffffff958616680100000000000000000263ffffffff60401b19969097166401000000000295909516600160201b600160601b0319988416620100000263ffffffff19909a1693909b16929092179790971795909516979097179190911716919091179390931793909316929092178255517fe04232512a5cb82c08e0f9b1f51432930cd7a0b7ea9f9f916f080cb0b4ac644b916101aa91600060e082019050825461ffff8082168452808260101c16602085015263ffffffff808360201c166040860152808360401c16606086015250808260601c166080850152808260701c1660a0850152808260801c1660c0850152505092915050565b60405180910390a1506102a0565b6040805160e08082018352600080835260208301819052828401819052606083018190526080830181905260a0830181905260c0830152825190810190925290806102066064610bb8610270565b61ffff168152612ee06020820152610168604082015261ea606060820152603b608082015261213460a0820152606460c090910152919050565b60006020828403121561025257600080fd5b81516001600160a01b038116811461026957600080fd5b9392505050565b61ffff82811682821603908082111561029957634e487b7160e01b600052601160045260246000fd5b5092915050565b608051615db2620002f460003960008181610239015281816102e8015281816103310152818161052901528181610643015281816107460152818161078d015281816108b70152610a2e0152615db26000f3fe60806040523480156200001157600080fd5b5060043610620000ca5760003560e01c80631d0338d914620000cf5780632773302614620000fe57806348566a3314620001155780634e09a96a14620001295780635235d1ef14620001c457806364fae8a914620001dd5780638a2ade5814620001f45780638d5ef8d114620002085780639b21f9ae146200021f578063a7b64b041462000233578063b001f618146200025b578063cddff2691462000272578063cdef16f61462000298578063f718949a14620002c4575b600080fd5b620000e6620000e036600462000bcd565b620002db565b604051620000f5919062000cb8565b60405180910390f35b620000e66200010f36600462000ccc565b6200032c565b600354620000e6906001600160a01b031681565b6000546200017b9061ffff8082169162010000810482169163ffffffff600160201b8304811692600160401b810490911691600160601b8204811691600160701b8104821691600160801b9091041687565b6040805161ffff9889168152968816602088015263ffffffff9586169087015293909216606085015284166080840152831660a08301529190911660c082015260e001620000f5565b620001db620001d536600462000d0a565b62000512565b005b620001db620001ee36600462000d0a565b6200062c565b600154620000e6906001600160a01b031681565b620001db6200021936600462000d31565b6200073b565b600254620000e6906001600160a01b031681565b620000e67f000000000000000000000000000000000000000000000000000000000000000081565b620001db6200026c36600462000d0a565b62000776565b6200028960008051602062005d8683398151915281565b604051908152602001620000f5565b620000e6620002a936600462000d0a565b6004602052600090815260409020546001600160a01b031681565b620001db620002d536600462000d83565b620008a0565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146200031357600080fd5b6200031e88620009c0565b90505b979650505050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000009050806001600160a01b031663e8ae2b69826001600160a01b031663b500a48b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003a0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003c6919062000d9c565b336040518363ffffffff1660e01b8152600401620003e692919062000db6565b602060405180830381865afa15801562000404573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200042a919062000dcd565b6200043457600080fd5b60405163d9a641e160e01b81526001600160a01b03858116600483015284811660248301526000919083169063d9a641e190604401602060405180830381865afa15801562000487573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004ad919062000df1565b90506001600160a01b038116620004fc5760405162461bcd60e51b815260206004820152600e60248201526d141bdbdb081b9bdd08195e1a5cdd60921b60448201526064015b60405180910390fd5b6200050781620009c0565b925050505b92915050565b60405163e8ae2b6960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e8ae2b6990620005719060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa1580156200058f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005b5919062000dcd565b620005d45760405162461bcd60e51b8152600401620004f39062000e11565b600380546001600160a01b0319166001600160a01b0383161790556040517f74bc9f04f2c495fde0eb0cf1c70fb6042393608f9e26c69a621bb23061ce5785906200062190839062000cb8565b60405180910390a150565b60405163e8ae2b6960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e8ae2b69906200068b9060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa158015620006a9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006cf919062000dcd565b620006ee5760405162461bcd60e51b8152600401620004f39062000e11565b600280546001600160a01b0319166001600160a01b0383161790556040517f9c2a61186234594f572ba9fd3d6137c9e751c20d5cad21e5c4bf008a768b3657906200062190839062000cb8565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146200077157600080fd5b505050565b60405163e8ae2b6960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e8ae2b6990620007d59060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa158015620007f3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000819919062000dcd565b620008385760405162461bcd60e51b8152600401620004f39062000e11565b6001546001600160a01b038083169116036200085357600080fd5b600180546001600160a01b0319166001600160a01b0383161790556040517f56b9e8342f530796ceed0d5529abdcdeae6e4f2ac1dc456ceb73bbda898e0cd3906200062190839062000cb8565b60405163e8ae2b6960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e8ae2b6990620008ff9060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa1580156200091d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000943919062000dcd565b620009625760405162461bcd60e51b8152600401620004f39062000e11565b6200097d620009773683900383018362000e80565b62000ad1565b8060006200098c828262000f6b565b9050507fe04232512a5cb82c08e0f9b1f51432930cd7a0b7ea9f9f916f080cb0b4ac644b81604051620006219190620010bb565b6001600160a01b038181166000908152600460205260408120549091161562000a1e5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818dc99585d1959608a1b6044820152606401620004f3565b60025460035460405160009285927f000000000000000000000000000000000000000000000000000000000000000092309286926001600160a01b039182169291169062000a6c9062000ba9565b62000a7d9695949392919062001172565b604051809103906000f08015801562000a9a573d6000803e3d6000fd5b506001600160a01b03938416600090815260046020526040902080546001600160a01b031916948216949094179093555090919050565b60c08101516020820151825161ffff9283169162000af5919084169084166200123f565b62000b0191906200123f565b111562000b445760405162461bcd60e51b815260206004820152601060248201526f13585e0819995948195e18d95959195960821b6044820152606401620004f3565b608081015161ffff161580159062000b63575060a081015161ffff1615155b62000ba65760405162461bcd60e51b8152602060048201526012602482015271047616d6d6173206d757374206265203e20360741b6044820152606401620004f3565b50565b614b24806200126283390190565b6001600160a01b038116811462000ba657600080fd5b600080600080600080600060c0888a03121562000be957600080fd5b873562000bf68162000bb7565b9650602088013562000c088162000bb7565b9550604088013562000c1a8162000bb7565b9450606088013562000c2c8162000bb7565b9350608088013562000c3e8162000bb7565b925060a08801356001600160401b038082111562000c5b57600080fd5b818a0191508a601f83011262000c7057600080fd5b81358181111562000c8057600080fd5b8b602082850101111562000c9357600080fd5b60208301945080935050505092959891949750929550565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b6000806040838503121562000ce057600080fd5b823562000ced8162000bb7565b9150602083013562000cff8162000bb7565b809150509250929050565b60006020828403121562000d1d57600080fd5b813562000d2a8162000bb7565b9392505050565b60008060006060848603121562000d4757600080fd5b833562000d548162000bb7565b9250602084013562000d668162000bb7565b9150604084013562000d788162000bb7565b809150509250925092565b600060e0828403121562000d9657600080fd5b50919050565b60006020828403121562000daf57600080fd5b5051919050565b9182526001600160a01b0316602082015260400190565b60006020828403121562000de057600080fd5b8151801515811462000d2a57600080fd5b60006020828403121562000e0457600080fd5b815162000d2a8162000bb7565b60208082526012908201527127b7363c9030b236b4b734b9ba3930ba37b960711b604082015260600190565b61ffff8116811462000ba657600080fd5b803562000e5b8162000e3d565b919050565b63ffffffff8116811462000ba657600080fd5b803562000e5b8162000e60565b600060e0828403121562000e9357600080fd5b60405160e081016001600160401b038111828210171562000ec457634e487b7160e01b600052604160045260246000fd5b60405262000ed28362000e4e565b815262000ee26020840162000e4e565b602082015262000ef56040840162000e73565b604082015262000f086060840162000e73565b606082015262000f1b6080840162000e4e565b608082015262000f2e60a0840162000e4e565b60a082015262000f4160c0840162000e4e565b60c08201529392505050565b600081356200050c8162000e3d565b600081356200050c8162000e60565b813562000f788162000e3d565b61ffff8116905081548161ffff198216178355602084013562000f9b8162000e3d565b63ffff00008160101b169050808363ffffffff19841617178455604085013562000fc58162000e60565b6001600160401b0319929092169092179190911760209190911b63ffffffff60201b161781556200102162000ffd6060840162000f5c565b82805463ffffffff60401b191660409290921b63ffffffff60401b16919091179055565b62001053620010336080840162000f4d565b82805461ffff60601b191660609290921b61ffff60601b16919091179055565b620010856200106560a0840162000f4d565b82805461ffff60701b191660709290921b61ffff60701b16919091179055565b620010b76200109760c0840162000f4d565b82805461ffff60801b191660809290921b61ffff60801b16919091179055565b5050565b60e081018235620010cc8162000e3d565b61ffff9081168352602084013590620010e58262000e3d565b9081166020840152604084013590620010fe8262000e60565b63ffffffff91821660408501526060850135916200111c8362000e60565b919091166060840152608084013590620011368262000e3d565b1660808301526200114a60a0840162000e4e565b61ffff1660a08301526200116160c0840162000e4e565b61ffff811660c08401525092915050565b60006101808201905060018060a01b0380891683528088166020840152808716604084015250845461ffff8082166060850152808260101c16608085015263ffffffff808360201c1660a0860152620011d860c08601828560401c1663ffffffff169052565b50620011ef60e08501828460601c1661ffff169052565b620012066101008501828460701c1661ffff169052565b6200121d6101208501828460801c1661ffff169052565b50506200122f61014083018562000cab565b6200032161016083018462000cab565b808201808211156200050c57634e487b7160e01b600052601160045260246000fdfe60e06040526000805460ff191690553480156200001b57600080fd5b5060405162004b2438038062004b248339810160408190526200003e916200050c565b6001600160a01b0380851660a052808716608052851660c0526000805460ff19811660ff9091161760811790558082846200007981620003d5565b620000ed81600080826000015161ffff166010846020015161ffff16901b6020856040015163ffffffff16901b6040866060015163ffffffff16901b6060876080015161ffff16901b60708860a0015161ffff16901b60808960c0015161ffff16901b171717171717905080915050919050565b600280546001600160901b0319166001600160901b0392909216919091179055600180548082018255600091909152604080518082019091526012815271223cb730b6b4b1902332b29028363ab3b4b760711b602082015260008051602062004b0483398151915290910190620001659082620006d2565b50506000805460ff19811660ff909116176002178155600180548082018255915260408051808201909152601481527f4661726d696e672050726f787920506c7567696e000000000000000000000000602082015260008051602062004b0483398151915290910190620001da9082620006d2565b506000805460ff19811660ff909116176041178155600180548082018255915260408051808201909152601881527f566f6c6174696c697479204f7261636c6520506c7567696e0000000000000000602082015260008051602062004b04833981519152909101906200024e9082620006d2565b506000805460ff19811660ff909116176002178155600180548082018255915260408051808201909152600a81526920a6269028363ab3b4b760b11b602082015260008051602062004b0483398151915290910190620002af9082620006d2565b506201000680546001600160a01b03831664010000000002600160201b600160c01b03199091161790556000805460ff19811660ff909116176015178155600180548082018255915260408051808201909152600f81526e29b2b1bab934ba3c9028363ab3b4b760891b602082015260008051602062004b04833981519152909101906200033e9082620006d2565b50506201000780546001600160a01b0383166001600160a01b03199091161790556000805460ff19811660ff90911617600217815560018054808201825591526040805180820190915260128152712634b6b4ba1027b93232b91028363ab3b4b760711b602082015260008051602062004b0483398151915290910190620003c79082620006d2565b5050505050505050620007c6565b60c08101516020820151825161ffff92831691620003f9919084169084166200079e565b6200040591906200079e565b11156200044c5760405162461bcd60e51b815260206004820152601060248201526f13585e0819995948195e18d95959195960821b60448201526064015b60405180910390fd5b608081015161ffff16158015906200046b575060a081015161ffff1615155b620004ae5760405162461bcd60e51b8152602060048201526012602482015271047616d6d6173206d757374206265203e20360741b604482015260640162000443565b50565b80516001600160a01b0381168114620004c957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b805161ffff81168114620004c957600080fd5b805163ffffffff81168114620004c957600080fd5b6000806000806000808688036101808112156200052857600080fd5b6200053388620004b1565b96506200054360208901620004b1565b95506200055360408901620004b1565b945060e0605f19820112156200056857600080fd5b5060405160e081016001600160401b03811182821017156200058e576200058e620004ce565b6040526200059f60608901620004e4565b8152620005af60808901620004e4565b6020820152620005c260a08901620004f7565b6040820152620005d560c08901620004f7565b6060820152620005e860e08901620004e4565b6080820152620005fc6101008901620004e4565b60a0820152620006106101208901620004e4565b60c08201529250620006266101408801620004b1565b9150620006376101608801620004b1565b90509295509295509295565b600181811c908216806200065857607f821691505b6020821081036200067957634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620006cd57600081815260208120601f850160051c81016020861015620006a85750805b601f850160051c820191505b81811015620006c957828155600101620006b4565b5050505b505050565b81516001600160401b03811115620006ee57620006ee620004ce565b6200070681620006ff845462000643565b846200067f565b602080601f8311600181146200073e5760008415620007255750858301515b600019600386901b1c1916600185901b178555620006c9565b600085815260208120601f198616915b828110156200076f578886015182559484019460019091019084016200074e565b50858210156200078e5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80820180821115620007c057634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c0516142d66200082e600039600061192a015260008181610c110152610cd40152600081816101ea0152818161029c015281816111ae0152818161164501528181611aa601528181611bdd01528181611c9a0152611d0c01526142d66000f3fe608060405234801561001057600080fd5b50600436106101e35760003560e01c8063026b1d5f146101e8578063029c1cb7146102205780630786feb61461025f578063080951411461028257806316f0115b146102975780631d39215e146102be5780631d4632ac146102d15780631e5eb1d0146102e457806320501a911461037157806331b25d1a1461038b578063343d37ff146103ae578063392e53cd146103da57806346b7748b1461040057806348566a331461042057806348b2acdd146104355780635235d1ef146104485780635e2411b21461045b578063636fd8041461049257806364fae8a9146104a5578063689ea370146104b85780636fb5bad1146104d757806374eceae6146104f35780637c1fe0c81461055d5780638129fc1c1461057057806382dd652214610578578063841c6a371461058b57806388f2e862146105b95780638de0a8ee146105ee578063918a1ab0146106015780639cb5a963146106145780639d3a524114610627578063aa6b14bb14610648578063ad1c37431461065b578063b6f78cc91461066d578063d49dda8514610682578063d685201014610695578063da705235146106a8578063e63015f0146106bb578063e72c652d146106ce578063f5985d35146106e1578063f70d9362146106f9575b600080fd5b7f00000000000000000000000000000000000000000000000000000000000000005b604051610217919061367c565b60405180910390f35b61023361022e3660046136fb565b610701565b604080516001600160e01b0319909416845262ffffff9283166020850152911690820152606001610217565b620100055461026f9061ffff1681565b60405161ffff9091168152602001610217565b6102956102903660046137bd565b610759565b005b61020a7f000000000000000000000000000000000000000000000000000000000000000081565b6102956102cc3660046137d8565b6107fb565b60035461020a906001600160a01b031681565b61032960025461ffff80821692601083901c82169263ffffffff602082901c811693604083901c90911692606083901c821692607081901c83169260809190911c1690565b6040805161ffff9889168152968816602088015263ffffffff9586169087015293909216606085015284166080840152831660a08301529190911660c082015260e001610217565b6201000654600160201b90046001600160a01b031661020a565b6103a06000805160206142aa83398151915281565b604051908152602001610217565b6103c16103bc3660046137f0565b6108f6565b6040516001600160e01b03199091168152602001610217565b62010005546103f090600160301b900460ff1681565b6040519015158152602001610217565b61041361040e36600461385e565b610924565b60405161021791906138bd565b620100075461020a906001600160a01b031681565b6102956104433660046137bd565b6109d0565b6102956104563660046138d0565b610a70565b61046e61046936600461390e565b610ac5565b604080516001600160e01b0319909316835262ffffff909116602083015201610217565b6103c16104a03660046139ac565b610b0b565b6102956104b33660046138d0565b610b34565b6000546104c59060ff1681565b60405160ff9091168152602001610217565b620100055461020a90600160381b90046001600160a01b031681565b61050661050136600461385e565b610b92565b60408051971515885263ffffffff909616602088015260069490940b948601949094526001600160581b039091166060850152600290810b60808501529190910b60a083015261ffff1660c082015260e001610217565b61029561056b3660046138d0565b610bfa565b610295610f32565b6103c16105863660046139e5565b611024565b62010005546105a490600160d81b900463ffffffff1681565b60405163ffffffff9091168152602001610217565b6105cc6105c73660046137bd565b611048565b6040805160069390930b83526001600160581b03909116602083015201610217565b6103c16105fc366004613a30565b61109e565b61029561060f3660046138d0565b6110c4565b6103c1610622366004613aab565b6110fa565b61063a610635366004613b9e565b6111f2565b604051610217929190613c4a565b6103c1610656366004613cd1565b61122e565b62010006546105a49063ffffffff1681565b610675611247565b6040516102179190613cf3565b610295610690366004613d55565b611381565b6103c16106a3366004613d9a565b6114c2565b6102956106b6366004613e1d565b6114f1565b6103f06106c93660046138d0565b6115a3565b6102956106dc366004613e4b565b61160b565b62010005546105a49062010000900463ffffffff1681565b61026f61161e565b600080600061070e61163a565b6107166116ae565b61071e61177e565b600061072861187e565b90506000610735826118c0565b63029c1cb760e01b955061ffff169350600092505050985098509895505050505050565b610761611901565b620100055463ffffffff600160d81b909104811690821611156107dd5760405162461bcd60e51b815260206004820152602960248201527f5f6661737454776170506572696f64206d757374206265203c3d20736c6f77546044820152681dd85c14195c9a5bd960ba1b60648201526084015b60405180910390fd5b62010006805463ffffffff191663ffffffff92909216919091179055565b610803611901565b61081a61081536839003830183613e82565b6119a6565b61089b61082c36839003830183613e82565b600080826000015161ffff166010846020015161ffff16901b6020856040015163ffffffff16901b6040866060015163ffffffff16901b6060876080015161ffff16901b60708860a0015161ffff16901b60808960c0015161ffff16901b171717171717905080915050919050565b600280546001600160901b0319166001600160901b03929092169190911790556040517f348d1d6a31fa4a2a3ef887c493549a9500fbb77b5218be0c81a71c553364305a906108eb908390613f39565b60405180910390a150565b600061090061163a565b60005461090f9060ff16611a72565b5063343d37ff60e01b98975050505050505050565b6001818154811061093457600080fd5b90600052602060002001600091509050805461094f90613fde565b80601f016020809104026020016040519081016040528092919081815260200182805461097b90613fde565b80156109c85780601f1061099d576101008083540402835291602001916109c8565b820191906000526020600020905b8154815290600101906020018083116109ab57829003601f168201915b505050505081565b6109d8611901565b620100065463ffffffff9081169082161015610a485760405162461bcd60e51b815260206004820152602960248201527f5f736c6f7754776170506572696f64206d757374206265203e3d2066617374546044820152681dd85c14195c9a5bd960ba1b60648201526084016107d4565b62010005805463ffffffff909216600160d81b0263ffffffff60d81b19909216919091179055565b610a78611901565b6201000780546001600160a01b0319166001600160a01b0383161790556040517f74bc9f04f2c495fde0eb0cf1c70fb6042393608f9e26c69a621bb23061ce5785906108eb90839061367c565b600080610ad061163a565b600085600f0b1215610ae957610ae4611b0e565b610af1565b610af161177e565b50632f1208d960e11b905060005b97509795505050505050565b6000610b1561163a565b600054610b249060ff16611a72565b506318dbf60160e21b5b92915050565b610b3c611901565b620100068054600160201b600160c01b031916600160201b6001600160a01b038416021790556040517f9c2a61186234594f572ba9fd3d6137c9e751c20d5cad21e5c4bf008a768b3657906108eb90839061367c565b600581620100008110610ba457600080fd5b015460ff81169150610100810463ffffffff1690600160281b810460060b90600160601b81046001600160581b031690600160b81b8104600290810b91600160d01b810490910b90600160e81b900461ffff1687565b6001600160a01b03811615801590600090610cac577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c919190614012565b6001600160a01b0316336001600160a01b0316149050610d6b565b6004546001600160a01b031615610ccd57506004546001600160a01b031633145b80610d6b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d549190614012565b6001600160a01b0316336001600160a01b03161490505b80610db75760405162461bcd60e51b815260206004820152601c60248201527b4e6f7420616c6c6f77656420746f2073657420696e63656e7469766560201b60448201526064016107d4565b600030610dc2611bd9565b6001600160a01b03161490508215610df15780610df15760405162461bcd60e51b81526004016107d49061402f565b6003546001600160a01b039081169085168103610e415760405162461bcd60e51b815260206004820152600e60248201526d416c72656164792061637469766560901b60448201526064016107d4565b8315610e95576001600160a01b03811615610e955760405162461bcd60e51b81526020600482015260146024820152734861732061637469766520696e63656e7469766560601b60448201526064016107d4565b600380546001600160a01b0319166001600160a01b0387161790556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490610ee090879061367c565b60405180910390a18315610f0557600480546001600160a01b03191633179055610f16565b600480546001600160a01b03191690555b8115610f2b57600054610f2b9060ff16611c62565b5050505050565b6201000554600160301b900460ff1615610f845760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016107d4565b30610f8d611bd9565b6001600160a01b031614610fb35760405162461bcd60e51b81526004016107d49061402f565b600080610fbe611d04565b505091509150816001600160a01b03166000036110175760405162461bcd60e51b8152602060048201526017602482015276141bdbdb081a5cc81b9bdd081a5b9a5d1a585b1a5e9959604a1b60448201526064016107d4565b61102081611d9c565b5050565b600061102e61163a565b61103782611d9c565b5063416eb29160e11b5b9392505050565b6000806000611055611d04565b5050620100055490925061ffff1690506000611072600583611de6565b9050600061108560054289878787611e19565b6040810151606090910151909890975095505050505050565b60006110a861163a565b6110b061177e565b506346f0547760e11b5b9695505050505050565b6110cc611901565b6201000580546001600160a01b03909216600160381b02600160381b600160d81b0319909216919091179055565b600061110461163a565b6201000554600160381b90046001600160a01b03161580159061113d5750620100055461113d90600160d81b900463ffffffff16612063565b156111a957600061114c611d04565b5050915050600061115b6120c5565b9050600061117c62010005601b9054906101000a900463ffffffff1661216d565b62010006549091506000906111969063ffffffff1661216d565b90506111a4848383866122fe565b505050505b6111d37f000000000000000000000000000000000000000000000000000000000000000089612389565b6111dc886123f1565b50639cb5a96360e01b9998505050505050505050565b60608060006111ff611d04565b505091505061122461120e4290565b6201000554600591908790859061ffff1661248f565b9250925050915091565b600061123861163a565b5063aa6b14bb60e01b92915050565b6001546060906001600160401b0381111561126457611264613b58565b60405190808252806020026020018201604052801561129757816020015b60608152602001906001900390816112825790505b50905060005b60015481101561137d57600181815481106112ba576112ba61405c565b9060005260206000200180546112cf90613fde565b80601f01602080910402602001604051908101604052809291908181526020018280546112fb90613fde565b80156113485780601f1061131d57610100808354040283529160200191611348565b820191906000526020600020905b81548152906001019060200180831161132b57829003601f168201915b505050505082828151811061135f5761135f61405c565b6020026020010181905250808061137590614088565b91505061129d565b5090565b611389611901565b6001600160a01b0383166113f25760405162461bcd60e51b815260206004820152602a60248201527f5f726562616c616e63654d616e61676572206d757374206265206e6f6e207a65604482015269726f206164647265737360b01b60648201526084016107d4565b8063ffffffff168263ffffffff1610156114615760405162461bcd60e51b815260206004820152602a60248201527f5f736c6f7754776170506572696f64206d757374206265203e3d205f66617374604482015269151dd85c14195c9a5bd960b21b60648201526084016107d4565b620100058054600160381b600160f81b031916600160381b6001600160a01b03959095169490940263ffffffff60d81b191693909317600160d81b63ffffffff938416021790925562010006805463ffffffff191692909116919091179055565b60006114cc61163a565b6000546114db9060ff16611a72565b50630d68520160e41b9998505050505050505050565b60058261ffff1662010000811061150a5761150a61405c565b015460ff161561151957600080fd5b60008161ffff1611801561154257508061ffff168261ffff61153b91906140a1565b61ffff1610155b61154b57600080fd5b61ffff82165b81830161ffff1681101561159e5760016005826201000081106115765761157661405c565b01805463ffffffff929092166101000264ffffffff0019909216919091179055600101611551565b505050565b6003546000906001600160a01b038381169116146115c357506000919050565b306115cc611bd9565b6001600160a01b0316146115e257506000919050565b60006115ec611d04565b9350505050600281166116025750600092915050565b50600192915050565b611613611901565b61159e8382846125df565b60008061162961187e565b9050611634816118c0565b91505090565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146116ac5760405162461bcd60e51b81526020600482015260176024820152764f6e6c7920706f6f6c2063616e2063616c6c207468697360481b60448201526064016107d4565b565b620100055461ffff81169062010000810463ffffffff1690600160301b900460ff168061170f5760405162461bcd60e51b815260206004820152600f60248201526e139bdd081a5b9a5d1a585b1a5e9959608a1b60448201526064016107d4565b4263ffffffff808216908416036117265750505050565b6000611730611d04565b509092506000915061174790506005878585612643565b5062010005805463ffffffff909516620100000265ffffffffffff1990951661ffff90921691909117939093179092555050505050565b6201000654600160201b90046001600160a01b0316156116ac5762010006546040516315d9d2f960e01b8152600091600160201b90046001600160a01b0316906315d9d2f9906117d290339060040161367c565b6020604051808303816000875af11580156117f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181591906140bc565b9050600081600281111561182b5761182b6140dd565b1461187b576002816002811115611844576118446140dd565b03611862576040516306756ca560e21b815260040160405180910390fd5b604051632930fa1560e11b815260040160405180910390fd5b50565b6000428161188a611d04565b5050620100055490925061ffff16905060006118a7600583611de6565b90506118b760058585858561287b565b94505050505090565b6002546000906001600160901b0381169060101c61ffff1661ffff82161761ffff166000036118f757608081901c61ffff16611041565b6110418382612add565b60405163e8ae2b6960e01b81526000805160206142aa83398151915260048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e8ae2b6990604401602060405180830381865afa158015611979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199d91906140f3565b6116ac57600080fd5b60c08101516020820151825161ffff928316916119c891908416908416614110565b6119d29190614110565b1115611a135760405162461bcd60e51b815260206004820152601060248201526f13585e0819995948195e18d95959195960821b60448201526064016107d4565b608081015161ffff1615801590611a31575060a081015161ffff1615155b61187b5760405162461bcd60e51b8152602060048201526012602482015271047616d6d6173206d757374206265203e20360741b60448201526064016107d4565b6000611a7c611d04565b93505050508160ff168160ff16146110205760405163bca57f8160e01b815260ff831660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063bca57f8190602401600060405180830381600087803b158015611af257600080fd5b505af1158015611b06573d6000803e3d6000fd5b505050505050565b6201000654600160201b90046001600160a01b0316156116ac5762010006546040516315d9d2f960e01b8152600091600160201b90046001600160a01b0316906315d9d2f990611b6290339060040161367c565b6020604051808303816000875af1158015611b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba591906140bc565b90506002816002811115611bbb57611bbb6140dd565b0361187b576040516306756ca560e21b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef01df4f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5d9190614012565b905090565b6000611c6c611d04565b93505050828217905060ff8083169082161461159e5760405163bca57f8160e01b815260ff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063bca57f81906024015b600060405180830381600087803b158015611ce757600080fd5b505af1158015611cfb573d6000803e3d6000fd5b50505050505050565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e76c01e46040518163ffffffff1660e01b815260040160c060405180830381865afa158015611d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8c9190614123565b5093989297509095509350915050565b42611da960058284612b70565b62010005805460ff60301b1963ffffffff841662010000021666ffffffffff00001990911617600160301b1790556000546110209060ff16611c62565b6000600182018361ffff8216620100008110611e0457611e0461405c565b015460ff1615611e12578091505b5092915050565b611e21613640565b84860360008080611e358b8b868a8a612bf4565b506040805160e081018252845460ff811615158252610100810463ffffffff90811660208401819052600160281b830460060b94840194909452600160601b82046001600160581b03166060840152600160b81b8204600290810b6080850152600160d01b8304900b60a0840152600160e81b90910461ffff1660c0830152909950939650919450925090851603611ed057505050506110ba565b8015611f0e57600080611ef08d878c8c8c8c602001518d60400151612db6565b91509150611f0187878c8585612ddd565b96505050505050506110ba565b815463ffffffff6101008204811691600160281b900460060b908616829003611fb75750506040805160e081018252925460ff811615158452610100810463ffffffff166020850152600160281b810460060b91840191909152600160601b81046001600160581b03166060840152600160b81b8104600290810b6080850152600160d01b8204900b60a0840152600160e81b900461ffff1660c08301525092506110ba915050565b602087015160408801518184039188039063ffffffff80831691908416600690810b918603900b81611feb57611feb6141b0565b60408c01805192909105929092020160060b90526060890151865463ffffffff80841692908516916001600160581b03600160601b9091048116919091031681612037576120376141b0565b60608c0180516001600160581b0393909204939093020116905250505050505050509695505050505050565b6201000554600090819061207d9060059061ffff16611de6565b9050600060058261ffff1662010000811061209a5761209a61405c565b0154610100900463ffffffff1690506120bd816120b786426141c6565b42612e64565b949350505050565b6201000554600090819060059061ffff166201000081106120e8576120e861405c565b6040805160e081018252919092015460ff811615158252610100810463ffffffff1660208301819052600160281b820460060b93830193909352600160601b81046001600160581b03166060830152600160b81b8104600290810b6080840152600160d01b8204900b60a0830152600160e81b900461ffff1660c09091015292915050565b60008163ffffffff166000036121b65760405162461bcd60e51b815260206004820152600e60248201526d506572696f64206973207a65726f60901b60448201526064016107d4565b60408051600280825260608201835260009260208301908036833701905050905082816000815181106121eb576121eb61405c565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061221a5761221a61405c565b602002602001019063ffffffff16908163ffffffff1681525050600061223e611d04565b5050915050600061226561224f4290565b6201000554600591908690869061ffff1661248f565b50905060008160008151811061227d5761227d61405c565b6020026020010151826001815181106122985761229861405c565b60200260200101516122aa91906141e3565b90506122bc63ffffffff871682614210565b945060008160060b1280156122e257506122dc63ffffffff87168261424e565b60060b15155b156122f557846122f181614270565b9550505b50505050919050565b6201000554604051637e7b25f160e01b8152600286810b600483015285810b602483015284900b604482015263ffffffff83166064820152600160381b9091046001600160a01b031690637e7b25f190608401600060405180830381600087803b15801561236b57600080fd5b505af115801561237f573d6000803e3d6000fd5b5050505050505050565b62010007546001600160a01b0316156110205760006123a6611d04565b50506201000754604051633f09ca0560e11b81526001600160a01b0387811660048301528615156024830152600284900b604483015292945091169150637e13940a90606401611ccd565b6003546001600160a01b0316801561102057600061240d611d04565b505060405163034d335960e41b8152600282900b600482015285151560248201529092506001600160a01b03841691506334d33590906044016020604051808303816000875af1158015612465573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248991906140f3565b50505050565b82516060908190806001600160401b038111156124ae576124ae613b58565b6040519080825280602002602001820160405280156124d7578160200160208202803683370190505b509250806001600160401b038111156124f2576124f2613b58565b60405190808252806020026020018201604052801561251b578160200160208202803683370190505b509150600061252a8986611de6565b9050612534613640565b60005b838110156125d1576125668b8b8b84815181106125565761255661405c565b60200260200101518b8b88611e19565b9150816040015182606001518783815181106125845761258461405c565b6020026020010187848151811061259d5761259d61405c565b60200260200101826001600160581b03166001600160581b03168152508260060b60060b8152505050806001019050612537565b505050509550959350505050565b600060405163a9059cbb60e01b6000526001600160a01b03841660045282602452602060006044600080895af19150813d1560203d14600160005114161716915080604052508061248957604051637232c81f60e11b815260040160405180910390fd5b6000806000868661ffff166201000081106126605761266061405c565b6040805160e081018252919092015460ff811615158252610100810463ffffffff90811660208401819052600160281b830460060b94840194909452600160601b82046001600160581b03166060840152600160b81b8204600290810b6080850152600160d01b8304900b60a0840152600160e81b90910461ffff1660c0830152909250861690036126f9578560009250925050612872565b856001019250868361ffff166201000081106127175761271761405c565b015460ff1615612725578291505b60008061273f8988888b8888602001518960400151612db6565b915091508461ffff168161ffff1603612756576001015b6127638388888585612ddd565b898661ffff1662010000811061277b5761277b61405c565b825191018054602084015160408501516060860151608087015160a088015160c09098015164ffffffffff1990951696151564ffffffff0019169690961761010063ffffffff9094169390930292909217600160281b600160b81b031916600160281b66ffffffffffffff90921691909102600160601b600160b81b03191617600160601b6001600160581b03909216919091021765ffffffffffff60b81b1916600160b81b62ffffff9485160262ffffff60d01b191617600160d01b93909416929092029290921761ffff60e81b1916600160e81b61ffff92831602179091558581169085160361286e576001909301925b5050505b94509492505050565b600080868461ffff166201000081106128965761289661405c565b018054909150610100810463ffffffff90811690881614906001600160581b03600160601b8204169061ffff600160e81b90910416826128e2576128df8a8a60008b8b8b612e8c565b91505b60008a8761ffff166201000081106128fc576128fc61405c565b0154610100900463ffffffff16905061291c816201517f198c018c612e64565b15612a475760008415612a15578b8361ffff166201000081106129415761294161405c565b0154610100900463ffffffff168c61ffff85166201000081106129665761296661405c565b0154909250600160601b90046001600160581b031690506000828d6001860161ffff1662010000811061299b5761299b61405c565b015463ffffffff610100909104811691909103915081811690848e036201517f190116838f61ffff60018901166201000081106129da576129da61405c565b01600001600c9054906101000a90046001600160581b031603026001600160581b031681612a0a57612a0a6141b0565b048201915050612a29565b612a268c8c620151808d8d8d612e8c565b90505b620151806001600160581b0382860316049650505050505050612ad4565b8063ffffffff168a63ffffffff1614612ace5760008b8861ffff16620100008110612a7457612a7461405c565b0154600160601b90046001600160581b03169050818b03600163ffffffff82161115612a9f57600019015b8063ffffffff168286036001600160581b031681612abf57612abf6141b0565b04975050505050505050612ad4565b50505050505b95945050505050565b6000600f6001600160581b0384160492506000612b266001600160581b038516607085901c61ffff16601086901c61ffff16604087901c63ffffffff165b63ffffffff16612fc5565b612b516001600160581b038616606086901c61ffff1661ffff8716602088901c63ffffffff16612b1b565b01905061ffff608084901c81168201908111156120bd576120bd614293565b8254839060ff1615612b95576040516308ab11a360e11b815260040160405180910390fd5b8054600165ffffffffffff60b81b19909116600160d01b62ffffff9490941693840262ffffff60b81b191617600160b81b939093029290921764ffffffffff191661010063ffffffff949094169390930260ff19169290921717905550565b6000806000806000898761ffff16620100008110612c1457612c1461405c565b01805490915063ffffffff610100820481169161ffff600160e81b90910416908a8116908c161480612c4c5750612c4c828b8d612e64565b15612c68575090945084935060019250505061ffff8516612daa565b6000620151808b840363ffffffff1611612c9657819850620151808c0363ffffffff168b63ffffffff161490505b60008d8a61ffff16620100008110612cb057612cb061405c565b0154610100900463ffffffff169050612cca818d8f612e64565b612ce65760405162bc60f160e41b815260040160405180910390fd5b8b63ffffffff168163ffffffff1603612d46578d8a61ffff16620100008110612d1157612d1161405c565b018e8b61ffff16620100008110612d2a57612d2a61405c565b919a50019750600196505061ffff89169450612daa9350505050565b8960010161ffff168b61ffff1603612d8b578d8a61ffff16620100008110612d7057612d7061405c565b019850939650600095505061ffff88169350612daa92505050565b50612d9a8d8d8d8d8d86613079565b9199509750600096509450505050505b95509550955095915050565b600080600080612dcb8b8b8b8b8b8b8b6130c7565b909c909b509950505050505050505050565b612de5613640565b6020860180516001885263ffffffff878116909252604088018051918803928316600288810b82810290940160060b90925260a08a0151612e2f9391928291810b9089900b6131ff565b606088018051919091016001600160581b031690525050600292830b6080860152910b60a084015261ffff1660c08301525090565b63ffffffff808216848216811091841611810361104157505063ffffffff9081169116111590565b6000848603818080612ea18b8b868a8a612bf4565b508254929550909350915063ffffffff61010082048116916001600160581b03600160601b90910416908616829003612ee15795506110ba945050505050565b8215612f48576000612f098e888d8d8d888c60000160059054906101000a900460060b612db6565b508654909150612f389063ffffffff858a03169060028e810b918291600160d01b909104810b9086900b6131ff565b82019750505050505050506110ba565b835463ffffffff61010082048116916001600160581b03600160601b90910416908816829003612f815797506110ba9650505050505050565b83820384890363ffffffff8082169083166001600160581b038786031681612fab57612fab6141b0565b040285019a50505050505050505050509695505050505050565b6000818511156130255781850394508361ffff166006028510612fed575061ffff82166120bd565b600461ffff85160a600061300287878461325a565b9050808201818661ffff16028161301b5761301b6141b0565b04925050506120bd565b93810393600661ffff851602851061303f575060006120bd565b600461ffff85160a600061305487878461325a565b8201905080828661ffff16028161306d5761306d6141b0565b04979650505050505050565b6000808061ffff80861690829088168211613098578761ffff166130a3565b620100008861ffff16015b90506130b38b8b8b85858b6133d0565b919d909c50909a5098505050505050505050565b6000806000808a8761ffff166201000081106130e5576130e561405c565b0154610100900463ffffffff168b61ffff891662010000811061310a5761310a61405c565b0154909250600160281b900460060b9050600289900b63ffffffff878c031602850161313d836201517f198d018d612e64565b61319e578263ffffffff168b63ffffffff160361316957505050600287900b91505061ffff8416610aff565b828b0363ffffffff1660060b82820360060b81613188576131886141b0565b0560060b94505061ffff87169250610aff915050565b6131ae87620151808d038d612e64565b156131c857505050600287900b91505061ffff8516610aff565b60006131db8d8d620151808e8e8e613541565b955090506201518081830360060b0560060b95505050505097509795505050505050565b600082820385850303838603870260018089018902906002808b029091018202906006818c0a81029180870a8402868802860283020190860a8d02909102018161324b5761324b6141b0565b059a9950505050505050505050565b60008083850480801561329957600181146132aa57600281146132bb57600381146132cc57600481146132dd576903248ca20bb7b5032f5f92506132eb565b68056bc75e2d6310000092506132eb565b680ebc5fb4174612111092506132eb565b68280e60114edb805d0392506132eb565b686ce2482d63ef54b74c92506132eb565b690127fa27722cc06cc5e392505b50509383900693600261ffff85160461ffff16851061333b57600261ffff85160461ffff168503945068056bc75e2d63100000816808f00f760a4b2db55c0281613337576133376141b0565b0490505b8291508461ffff85168381613352576133526141b0565b049350838102830192508461ffff16848161336f5761336f6141b0565b0493508502600281850204830192508461ffff168481613391576133916141b0565b04935085026018600461ffff871683020282880201048301925068056bc75e2d63100000828402816133c5576133c56141b0565b049695505050505050565b60008060008380156133e457506002868603115b156133f35750600185016133fb565b5084840160011c5b888161ffff166201000081106134135761341361405c565b01925082915060015b835460ff811690610100900463ffffffff1681156134c05761343f818b8d612e64565b156134b5578b8460010161ffff1662010000811061345f5761345f61405c565b01805490955060ff811690610100900463ffffffff1681156134a0576134868c828f612e64565b15613495575050505050613535565b856001019a506134ae565b508695506135359350505050565b50506134c7565b6001840397506134c7565b8360010198505b60008380156134d35750875b80156134e15750846001018a145b90508080156134f2575060108a8a03115b156135025789600801945061350b565b89890160011c94505b8c8561ffff166201000081106135235761352361405c565b01965060009350505050600161341c57505b96509650969350505050565b600080858703818080806135588d8d878c8c612bf4565b83549397509195509350915063ffffffff6101008204811691600160281b900460060b908716829003613595579750909550613635945050505050565b83156135bc5781870363ffffffff168c60020b028101839850985050505050505050613635565b845463ffffffff6101008204811691600160281b900460060b9089168290036135f757995050505060010161ffff1694506136359350505050565b838203848a0363ffffffff808216908316600690810b90878603900b81613620576136206141b0565b05028501879c509c5050505050505050505050505b965096945050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6001600160a01b0391909116815260200190565b6001600160a01b038116811461187b57600080fd5b801515811461187b57600080fd5b60008083601f8401126136c557600080fd5b5081356001600160401b038111156136dc57600080fd5b6020830191508360208285010111156136f457600080fd5b9250929050565b60008060008060008060008060e0898b03121561371757600080fd5b883561372281613690565b9750602089013561373281613690565b96506040890135613742816136a5565b955060608901359450608089013561375981613690565b935060a0890135613769816136a5565b925060c08901356001600160401b0381111561378457600080fd5b6137908b828c016136b3565b999c989b5096995094979396929594505050565b803563ffffffff811681146137b857600080fd5b919050565b6000602082840312156137cf57600080fd5b611041826137a4565b600060e082840312156137ea57600080fd5b50919050565b60008060008060008060008060e0898b03121561380c57600080fd5b883561381781613690565b9750602089013561382781613690565b965060408901359550606089013594506080890135935060a0890135925060c08901356001600160401b0381111561378457600080fd5b60006020828403121561387057600080fd5b5035919050565b6000815180845260005b8181101561389d57602081850181015186830182015201613881565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006110416020830184613877565b6000602082840312156138e257600080fd5b813561104181613690565b8060020b811461187b57600080fd5b8035600f81900b81146137b857600080fd5b600080600080600080600060c0888a03121561392957600080fd5b873561393481613690565b9650602088013561394481613690565b95506040880135613954816138ed565b94506060880135613964816138ed565b9350613972608089016138fc565b925060a08801356001600160401b0381111561398d57600080fd5b6139998a828b016136b3565b989b979a50959850939692959293505050565b600080604083850312156139bf57600080fd5b82356139ca81613690565b915060208301356139da81613690565b809150509250929050565b6000806000606084860312156139fa57600080fd5b8335613a0581613690565b92506020840135613a1581613690565b91506040840135613a25816138ed565b809150509250925092565b60008060008060008060a08789031215613a4957600080fd5b8635613a5481613690565b95506020870135613a6481613690565b9450604087013593506060870135925060808701356001600160401b03811115613a8d57600080fd5b613a9989828a016136b3565b979a9699509497509295939492505050565b60008060008060008060008060006101008a8c031215613aca57600080fd5b8935613ad581613690565b985060208a0135613ae581613690565b975060408a0135613af5816136a5565b965060608a0135955060808a0135613b0c81613690565b945060a08a0135935060c08a0135925060e08a01356001600160401b03811115613b3557600080fd5b613b418c828d016136b3565b915080935050809150509295985092959850929598565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613b9657613b96613b58565b604052919050565b60006020808385031215613bb157600080fd5b82356001600160401b0380821115613bc857600080fd5b818501915085601f830112613bdc57600080fd5b813581811115613bee57613bee613b58565b8060051b9150613bff848301613b6e565b8181529183018401918481019088841115613c1957600080fd5b938501935b83851015613c3e57613c2f856137a4565b82529385019390850190613c1e565b98975050505050505050565b604080825283519082018190526000906020906060840190828701845b82811015613c8657815160060b84529284019290840190600101613c67565b5050508381038285015284518082528583019183019060005b81811015613cc45783516001600160581b031683529284019291840191600101613c9f565b5090979650505050505050565b60008060408385031215613ce457600080fd5b50508035926020909101359150565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613d4857603f19888603018452613d36858351613877565b94509285019290850190600101613d1a565b5092979650505050505050565b600080600060608486031215613d6a57600080fd5b8335613d7581613690565b9250613d83602085016137a4565b9150613d91604085016137a4565b90509250925092565b60008060008060008060008060006101008a8c031215613db957600080fd5b8935613dc481613690565b985060208a0135613dd481613690565b975060408a0135613de4816138ed565b965060608a0135613df4816138ed565b9550613b0c60808b016138fc565b61ffff8116811461187b57600080fd5b80356137b881613e02565b60008060408385031215613e3057600080fd5b8235613e3b81613e02565b915060208301356139da81613e02565b600080600060608486031215613e6057600080fd5b8335613e6b81613690565b9250602084013591506040840135613a2581613690565b600060e08284031215613e9457600080fd5b60405160e081016001600160401b0381118282101715613eb657613eb6613b58565b6040528235613ec481613e02565b81526020830135613ed481613e02565b6020820152613ee5604084016137a4565b6040820152613ef6606084016137a4565b60608201526080830135613f0981613e02565b608082015260a0830135613f1c81613e02565b60a0820152613f2d60c08401613e12565b60c08201529392505050565b60e081018235613f4881613e02565b61ffff9081168352602084013590613f5f82613e02565b8082166020850152613f73604086016137a4565b915063ffffffff808316604086015280613f8f606088016137a4565b1660608601525060808501359150613fa682613e02565b908116608084015260a084013590613fbd82613e02565b1660a0830152613fcf60c08401613e12565b61ffff811660c0840152611e12565b600181811c90821680613ff257607f821691505b6020821081036137ea57634e487b7160e01b600052602260045260246000fd5b60006020828403121561402457600080fd5b815161104181613690565b602080825260139082015272141b1d59da5b881b9bdd08185d1d1858da1959606a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161409a5761409a614072565b5060010190565b61ffff828116828216039080821115611e1257611e12614072565b6000602082840312156140ce57600080fd5b81516003811061104157600080fd5b634e487b7160e01b600052602160045260246000fd5b60006020828403121561410557600080fd5b8151611041816136a5565b80820180821115610b2e57610b2e614072565b60008060008060008060c0878903121561413c57600080fd5b865161414781613690565b6020880151909650614158816138ed565b604088015190955061416981613e02565b606088015190945060ff8116811461418057600080fd5b608088015190935061419181613e02565b60a08801519092506141a2816136a5565b809150509295509295509295565b634e487b7160e01b600052601260045260246000fd5b63ffffffff828116828216039080821115611e1257611e12614072565b600682810b9082900b03667fffffffffffff198112667fffffffffffff82131715610b2e57610b2e614072565b60008160060b8360060b80614227576142276141b0565b667fffffffffffff1982146000198214161561424557614245614072565b90059392505050565b60008260060b80614261576142616141b0565b808360060b0791505092915050565b60008160020b627fffff19810361428957614289614072565b6000190192915050565b634e487b7160e01b600052600160045260246000fdfe8e8000aba5b365c0be9685da1153f7f096e76d1ecfb42c050ae1e387aa65b4f5a164736f6c6343000814000ab10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6267da724c255813ae00f4522fe843cb70148a4b8099cbc5af64f9a4151e55ed6a164736f6c6343000814000a00000000000000000000000010253594a832f967994b44f33411940533302acb

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620000ca5760003560e01c80631d0338d914620000cf5780632773302614620000fe57806348566a3314620001155780634e09a96a14620001295780635235d1ef14620001c457806364fae8a914620001dd5780638a2ade5814620001f45780638d5ef8d114620002085780639b21f9ae146200021f578063a7b64b041462000233578063b001f618146200025b578063cddff2691462000272578063cdef16f61462000298578063f718949a14620002c4575b600080fd5b620000e6620000e036600462000bcd565b620002db565b604051620000f5919062000cb8565b60405180910390f35b620000e66200010f36600462000ccc565b6200032c565b600354620000e6906001600160a01b031681565b6000546200017b9061ffff8082169162010000810482169163ffffffff600160201b8304811692600160401b810490911691600160601b8204811691600160701b8104821691600160801b9091041687565b6040805161ffff9889168152968816602088015263ffffffff9586169087015293909216606085015284166080840152831660a08301529190911660c082015260e001620000f5565b620001db620001d536600462000d0a565b62000512565b005b620001db620001ee36600462000d0a565b6200062c565b600154620000e6906001600160a01b031681565b620001db6200021936600462000d31565b6200073b565b600254620000e6906001600160a01b031681565b620000e67f00000000000000000000000010253594a832f967994b44f33411940533302acb81565b620001db6200026c36600462000d0a565b62000776565b6200028960008051602062005d8683398151915281565b604051908152602001620000f5565b620000e6620002a936600462000d0a565b6004602052600090815260409020546001600160a01b031681565b620001db620002d536600462000d83565b620008a0565b6000336001600160a01b037f00000000000000000000000010253594a832f967994b44f33411940533302acb16146200031357600080fd5b6200031e88620009c0565b90505b979650505050505050565b6000807f00000000000000000000000010253594a832f967994b44f33411940533302acb9050806001600160a01b031663e8ae2b69826001600160a01b031663b500a48b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003a0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003c6919062000d9c565b336040518363ffffffff1660e01b8152600401620003e692919062000db6565b602060405180830381865afa15801562000404573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200042a919062000dcd565b6200043457600080fd5b60405163d9a641e160e01b81526001600160a01b03858116600483015284811660248301526000919083169063d9a641e190604401602060405180830381865afa15801562000487573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004ad919062000df1565b90506001600160a01b038116620004fc5760405162461bcd60e51b815260206004820152600e60248201526d141bdbdb081b9bdd08195e1a5cdd60921b60448201526064015b60405180910390fd5b6200050781620009c0565b925050505b92915050565b60405163e8ae2b6960e01b81526001600160a01b037f00000000000000000000000010253594a832f967994b44f33411940533302acb169063e8ae2b6990620005719060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa1580156200058f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005b5919062000dcd565b620005d45760405162461bcd60e51b8152600401620004f39062000e11565b600380546001600160a01b0319166001600160a01b0383161790556040517f74bc9f04f2c495fde0eb0cf1c70fb6042393608f9e26c69a621bb23061ce5785906200062190839062000cb8565b60405180910390a150565b60405163e8ae2b6960e01b81526001600160a01b037f00000000000000000000000010253594a832f967994b44f33411940533302acb169063e8ae2b69906200068b9060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa158015620006a9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006cf919062000dcd565b620006ee5760405162461bcd60e51b8152600401620004f39062000e11565b600280546001600160a01b0319166001600160a01b0383161790556040517f9c2a61186234594f572ba9fd3d6137c9e751c20d5cad21e5c4bf008a768b3657906200062190839062000cb8565b336001600160a01b037f00000000000000000000000010253594a832f967994b44f33411940533302acb16146200077157600080fd5b505050565b60405163e8ae2b6960e01b81526001600160a01b037f00000000000000000000000010253594a832f967994b44f33411940533302acb169063e8ae2b6990620007d59060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa158015620007f3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000819919062000dcd565b620008385760405162461bcd60e51b8152600401620004f39062000e11565b6001546001600160a01b038083169116036200085357600080fd5b600180546001600160a01b0319166001600160a01b0383161790556040517f56b9e8342f530796ceed0d5529abdcdeae6e4f2ac1dc456ceb73bbda898e0cd3906200062190839062000cb8565b60405163e8ae2b6960e01b81526001600160a01b037f00000000000000000000000010253594a832f967994b44f33411940533302acb169063e8ae2b6990620008ff9060008051602062005d8683398151915290339060040162000db6565b602060405180830381865afa1580156200091d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000943919062000dcd565b620009625760405162461bcd60e51b8152600401620004f39062000e11565b6200097d620009773683900383018362000e80565b62000ad1565b8060006200098c828262000f6b565b9050507fe04232512a5cb82c08e0f9b1f51432930cd7a0b7ea9f9f916f080cb0b4ac644b81604051620006219190620010bb565b6001600160a01b038181166000908152600460205260408120549091161562000a1e5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818dc99585d1959608a1b6044820152606401620004f3565b60025460035460405160009285927f00000000000000000000000010253594a832f967994b44f33411940533302acb92309286926001600160a01b039182169291169062000a6c9062000ba9565b62000a7d9695949392919062001172565b604051809103906000f08015801562000a9a573d6000803e3d6000fd5b506001600160a01b03938416600090815260046020526040902080546001600160a01b031916948216949094179093555090919050565b60c08101516020820151825161ffff9283169162000af5919084169084166200123f565b62000b0191906200123f565b111562000b445760405162461bcd60e51b815260206004820152601060248201526f13585e0819995948195e18d95959195960821b6044820152606401620004f3565b608081015161ffff161580159062000b63575060a081015161ffff1615155b62000ba65760405162461bcd60e51b8152602060048201526012602482015271047616d6d6173206d757374206265203e20360741b6044820152606401620004f3565b50565b614b24806200126283390190565b6001600160a01b038116811462000ba657600080fd5b600080600080600080600060c0888a03121562000be957600080fd5b873562000bf68162000bb7565b9650602088013562000c088162000bb7565b9550604088013562000c1a8162000bb7565b9450606088013562000c2c8162000bb7565b9350608088013562000c3e8162000bb7565b925060a08801356001600160401b038082111562000c5b57600080fd5b818a0191508a601f83011262000c7057600080fd5b81358181111562000c8057600080fd5b8b602082850101111562000c9357600080fd5b60208301945080935050505092959891949750929550565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b6000806040838503121562000ce057600080fd5b823562000ced8162000bb7565b9150602083013562000cff8162000bb7565b809150509250929050565b60006020828403121562000d1d57600080fd5b813562000d2a8162000bb7565b9392505050565b60008060006060848603121562000d4757600080fd5b833562000d548162000bb7565b9250602084013562000d668162000bb7565b9150604084013562000d788162000bb7565b809150509250925092565b600060e0828403121562000d9657600080fd5b50919050565b60006020828403121562000daf57600080fd5b5051919050565b9182526001600160a01b0316602082015260400190565b60006020828403121562000de057600080fd5b8151801515811462000d2a57600080fd5b60006020828403121562000e0457600080fd5b815162000d2a8162000bb7565b60208082526012908201527127b7363c9030b236b4b734b9ba3930ba37b960711b604082015260600190565b61ffff8116811462000ba657600080fd5b803562000e5b8162000e3d565b919050565b63ffffffff8116811462000ba657600080fd5b803562000e5b8162000e60565b600060e0828403121562000e9357600080fd5b60405160e081016001600160401b038111828210171562000ec457634e487b7160e01b600052604160045260246000fd5b60405262000ed28362000e4e565b815262000ee26020840162000e4e565b602082015262000ef56040840162000e73565b604082015262000f086060840162000e73565b606082015262000f1b6080840162000e4e565b608082015262000f2e60a0840162000e4e565b60a082015262000f4160c0840162000e4e565b60c08201529392505050565b600081356200050c8162000e3d565b600081356200050c8162000e60565b813562000f788162000e3d565b61ffff8116905081548161ffff198216178355602084013562000f9b8162000e3d565b63ffff00008160101b169050808363ffffffff19841617178455604085013562000fc58162000e60565b6001600160401b0319929092169092179190911760209190911b63ffffffff60201b161781556200102162000ffd6060840162000f5c565b82805463ffffffff60401b191660409290921b63ffffffff60401b16919091179055565b62001053620010336080840162000f4d565b82805461ffff60601b191660609290921b61ffff60601b16919091179055565b620010856200106560a0840162000f4d565b82805461ffff60701b191660709290921b61ffff60701b16919091179055565b620010b76200109760c0840162000f4d565b82805461ffff60801b191660809290921b61ffff60801b16919091179055565b5050565b60e081018235620010cc8162000e3d565b61ffff9081168352602084013590620010e58262000e3d565b9081166020840152604084013590620010fe8262000e60565b63ffffffff91821660408501526060850135916200111c8362000e60565b919091166060840152608084013590620011368262000e3d565b1660808301526200114a60a0840162000e4e565b61ffff1660a08301526200116160c0840162000e4e565b61ffff811660c08401525092915050565b60006101808201905060018060a01b0380891683528088166020840152808716604084015250845461ffff8082166060850152808260101c16608085015263ffffffff808360201c1660a0860152620011d860c08601828560401c1663ffffffff169052565b50620011ef60e08501828460601c1661ffff169052565b620012066101008501828460701c1661ffff169052565b6200121d6101208501828460801c1661ffff169052565b50506200122f61014083018562000cab565b6200032161016083018462000cab565b808201808211156200050c57634e487b7160e01b600052601160045260246000fdfe60e06040526000805460ff191690553480156200001b57600080fd5b5060405162004b2438038062004b248339810160408190526200003e916200050c565b6001600160a01b0380851660a052808716608052851660c0526000805460ff19811660ff9091161760811790558082846200007981620003d5565b620000ed81600080826000015161ffff166010846020015161ffff16901b6020856040015163ffffffff16901b6040866060015163ffffffff16901b6060876080015161ffff16901b60708860a0015161ffff16901b60808960c0015161ffff16901b171717171717905080915050919050565b600280546001600160901b0319166001600160901b0392909216919091179055600180548082018255600091909152604080518082019091526012815271223cb730b6b4b1902332b29028363ab3b4b760711b602082015260008051602062004b0483398151915290910190620001659082620006d2565b50506000805460ff19811660ff909116176002178155600180548082018255915260408051808201909152601481527f4661726d696e672050726f787920506c7567696e000000000000000000000000602082015260008051602062004b0483398151915290910190620001da9082620006d2565b506000805460ff19811660ff909116176041178155600180548082018255915260408051808201909152601881527f566f6c6174696c697479204f7261636c6520506c7567696e0000000000000000602082015260008051602062004b04833981519152909101906200024e9082620006d2565b506000805460ff19811660ff909116176002178155600180548082018255915260408051808201909152600a81526920a6269028363ab3b4b760b11b602082015260008051602062004b0483398151915290910190620002af9082620006d2565b506201000680546001600160a01b03831664010000000002600160201b600160c01b03199091161790556000805460ff19811660ff909116176015178155600180548082018255915260408051808201909152600f81526e29b2b1bab934ba3c9028363ab3b4b760891b602082015260008051602062004b04833981519152909101906200033e9082620006d2565b50506201000780546001600160a01b0383166001600160a01b03199091161790556000805460ff19811660ff90911617600217815560018054808201825591526040805180820190915260128152712634b6b4ba1027b93232b91028363ab3b4b760711b602082015260008051602062004b0483398151915290910190620003c79082620006d2565b5050505050505050620007c6565b60c08101516020820151825161ffff92831691620003f9919084169084166200079e565b6200040591906200079e565b11156200044c5760405162461bcd60e51b815260206004820152601060248201526f13585e0819995948195e18d95959195960821b60448201526064015b60405180910390fd5b608081015161ffff16158015906200046b575060a081015161ffff1615155b620004ae5760405162461bcd60e51b8152602060048201526012602482015271047616d6d6173206d757374206265203e20360741b604482015260640162000443565b50565b80516001600160a01b0381168114620004c957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b805161ffff81168114620004c957600080fd5b805163ffffffff81168114620004c957600080fd5b6000806000806000808688036101808112156200052857600080fd5b6200053388620004b1565b96506200054360208901620004b1565b95506200055360408901620004b1565b945060e0605f19820112156200056857600080fd5b5060405160e081016001600160401b03811182821017156200058e576200058e620004ce565b6040526200059f60608901620004e4565b8152620005af60808901620004e4565b6020820152620005c260a08901620004f7565b6040820152620005d560c08901620004f7565b6060820152620005e860e08901620004e4565b6080820152620005fc6101008901620004e4565b60a0820152620006106101208901620004e4565b60c08201529250620006266101408801620004b1565b9150620006376101608801620004b1565b90509295509295509295565b600181811c908216806200065857607f821691505b6020821081036200067957634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620006cd57600081815260208120601f850160051c81016020861015620006a85750805b601f850160051c820191505b81811015620006c957828155600101620006b4565b5050505b505050565b81516001600160401b03811115620006ee57620006ee620004ce565b6200070681620006ff845462000643565b846200067f565b602080601f8311600181146200073e5760008415620007255750858301515b600019600386901b1c1916600185901b178555620006c9565b600085815260208120601f198616915b828110156200076f578886015182559484019460019091019084016200074e565b50858210156200078e5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80820180821115620007c057634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c0516142d66200082e600039600061192a015260008181610c110152610cd40152600081816101ea0152818161029c015281816111ae0152818161164501528181611aa601528181611bdd01528181611c9a0152611d0c01526142d66000f3fe608060405234801561001057600080fd5b50600436106101e35760003560e01c8063026b1d5f146101e8578063029c1cb7146102205780630786feb61461025f578063080951411461028257806316f0115b146102975780631d39215e146102be5780631d4632ac146102d15780631e5eb1d0146102e457806320501a911461037157806331b25d1a1461038b578063343d37ff146103ae578063392e53cd146103da57806346b7748b1461040057806348566a331461042057806348b2acdd146104355780635235d1ef146104485780635e2411b21461045b578063636fd8041461049257806364fae8a9146104a5578063689ea370146104b85780636fb5bad1146104d757806374eceae6146104f35780637c1fe0c81461055d5780638129fc1c1461057057806382dd652214610578578063841c6a371461058b57806388f2e862146105b95780638de0a8ee146105ee578063918a1ab0146106015780639cb5a963146106145780639d3a524114610627578063aa6b14bb14610648578063ad1c37431461065b578063b6f78cc91461066d578063d49dda8514610682578063d685201014610695578063da705235146106a8578063e63015f0146106bb578063e72c652d146106ce578063f5985d35146106e1578063f70d9362146106f9575b600080fd5b7f00000000000000000000000000000000000000000000000000000000000000005b604051610217919061367c565b60405180910390f35b61023361022e3660046136fb565b610701565b604080516001600160e01b0319909416845262ffffff9283166020850152911690820152606001610217565b620100055461026f9061ffff1681565b60405161ffff9091168152602001610217565b6102956102903660046137bd565b610759565b005b61020a7f000000000000000000000000000000000000000000000000000000000000000081565b6102956102cc3660046137d8565b6107fb565b60035461020a906001600160a01b031681565b61032960025461ffff80821692601083901c82169263ffffffff602082901c811693604083901c90911692606083901c821692607081901c83169260809190911c1690565b6040805161ffff9889168152968816602088015263ffffffff9586169087015293909216606085015284166080840152831660a08301529190911660c082015260e001610217565b6201000654600160201b90046001600160a01b031661020a565b6103a06000805160206142aa83398151915281565b604051908152602001610217565b6103c16103bc3660046137f0565b6108f6565b6040516001600160e01b03199091168152602001610217565b62010005546103f090600160301b900460ff1681565b6040519015158152602001610217565b61041361040e36600461385e565b610924565b60405161021791906138bd565b620100075461020a906001600160a01b031681565b6102956104433660046137bd565b6109d0565b6102956104563660046138d0565b610a70565b61046e61046936600461390e565b610ac5565b604080516001600160e01b0319909316835262ffffff909116602083015201610217565b6103c16104a03660046139ac565b610b0b565b6102956104b33660046138d0565b610b34565b6000546104c59060ff1681565b60405160ff9091168152602001610217565b620100055461020a90600160381b90046001600160a01b031681565b61050661050136600461385e565b610b92565b60408051971515885263ffffffff909616602088015260069490940b948601949094526001600160581b039091166060850152600290810b60808501529190910b60a083015261ffff1660c082015260e001610217565b61029561056b3660046138d0565b610bfa565b610295610f32565b6103c16105863660046139e5565b611024565b62010005546105a490600160d81b900463ffffffff1681565b60405163ffffffff9091168152602001610217565b6105cc6105c73660046137bd565b611048565b6040805160069390930b83526001600160581b03909116602083015201610217565b6103c16105fc366004613a30565b61109e565b61029561060f3660046138d0565b6110c4565b6103c1610622366004613aab565b6110fa565b61063a610635366004613b9e565b6111f2565b604051610217929190613c4a565b6103c1610656366004613cd1565b61122e565b62010006546105a49063ffffffff1681565b610675611247565b6040516102179190613cf3565b610295610690366004613d55565b611381565b6103c16106a3366004613d9a565b6114c2565b6102956106b6366004613e1d565b6114f1565b6103f06106c93660046138d0565b6115a3565b6102956106dc366004613e4b565b61160b565b62010005546105a49062010000900463ffffffff1681565b61026f61161e565b600080600061070e61163a565b6107166116ae565b61071e61177e565b600061072861187e565b90506000610735826118c0565b63029c1cb760e01b955061ffff169350600092505050985098509895505050505050565b610761611901565b620100055463ffffffff600160d81b909104811690821611156107dd5760405162461bcd60e51b815260206004820152602960248201527f5f6661737454776170506572696f64206d757374206265203c3d20736c6f77546044820152681dd85c14195c9a5bd960ba1b60648201526084015b60405180910390fd5b62010006805463ffffffff191663ffffffff92909216919091179055565b610803611901565b61081a61081536839003830183613e82565b6119a6565b61089b61082c36839003830183613e82565b600080826000015161ffff166010846020015161ffff16901b6020856040015163ffffffff16901b6040866060015163ffffffff16901b6060876080015161ffff16901b60708860a0015161ffff16901b60808960c0015161ffff16901b171717171717905080915050919050565b600280546001600160901b0319166001600160901b03929092169190911790556040517f348d1d6a31fa4a2a3ef887c493549a9500fbb77b5218be0c81a71c553364305a906108eb908390613f39565b60405180910390a150565b600061090061163a565b60005461090f9060ff16611a72565b5063343d37ff60e01b98975050505050505050565b6001818154811061093457600080fd5b90600052602060002001600091509050805461094f90613fde565b80601f016020809104026020016040519081016040528092919081815260200182805461097b90613fde565b80156109c85780601f1061099d576101008083540402835291602001916109c8565b820191906000526020600020905b8154815290600101906020018083116109ab57829003601f168201915b505050505081565b6109d8611901565b620100065463ffffffff9081169082161015610a485760405162461bcd60e51b815260206004820152602960248201527f5f736c6f7754776170506572696f64206d757374206265203e3d2066617374546044820152681dd85c14195c9a5bd960ba1b60648201526084016107d4565b62010005805463ffffffff909216600160d81b0263ffffffff60d81b19909216919091179055565b610a78611901565b6201000780546001600160a01b0319166001600160a01b0383161790556040517f74bc9f04f2c495fde0eb0cf1c70fb6042393608f9e26c69a621bb23061ce5785906108eb90839061367c565b600080610ad061163a565b600085600f0b1215610ae957610ae4611b0e565b610af1565b610af161177e565b50632f1208d960e11b905060005b97509795505050505050565b6000610b1561163a565b600054610b249060ff16611a72565b506318dbf60160e21b5b92915050565b610b3c611901565b620100068054600160201b600160c01b031916600160201b6001600160a01b038416021790556040517f9c2a61186234594f572ba9fd3d6137c9e751c20d5cad21e5c4bf008a768b3657906108eb90839061367c565b600581620100008110610ba457600080fd5b015460ff81169150610100810463ffffffff1690600160281b810460060b90600160601b81046001600160581b031690600160b81b8104600290810b91600160d01b810490910b90600160e81b900461ffff1687565b6001600160a01b03811615801590600090610cac577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c919190614012565b6001600160a01b0316336001600160a01b0316149050610d6b565b6004546001600160a01b031615610ccd57506004546001600160a01b031633145b80610d6b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d549190614012565b6001600160a01b0316336001600160a01b03161490505b80610db75760405162461bcd60e51b815260206004820152601c60248201527b4e6f7420616c6c6f77656420746f2073657420696e63656e7469766560201b60448201526064016107d4565b600030610dc2611bd9565b6001600160a01b03161490508215610df15780610df15760405162461bcd60e51b81526004016107d49061402f565b6003546001600160a01b039081169085168103610e415760405162461bcd60e51b815260206004820152600e60248201526d416c72656164792061637469766560901b60448201526064016107d4565b8315610e95576001600160a01b03811615610e955760405162461bcd60e51b81526020600482015260146024820152734861732061637469766520696e63656e7469766560601b60448201526064016107d4565b600380546001600160a01b0319166001600160a01b0387161790556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490610ee090879061367c565b60405180910390a18315610f0557600480546001600160a01b03191633179055610f16565b600480546001600160a01b03191690555b8115610f2b57600054610f2b9060ff16611c62565b5050505050565b6201000554600160301b900460ff1615610f845760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016107d4565b30610f8d611bd9565b6001600160a01b031614610fb35760405162461bcd60e51b81526004016107d49061402f565b600080610fbe611d04565b505091509150816001600160a01b03166000036110175760405162461bcd60e51b8152602060048201526017602482015276141bdbdb081a5cc81b9bdd081a5b9a5d1a585b1a5e9959604a1b60448201526064016107d4565b61102081611d9c565b5050565b600061102e61163a565b61103782611d9c565b5063416eb29160e11b5b9392505050565b6000806000611055611d04565b5050620100055490925061ffff1690506000611072600583611de6565b9050600061108560054289878787611e19565b6040810151606090910151909890975095505050505050565b60006110a861163a565b6110b061177e565b506346f0547760e11b5b9695505050505050565b6110cc611901565b6201000580546001600160a01b03909216600160381b02600160381b600160d81b0319909216919091179055565b600061110461163a565b6201000554600160381b90046001600160a01b03161580159061113d5750620100055461113d90600160d81b900463ffffffff16612063565b156111a957600061114c611d04565b5050915050600061115b6120c5565b9050600061117c62010005601b9054906101000a900463ffffffff1661216d565b62010006549091506000906111969063ffffffff1661216d565b90506111a4848383866122fe565b505050505b6111d37f000000000000000000000000000000000000000000000000000000000000000089612389565b6111dc886123f1565b50639cb5a96360e01b9998505050505050505050565b60608060006111ff611d04565b505091505061122461120e4290565b6201000554600591908790859061ffff1661248f565b9250925050915091565b600061123861163a565b5063aa6b14bb60e01b92915050565b6001546060906001600160401b0381111561126457611264613b58565b60405190808252806020026020018201604052801561129757816020015b60608152602001906001900390816112825790505b50905060005b60015481101561137d57600181815481106112ba576112ba61405c565b9060005260206000200180546112cf90613fde565b80601f01602080910402602001604051908101604052809291908181526020018280546112fb90613fde565b80156113485780601f1061131d57610100808354040283529160200191611348565b820191906000526020600020905b81548152906001019060200180831161132b57829003601f168201915b505050505082828151811061135f5761135f61405c565b6020026020010181905250808061137590614088565b91505061129d565b5090565b611389611901565b6001600160a01b0383166113f25760405162461bcd60e51b815260206004820152602a60248201527f5f726562616c616e63654d616e61676572206d757374206265206e6f6e207a65604482015269726f206164647265737360b01b60648201526084016107d4565b8063ffffffff168263ffffffff1610156114615760405162461bcd60e51b815260206004820152602a60248201527f5f736c6f7754776170506572696f64206d757374206265203e3d205f66617374604482015269151dd85c14195c9a5bd960b21b60648201526084016107d4565b620100058054600160381b600160f81b031916600160381b6001600160a01b03959095169490940263ffffffff60d81b191693909317600160d81b63ffffffff938416021790925562010006805463ffffffff191692909116919091179055565b60006114cc61163a565b6000546114db9060ff16611a72565b50630d68520160e41b9998505050505050505050565b60058261ffff1662010000811061150a5761150a61405c565b015460ff161561151957600080fd5b60008161ffff1611801561154257508061ffff168261ffff61153b91906140a1565b61ffff1610155b61154b57600080fd5b61ffff82165b81830161ffff1681101561159e5760016005826201000081106115765761157661405c565b01805463ffffffff929092166101000264ffffffff0019909216919091179055600101611551565b505050565b6003546000906001600160a01b038381169116146115c357506000919050565b306115cc611bd9565b6001600160a01b0316146115e257506000919050565b60006115ec611d04565b9350505050600281166116025750600092915050565b50600192915050565b611613611901565b61159e8382846125df565b60008061162961187e565b9050611634816118c0565b91505090565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146116ac5760405162461bcd60e51b81526020600482015260176024820152764f6e6c7920706f6f6c2063616e2063616c6c207468697360481b60448201526064016107d4565b565b620100055461ffff81169062010000810463ffffffff1690600160301b900460ff168061170f5760405162461bcd60e51b815260206004820152600f60248201526e139bdd081a5b9a5d1a585b1a5e9959608a1b60448201526064016107d4565b4263ffffffff808216908416036117265750505050565b6000611730611d04565b509092506000915061174790506005878585612643565b5062010005805463ffffffff909516620100000265ffffffffffff1990951661ffff90921691909117939093179092555050505050565b6201000654600160201b90046001600160a01b0316156116ac5762010006546040516315d9d2f960e01b8152600091600160201b90046001600160a01b0316906315d9d2f9906117d290339060040161367c565b6020604051808303816000875af11580156117f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181591906140bc565b9050600081600281111561182b5761182b6140dd565b1461187b576002816002811115611844576118446140dd565b03611862576040516306756ca560e21b815260040160405180910390fd5b604051632930fa1560e11b815260040160405180910390fd5b50565b6000428161188a611d04565b5050620100055490925061ffff16905060006118a7600583611de6565b90506118b760058585858561287b565b94505050505090565b6002546000906001600160901b0381169060101c61ffff1661ffff82161761ffff166000036118f757608081901c61ffff16611041565b6110418382612add565b60405163e8ae2b6960e01b81526000805160206142aa83398151915260048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e8ae2b6990604401602060405180830381865afa158015611979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199d91906140f3565b6116ac57600080fd5b60c08101516020820151825161ffff928316916119c891908416908416614110565b6119d29190614110565b1115611a135760405162461bcd60e51b815260206004820152601060248201526f13585e0819995948195e18d95959195960821b60448201526064016107d4565b608081015161ffff1615801590611a31575060a081015161ffff1615155b61187b5760405162461bcd60e51b8152602060048201526012602482015271047616d6d6173206d757374206265203e20360741b60448201526064016107d4565b6000611a7c611d04565b93505050508160ff168160ff16146110205760405163bca57f8160e01b815260ff831660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063bca57f8190602401600060405180830381600087803b158015611af257600080fd5b505af1158015611b06573d6000803e3d6000fd5b505050505050565b6201000654600160201b90046001600160a01b0316156116ac5762010006546040516315d9d2f960e01b8152600091600160201b90046001600160a01b0316906315d9d2f990611b6290339060040161367c565b6020604051808303816000875af1158015611b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba591906140bc565b90506002816002811115611bbb57611bbb6140dd565b0361187b576040516306756ca560e21b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef01df4f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5d9190614012565b905090565b6000611c6c611d04565b93505050828217905060ff8083169082161461159e5760405163bca57f8160e01b815260ff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063bca57f81906024015b600060405180830381600087803b158015611ce757600080fd5b505af1158015611cfb573d6000803e3d6000fd5b50505050505050565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e76c01e46040518163ffffffff1660e01b815260040160c060405180830381865afa158015611d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8c9190614123565b5093989297509095509350915050565b42611da960058284612b70565b62010005805460ff60301b1963ffffffff841662010000021666ffffffffff00001990911617600160301b1790556000546110209060ff16611c62565b6000600182018361ffff8216620100008110611e0457611e0461405c565b015460ff1615611e12578091505b5092915050565b611e21613640565b84860360008080611e358b8b868a8a612bf4565b506040805160e081018252845460ff811615158252610100810463ffffffff90811660208401819052600160281b830460060b94840194909452600160601b82046001600160581b03166060840152600160b81b8204600290810b6080850152600160d01b8304900b60a0840152600160e81b90910461ffff1660c0830152909950939650919450925090851603611ed057505050506110ba565b8015611f0e57600080611ef08d878c8c8c8c602001518d60400151612db6565b91509150611f0187878c8585612ddd565b96505050505050506110ba565b815463ffffffff6101008204811691600160281b900460060b908616829003611fb75750506040805160e081018252925460ff811615158452610100810463ffffffff166020850152600160281b810460060b91840191909152600160601b81046001600160581b03166060840152600160b81b8104600290810b6080850152600160d01b8204900b60a0840152600160e81b900461ffff1660c08301525092506110ba915050565b602087015160408801518184039188039063ffffffff80831691908416600690810b918603900b81611feb57611feb6141b0565b60408c01805192909105929092020160060b90526060890151865463ffffffff80841692908516916001600160581b03600160601b9091048116919091031681612037576120376141b0565b60608c0180516001600160581b0393909204939093020116905250505050505050509695505050505050565b6201000554600090819061207d9060059061ffff16611de6565b9050600060058261ffff1662010000811061209a5761209a61405c565b0154610100900463ffffffff1690506120bd816120b786426141c6565b42612e64565b949350505050565b6201000554600090819060059061ffff166201000081106120e8576120e861405c565b6040805160e081018252919092015460ff811615158252610100810463ffffffff1660208301819052600160281b820460060b93830193909352600160601b81046001600160581b03166060830152600160b81b8104600290810b6080840152600160d01b8204900b60a0830152600160e81b900461ffff1660c09091015292915050565b60008163ffffffff166000036121b65760405162461bcd60e51b815260206004820152600e60248201526d506572696f64206973207a65726f60901b60448201526064016107d4565b60408051600280825260608201835260009260208301908036833701905050905082816000815181106121eb576121eb61405c565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061221a5761221a61405c565b602002602001019063ffffffff16908163ffffffff1681525050600061223e611d04565b5050915050600061226561224f4290565b6201000554600591908690869061ffff1661248f565b50905060008160008151811061227d5761227d61405c565b6020026020010151826001815181106122985761229861405c565b60200260200101516122aa91906141e3565b90506122bc63ffffffff871682614210565b945060008160060b1280156122e257506122dc63ffffffff87168261424e565b60060b15155b156122f557846122f181614270565b9550505b50505050919050565b6201000554604051637e7b25f160e01b8152600286810b600483015285810b602483015284900b604482015263ffffffff83166064820152600160381b9091046001600160a01b031690637e7b25f190608401600060405180830381600087803b15801561236b57600080fd5b505af115801561237f573d6000803e3d6000fd5b5050505050505050565b62010007546001600160a01b0316156110205760006123a6611d04565b50506201000754604051633f09ca0560e11b81526001600160a01b0387811660048301528615156024830152600284900b604483015292945091169150637e13940a90606401611ccd565b6003546001600160a01b0316801561102057600061240d611d04565b505060405163034d335960e41b8152600282900b600482015285151560248201529092506001600160a01b03841691506334d33590906044016020604051808303816000875af1158015612465573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248991906140f3565b50505050565b82516060908190806001600160401b038111156124ae576124ae613b58565b6040519080825280602002602001820160405280156124d7578160200160208202803683370190505b509250806001600160401b038111156124f2576124f2613b58565b60405190808252806020026020018201604052801561251b578160200160208202803683370190505b509150600061252a8986611de6565b9050612534613640565b60005b838110156125d1576125668b8b8b84815181106125565761255661405c565b60200260200101518b8b88611e19565b9150816040015182606001518783815181106125845761258461405c565b6020026020010187848151811061259d5761259d61405c565b60200260200101826001600160581b03166001600160581b03168152508260060b60060b8152505050806001019050612537565b505050509550959350505050565b600060405163a9059cbb60e01b6000526001600160a01b03841660045282602452602060006044600080895af19150813d1560203d14600160005114161716915080604052508061248957604051637232c81f60e11b815260040160405180910390fd5b6000806000868661ffff166201000081106126605761266061405c565b6040805160e081018252919092015460ff811615158252610100810463ffffffff90811660208401819052600160281b830460060b94840194909452600160601b82046001600160581b03166060840152600160b81b8204600290810b6080850152600160d01b8304900b60a0840152600160e81b90910461ffff1660c0830152909250861690036126f9578560009250925050612872565b856001019250868361ffff166201000081106127175761271761405c565b015460ff1615612725578291505b60008061273f8988888b8888602001518960400151612db6565b915091508461ffff168161ffff1603612756576001015b6127638388888585612ddd565b898661ffff1662010000811061277b5761277b61405c565b825191018054602084015160408501516060860151608087015160a088015160c09098015164ffffffffff1990951696151564ffffffff0019169690961761010063ffffffff9094169390930292909217600160281b600160b81b031916600160281b66ffffffffffffff90921691909102600160601b600160b81b03191617600160601b6001600160581b03909216919091021765ffffffffffff60b81b1916600160b81b62ffffff9485160262ffffff60d01b191617600160d01b93909416929092029290921761ffff60e81b1916600160e81b61ffff92831602179091558581169085160361286e576001909301925b5050505b94509492505050565b600080868461ffff166201000081106128965761289661405c565b018054909150610100810463ffffffff90811690881614906001600160581b03600160601b8204169061ffff600160e81b90910416826128e2576128df8a8a60008b8b8b612e8c565b91505b60008a8761ffff166201000081106128fc576128fc61405c565b0154610100900463ffffffff16905061291c816201517f198c018c612e64565b15612a475760008415612a15578b8361ffff166201000081106129415761294161405c565b0154610100900463ffffffff168c61ffff85166201000081106129665761296661405c565b0154909250600160601b90046001600160581b031690506000828d6001860161ffff1662010000811061299b5761299b61405c565b015463ffffffff610100909104811691909103915081811690848e036201517f190116838f61ffff60018901166201000081106129da576129da61405c565b01600001600c9054906101000a90046001600160581b031603026001600160581b031681612a0a57612a0a6141b0565b048201915050612a29565b612a268c8c620151808d8d8d612e8c565b90505b620151806001600160581b0382860316049650505050505050612ad4565b8063ffffffff168a63ffffffff1614612ace5760008b8861ffff16620100008110612a7457612a7461405c565b0154600160601b90046001600160581b03169050818b03600163ffffffff82161115612a9f57600019015b8063ffffffff168286036001600160581b031681612abf57612abf6141b0565b04975050505050505050612ad4565b50505050505b95945050505050565b6000600f6001600160581b0384160492506000612b266001600160581b038516607085901c61ffff16601086901c61ffff16604087901c63ffffffff165b63ffffffff16612fc5565b612b516001600160581b038616606086901c61ffff1661ffff8716602088901c63ffffffff16612b1b565b01905061ffff608084901c81168201908111156120bd576120bd614293565b8254839060ff1615612b95576040516308ab11a360e11b815260040160405180910390fd5b8054600165ffffffffffff60b81b19909116600160d01b62ffffff9490941693840262ffffff60b81b191617600160b81b939093029290921764ffffffffff191661010063ffffffff949094169390930260ff19169290921717905550565b6000806000806000898761ffff16620100008110612c1457612c1461405c565b01805490915063ffffffff610100820481169161ffff600160e81b90910416908a8116908c161480612c4c5750612c4c828b8d612e64565b15612c68575090945084935060019250505061ffff8516612daa565b6000620151808b840363ffffffff1611612c9657819850620151808c0363ffffffff168b63ffffffff161490505b60008d8a61ffff16620100008110612cb057612cb061405c565b0154610100900463ffffffff169050612cca818d8f612e64565b612ce65760405162bc60f160e41b815260040160405180910390fd5b8b63ffffffff168163ffffffff1603612d46578d8a61ffff16620100008110612d1157612d1161405c565b018e8b61ffff16620100008110612d2a57612d2a61405c565b919a50019750600196505061ffff89169450612daa9350505050565b8960010161ffff168b61ffff1603612d8b578d8a61ffff16620100008110612d7057612d7061405c565b019850939650600095505061ffff88169350612daa92505050565b50612d9a8d8d8d8d8d86613079565b9199509750600096509450505050505b95509550955095915050565b600080600080612dcb8b8b8b8b8b8b8b6130c7565b909c909b509950505050505050505050565b612de5613640565b6020860180516001885263ffffffff878116909252604088018051918803928316600288810b82810290940160060b90925260a08a0151612e2f9391928291810b9089900b6131ff565b606088018051919091016001600160581b031690525050600292830b6080860152910b60a084015261ffff1660c08301525090565b63ffffffff808216848216811091841611810361104157505063ffffffff9081169116111590565b6000848603818080612ea18b8b868a8a612bf4565b508254929550909350915063ffffffff61010082048116916001600160581b03600160601b90910416908616829003612ee15795506110ba945050505050565b8215612f48576000612f098e888d8d8d888c60000160059054906101000a900460060b612db6565b508654909150612f389063ffffffff858a03169060028e810b918291600160d01b909104810b9086900b6131ff565b82019750505050505050506110ba565b835463ffffffff61010082048116916001600160581b03600160601b90910416908816829003612f815797506110ba9650505050505050565b83820384890363ffffffff8082169083166001600160581b038786031681612fab57612fab6141b0565b040285019a50505050505050505050509695505050505050565b6000818511156130255781850394508361ffff166006028510612fed575061ffff82166120bd565b600461ffff85160a600061300287878461325a565b9050808201818661ffff16028161301b5761301b6141b0565b04925050506120bd565b93810393600661ffff851602851061303f575060006120bd565b600461ffff85160a600061305487878461325a565b8201905080828661ffff16028161306d5761306d6141b0565b04979650505050505050565b6000808061ffff80861690829088168211613098578761ffff166130a3565b620100008861ffff16015b90506130b38b8b8b85858b6133d0565b919d909c50909a5098505050505050505050565b6000806000808a8761ffff166201000081106130e5576130e561405c565b0154610100900463ffffffff168b61ffff891662010000811061310a5761310a61405c565b0154909250600160281b900460060b9050600289900b63ffffffff878c031602850161313d836201517f198d018d612e64565b61319e578263ffffffff168b63ffffffff160361316957505050600287900b91505061ffff8416610aff565b828b0363ffffffff1660060b82820360060b81613188576131886141b0565b0560060b94505061ffff87169250610aff915050565b6131ae87620151808d038d612e64565b156131c857505050600287900b91505061ffff8516610aff565b60006131db8d8d620151808e8e8e613541565b955090506201518081830360060b0560060b95505050505097509795505050505050565b600082820385850303838603870260018089018902906002808b029091018202906006818c0a81029180870a8402868802860283020190860a8d02909102018161324b5761324b6141b0565b059a9950505050505050505050565b60008083850480801561329957600181146132aa57600281146132bb57600381146132cc57600481146132dd576903248ca20bb7b5032f5f92506132eb565b68056bc75e2d6310000092506132eb565b680ebc5fb4174612111092506132eb565b68280e60114edb805d0392506132eb565b686ce2482d63ef54b74c92506132eb565b690127fa27722cc06cc5e392505b50509383900693600261ffff85160461ffff16851061333b57600261ffff85160461ffff168503945068056bc75e2d63100000816808f00f760a4b2db55c0281613337576133376141b0565b0490505b8291508461ffff85168381613352576133526141b0565b049350838102830192508461ffff16848161336f5761336f6141b0565b0493508502600281850204830192508461ffff168481613391576133916141b0565b04935085026018600461ffff871683020282880201048301925068056bc75e2d63100000828402816133c5576133c56141b0565b049695505050505050565b60008060008380156133e457506002868603115b156133f35750600185016133fb565b5084840160011c5b888161ffff166201000081106134135761341361405c565b01925082915060015b835460ff811690610100900463ffffffff1681156134c05761343f818b8d612e64565b156134b5578b8460010161ffff1662010000811061345f5761345f61405c565b01805490955060ff811690610100900463ffffffff1681156134a0576134868c828f612e64565b15613495575050505050613535565b856001019a506134ae565b508695506135359350505050565b50506134c7565b6001840397506134c7565b8360010198505b60008380156134d35750875b80156134e15750846001018a145b90508080156134f2575060108a8a03115b156135025789600801945061350b565b89890160011c94505b8c8561ffff166201000081106135235761352361405c565b01965060009350505050600161341c57505b96509650969350505050565b600080858703818080806135588d8d878c8c612bf4565b83549397509195509350915063ffffffff6101008204811691600160281b900460060b908716829003613595579750909550613635945050505050565b83156135bc5781870363ffffffff168c60020b028101839850985050505050505050613635565b845463ffffffff6101008204811691600160281b900460060b9089168290036135f757995050505060010161ffff1694506136359350505050565b838203848a0363ffffffff808216908316600690810b90878603900b81613620576136206141b0565b05028501879c509c5050505050505050505050505b965096945050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6001600160a01b0391909116815260200190565b6001600160a01b038116811461187b57600080fd5b801515811461187b57600080fd5b60008083601f8401126136c557600080fd5b5081356001600160401b038111156136dc57600080fd5b6020830191508360208285010111156136f457600080fd5b9250929050565b60008060008060008060008060e0898b03121561371757600080fd5b883561372281613690565b9750602089013561373281613690565b96506040890135613742816136a5565b955060608901359450608089013561375981613690565b935060a0890135613769816136a5565b925060c08901356001600160401b0381111561378457600080fd5b6137908b828c016136b3565b999c989b5096995094979396929594505050565b803563ffffffff811681146137b857600080fd5b919050565b6000602082840312156137cf57600080fd5b611041826137a4565b600060e082840312156137ea57600080fd5b50919050565b60008060008060008060008060e0898b03121561380c57600080fd5b883561381781613690565b9750602089013561382781613690565b965060408901359550606089013594506080890135935060a0890135925060c08901356001600160401b0381111561378457600080fd5b60006020828403121561387057600080fd5b5035919050565b6000815180845260005b8181101561389d57602081850181015186830182015201613881565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006110416020830184613877565b6000602082840312156138e257600080fd5b813561104181613690565b8060020b811461187b57600080fd5b8035600f81900b81146137b857600080fd5b600080600080600080600060c0888a03121561392957600080fd5b873561393481613690565b9650602088013561394481613690565b95506040880135613954816138ed565b94506060880135613964816138ed565b9350613972608089016138fc565b925060a08801356001600160401b0381111561398d57600080fd5b6139998a828b016136b3565b989b979a50959850939692959293505050565b600080604083850312156139bf57600080fd5b82356139ca81613690565b915060208301356139da81613690565b809150509250929050565b6000806000606084860312156139fa57600080fd5b8335613a0581613690565b92506020840135613a1581613690565b91506040840135613a25816138ed565b809150509250925092565b60008060008060008060a08789031215613a4957600080fd5b8635613a5481613690565b95506020870135613a6481613690565b9450604087013593506060870135925060808701356001600160401b03811115613a8d57600080fd5b613a9989828a016136b3565b979a9699509497509295939492505050565b60008060008060008060008060006101008a8c031215613aca57600080fd5b8935613ad581613690565b985060208a0135613ae581613690565b975060408a0135613af5816136a5565b965060608a0135955060808a0135613b0c81613690565b945060a08a0135935060c08a0135925060e08a01356001600160401b03811115613b3557600080fd5b613b418c828d016136b3565b915080935050809150509295985092959850929598565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613b9657613b96613b58565b604052919050565b60006020808385031215613bb157600080fd5b82356001600160401b0380821115613bc857600080fd5b818501915085601f830112613bdc57600080fd5b813581811115613bee57613bee613b58565b8060051b9150613bff848301613b6e565b8181529183018401918481019088841115613c1957600080fd5b938501935b83851015613c3e57613c2f856137a4565b82529385019390850190613c1e565b98975050505050505050565b604080825283519082018190526000906020906060840190828701845b82811015613c8657815160060b84529284019290840190600101613c67565b5050508381038285015284518082528583019183019060005b81811015613cc45783516001600160581b031683529284019291840191600101613c9f565b5090979650505050505050565b60008060408385031215613ce457600080fd5b50508035926020909101359150565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613d4857603f19888603018452613d36858351613877565b94509285019290850190600101613d1a565b5092979650505050505050565b600080600060608486031215613d6a57600080fd5b8335613d7581613690565b9250613d83602085016137a4565b9150613d91604085016137a4565b90509250925092565b60008060008060008060008060006101008a8c031215613db957600080fd5b8935613dc481613690565b985060208a0135613dd481613690565b975060408a0135613de4816138ed565b965060608a0135613df4816138ed565b9550613b0c60808b016138fc565b61ffff8116811461187b57600080fd5b80356137b881613e02565b60008060408385031215613e3057600080fd5b8235613e3b81613e02565b915060208301356139da81613e02565b600080600060608486031215613e6057600080fd5b8335613e6b81613690565b9250602084013591506040840135613a2581613690565b600060e08284031215613e9457600080fd5b60405160e081016001600160401b0381118282101715613eb657613eb6613b58565b6040528235613ec481613e02565b81526020830135613ed481613e02565b6020820152613ee5604084016137a4565b6040820152613ef6606084016137a4565b60608201526080830135613f0981613e02565b608082015260a0830135613f1c81613e02565b60a0820152613f2d60c08401613e12565b60c08201529392505050565b60e081018235613f4881613e02565b61ffff9081168352602084013590613f5f82613e02565b8082166020850152613f73604086016137a4565b915063ffffffff808316604086015280613f8f606088016137a4565b1660608601525060808501359150613fa682613e02565b908116608084015260a084013590613fbd82613e02565b1660a0830152613fcf60c08401613e12565b61ffff811660c0840152611e12565b600181811c90821680613ff257607f821691505b6020821081036137ea57634e487b7160e01b600052602260045260246000fd5b60006020828403121561402457600080fd5b815161104181613690565b602080825260139082015272141b1d59da5b881b9bdd08185d1d1858da1959606a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161409a5761409a614072565b5060010190565b61ffff828116828216039080821115611e1257611e12614072565b6000602082840312156140ce57600080fd5b81516003811061104157600080fd5b634e487b7160e01b600052602160045260246000fd5b60006020828403121561410557600080fd5b8151611041816136a5565b80820180821115610b2e57610b2e614072565b60008060008060008060c0878903121561413c57600080fd5b865161414781613690565b6020880151909650614158816138ed565b604088015190955061416981613e02565b606088015190945060ff8116811461418057600080fd5b608088015190935061419181613e02565b60a08801519092506141a2816136a5565b809150509295509295509295565b634e487b7160e01b600052601260045260246000fd5b63ffffffff828116828216039080821115611e1257611e12614072565b600682810b9082900b03667fffffffffffff198112667fffffffffffff82131715610b2e57610b2e614072565b60008160060b8360060b80614227576142276141b0565b667fffffffffffff1982146000198214161561424557614245614072565b90059392505050565b60008260060b80614261576142616141b0565b808360060b0791505092915050565b60008160020b627fffff19810361428957614289614072565b6000190192915050565b634e487b7160e01b600052600160045260246000fdfe8e8000aba5b365c0be9685da1153f7f096e76d1ecfb42c050ae1e387aa65b4f5a164736f6c6343000814000ab10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6267da724c255813ae00f4522fe843cb70148a4b8099cbc5af64f9a4151e55ed6a164736f6c6343000814000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000010253594a832f967994b44f33411940533302acb

-----Decoded View---------------
Arg [0] : _algebraFactory (address): 0x10253594A832f967994b44f33411940533302ACb

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000010253594a832f967994b44f33411940533302acb


Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.