ETH Price: $3,253.53 (+6.46%)
Gas: 40 Gwei

Contract

0x6910C4e32D425a834Fb61e983C8083a84b0EBd01
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Execute Virtual ...181894432023-09-22 5:55:11158 days 4 hrs ago1695362111IN
0x6910C4...4b0EBd01
0 ETH0.006722798.80597758
Execute Virtual ...181894362023-09-22 5:53:47158 days 4 hrs ago1695362027IN
0x6910C4...4b0EBd01
0 ETH0.006767268.86423274
Execute Virtual ...181822342023-09-21 5:40:23159 days 4 hrs ago1695274823IN
0x6910C4...4b0EBd01
0 ETH0.006886859.11345732
Execute Virtual ...181822272023-09-21 5:38:59159 days 4 hrs ago1695274739IN
0x6910C4...4b0EBd01
0 ETH0.007157919.37592949
Execute Virtual ...181750262023-09-20 5:29:11160 days 4 hrs ago1695187751IN
0x6910C4...4b0EBd01
0 ETH0.008082569.67276556
Execute Virtual ...181669772023-09-19 2:26:35161 days 7 hrs ago1695090395IN
0x6910C4...4b0EBd01
0 ETH0.006852339.06777142
Execute Virtual ...181597752023-09-18 2:08:59162 days 7 hrs ago1695002939IN
0x6910C4...4b0EBd01
0 ETH0.006828589.03634543
Execute Virtual ...181525572023-09-17 1:33:59163 days 8 hrs ago1694914439IN
0x6910C4...4b0EBd01
0 ETH0.0079400210.14932868
Execute Virtual ...181451002023-09-16 0:16:11164 days 9 hrs ago1694823371IN
0x6910C4...4b0EBd01
0 ETH0.006674398.83230597
Execute Virtual ...181378992023-09-14 23:54:11165 days 10 hrs ago1694735651IN
0x6910C4...4b0EBd01
0 ETH0.0113760411.13028391
Execute Virtual ...181164432023-09-11 23:39:23168 days 10 hrs ago1694475563IN
0x6910C4...4b0EBd01
0 ETH0.0092118711.02426881
Execute Virtual ...181039092023-09-10 5:32:23170 days 4 hrs ago1694323943IN
0x6910C4...4b0EBd01
0 ETH0.0080352810.63317973
Execute Virtual ...180966552023-09-09 5:08:23171 days 4 hrs ago1694236103IN
0x6910C4...4b0EBd01
0 ETH0.0079128210.47113659
Execute Virtual ...180894542023-09-08 4:55:47172 days 5 hrs ago1694148947IN
0x6910C4...4b0EBd01
0 ETH0.0082306110.8916735
Execute Virtual ...180822502023-09-07 4:44:35173 days 5 hrs ago1694061875IN
0x6910C4...4b0EBd01
0 ETH0.0083403611.03689765
Execute Virtual ...180750042023-09-06 4:21:59174 days 5 hrs ago1693974119IN
0x6910C4...4b0EBd01
0 ETH0.0094248810.60310176
Execute Virtual ...180664402023-09-04 23:35:59175 days 10 hrs ago1693870559IN
0x6910C4...4b0EBd01
0 ETH0.0097341912.44272577
Execute Virtual ...180589352023-09-03 22:26:59176 days 11 hrs ago1693780019IN
0x6910C4...4b0EBd01
0 ETH0.0119577711.40225359
Execute Virtual ...180483792023-09-02 10:57:35177 days 23 hrs ago1693652255IN
0x6910C4...4b0EBd01
0 ETH0.0097024912.83941696
Execute Virtual ...180411742023-09-01 10:41:59178 days 23 hrs ago1693564919IN
0x6910C4...4b0EBd01
0 ETH0.0102636513.11951313
Execute Virtual ...180336402023-08-31 9:24:11180 days 43 mins ago1693473851IN
0x6910C4...4b0EBd01
0 ETH0.0112597314.39274603
Execute Virtual ...180263402023-08-30 8:50:59181 days 1 hr ago1693385459IN
0x6910C4...4b0EBd01
0 ETH0.0147355918.83575879
Execute Virtual ...180187012023-08-29 7:09:59182 days 2 hrs ago1693292999IN
0x6910C4...4b0EBd01
0 ETH0.0231138916.89118033
Execute Virtual ...180047762023-08-27 8:20:59184 days 1 hr ago1693124459IN
0x6910C4...4b0EBd01
0 ETH0.0091332212.08610135
Execute Virtual ...179975732023-08-26 8:12:35185 days 1 hr ago1693037555IN
0x6910C4...4b0EBd01
0 ETH0.009260512.25452946
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Txn Hash Block From To Value
171350442023-04-27 4:07:35306 days 5 hrs ago1682568455  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x0018C3...e4F1C8C9
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
CronV1Pool

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 575 runs

Other Settings:
default evmVersion
File 1 of 38 : CronV1Pool.sol
// SPDX-License-Identifier: BUSL-1.1

// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//

pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;

import { Math } from "./balancer-core-v2/lib/math/Math.sol";

import { IERC20 } from "./balancer-core-v2/lib/openzeppelin/IERC20.sol";
import { IERC20Decimals } from "./interfaces/IERC20Decimals.sol";
import { ReentrancyGuard } from "./balancer-core-v2/lib/openzeppelin/ReentrancyGuard.sol";

import { IMinimalSwapInfoPool } from "./balancer-core-v2/vault/interfaces/IMinimalSwapInfoPool.sol";
import { IBasePool } from "./balancer-core-v2/vault/interfaces/IBasePool.sol";
import { IVault } from "./balancer-core-v2/vault/interfaces/IVault.sol";
import { BalancerPoolToken } from "./balancer-core-v2/pools/BalancerPoolToken.sol";

import { IArbitrageurList } from "./interfaces/IArbitrageurList.sol";
import { ICronV1Pool } from "./interfaces/ICronV1Pool.sol";
import { ICronV1PoolFactory } from "./interfaces/ICronV1PoolFactory.sol";
import { ICronV1FactoryOwnerActions } from "./interfaces/pool/ICronV1FactoryOwnerActions.sol";
import { ICronV1PoolAdminActions } from "./interfaces/pool/ICronV1PoolAdminActions.sol";
import { ICronV1PoolArbitrageurActions } from "./interfaces/pool/ICronV1PoolArbitrageurActions.sol";
import { ICronV1PoolEnums } from "./interfaces/pool/ICronV1PoolEnums.sol";
import { ICronV1PoolEvents } from "./interfaces/pool/ICronV1PoolEvents.sol";
import { ICronV1PoolHelpers } from "./interfaces/pool/ICronV1PoolHelpers.sol";

import { C } from "./miscellany/Constants.sol";
import { requireErrCode, CronErrors } from "./miscellany/Errors.sol";
import { sqrt } from "./miscellany/Misc.sol";
import { BitPackingLib } from "./miscellany/BitPacking.sol";
import { VirtualOrders, OrderPools, Order, ExecVirtualOrdersMem, LoopMem, PriceOracle } from "./interfaces/Structs.sol";

/// @title  An implementation of a Time-Weighted Average Market Maker on Balancer V2 Vault Pools.
/// @author Zero Slippage (0slippage), Based upon the Paradigm paper TWAMM and the reference design
///         created by frankieislost with optimizations from FRAX incorporated for gas efficiency.
///
/// @notice For usage details, see the online Cron-Fi documentation at https://docs.cronfi.com/.
///
/// @dev Uses Balancer math library for overflow/underflow checks on standard U256 containers.
///      However, as many custom representations are used (i.e. non native word lengths) there
///      are a number of explicit checks against the maximum of other word lengths.
///      Furthermore there are unchecked operations (this code targets Solidity 0.7.x which
///      didn't yet feature implicit arithmetic checks or have the 'unchecked' block feature)
///      herein for reasons of efficiency or desired overflow. Wherever they appear they will
///      be documented and accompanied with one of the following tags:
///        - #unchecked
///        - #overUnderFlowIntended
///      Identified risks will be accompanied and described with the following tag:
///        - #RISK
///
/// @dev Conventions in the methods, variables and constants are as follows:
///
///      Prefixes:
///
///      - In constants, the prefix "Sn", where 1 <= n <= 4, denotes which slot the constant
///        pertains too. There are four storage slots that are bitpacked. For example,
///        "S2_OFFSET_ORACLE_TIMESTAMP" refers to the offset of the oracle timestamp in bit-
///        packed storage slot 2.
///
///      Suffixes:
///
///      - The suffix of a variable name denotes the type contained within the variable.
///        For instance "uint256 _incrementU96" is a 256-bit unsigned container representing
///        the 96-bit value "_increment".
///        In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is
///        representing a 19 digit decimal value with 18 fractional digits. In this scenario,
///        the D=Decimal, U=Unsigned, F=Fractional.
///        Finally, "uint128 valueU128F64" is a 128-bit container representing a 128-bit value
///        with 64 fractional bits.
///
///      - The suffix of a function name denotes what slot it is proprietary too as a
///        matter of convention. While unchecked at run-time or by the compiler, the naming
///        convention easily aids in understanding what slot a packed value is stored within.
///        For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3.
///        If the value of slot 2 were passed to this method, the unpacked value would be
///        incorrect.
///
/// @dev Fee Points (FP) is a system used herein to calculate applicable fees. THESE ABSOLUTELY
///      SHOULD NOT BE CONFUSED WITH BASIS POINTS--THEY ARE NOT BASIS POINTS! It consists of
///      fees, such as a swap fee, expressed in FP. The swap fee is multiplied by the amount
///      of token being swapped and divided by the total fee points (TOTAL_FP), which is 100,000,
///      to obtain the fee. For instance, a swap fee of 0.050% can be realized as follows:
///
///                    token_in x FEE_BP
///         swap_fee = -----------------
///                         TOTAL_FP
///
///                      token_in x 50
///                  = -----------------
///                          100000
///
contract CronV1Pool is ICronV1Pool, IMinimalSwapInfoPool, BalancerPoolToken, ReentrancyGuard {
  using Math for uint256;

  IVault private immutable VAULT;
  bytes32 public immutable override POOL_ID;
  IERC20 private immutable TOKEN0;
  IERC20 private immutable TOKEN1;
  PoolType public immutable override POOL_TYPE;
  uint16 private immutable ORDER_BLOCK_INTERVAL;
  uint24 private immutable MAX_ORDER_INTERVALS;
  uint256 private immutable ORDER_POOL0_PROCEEDS_SCALING;
  uint256 private immutable ORDER_POOL1_PROCEEDS_SCALING;
  address private immutable FACTORY;

  VirtualOrders private virtualOrders;

  /* Slot 1 Layout:
   *
   * The following variables are bit-mapped into uint256 slot1 in
   * container sizes related to their actual ranges as depicted below:
   *
   *   256-255  free (2-bits)
   *   -------
   *   254-245  shortTermFeeFP (10-bits)
   *   -------
   *   244-235  partnerFeeFP (10-bits)
   *   -------
   *   234-225  longTermFeeFP (10-bits)
   *   -------
   *   224-113  token0Orders (112-bits)
   *   -------
   *   112-  1  token1Orders (112-bits)
   *
   */
  uint256 internal slot1;

  /* Slot 2 Layout:
   *
   * The following variables are bit-mapped into uint256 slot2 in
   * container sizes related to their actual ranges as depicted below:
   *
   *   256-225  oracleTimeStamp (32-bits)
   *   -------
   *   224-113  token0Proceeds (112-bits)
   *   -------
   *   112-  1  token1Proceeds (112-bits)
   *
   */
  uint256 internal slot2;

  /* Slot 3 Layout:
   *
   * The following variables are bit-mapped into uint256 slot3 in
   * container sizes related to their actual ranges as depicted below:
   *
   *   256-226  free (31-bits)
   *   -------
   *   225-223  feeShiftU3 (3-bits)
   *   -------
   *   222-213  free (10-bits)
   *   -------
   *   212-193  free (20-bits)
   *   -------
   *   192- 97  token0CronFiFees (96-bits)
   *   -------
   *    96-  1  token1CronFiFees (96-bits)
   *
   */
  uint256 internal slot3;

  /* Slot 4 Layout
   *
   * The following variables are bit-mapped into uint256 slot4 in
   * container sizes related to their actual ranges as depicted below:
   *
   *       256  paused (1-bit)
   *   -------
   *       255  cronFeeEnabled (1-bit)
   *   -------
   *       254  collectBalancerFees (1-bit)
   *   -------
   *       253  zeroCronFiFees (1-bit)
   *   -------
   *   252-193  balancerFeeDU1F18 (60-bits)
   *   -------
   *   192- 97  token0BalancerFees (96-bits)
   *   -------
   *    96-  1  token1BalancerFees (96-bits)
   *
   */
  uint256 internal slot4;

  // @notice Uniswap V2 Style Oracle.
  // @dev Call function getOracleTimeStamp to get the timestamp associated
  //      with the oracle price values in this state variable.
  PriceOracle internal priceOracle;

  mapping(address => bool) internal adminAddrMap;
  mapping(address => address) internal partnerContractAddrMap;

  address internal feeAddr;

  mapping(address => uint256[]) private orderIdMap;

  /// @notice Ensure that the modified function is called by an address that is the factory owner.
  /// @dev    Cannot be used on Balancer Vault callbacks (onJoin, onExit,
  ///         onSwap) because msg.sender is the Vault address.
  ///
  modifier senderIsFactoryOwner() {
    _senderIsFactoryOwner(); // #contractsize optimization
    _;
  }

  /// @notice Ensures that the modified function is called by an address with administrator privileges.
  /// @dev    Cannot be used on Balancer Vault callbacks (onJoin, onExit,
  ///         onSwap) because msg.sender is the Vault address.
  ///
  modifier senderIsAdmin() {
    _senderIsAdmin(); // #contractsize optimization
    _;
  }

  /// @notice Ensures the modified function is called by an address with arbitrage partner privileges.
  /// @dev    Cannot be used on Balancer Vault callbacks (onJoin, onExit,
  ///         onSwap) because msg.sender is the Vault address.
  ///
  modifier senderIsArbitragePartner() {
    _senderIsArbitragePartner(); // #contractsize optimization
    _;
  }

  /// @notice Ensures that the modified function is not executed if the pool is currently paused.
  ///
  modifier poolNotPaused() {
    _poolNotPaused(); // #contractsize optimization
    _;
  }

  /// @notice Creates an instance of the Cron-Fi TWAMM pool. A Cron-Fi TWAMM pool features virtual order management and
  ///         virtualized reserves. Liquidity is managed through an instance of BalancerPoolToken.
  ///         The fees associated with the pool are configurable at run-time.
  ///
  ///         Importantly, the OBI cannot be changed after instantiation. If a pool's OBI is inappropriate to the
  ///         properties of the pair of tokens, it is recommended to create a new pool.
  ///
  ///         In the event of a failure, the pool can be paused which bypasses computation of virtual orders and allows
  ///         liquidity to be removed and long-term virtual orders to be withdrawn and refunded. Other operations are
  ///         blocked.
  ///
  ///         Management of the pool is performed by administrators who are able set gross swap fee amounts and
  ///         aribtrage partner status.
  ///
  ///         The pool factory owner is able to set the status of administrators, enable Cron-Fi fees, modify the
  ///         Cron-Fi fee address, adjust the fee-split between Cron-Fi and liquidity providers, and enable Balancer
  ///         fees.
  ///
  ///         Arbitrage partners are able to set and update a contract address that lists their arbitrageur's addresses,
  ///         which are able to swap at reduced fees as an incentive to provide better long-term order execution by
  ///         adjusting the bonding curve to compensate for the effect of virtual orders. These partners perform
  ///         accounting and capture a percentage of the trades or capture fees in another way which are periodically
  ///         remitted to the pool, rewarding the liquidity providers. This may be thought of as a constructive pay for
  ///         order flow or Maximal Extractable Value (MEV) recapture.
  ///
  /// @param _token0Inst The contract instance for token 0.
  /// @param _token1Inst The contract instance for token 1.
  /// @param _vaultInst The Balancer Vault instance this pool be a member of.
  /// @param _poolName The name for this pool.
  /// @param _poolSymbol The symbol for this pool.
  /// @param _poolType A value in the enumeration PoolType that controls the initial fee values and Order Block
  ///                  Interval (OBI) of the pool. See the documentation for the PoolType enumeration for details.
  ///
  constructor(
    IERC20 _token0Inst,
    IERC20 _token1Inst,
    IVault _vaultInst,
    string memory _poolName,
    string memory _poolSymbol,
    PoolType _poolType
  ) BalancerPoolToken(_poolName, _poolSymbol) {
    // Only factory can create pools, or else _senderIsFactoryOwner will revert:
    FACTORY = msg.sender;

    bytes32 poolIdValue = _vaultInst.registerPool(IVault.PoolSpecialization.TWO_TOKEN);

    IERC20[] memory tokens = new IERC20[](2);
    tokens[C.INDEX_TOKEN0] = _token0Inst;
    tokens[C.INDEX_TOKEN1] = _token1Inst;
    _vaultInst.registerTokens(
      poolIdValue,
      tokens,
      new address[](2) /* assetManagers */
    );

    VAULT = _vaultInst;
    POOL_ID = poolIdValue;
    TOKEN0 = _token0Inst;
    TOKEN1 = _token1Inst;

    POOL_TYPE = _poolType;

    // Compute the scaling factors for the order pool proceeds of each token. The addition of 1 is buffering the scaled
    // result to preserve additional precision.
    //
    uint256 token0Decimals = IERC20Decimals(address(_token0Inst)).decimals();
    requireErrCode(
      C.MIN_DECIMALS <= token0Decimals && token0Decimals <= C.MAX_DECIMALS,
      CronErrors.UNSUPPORTED_TOKEN_DECIMALS
    );
    ORDER_POOL0_PROCEEDS_SCALING = (10**(token0Decimals + 1));

    uint256 token1Decimals = IERC20Decimals(address(_token1Inst)).decimals();
    requireErrCode(
      C.MIN_DECIMALS <= token1Decimals && token1Decimals <= C.MAX_DECIMALS,
      CronErrors.UNSUPPORTED_TOKEN_DECIMALS
    );
    ORDER_POOL1_PROCEEDS_SCALING = (10**(token1Decimals + 1));

    // NOTE: Conditional assignment style / ternary operator hell required for immutables.
    //
    ORDER_BLOCK_INTERVAL = (_poolType == PoolType.Stable) ? C.STABLE_OBI : (_poolType == PoolType.Liquid)
      ? C.LIQUID_OBI
      : C.VOLATILE_OBI;
    MAX_ORDER_INTERVALS = (_poolType == PoolType.Stable) ? C.STABLE_MAX_INTERVALS : (_poolType == PoolType.Liquid)
      ? C.LIQUID_MAX_INTERVALS
      : C.VOLATILE_MAX_INTERVALS;

    if (_poolType == PoolType.Stable) {
      uint256 localSlot1 = BitPackingLib.packU10(0, C.STABLE_ST_FEE_FP, C.S1_OFFSET_SHORT_TERM_FEE_FP);
      localSlot1 = BitPackingLib.packU10(localSlot1, C.STABLE_ST_PARTNER_FEE_FP, C.S1_OFFSET_PARTNER_FEE_FP);
      slot1 = BitPackingLib.packU10(localSlot1, C.STABLE_LT_FEE_FP, C.S1_OFFSET_LONG_TERM_FEE_FP);
    } else if (_poolType == PoolType.Liquid) {
      uint256 localSlot1 = BitPackingLib.packU10(0, C.LIQUID_ST_FEE_FP, C.S1_OFFSET_SHORT_TERM_FEE_FP);
      localSlot1 = BitPackingLib.packU10(localSlot1, C.LIQUID_ST_PARTNER_FEE_FP, C.S1_OFFSET_PARTNER_FEE_FP);
      slot1 = BitPackingLib.packU10(localSlot1, C.LIQUID_LT_FEE_FP, C.S1_OFFSET_LONG_TERM_FEE_FP);
    } else {
      // PoolType.Volatile
      uint256 localSlot1 = BitPackingLib.packU10(0, C.VOLATILE_ST_FEE_FP, C.S1_OFFSET_SHORT_TERM_FEE_FP);
      localSlot1 = BitPackingLib.packU10(localSlot1, C.VOLATILE_ST_PARTNER_FEE_FP, C.S1_OFFSET_PARTNER_FEE_FP);
      slot1 = BitPackingLib.packU10(localSlot1, C.VOLATILE_LT_FEE_FP, C.S1_OFFSET_LONG_TERM_FEE_FP);
    }

    slot3 = BitPackingLib.packFeeShiftS3(slot3, C.DEFAULT_FEE_SHIFT);

    uint256 localSlot4 = BitPackingLib.packBit(0, 0, C.S4_OFFSET_PAUSE);
    localSlot4 = BitPackingLib.packBit(localSlot4, 0, C.S4_OFFSET_CRON_FEE_ENABLED);
    localSlot4 = BitPackingLib.packBit(localSlot4, 1, C.S4_OFFSET_COLLECT_BALANCER_FEES);
    slot4 = BitPackingLib.packBit(localSlot4, 1, C.S4_OFFSET_ZERO_CRONFI_FEES);

    adminAddrMap[C.CRON_DEPLOYER_ADMIN] = true;
    emit AdministratorStatusChange(msg.sender, C.CRON_DEPLOYER_ADMIN, true);

    emit FeeAddressChange(msg.sender, C.NULL_ADDR);
  }

  /// @notice Called by the vault when a user calls IVault.swap. Can be used to perform a Short-Term (ST)
  ///         swap, Long-Term (LT) swap, or Partner swap
  ///         ST swaps and Partner swaps behave like traditional Automated Market Maker atomic swaps
  ///         (think Uniswap V2 swaps).
  ///         LT swaps are virtual orders and behave differently, executing over successive blocks until
  ///         their expiry. Each LT swap is assigned an order id that is logged in a LongTermSwap event and
  ///         can also be fetched using getOrderIds for a given address. LT swaps can be withdrawn or
  ///         cancelled through the IVault.exit function (see onExitPool documentation).
  /// @param _swapRequest Is documented in Balancer's IPoolSwapStructs.sol. However, the userData field
  ///                     of this _swapRequest struct is a uint256 value, swapTypeU, followed by another
  ///                     uint256 value, argument, detailed below:
  ///                       * swapTypeU is decoded into the enum SwapType and determines if the transaction
  ///                         is a RegularSwap, LongTermSwap, or PartnerSwap.
  ///                         Min. = 0, Max. = 3
  ///                       * argument is a value, the use of which depends upon the SwapType value passed
  ///                                  into swapTypeU:
  ///                           - swapTypeU=0 (RegularSwap):  argument is ignored / not used.
  ///                           - swapTypeU=1 (LongTermSwap): argument is the number of order intervals for
  ///                                                         the LT trade before expiry.
  ///                           - swapTypeU=2 (PartnerSwap):  argument is the Partner address stored in a
  ///                                                         uint256. It is used to loop up the Partner's
  ///                                                         current arbitrage list contract address.
  ///                     Delegates:
  ///                     If the specified swapType is a LongTermSwap, the _swapRequest.to field can be
  ///                     used to specify a LT-Swap delegate. The delegate account is able to withdraw or
  ///                     cancel the LT-swap on behalf of the order owner (_swapRequest.from) at any time,
  ///                     so long as the recipient account specified for proceeds or refunds is the order
  ///                     owner. (The order owner does not have this restriction and direct proceeds or
  ///                     refunds to any desired account.)
  ///                     If the specified _swapRequest.to field is the null address or the order owner,
  ///                     then the delegate is disabled (and set to the null address).
  /// @param _currentBalanceTokenInU112 The Balancer Vault balance of the token being sold to the pool.
  ///                                   Min. = 0, Max. = (2**112) - 1
  /// @param _currentBalanceTokenOutU112 The Balancer Vault balance of the token being bought from the pool.
  ///                                    Min. = 0, Max. = (2**112) - 1
  /// @return amountOutU112 The amount of token being bought from the pool in this swap. For LT swaps this
  ///                       will always be zero. Proceeds from an LT swap can be withdrawn or the order
  ///                       refunded with an appropriate call to the IVault.exit function (see onExitPool
  ///                       documentation).
  ///
  function onSwap(
    SwapRequest memory _swapRequest,
    uint256 _currentBalanceTokenInU112,
    uint256 _currentBalanceTokenOutU112
  ) external override(IMinimalSwapInfoPool) poolNotPaused returns (uint256 amountOutU112) {
    requireErrCode(msg.sender == address(VAULT), CronErrors.NON_VAULT_CALLER);

    // NOTE: Not checking for balance overflow (amount + _currentBalanceTokenInU112) because this is
    //       handled by Balancer error BAL#526 BALANCE_TOTAL_OVERFLOW

    // #savegas #savesize
    IERC20 tokenIn = _swapRequest.tokenIn;
    address from = _swapRequest.from;
    uint256 amount = _swapRequest.amount;

    requireErrCode(_swapRequest.kind == IVault.SwapKind.GIVEN_IN, CronErrors.UNSUPPORTED_SWAP_KIND);

    // This style of decoding the data into the enum saves 16b of contract size.
    (uint256 swapTypeU, uint256 argument) = abi.decode(_swapRequest.userData, (uint256, uint256));
    SwapType swapType = SwapType(swapTypeU);

    bool token0To1 = (tokenIn == TOKEN0);
    (uint256 token0ReserveU112, uint256 token1ReserveU112) = _executeVirtualOrders(
      token0To1 ? _currentBalanceTokenInU112 : _currentBalanceTokenOutU112,
      token0To1 ? _currentBalanceTokenOutU112 : _currentBalanceTokenInU112,
      block.number
    );

    if (swapType == SwapType.LongTermSwap) {
      address delegate = _swapRequest.to;
      uint256 orderId = _longTermSwap(from, delegate, token0To1, amount, argument);
      emit LongTermSwap(from, delegate, address(tokenIn), amount, argument, orderId);
    } else {
      if (swapType == SwapType.PartnerSwap) {
        address contractAddress = partnerContractAddrMap[address(argument)];
        requireErrCode(IArbitrageurList(contractAddress).isArbitrageur(from), CronErrors.SENDER_NOT_PARTNER);
      }

      amountOutU112 = _shortTermSwap(
        token0To1,
        (swapType == SwapType.RegularSwap),
        amount,
        token0ReserveU112,
        token1ReserveU112
      );
      emit ShortTermSwap(from, address(tokenIn), amount, amountOutU112, swapTypeU);
    }
  }

  /// @notice Called by the Vault when a user calls IVault.joinPool. Can be use to add liquidity to
  ///         the pool in exchange for Liquidity Provider (LP) pool tokens or to reward the pool with
  ///         liquidity (MEV rewards from arbitrageurs).
  ///         WARNING: The initial liquidity provider, in a call to join the pool with joinTypeU=0
  ///                  (JoinType.Join), will sacrifice MINIMUM_LIQUIDITY, 1000, Liquidity Provider (LP)
  ///                  tokens. This may be an insignificant sacrifice for tokens with fewer decimal
  ///                  places and high worth (i.e. WBTC).
  ///         Importantly, the reward capability remains when the pool is paused to mitigate any
  ///         possible issue with underflowed pool reserves computed by differencing the pool accounting
  ///         from the pool token balances.
  /// @param _poolId The ID for this pool in the Balancer Vault
  /// @param _sender is the account performing the Join or Reward transaction (typically an LP or MEV reward
  ///                contract, respectively).
  /// @param _recipient is the account designated to receive pool shares in the form of LP tokens when
  ///                   Joining the pool. Can be set to _sender if sender wishes to receive the tokens
  ///                   and Join Events.
  /// @param _currentBalancesU112 an array containing the Balancer Vault balances of Token 0 and Token 1
  ///                             in this pool. The balances are in the same order that IVault.getPoolTokens
  ///                             returns.
  ///                             Min. = 0, Max. = (2**112) - 1
  /// @param _protocolFeeDU1F18 the Balancer protocol fee.
  ///                           Min. = 0, Max. = 10**18
  /// @param _userData is uint256 value, joinTypeU, followed by an array of 2 uint256 values, amounts,
  ///                  and another array of 2 uint256 values, minAmounts, detailed below:
  ///                    * joinTypeU is decoded into the enum JoinType and determines if the transaction is
  ///                                a Join or Reward.
  ///                                Min. = 0, Max. = 1
  ///                    * amountsInU112 are the amount of Token 0 and Token 1 to Join or Reward the pool
  ///                                    with, passed in the same array ordering that IVault.getPoolTokens
  ///                                    returns.
  ///                                    Min. = 0, Max. = (2**112) - 1
  ///                    * minAmountsU112 are the minimum amount of Token 0 and Token 1 prices at which
  ///                                     to Join the pool (protecting against sandwich attacks), passed
  ///                                     in the same array ordering that IVault.getPoolTokens returns.
  ///                                     The minAmountsU112 values are ignored unless joinTypeU is
  ///                                     0 (JoinType.Join). In the initial join, these values are
  ///                                     ignored.
  ///                                     Min. = 0, Max. = (2**112) - 1
  /// @return amountsInU112 is the amount of Token 0 and Token 1 provided to the pool as part of a Join or
  ///                       Reward transaction. Values are returned in the same array ordering that
  ///                       IVault.getPoolTokens returns.
  ///                       Min. = 0, Max. = (2**112) - 1
  /// @return dueProtocolFeeAmountsU96 the amount of Token 0 and Token 1 collected by the pool for
  ///                                  Balancer. Values are returned in the same array ordering that
  ///                                  IVault.getPoolTokens returns.
  ///                                  Min. = 0, Max. = (2**96) - 1
  ///
  function onJoinPool(
    bytes32 _poolId,
    address _sender,
    address _recipient,
    uint256[] memory _currentBalancesU112,
    uint256, /* lastChangeBlock - not used */
    uint256 _protocolFeeDU1F18,
    bytes calldata _userData
  ) external override(IBasePool) returns (uint256[] memory amountsInU112, uint256[] memory dueProtocolFeeAmountsU96) {
    _poolSafetyChecks(msg.sender, _poolId);

    JoinType joinType;
    uint256[] memory minAmountsU112;
    // Following block is a Stack Too Deep Workaround
    {
      // This style of decoding the data into the enum saves 16b of contract size.
      uint256 joinTypeU;
      (joinTypeU, amountsInU112, minAmountsU112) = abi.decode(_userData, (uint256, uint256[], uint256[]));
      joinType = JoinType(joinTypeU);
    }

    // NOTE: Not checking for balance overflows (amountsInU112 + _currentBalancesU112) because this is
    //       handled by Balancer error BAL#526 BALANCE_TOTAL_OVERFLOW

    ////////////////////////////////////////////////////////////////////////////////
    //                                                                            //
    // BEGIN WARNING: Moving the following 3 lines of code or separating them     //
    //                results in increased contract size (3b - ~53b or more       //
    //                depending on where the code is moved or how separated).     //
    //                                                                            //
    ////////////////////////////////////////////////////////////////////////////////
    // #savesize #savegas
    uint256 token0InU112 = amountsInU112[C.INDEX_TOKEN0];
    uint256 token1InU112 = amountsInU112[C.INDEX_TOKEN1];

    uint256 amountLP;
    ////////////////////////////////////////////////////////////////////////////////
    // END WARNING                                                                //
    ////////////////////////////////////////////////////////////////////////////////

    // NOTE:  If the pool is paused, JoinType.Join reverts.  JoinType.Reward does not revert, but proceeds
    //        without running execute virtual orders deliberately; this is because it is a fallback mitigation
    //        if _calculateReserves within the execute virtual orders process fails due to unforeseen finite
    //        precision effects (specifically if Balancer's accounting is less than the sum of this pool's
    //        orders, proceeds, and fees for either pool token--in this case a reward can be used to increase
    //        the balancer vault accounting preventing the failure and allowing withdraws, cancels, and pool
    //        exits that would otherwise be blocked by the failing _calculateReserves call).
    //
    //        If the pool is not paused, execute virtual orders is run for JoinType.Reward to ensure the added
    //        liquidity applies to the newest trades (i.e. those after the JoinType.Reward transaction).
    //
    if (!isPaused()) {
      uint256 token0ReserveU112;
      uint256 token1ReserveU112;

      if ((joinType == JoinType.Join && totalSupply() != 0) || (joinType == JoinType.Reward)) {
        (token0ReserveU112, token1ReserveU112) = _executeVirtualOrders(
          _currentBalancesU112[C.INDEX_TOKEN0],
          _currentBalancesU112[C.INDEX_TOKEN1],
          block.number
        );
      }

      if (joinType == JoinType.Join) {
        _joinMinimumCheck(
          token0InU112,
          token1InU112,
          minAmountsU112[C.INDEX_TOKEN0],
          minAmountsU112[C.INDEX_TOKEN1],
          token0ReserveU112,
          token1ReserveU112
        );
        amountLP = _join(_recipient, token0InU112, token1InU112, token0ReserveU112, token1ReserveU112);
      }
    } else {
      requireErrCode(joinType != JoinType.Join, CronErrors.POOL_PAUSED);
    }

    // When amountLP = 0, the PoolJoin event log doubles as a Reward event log.
    emit PoolJoin(_sender, _recipient, token0InU112, token1InU112, amountLP);

    dueProtocolFeeAmountsU96 = _handleBalancerFees(_protocolFeeDU1F18);
  }

  /// @notice Called by the Vault when a user calls IVault.exitPool. Can be used to remove liquidity from
  ///         the pool in exchange for Liquidity Provider (LP) pool tokens, to withdraw proceeds of a
  ///         Long-Term (LT) order, to cancel an LT order, or by the factory owner to withdraw protocol
  ///         fees if they are being collected.
  /// @param _poolId is the ID for this pool in the Balancer Vault
  /// @param _sender is the account performing the liquidity removal, LT order withdrawl, LT order
  ///                cancellation or the factory owner withdrawing fees.
  ///                For long term orders, a "delegate" may be specified, this address is able to
  ///                perform LT order withdraws and cancellations on behalf of the LT swap owner as
  ///                long as the recipient is the LT swap owner.
  /// @param _recipient For LT swaps the recipient must always be the original order owner (the address
  ///                   that issued the order) if a "delegate" address is performing the withdrawl or
  ///                   cancellation. If the order owner is performing the withdrawl or cancellation, the
  ///                   recipient can be set to whatever destination address desired.
  ///                   For other exit types (Exit & FeeWithdraw), the recipient can be set as desired
  ///                   so long as the sender is set to the authorized address.
  /// @param _currentBalancesU112 is an array containing the Balancer Vault balances of Token 0 and Token 1
  ///                             in this pool. The balances are in the same order that IVault.getPoolTokens
  ///                             returns.
  ///                             Min. = 0, Max. = (2**112) - 1
  /// @param _protocolFeeDU1F18 is the Balancer protocol fee.
  ///                           Min. = 0, Max. = 10**18
  /// @param _userData is uint256 value, exitTypeU, followed by a uint256, argument, detailed below:
  ///                    * exitTypeU is decoded into the enum ExitType and determines if the transaction is
  ///                                an Exit, Withdraw, Cancel, or FeeWithdraw.
  ///                                Min. = 0, Max. = 3
  ///                    * argument is used differently based on the ExitType value passed into exitTypeU:
  ///                                 - exitTypeU=0 (Exit):        argument is the number of LP tokens to
  ///                                                              redeem on Exit.
  ///                                 - exitTypeU=1 (Withdraw):    argument is the LT Swap order ID.
  ///                                 - exitTypeU=2 (Cancel):      argument is the LT Swap order ID.
  ///                                 - exitTypeU=3 (FeeWithdraw): argument is ignored / not used.
  /// @return amountsOutU112 is the amount of Token 0 and Token 1 provided by the pool as part of an Exit,
  ///                        LT Swap Withdrawl, LT Swap Cancel, or Fee Withdraw transaction. Values are
  ///                        returned in the same array ordering that IVault.getPoolTokens returns.
  ///                        Min. = 0, Max. = (2**112) - 1
  /// @return dueProtocolFeeAmountsU96 the amount of Token 0 and Token 1 collected by the pool for
  ///                                  Balancer. Values are returned in the same array ordering that
  ///                                  IVault.getPoolTokens returns.
  ///                                  Min. = 0, Max. = (2**96) - 1
  function onExitPool(
    bytes32 _poolId,
    address _sender,
    address _recipient,
    uint256[] memory _currentBalancesU112,
    uint256, /* lastChangeBlock - not used */
    uint256 _protocolFeeDU1F18,
    bytes calldata _userData
  ) external override(IBasePool) returns (uint256[] memory amountsOutU112, uint256[] memory dueProtocolFeeAmountsU96) {
    _poolSafetyChecks(msg.sender, _poolId);

    ExitType exitType;
    uint256 argument;
    // Following block is a Stack Too Deep Workaround
    {
      // This style of decoding the data into the enum saves 16b of contract size.
      uint256 exitTypeU;
      (exitTypeU, argument) = abi.decode(_userData, (uint256, uint256));
      exitType = ExitType(exitTypeU);
    }

    uint256 token0OutU112;
    uint256 token1OutU112;
    if (exitType == ExitType.FeeWithdraw) {
      (token0OutU112, token1OutU112) = _withdrawCronFees(_sender);
    } else {
      // Following block is a Stack Too Deep Workaround
      {
        // _executeVirtualOrders must be run for all exit types below, but it is in
        // this block b/c token0ReserveU112 and token1ReserveU112 variables are only for the
        // ExitType.Exit and cause Stack Too Deep otherwise.
        (uint256 token0ReserveU112, uint256 token1ReserveU112) = _executeVirtualOrders(
          _currentBalancesU112[C.INDEX_TOKEN0],
          _currentBalancesU112[C.INDEX_TOKEN1],
          block.number
        );

        if (exitType == ExitType.Exit) {
          (token0OutU112, token1OutU112) = _exit(_sender, argument, token0ReserveU112, token1ReserveU112);
        }
      }

      if (exitType == ExitType.Withdraw || exitType == ExitType.Cancel) {
        // NOTE: For all calls in this else block:
        //   - argument is the Order ID
        //   - _sender must be the original virtual order _sender
        (token0OutU112, token1OutU112) = _withdrawLongTermSwapWrapper(
          argument,
          _sender,
          _recipient,
          exitType == ExitType.Cancel
        );
      }
    }

    amountsOutU112 = new uint256[](2);
    amountsOutU112[C.INDEX_TOKEN0] = token0OutU112;
    amountsOutU112[C.INDEX_TOKEN1] = token1OutU112;

    dueProtocolFeeAmountsU96 = _handleBalancerFees(_protocolFeeDU1F18);
  }

  /// @notice Set the administrator status of the provided address, _admin. Status "true" gives
  ///         administrative privileges, "false" removes privileges.
  /// @param _admin  The address to add or remove administrative privileges from.
  /// @param _status Whether to grant (true) or deny (false) administrative privileges.
  /// @dev CAREFUL! You can remove all administrative privileges, rendering the contract unmanageable.
  /// @dev NOTE: Must be called by the factory owner.
  ///
  function setAdminStatus(address _admin, bool _status)
    external
    override(ICronV1FactoryOwnerActions)
    senderIsFactoryOwner
    nonReentrant
  {
    adminAddrMap[_admin] = _status;
    emit AdministratorStatusChange(msg.sender, _admin, _status);
  }

  /// @notice Enables Cron-Fi fee collection for Long-Term swaps when the provided address,
  ///         _feeDestination is not the null address.
  /// @param _feeDestination The address that can collect Cron-Fi Swap fees. If set to the null address,
  ///                        no Cron-Fi Long-Term swap fees are collected.
  /// @dev CAREFUL! Only the _feeDestination address can collect Cron-Fi fees from the pool.
  /// @dev NOTE: Must be called by the factory owner.
  ///
  function setFeeAddress(address _feeDestination)
    external
    override(ICronV1FactoryOwnerActions)
    senderIsFactoryOwner
    nonReentrant
  {
    feeAddr = _feeDestination;

    uint256 cronFeeEnabled = (_feeDestination != C.NULL_ADDR) ? 1 : 0;
    slot4 = BitPackingLib.packBit(slot4, cronFeeEnabled, C.S4_OFFSET_CRON_FEE_ENABLED);

    emit FeeAddressChange(msg.sender, _feeDestination);
  }

  /// @notice Sets whether the pool is paused or not. When the pool is paused:
  ///             * New swaps of any kind cannot be issued.
  ///             * Liquidity cannot be provided.
  ///             * Virtual orders are not executed for the remainder of allowable
  ///               operations, which include: removing liquidity, cancelling or
  ///               withdrawing a Long-Term swap order,
  ///         This is a safety measure that is not a part of expected pool operations.
  /// @param _pauseValue Pause the pool (true) or not (false).
  /// @dev NOTE: Must be called by an administrator.
  ///
  function setPause(bool _pauseValue) external override(ICronV1PoolAdminActions) senderIsAdmin nonReentrant {
    slot4 = BitPackingLib.packBit(slot4, _pauseValue ? 1 : 0, C.S4_OFFSET_PAUSE);

    emit BoolParameterChange(msg.sender, BoolParamType.Paused, _pauseValue);
  }

  /// @notice Set fee parameters.
  /// @param _paramTypeU A numerical value corresponding to the enum ParamType (see documentation
  ///                    for that above or values and corresponding ranges below).
  /// @param _value A value to set the specified parameter given in _paramTypeU. The values and
  ///               their ranges are as follows:
  ///
  ///     Short-Term Swap Fee Points:
  ///         * _paramTypeU = 0  (ParamType.SwapFeeFP)
  ///         * 0 <= _value <= C.MAX_FEE_FP (1000, ~1.000%)
  ///
  ///     Partner Swap Fee Points:
  ///         * _paramTypeU = 1  (ParamType.PartnerFeeFP)
  ///         * 0 <= _value <= C.MAX_FEE_FP (1000, ~1.000%)
  ///
  ///     Long-Term Swap Fee Points:
  ///         * _paramTypeU = 2  (ParamType.LongSwapFeeFP)
  ///         * 0 <= _value <= C.MAX_FEE_FP (1000, ~1.000%)
  ///
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000.
  /// @dev NOTE: Must be called by an administrator.
  ///
  function setParameter(uint256 _paramTypeU, uint256 _value)
    external
    override(ICronV1PoolAdminActions)
    senderIsAdmin
    nonReentrant
  {
    ParamType paramType = ParamType(_paramTypeU);

    // 1. Determine the bit-offset for any values that require an offset to be set:
    //
    uint256 offset;
    if (paramType == ParamType.SwapFeeFP) {
      offset = C.S1_OFFSET_SHORT_TERM_FEE_FP;
    } else if (paramType == ParamType.PartnerFeeFP) {
      offset = C.S1_OFFSET_PARTNER_FEE_FP;
    } else if (paramType == ParamType.LongSwapFeeFP) {
      offset = C.S1_OFFSET_LONG_TERM_FEE_FP;
    } // else offset can be zero.

    // 2. Map _paramType numerically to the corresponding slot and then do numerical limits test
    //    and set the value if conformant. Otherwise revert with a numerical limit error from a
    //    call to BitPackingLib or a parameter error.
    //
    uint256 value = _value;
    if (paramType <= ParamType.LongSwapFeeFP && offset != 0 && _value <= C.MAX_FEE_FP) {
      slot1 = BitPackingLib.packU10(slot1, value, offset);
    } else {
      requireErrCode(
        false, /* error intentionally, 9b smaller than dedicated revert refactor of this */
        CronErrors.PARAM_ERROR
      );
    }

    emit ParameterChange(msg.sender, paramType, value);
  }

  /// @notice Enable or disable the collection of Balancer Fees. When enabled Balancer takes a
  ///         a portion of every fee collected in the pool. The pool remits fees to Balancer
  ///         automatically when onJoinPool and onExitPool are called. Disabling balancer fees
  ///         through this function supersedes any setting of balancer fee values in onJoinPool
  ///         and onExitPool.
  /// @param _collectBalancerFee When true, Balancer fees are collected, when false they are
  ///                            not collected.
  /// @dev NOTE: Must be called by the factory owner.
  ///
  function setCollectBalancerFees(bool _collectBalancerFee)
    external
    override(ICronV1FactoryOwnerActions)
    senderIsFactoryOwner
    nonReentrant
  {
    slot4 = BitPackingLib.packBit(slot4, _collectBalancerFee ? 1 : 0, C.S4_OFFSET_COLLECT_BALANCER_FEES);

    emit BoolParameterChange(msg.sender, BoolParamType.CollectBalancerFees, _collectBalancerFee);
  }

  /// @notice Sets the fee shift that splits Long-Term (LT) swap fees remaining after Balancer's cut
  ///         between the Liquidity Providers (LP) and Cron-Fi, if Cron-Fi fee collection is enabled.
  /// @param _feeShift A value between 1 and 4. Specifiying invalid values results in no-operation
  ///                  (percentages are approximate). The implication of these values are outlined
  ///                  below and are only applicable if Cron-Fi fees are being collected:
  ///
  ///                      _feeShift = 1:
  ///                          LP gets 2 fee shares (~66%), Cron-Fi gets 1 fee share (~33%)
  ///
  ///                      _feeShift = 2:
  ///                          LP gets 4 fee shares (~80%), Cron-Fi gets 1 fee share (~20%)
  ///
  ///                      _feeShift = 3:
  ///                          LP gets 8 fee shares (~88%), Cron-Fi gets 1 fee share (~12%)
  ///
  ///                      _feeShift = 4:
  ///                          LP gets 16 fee shares (~94%), Cron-Fi gets 1 fee share (~6%)
  ///
  /// @dev NOTE: Must be called by the factory owner.
  ///
  function setFeeShift(uint256 _feeShift)
    external
    override(ICronV1FactoryOwnerActions)
    senderIsFactoryOwner
    nonReentrant
  {
    requireErrCode(_feeShift >= 1 && _feeShift <= 4, CronErrors.PARAM_ERROR);

    slot3 = BitPackingLib.packFeeShiftS3(slot3, _feeShift);

    emit FeeShiftChange(msg.sender, _feeShift);
  }

  /// @notice Sets the arbitrageur list contract address, _arbitrageList, for an arbitrage
  ///         partner, _arbPartner. To clear an arbitrage partner, set _arbitrageList to the
  ///         null address.
  /// @param _arbPartner The address of the arbitrage partner. This should be the aribtrage
  ///                    partner's public address and shareable with members of the the
  ///                    arbitrage list contract.
  /// @param _arbitrageList The address of a deployed arbitrageur list contract for the
  ///                       specified arbitrage partner. The deployed contract should
  ///                       conform to the interface IArbitrageurList.
  ///
  /// @dev NOTE: Must be called by an administrator.
  ///
  function setArbitragePartner(address _arbPartner, address _arbitrageList)
    external
    override(ICronV1PoolAdminActions)
    senderIsAdmin
    nonReentrant
  {
    partnerContractAddrMap[_arbPartner] = _arbitrageList;
    emit UpdatedArbitragePartner(msg.sender, _arbPartner, _arbitrageList);
  }

  /// @notice Advances the specified arbitrage partner (msg.sender) arbitrageur list
  ///         contract to the newest contract, if available. See IArbitrageurList for
  ///         details on the calls made by this contract to that interface's nextList
  ///         function.
  /// @return the new arbitrageur list contract address.
  /// @dev NOTE: Must be called by an arbitrage partner.
  ///
  function updateArbitrageList()
    external
    override(ICronV1PoolArbitrageurActions)
    senderIsArbitragePartner
    nonReentrant
    returns (address)
  {
    address currentList;
    address oldList = partnerContractAddrMap[msg.sender];
    address nextList = oldList;

    do {
      currentList = nextList;
      nextList = IArbitrageurList(currentList).nextList();
    } while (nextList != C.NULL_ADDR);

    partnerContractAddrMap[msg.sender] = currentList;

    emit UpdatedArbitrageList(msg.sender, oldList, currentList);
    return currentList;
  }

  /// @notice Executes active virtual orders, Long-Term swaps, since the last virtual order block
  ///         executed,  updating reserve and other state variables to the current block.
  /// @param _maxBlock A block to update the virtual orders to. In most situations this would be
  ///                  the current block, however if the pool has been inactive for a considerable
  ///                  duration, specifying an earlier block allows gas use to be reduced in the event
  ///                  that the gas needed to update to the current block for a transaction to be
  ///                  performed exceeds the nominal or extended amounts available to an Ethereum
  ///                  transaction (15M & 30M respectively at the time of this writing).
  ///                  If the specified max block preceeds the last virtual order block then the
  ///                  current block number is automatically used.
  ///
  function executeVirtualOrdersToBlock(uint256 _maxBlock)
    external
    override(ICronV1PoolArbitrageurActions)
    nonReentrant
  {
    _triggerVaultReentrancyCheck();
    (, uint256[] memory balances, ) = VAULT.getPoolTokens(POOL_ID);

    _executeVirtualOrders(balances[C.INDEX_TOKEN0], balances[C.INDEX_TOKEN1], _maxBlock);

    emit ExecuteVirtualOrdersEvent(msg.sender, _maxBlock);
  }

  /// @notice Get the virtual price oracle data for the pool at the specified block, _maxBlock.
  ///
  ///         IMPORTANT - This function calls _getVirtualReserves, which triggers a re-entrancy check. Due
  ///                     to contract size challanges, there is no explicit call to that re-entrancy check
  ///                     in this function, where it's presence would be more obvious.
  ///
  ///         IMPORTANT - This function does not meaningfully modify state despite the lack of a "view"
  ///                     designator for state mutability. (The call to _triggerVaultReentrancyCheck
  ///                     Unfortunately prevents the "view" designator as meaningless value is written
  ///                     to state to trigger a reentracy check).
  ///
  ///         Runs virtual orders from the last virtual order block up to the current block to provide
  ///         visibility into the current accounting for the pool.
  ///         If the pool is paused, this function reflects the accounting values of the pool at
  ///         the last virtual order block (i.e. it does not execute virtual orders to deliver the result).
  /// @param _maxBlock is the block to determine the virtual oracle values at. Its value must be
  ///                  greater than the last virtual order block and less than or equal to the
  ///                  current block. Otherwise, current block is used in the computation and
  ///                  reflected in the return value, blockNumber.
  /// @return timestamp is the virtual timestamp in seconds corresponding to the price oracle
  ///                   values.
  /// @return token0U256F112 The virtual cumulative price of Token0 measured in amount of
  ///                        Token1 * seconds at timestamp.
  /// @return token1U256F112 The virtual cumulative price of Token1 measured in amount of
  ///                        Token0 * seconds at timestamp.
  /// @return blockNumber The block that the virtual oracle values were computed at. Should
  ///                     match parameter _maxBlock, unless _maxBlock was not greater than the
  ///                     last virtual order block or less than or equal to the current block.
  /// @dev Check that blockNumber matches _maxBlock to ensure that _maxBlock was correctly
  ///      specified.
  ///
  function getVirtualPriceOracle(uint256 _maxBlock)
    external
    override(ICronV1PoolHelpers)
    returns (
      uint256 timestamp,
      uint256 token0U256F112,
      uint256 token1U256F112,
      uint256 blockNumber
    )
  {
    ExecVirtualOrdersMem memory evoMem;
    (evoMem, blockNumber) = _getVirtualReserves(
      _maxBlock,
      false /* not paused */
    );

    if (virtualOrders.lastVirtualOrderBlock > 0) {
      uint32 timeElapsed;
      (timeElapsed, timestamp) = _getOracleTimeData(blockNumber);

      token0U256F112 = priceOracle.token0U256F112;
      token1U256F112 = priceOracle.token1U256F112;
      if (timeElapsed > 0) {
        // #overUnderFlowIntended
        //                        The UQ256x112 incremented result relies upon and expects overflow
        //                        and is unchecked. The reason is that it is the difference between
        //                        price oracle samples over time that matters, not the absolute value.
        //                        As noted above, see the Uniswap V2 whitepaper.
        token0U256F112 += evoMem.token0OracleU256F112;
        token1U256F112 += evoMem.token1OracleU256F112;
      }
    }
  }

  /// @notice Returns the TWAMM pool's reserves after the non-stateful execution of all virtual orders
  ///         up to the specified maximum block (unless an invalid block is specified, which results
  ///         in execution to the current block).
  ///
  ///         IMPORTANT - This function calls _getVirtualReserves, which triggers a re-entrancy check. Due
  ///                     to contract size challanges, there is no explicit call to that re-entrancy check
  ///                     in this function, where it's presence would be more obvious.
  ///
  ///         IMPORTANT - This function does not meaningfully modify state despite the lack of a "view"
  ///                     designator for state mutability. (The call to _triggerVaultReentrancyCheck
  ///                     Unfortunately prevents the "view" designator as meaningless value is written
  ///                     to state to trigger a reentracy check).
  ///
  /// @param _maxBlock a block to update virtual orders to. If less than or equal to the last virtual order
  ///                  block or greater than the current block, the value is set to the current
  ///                  block number.
  /// @param _paused is true to indicate the result should be returned as though the pool is in a paused
  ///                state where virtual orders are not executed and only withdraw, cancel and liquidations
  ///                are possible (check function isPaused to see if the pool is in that state). If false
  ///                then the virtual reserves are computed from virtual order execution to the specified
  ///                block.
  /// @return blockNumber The block that the virtual reserve values were computed at. Should
  ///                     match parameter _maxBlock, unless _maxBlock was not greater than the
  ///                     last virtual order block or less than or equal to the current block.
  /// @return token0ReserveU112 virtual reserves of Token0 in the TWAMM pool at blockNumber.
  /// @return token1ReserveU112 virtual reserves of Token1 in the TWAMM pool at blockNumber.
  /// @return token0OrdersU112 virtual amount of Token0 remaining to be sold to the pool in LT swap orders
  ///                          at blockNumber.
  /// @return token1OrdersU112 virtual amount of Token1 remaining to be sold to the pool in LT swap orders
  ///                          at blockNumber.
  /// @return token0ProceedsU112 virtual amount of Token0 purchased from the pool by LT swap orders
  ///                            at blockNumber.
  /// @return token1ProceedsU112 virtual amount of Token1 purchased from the pool by LT swap orders
  ///                            at blockNumber.
  /// @return token0BalancerFeesU96 virtual Balancer fees collected for all types of Token0-->Token1 swaps
  ///                               at blockNumber.
  /// @return token1BalancerFeesU96 virtual Balancer fees collected for all types of Token1-->Token0 swaps
  ///                               at blockNumber.
  /// @return token0CronFiFeesU96 virtual Cron-Fi fees collected for Token0-->Token1 Long-Term swaps at
  ///                             blockNumber.
  /// @return token1CronFiFeesU96 virtual Cron-Fi fees collected for Token1-->Token0 Long-Term swaps at
  ///                             blockNumber.
  ///
  function getVirtualReserves(uint256 _maxBlock, bool _paused)
    external
    override(ICronV1PoolHelpers)
    returns (
      uint256 blockNumber,
      uint256 token0ReserveU112,
      uint256 token1ReserveU112,
      uint256 token0OrdersU112,
      uint256 token1OrdersU112,
      uint256 token0ProceedsU112,
      uint256 token1ProceedsU112,
      uint256 token0BalancerFeesU96,
      uint256 token1BalancerFeesU96,
      uint256 token0CronFiFeesU96,
      uint256 token1CronFiFeesU96
    )
  {
    ExecVirtualOrdersMem memory evoMem;
    (evoMem, blockNumber) = _getVirtualReserves(_maxBlock, _paused);

    token0ReserveU112 = evoMem.token0ReserveU112;
    token1ReserveU112 = evoMem.token1ReserveU112;

    // Note that the order difference is a subtraction (virtual orders change orders
    // by selling them into the pool reserves):
    (uint256 t0Orders, uint256 t1Orders) = BitPackingLib.unpackPairU112(slot1);
    token0OrdersU112 = t0Orders - evoMem.token0OrdersU112;
    token1OrdersU112 = t1Orders - evoMem.token1OrdersU112;

    (uint256 t0Proceeds, uint256 t1Proceeds) = BitPackingLib.unpackPairU112(slot2);
    token0ProceedsU112 = t0Proceeds + evoMem.token0ProceedsU112;
    token1ProceedsU112 = t1Proceeds + evoMem.token1ProceedsU112;

    (uint256 t0BalFees, uint256 t1BalFees) = BitPackingLib.unpackPairU96(slot4);
    token0BalancerFeesU96 = t0BalFees + evoMem.token0BalancerFeesU96;
    token1BalancerFeesU96 = t1BalFees + evoMem.token1BalancerFeesU96;

    (uint256 t0CronFiFees, uint256 t1CronFiFees) = BitPackingLib.unpackPairU96(slot3);
    token0CronFiFeesU96 = t0CronFiFees + evoMem.token0CronFiFeesU96;
    token1CronFiFeesU96 = t1CronFiFees + evoMem.token1CronFiFeesU96;
  }

  /// @notice Get the price oracle data for the pool as of the last virtual order block.
  /// @return timestamp is the timestamp in seconds when the price oracle was last updated.
  /// @return token0U256F112 The cumulative price of Token0 measured in amount of
  ///                        Token1 * seconds.
  /// @return token1U256F112 The cumulative price of Token1 measured in amount of
  ///                        Token0 * seconds.
  ///
  function getPriceOracle()
    external
    view
    override(ICronV1PoolHelpers)
    returns (
      uint256 timestamp,
      uint256 token0U256F112,
      uint256 token1U256F112
    )
  {
    timestamp = BitPackingLib.unpackOracleTimeStampS2(slot2);
    token0U256F112 = priceOracle.token0U256F112;
    token1U256F112 = priceOracle.token1U256F112;
  }

  /// @notice Return an array of the order IDs for the specified user _owner. Allows all orders to be fetched
  ///         at once or pagination through the _offset and _maxResults parameters. For instance to get the
  ///         first 100 orderIds of a user, specify _offset=0 and _maxResults=100. To get the second 100
  ///         orderIds for the same user, specify _offset=100 and _maxResults=100. To get all results at once,
  ///         either specify all known results or 0 for _maxResults.
  /// @param  _owner is the address of the owner to fetch order ids for.
  /// @param  _offset is the number of elements from the end of the list to start fetching results from (
  ///                 consult the operating description above).
  /// @param  _maxResults is the maximum number of results to return when calling this function (i.e. if
  ///                     this is set to 1,000 and there are 10,000 results available, only 1,000 from the
  ///                     specified offset will be returned). If 0 is specified, then all results available are
  ///                     returned.
  /// @return orderIds A uint256 array of order IDs associated with user's address.
  /// @return numResults The number of order IDs returned (if a user has less than the specified maximum
  ///                    number of results, indices in the returned order ids array after numResults-1 will
  ///                    be zero).
  /// @return totalResults The total number of order IDs associated with this user's address.
  ///                      (Useful for pagination of results--i.e. increase _offset by 100 until
  ///                       totalResults - 100 is reached).
  ///
  function getOrderIds(
    address _owner,
    uint256 _offset,
    uint256 _maxResults
  )
    external
    view
    override(ICronV1PoolHelpers)
    returns (
      uint256[] memory orderIds,
      uint256 numResults,
      uint256 totalResults
    )
  {
    uint256[] storage orderIdArr = orderIdMap[_owner];
    totalResults = orderIdArr.length;

    uint256 maxResults = (_maxResults == 0) ? totalResults : _maxResults;
    orderIds = new uint256[](maxResults);

    for (uint256 index = _offset; numResults < maxResults && index < totalResults; index++) {
      orderIds[numResults++] = orderIdArr[index];
    }
  }

  /// @notice Return the order information of a given order id.
  /// @param _orderId is the id of the order to return.
  /// @return order is the order data corresponding to the given order id. See Order struct documentation
  ///               for additional information.
  ///
  function getOrder(uint256 _orderId) external view override(ICronV1PoolHelpers) returns (Order memory order) {
    return virtualOrders.orderMap[_orderId];
  }

  /// @notice Returns the number of virtual orders, Long-Term (LT) swaps, that have been transacted in
  ///         this pool.
  /// @return nextOrderId The number of virtual orders issued (also the next order ID that is
  ///                     assigned to a LT swap.)
  ///
  function getOrderIdCount() external view override(ICronV1PoolHelpers) returns (uint256 nextOrderId) {
    nextOrderId = virtualOrders.nextOrderId;
  }

  /// @notice Returns the sales rate of each of the two order pools at the last virtual order block.
  ///         This is the value persisted to state.
  /// @return salesRate0U112 order pool 0 sales rate. The amount of Token 0 sold to the pool, per block,
  ///                        in exchange for Token 1, on behalf of all active Long-Term (LT) swap orders,
  ///                        swapping Token0 for Token1, as of the last virtual order block.
  ///                        Min. = 0, Max. = (2**112) - 1
  /// @return salesRate1U112 order pool 1 sales rate. The amount of Token 1 sold to the pool, per block,
  ///                        in exchange for Token 0, on behalf of all active Long-Term (LT) swap orders,
  ///                        swapping Token1 for Token0, as of the last virtual order block.
  ///                        Min. = 0, Max. = (2**112) - 1
  ///
  function getSalesRates()
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 salesRate0U112, uint256 salesRate1U112)
  {
    uint256 salesRates = virtualOrders.orderPools.currentSalesRates;
    salesRate0U112 = (salesRates >> 112) & C.MAX_U112;
    salesRate1U112 = salesRates & C.MAX_U112;
  }

  /// @notice Get the Last Virtual Order Block (LVOB) for the pool. This is the block number indicating the last block
  ///         where virtual orders have been executed by the pool. If the LVOB is significantly less than the current
  ///         block number, it indicates that the pool has been inactive and that a call to any function that requires
  ///         the execution of virtual orders may incur siginificant gas use.
  /// @return lastVirtualOrderBlock is the last block number that virtual orders have been executed to.
  ///
  function getLastVirtualOrderBlock()
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 lastVirtualOrderBlock)
  {
    return virtualOrders.lastVirtualOrderBlock;
  }

  /// @notice Get the sales rate ending (per block) at the specified block number.
  /// @param salesRateEndingPerBlock0U112 the amount of Token 0 per block that will stop being sold
  ///                                     to the pool after the specified block.
  ///                                     Min. = 0, Max. = (2**112) - 1
  /// @param salesRateEndingPerBlock1U112 the amount of Token 0 per block that will stop being sold
  ///                                     to the pool after the specified block.
  ///                                     Min. = 0, Max. = (2**112) - 1
  /// @dev NOTE: these values are inserted into state at block numbers divisible by the Order Block
  ///            Interval (OBI)--specifiying block numbers other than those evenly divisible by the
  ///            OBI will result in the returned values being zero.
  ///
  function getSalesRatesEndingPerBlock(uint256 _blockNumber)
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 salesRateEndingPerBlock0U112, uint256 salesRateEndingPerBlock1U112)
  {
    (salesRateEndingPerBlock0U112, salesRateEndingPerBlock1U112) = BitPackingLib.unpackPairU112(
      virtualOrders.orderPools.salesRatesEndingPerBlock[_blockNumber]
    );
  }

  // Slot 1 Access Functions:
  //
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Gets the current Short-Term (ST) swap fee for the pool in Fee Points.
  /// @return The ST swap Fee Points (FP).
  ///         Min. = 0, Max. = 1000 (C.MAX_FEE_FP)
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out
  ///            of 100,000.
  ///
  function getShortTermFeePoints() external view override(ICronV1PoolHelpers) returns (uint256) {
    return BitPackingLib.unpackU10(slot1, C.S1_OFFSET_SHORT_TERM_FEE_FP);
  }

  /// @notice Gets the current Partner swap fee for the pool in Fee Points.
  /// @return The Partner swap Fee Points (FP).
  ///         Min. = 0, Max. = 1000 (C.MAX_FEE_FP)
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out
  ///            of 100,000.
  ///
  function getPartnerFeePoints() external view override(ICronV1PoolHelpers) returns (uint256) {
    return BitPackingLib.unpackU10(slot1, C.S1_OFFSET_PARTNER_FEE_FP);
  }

  /// @notice Gets the current Long-Term (LT) swap fee for the pool in Fee Points.
  /// @return The LT swap Fee Points (FP).
  ///         Min. = 0, Max. = 1000 (C.MAX_FEE_FP)
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out
  ///            of 100,000.
  ///
  function getLongTermFeePoints() external view override(ICronV1PoolHelpers) returns (uint256) {
    return BitPackingLib.unpackU10(slot1, C.S1_OFFSET_LONG_TERM_FEE_FP);
  }

  /// @notice Gets the amounts of Token0 and Token1 in active virtual orders waiting to
  ///         be sold to the pool as of the last virtual order block.
  /// @return orders0U112 is the aggregated amount of Token0 for active swaps from Token0 to Token1,
  ///         waiting to be sold to the pool since the last virtual order block.
  ///         Min. = 0, Max. = (2**112)-1
  /// @return orders1U112 is the aggregated amount of Token1 for active swaps from Token1 to Token0,
  ///         waiting to be sold to the pool since the last virtual order block.
  ///         Min. = 0, Max. = (2**112)-1
  ///
  function getOrderAmounts()
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 orders0U112, uint256 orders1U112)
  {
    (orders0U112, orders1U112) = BitPackingLib.unpackPairU112(slot1);
  }

  // Slot 2 Access Functions:
  //
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Get the proceeds of Token0 and Token1 resulting from
  ///         virtual orders, Long-Term swaps, up to the last virtual order block.
  /// @return proceeds0U112 is the aggregated amount of Token0 from swaps selling Token1 for Token0
  ///                       to the pool, waiting to be withdrawn, as of the last virtual order block.
  ///                       Min. = 0, Max. = (2**112)-1
  /// @return proceeds1U112 is the aggregated amount of Token1 from swaps selling Token1 for Token0
  ///                       to the pool, waiting to be withdrawn, as of the last virtual order block.
  ///                       Min. = 0, Max. = (2**112)-1
  ///
  function getProceedAmounts()
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 proceeds0U112, uint256 proceeds1U112)
  {
    (proceeds0U112, proceeds1U112) = BitPackingLib.unpackPairU112(slot2);
  }

  // Slot 3 Access Functions:
  //
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Gets the current value of the fee shift, which indicates how Long-Term (LT) swap fees are
  ///         split between Cron-Fi and Liquidity Providers (LPs) when Cron-Fi fee collection is enabled.
  /// @return A value between 1 and 4 that is the fee shift used to determine fee spliting between Cron-Fi
  ///         and LPs:
  ///
  ///               Fee Shift = 1:
  ///                   LP gets 2 fee shares (~66%), Cron-Fi gets 1 fee share (~33%)
  ///
  ///               Fee Shift = 2:
  ///                   LP gets 4 fee shares (~80%), Cron-Fi gets 1 fee share (~20%)
  ///
  ///               Fee Shift = 3:
  ///                   LP gets 8 fee shares (~88%), Cron-Fi gets 1 fee share (~12%)
  ///
  ///               Fee Shift = 4:
  ///                   LP gets 16 fee shares (~94%), Cron-Fi gets 1 fee share (~6%)
  ///
  function getFeeShift() external view override(ICronV1PoolHelpers) returns (uint256) {
    return BitPackingLib.unpackFeeShiftS3(slot3);
  }

  /// @notice Gets the amounts of Token0 and Token1 collected as Cron-Fi fees on Long-Term (LT) swaps
  ///         as of the last virtual order block.
  /// @return cronFee0U96 the amount of Token0 Cron-Fi fees collected as of the last virtual order block.
  ///                      Min. = 0, Max. = (2**96) - 1
  /// @return cronFee1U96 the amount of Token1 Cron-Fi fees collected as of the last virtual order block.
  ///                      Min. = 0, Max. = (2**96) - 1
  ///
  function getCronFeeAmounts()
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 cronFee0U96, uint256 cronFee1U96)
  {
    (cronFee0U96, cronFee1U96) = BitPackingLib.unpackPairU96(slot3);
  }

  // Slot 4 Access Functions:
  //
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Use to determine if the pool is collecting Cron-Fi fees currently (Cron-Fi fees are only
  ///         collected on Long-Term swaps if enabled).
  /// @return True if the pool is collecting Cron-Fi fees, false otherwise.
  ///
  function isCollectingCronFees() external view override(ICronV1PoolHelpers) returns (bool) {
    return BitPackingLib.unpackBit(slot4, C.S4_OFFSET_CRON_FEE_ENABLED) != C.FALSE;
  }

  /// @notice Use to determine if the pool is collecting Balancer fees currently (Balancer fees apply to
  ///         any fee collected by the pool--Short and Long Term swaps).
  /// @return True if the pool is collecting Balancer fees, false otherwise.
  ///
  function isCollectingBalancerFees() external view override(ICronV1PoolHelpers) returns (bool) {
    return BitPackingLib.unpackBit(slot4, C.S4_OFFSET_COLLECT_BALANCER_FEES) != C.FALSE;
  }

  /// @notice Get the Balancer Fee charged by the pool.
  /// @return The current Balancer Fee, a number that is divided by 1e18 (C.ONE_DU1_18) to arrive at a
  ///         fee multiplier between 0 and 1 with 18 fractional decimal digits.
  ///         Min. = 0.000000000000000000, Max. = 1.000000000000000000
  ///
  function getBalancerFee() external view override(ICronV1PoolHelpers) returns (uint256) {
    return BitPackingLib.unpackBalancerFeeS4(slot4);
  }

  /// @notice Gets the amounts of Token0 and Token1 collected as Balancer fees on all swaps as of the last
  ///         virtual order block.
  /// @return balFee0U96 the amount of Token0 Balancer fees collected as of the last virtual order block.
  ///                      Min. = 0, Max. = (2**96) - 1
  /// @return balFee1U96 the amount of Token1 Balancer fees collected as of the last virtual order block.
  ///                      Min. = 0, Max. = (2**96) - 1
  ///
  function getBalancerFeeAmounts()
    external
    view
    override(ICronV1PoolHelpers)
    returns (uint256 balFee0U96, uint256 balFee1U96)
  {
    (balFee0U96, balFee1U96) = BitPackingLib.unpackPairU96(slot4);
  }

  /// @notice Use to determine if the pool's virtual orders are currently paused. If virtual orders are
  ///         paused, the pool will allow Long-Term (LT) swaps to be cancelled and withdrawn from as well
  ///         as liquidity positions to be withdrawn.
  /// @return True if the pool is paused, false otherwise.
  ///
  function isPaused() public view override(ICronV1PoolHelpers) returns (bool) {
    return BitPackingLib.unpackBit(slot4, C.S4_OFFSET_PAUSE) != C.FALSE;
  }

  /// @notice Reverts with error if msg.sender is not the factory owner.
  /// @dev This internal function is a modifier contract size optimization.
  ///
  function _senderIsFactoryOwner() internal view {
    requireErrCode(msg.sender == ICronV1PoolFactory(FACTORY).owner(), CronErrors.SENDER_NOT_FACTORY_OWNER);
  }

  /// @notice Reverts with error if msg.sender is not a pool administrator.
  /// @dev This internal function is a modifier contract size optimization.
  ///
  function _senderIsAdmin() internal view {
    requireErrCode(adminAddrMap[msg.sender], CronErrors.SENDER_NOT_ADMIN);
  }

  /// @notice Reverts with error if msg.sender is not an arbitrage partner.
  /// @dev This internal function is a modifier contract size optimization.
  ///
  function _senderIsArbitragePartner() internal view {
    requireErrCode(partnerContractAddrMap[msg.sender] != C.NULL_ADDR, CronErrors.SENDER_NOT_ARBITRAGE_PARTNER);
  }

  /// @notice Reverts with error if the pool is paused.
  /// @dev This internal function is a modifier contract size optimization.
  ///
  function _poolNotPaused() internal view {
    requireErrCode(!isPaused(), CronErrors.POOL_PAUSED);
  }

  /// @notice Computes the proceeds of a virtual order, Long-Term (LT) swap, for withdrawl or
  ///         cancellation purposes. Proceeds are determined using the staking algorithm, where
  ///         the user's order sales rate, _stakedAmountU128, represents their stake and the
  ///         difference between the normalized proceeds at this juncture or their order end and
  ///         order start are used to calculate their share. The normalized proceeds are
  ///         stored in 128-bits with 64 fractional-bits, hence the scaling down by 64-bits below.
  /// @param _scaledProceedsU128 The current or order end normalized scaled proceeds value.
  /// @param _startScaledProceedsU128 The normalized scaled proceeds value at the start of the
  ///                                 order (or when it was last withdrawn from).
  /// @param _salesRateU112 The order's sales rate in token per block.
  /// @param _token0To1 the direction of this swap, true if selling Token 0 for Token1, false otherwise.
  /// @dev Note explanations for required underflow in this calculation below.
  ///
  function _calculateProceeds(
    uint256 _scaledProceedsU128,
    uint256 _startScaledProceedsU128,
    uint256 _salesRateU112,
    bool _token0To1
  ) internal view returns (uint256 proceedsU112) {
    // NOTE: uint128 casts used here instead of as arguments to this function for gas/size efficiency.

    // #overUnderFlowIntended
    //                        Underflow is required here because it's not the scaled proceeds value but the distance
    //                        between them that is important. Underflow maintains the distance relationship. Consult
    //                        the Cron-Fi TWAMM Numerical Analysis for a discussion and analysis on this matter.
    uint256 orderProceedsU128 = uint128(uint128(_scaledProceedsU128) - uint128(_startScaledProceedsU128));

    // #unchecked
    //            The multiplication below is not checked for overflow because the product of a U128 and U112
    //            cannot exceed a U256. (The order proceeds is correct by construction because it is recovered
    //            from a 128-bit storage that checks for 128-bit overflow when stored, the sales rate is known
    //            to be 112-bits or less because Balancer checks the amount in with BAL#526
    //            BALANCE_TOTAL_OVERFLOW, which is then divided by the number of sales blocks).
    //
    //            Proceeds is not examined to see if it exceeds MAX_U112 because it is checked downstream from
    //            its use here in the bit packing library's decrementPairU112 function call for underflow on
    //            subtraction from a U112.
    proceedsU112 =
      (orderProceedsU128 * _salesRateU112) /
      (_token0To1 ? ORDER_POOL0_PROCEEDS_SCALING : ORDER_POOL1_PROCEEDS_SCALING);
  }

  /// @notice executes existing Virtual Orders (Long-Term-swaps) since last virtual order block,
  ///         updating TWAMM reserve values and other TWAMM state variables up to the specified
  ///         maximum block.
  /// @param _balance0U112 The Balancer Vault balance of Token 0 for this pool.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _balance1U112 The Balancer Vault balance of Token 1 for this pool.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _maxBlock a block to update virtual orders to (useful to specify in emergency situation
  ///                  where inactive pool requires too much gas for a single successful call to
  ///                  executeVirtualOrders.) If less than or equal to the last virtual order block
  ///                  or greater than the current block, the value is set to the current block number.
  /// @param token0ReserveU112 is the Token 0 reserves of the pool as of _maxBlock.
  ///                          Min. = 0, Max. = (2**112) - 1
  /// @param token1ReserveU112 is the Token 1 reserves of the pool as of _maxBlock.
  ///                          Min. = 0, Max. = (2**112) - 1
  ///
  function _executeVirtualOrders(
    uint256 _balance0U112,
    uint256 _balance1U112,
    uint256 _maxBlock
  ) private returns (uint256 token0ReserveU112, uint256 token1ReserveU112) {
    if (!(virtualOrders.lastVirtualOrderBlock < _maxBlock && _maxBlock <= block.number)) {
      _maxBlock = block.number;
    }

    uint256 localSlot4 = slot4; // #savegas
    ExecVirtualOrdersMem memory evoMem = _getExecVirtualOrdersMem(_balance0U112, _balance1U112);

    if (!isPaused()) {
      _executeVirtualOrdersToBlock(evoMem, _maxBlock);

      // Optimization:
      //               (evoMem.feeShiftU3 != 0) --> cronFeeEnabled == true
      //
      //               Rather than read from storage again, use the value of feeShiftU3 that was set in
      //               _getExecVirtualOrdersMem based on the cron fee enabled flag (S4_OFFSET_CRON_FEE_ENABLED).
      //               (feeShiftU3 can only take on values 1 through 4 once Cron-Fi fees are enabled, and is 1
      //               on construction).
      if (evoMem.feeShiftU3 != 0) {
        // CAREFUL!  Note the change to localSlot4 here!!! It gets set below when balancer fees
        //           are updated.
        localSlot4 = BitPackingLib.packBit(localSlot4, 0, C.S4_OFFSET_ZERO_CRONFI_FEES);

        slot3 = BitPackingLib.incrementPairWithClampU96(slot3, evoMem.token0CronFiFeesU96, evoMem.token1CronFiFeesU96);
      }

      slot4 = BitPackingLib.incrementPairWithClampU96(
        localSlot4,
        evoMem.token0BalancerFeesU96,
        evoMem.token1BalancerFeesU96
      );

      // Update order accounting:
      //
      slot1 = BitPackingLib.decrementPairU112(slot1, evoMem.token0OrdersU112, evoMem.token1OrdersU112);

      // Update proceeds accounting:
      //
      slot2 = BitPackingLib.incrementPairU112(slot2, evoMem.token0ProceedsU112, evoMem.token1ProceedsU112);

      // As in UNI V2, the oracle price is based on the reserves before the current operation (i.e. swap).
      // For TWAMM, we augment that to incorporate the reserves after executing virtual operations.
      //
      // NOTE: We only update the oracle when not paused.  If paused, pricing info becomes distorted and
      //       shouldn't be incorporated into the oracle.
      //
      _updateOracle(evoMem, _maxBlock);
    }

    token0ReserveU112 = evoMem.token0ReserveU112;
    token1ReserveU112 = evoMem.token1ReserveU112;
  }

  /// @notice Executes all active virtual orders from the last virtual order block stored
  ///         in state to the specified order block, _blockNumber. The specified order block
  ///         should be greater than the last virtual order block. Writes updates and changes
  ///         to state.
  /// @param _evoMem aggregated information for gas efficient virtual order exection. See
  ///                documentation in Structs.sol.
  /// @param _blockNumber is the block number to execute active virtual orders up to from
  ///                     the last virtual order block.
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000.
  ///
  function _executeVirtualOrdersToBlock(ExecVirtualOrdersMem memory _evoMem, uint256 _blockNumber) private {
    uint256 poolFeeLTFP = BitPackingLib.unpackU10(slot1, C.S1_OFFSET_LONG_TERM_FEE_FP);
    (LoopMem memory loopMem, uint256 expiryBlock) = _getLoopMem();

    // Loop through active virtual orders preceeding the final block interval, performing
    // aggregate Long-Term (LT) swaps and updating sales rates in memory and scaled proceeds
    // in state.
    //
    uint256 prevLVOB = loopMem.lastVirtualOrderBlock;
    while (expiryBlock < _blockNumber) {
      _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP);

      _calculateOracleIncrement(_evoMem, prevLVOB, expiryBlock);
      prevLVOB = loopMem.lastVirtualOrderBlock;

      // Handle orders expiring at end of interval
      _decrementSalesRates(expiryBlock, loopMem);
      _storeScaledProceeds(loopMem, expiryBlock);

      expiryBlock += ORDER_BLOCK_INTERVAL;
    }

    // Process the active virtual orders of the final block interval, performing
    // aggregate Long-Term (LT) swaps and updating sales rates in memory and scaled proceeds
    // in state.
    //
    if (loopMem.lastVirtualOrderBlock != _blockNumber) {
      expiryBlock = _blockNumber;
      _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP);

      _calculateOracleIncrement(_evoMem, prevLVOB, _blockNumber);

      // Handle orders expiring at end of interval
      _decrementSalesRates(expiryBlock, loopMem);
      _storeScaledProceeds(loopMem, expiryBlock);
    }

    // Update virtual order and order pool state from values in memory:
    //
    virtualOrders.lastVirtualOrderBlock = loopMem.lastVirtualOrderBlock;
    virtualOrders.orderPools.currentSalesRates = BitPackingLib.packPairU112(
      0,
      loopMem.currentSalesRate0U112,
      loopMem.currentSalesRate1U112
    );
    virtualOrders.orderPools.scaledProceeds = BitPackingLib.packPairU128(
      loopMem.scaledProceeds0U128,
      loopMem.scaledProceeds1U128
    );
  }

  /// @notice Stores the scaled proceeds contained in _loopMem in state at the given block number,
  ///         _expiryBlock.
  /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See
  ///                 documentation in Structs.sol.
  /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned on
  ///                     multiples of the order block interval.
  ///
  function _storeScaledProceeds(LoopMem memory _loopMem, uint256 _expiryBlock) private {
    virtualOrders.scaledProceedsAtBlock[_expiryBlock] = BitPackingLib.packPairU128(
      _loopMem.scaledProceeds0U128,
      _loopMem.scaledProceeds1U128
    );
  }

  /// @notice Update the price oracle on the first transaction within a block (Uniswap V2 style Price
  ///         Oracle adapted for TWAMM).
  /// @param _evoMem aggregated information for gas efficient virtual order exection. See
  ///                documentation in Structs.sol.
  /// @param _updateBlock is the block number specified in the calling function. This may be less
  ///                     than the current block number (for example if executeVirtualOrdersToBlock
  ///                     is called with a number less than the current block number to free up
  ///                     excess gas load from inactivity).
  ///
  function _updateOracle(ExecVirtualOrdersMem memory _evoMem, uint256 _updateBlock) private {
    (uint32 timeElapsed, uint32 blockTimeStamp) = _getOracleTimeData(_updateBlock);

    if (timeElapsed > 0) {
      // #overUnderFlowIntended
      //                        The UQ256x112 incremented result relies upon and expects overflow
      //                        and is unchecked. The reason is that it is the difference between
      //                        price oracle samples over time that matters, not the absolute value.
      //                        As noted above, see the Uniswap V2 whitepaper.
      priceOracle.token0U256F112 += _evoMem.token0OracleU256F112;
      priceOracle.token1U256F112 += _evoMem.token1OracleU256F112;
    }

    slot2 = BitPackingLib.packOracleTimeStampS2(slot2, blockTimeStamp);
  }

  /// @notice Trigger Balancer Vault re-entrancy check for read-only re-entrancy vulnerability
  ///         described here:
  ///
  ///             https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345
  /// @dev Balancer describes this workaround as follows:
  ///
  ///          '... this is the cheapest way to trigger a reentrancy check on the Vault. In
  ///           short, it does nothing. The code "withdraws" an amount of 0 of token address(0)
  ///           from the Vault's internal balance for the calling contract (the pool) and sends
  ///           it to address(0).'
  ///
  function _triggerVaultReentrancyCheck() private {
    IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1);
    ops[0].kind = IVault.UserBalanceOpKind.WITHDRAW_INTERNAL;
    ops[0].sender = address(this);
    VAULT.manageUserBalance(ops);
  }

  /// @notice Execute a Short-Term (ST) swap atomically in a single block on one token for the other as
  ///         described in the parameters below.
  /// @param _token0To1 the direction of this swap, true if selling Token 0 for Token1, false otherwise.
  /// @param _regularSwapType is true if the swap is SwapType.RegularSwap, false if it is
  ///                         SwapType.PartnerSwap.
  /// @param _amountInU112 is the amount of token being sold to the pool in this swap.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _token0ReserveU112 is the current Token 0 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @param _token1ReserveU112 is the current Token 1 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @return amountOutU112 is the amount of token being bought from the pool in this swap.
  ///                       Min. = 0, Max. = (2**112) - 1
  function _shortTermSwap(
    bool _token0To1,
    bool _regularSwapType,
    uint256 _amountInU112,
    uint256 _token0ReserveU112,
    uint256 _token1ReserveU112
  ) private returns (uint256 amountOutU112) {
    uint256 swapFeeU10 = BitPackingLib.unpackU10(
      slot1,
      _regularSwapType ? C.S1_OFFSET_SHORT_TERM_FEE_FP : C.S1_OFFSET_PARTNER_FEE_FP
    );
    // #unchecked
    //            Balancer confirms the amount in does not overflow the pool (BAL#526
    //            BALANCE_TOTAL_OVERFLOW), thus the multiply of _amountInU112 and a U10
    //            cannot overflow a U256.
    uint256 grossFee = (_amountInU112 * swapFeeU10).divDown(C.TOTAL_FP);

    uint256 localSlot4 = slot4; // #savegas
    uint256 collectBalancerFees = BitPackingLib.unpackBit(localSlot4, C.S4_OFFSET_COLLECT_BALANCER_FEES);
    if (collectBalancerFees != C.FALSE) {
      uint256 balancerFeeDU1F18 = BitPackingLib.unpackBalancerFeeS4(localSlot4);
      // #unchecked
      //            Multiplication of the grossFee (which at most could be U122 as described
      //            above--and is much less because of division by C.TOTAL_FP, i.e. U106) by
      //            balancerFeeDU1F18, a U60 at most, results in U166 to U182 at worst which
      //            will not overflow a U256. Further mitigated with the division by 1e18 (U59).
      uint256 balancerFeeU112 = (grossFee * balancerFeeDU1F18).divDown(C.DENOMINATOR_DU1_18);

      // #contractsize add balancer fees from swap
      slot4 = BitPackingLib.incrementPairWithClampU96(
        localSlot4,
        (_token0To1 ? balancerFeeU112 : 0),
        (_token0To1 ? 0 : balancerFeeU112)
      );
    }

    // NOTE: LP Fees are automatically collected in the vault balances and need not be calculated here.
    // #unchecked
    //            Subtraction unchecked below because grossFee is a fraction of _amountInU112 and
    //            cannot underflow.
    //            IMPORTANT: divDown in computing the grossFee above essential to prevent underflow
    //                       zero amount in attack.
    uint256 amountInLessFeesU112 = _amountInU112 - grossFee;

    // #unchecked
    //            Balancer confirms the amount in does not overflow the pool (BAL#526
    //            BALANCE_TOTAL_OVERFLOW), thus the multiply of _amountInU112 and a value,
    //            _tokenNReserve, less than the value confirmed to not overflow will
    //            not exceed the U256 container (worst case U113).
    uint256 nextTokenInReserveU112 = (
      /* token-in reserve: */
      _token0To1 ? _token0ReserveU112 : _token1ReserveU112
    ) + amountInLessFeesU112;

    // #unchecked
    //            Because of Balancer Vault overflow checks and the way the token reserves
    //            are computed from known U112 maximum values, the multiplication below is
    //            unchecked as the product of two U112 values is not going to overflow the
    //            U256 container.
    amountOutU112 = ((
      /* token-out reserve: */
      _token0To1 ? _token1ReserveU112 : _token0ReserveU112
    ) * amountInLessFeesU112).divDown(nextTokenInReserveU112);
  }

  /// @notice Execute a Long-Term swap (LT) Virtual Order, which sells an amount of one of the pool
  ///         tokens to the pool over a number of blocks, as described in the parameters below.
  ///         An LT swap can be withdrawn one or more times until the order has expired and all proceeds
  ///         have been withdrawn.
  ///         An LT swap can be cancelled anytime before expiry for a refund of unsold tokens and any
  ///         proceeds obtained before cancellation.
  ///         To withdraw or cancel, call the IVault.exit function (see documentaion for onExitPool).
  ///         WARNING: Any amount specified that does not divide evenly into the amount of blocks for
  ///         the virtual order will end up in pool reserves, yielding the user nothing for their capital.
  ///         For instance a trade of 224 Token-0 over two intervals (OBI=75) starting at block 75 would
  ///         result in a sales rate of 2 Token-0 per block for 150 blocks, 2 Order Block Intervals (OBI),
  ///         and the remaining 74 Token-0 would end up in the pool reserves yielding no benefit to the user.
  /// @param _sender is the account issuing the LT swap transaction. Only this account can withdraw the
  ///                order.
  /// @param _delegate is an account that is able to withdraw or cancel the LT swap on behalf of the
  ///                  sender account, as long as the recipient specified is the sender account.
  ///                  If the delegate is set to the sender account, then the delegate is set
  ///                  to the null address.
  /// @param _token0To1 is the direction of this swap, true if selling Token 0 for Token1, false otherwise.
  /// @param _amountInU112 is the amount of token being sold to the pool in this swap.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _orderIntervals is the number of intervals to execute the LT swap before expiring. An interval
  ///                        can be 75 blocks (Stable Pool), 300 blocks (Liquid Pool) or 1200 blocks (Volatile
  ///                        Pool).
  ///                        Min. = 0, Max. = STABLE_MAX_INTERVALS,
  ///                                         LIQUID_MAX_INTERVALS,
  ///                                         VOLATILE_MAX_INTERVALS (depending on POOL_TYPE).
  /// @return orderId is the order identifier for the LT swap virtual order created. Users can see what
  ///                 order identifiers they've created by callin getOrderIds.
  /// @dev The order id for an issued order is in the event log emitted by this function. No safety is provided
  ///      for checking existing order ids being reissued because the order id space is very large: (2**256) - 1.
  ///
  /// #RISK: An order-wrap attack (where a user trys to issue so many orders that the order ID counter
  ///        overflows, giving them access to order proceeds) is unlikely since the minimum measured gas
  ///        cost of a call to onSwap resulting in a call to _longTermSwap is ~220k gas (not including
  ///        approvals and transfers).
  ///
  function _longTermSwap(
    address _sender,
    address _delegate,
    bool _token0To1,
    uint256 _amountInU112,
    uint256 _orderIntervals
  ) private returns (uint256 orderId) {
    // #RISK: The accumulation of scaled proceeds works based on the difference between the proceeds
    //        at collection and the proceeds at submission. Underflow is supported when subtracting these
    //        values, however, there is a risk that an order goes so long that the proceeds overflow twice.
    //        It's an unlikely risk that occurs in pools with extremely asymmetric reserves, but this
    //        maximum order length serves to reduce that risk.
    requireErrCode(_orderIntervals <= MAX_ORDER_INTERVALS, CronErrors.MAX_ORDER_LENGTH_EXCEEDED);

    // Determine selling rate based on number of blocks to expiry and total amount:
    //
    // #unchecked:
    //             Subtraction in assignment of lastExpiryBlock won't underflow because the
    //             block number will always be greater than the ORDER_BLOCK_INTERVAL on mainnet until
    //             it overflows in a very long time (suspect the universe may have collapsed by then).
    //
    //             Overflow not possible in the multiplication and addition of the assignment of
    //             orderExpiry in a reasonable time (i.e. universe may collapse first). Reason is
    //             that max(_orderIntervals) = STABLE_MAX_INTERVALS (18-bits), max(ORDER_BLOCK_INTERVAL) =
    //             VOLATILE_OBI (16-bits), which the product of maxes at 34-bits. Thus lastExpiryBlock
    //             is the dominant term when the block.number approaches (2**256 - 1) - (2**34 - 1).
    //
    //             Underflow not possible in assignment to tradeBlocks since orderExpiry always >
    //             block.number.
    uint256 lastExpiryBlock = block.number - (block.number % ORDER_BLOCK_INTERVAL);
    uint256 orderExpiry = ORDER_BLOCK_INTERVAL * (_orderIntervals + 1) + lastExpiryBlock; // +1 protects from div 0
    uint256 tradeBlocks = orderExpiry - block.number;
    uint256 sellingRateU112 = _amountInU112 / tradeBlocks; // Intended: Solidity rounds towards zero.
    requireErrCode(sellingRateU112 > 0, CronErrors.ZERO_SALES_RATE);

    // Update the current and ending sales rates state:
    //
    // NOTE: re-using pair increment methods with single value for #contractsize reduction.
    OrderPools storage ops = virtualOrders.orderPools;
    ops.currentSalesRates = BitPackingLib.incrementPairU112(
      ops.currentSalesRates,
      _token0To1 ? sellingRateU112 : 0,
      _token0To1 ? 0 : sellingRateU112
    );
    ops.salesRatesEndingPerBlock[orderExpiry] = BitPackingLib.incrementPairU112(
      ops.salesRatesEndingPerBlock[orderExpiry],
      _token0To1 ? sellingRateU112 : 0,
      _token0To1 ? 0 : sellingRateU112
    );

    // NOTE: Cast to uint112 below for sellingRateU112 is safe because _amountInU112 must be less than 112-bits
    //       as checked by Balancer and reverted with Balancer error BAL#526 BALANCE_TOTAL_OVERFLOW.
    // NOTE: Cast to uint128 below for unpackPairU128 is safe because the result of that function
    //       cannot execeed 128-bits (correct by construction, i.e. function pulls a 128-bit value from
    //       packed storage in 256-bit slot).
    // NOTE: scaledProceeds0 is measured in amount of Token 1 received for selling Token 0 from Order Pool 0
    //       to the AMM (and vice-versa scaledProceeds1).
    //
    // #overUnderFlowIntended
    //                        In the very distant future, the nextOrderId will overflow. Given the 5 year
    //                        order length limit, it's improbable that 2**256 -1 orders will occur and
    //                        result in the overwriting of an existing order:
    //
    //                            * Assume 12s blocks.
    //                            * Five years in seconds 157788000 (365.25d / year).
    //                            * Blocks in 5 years = 13149000.
    //                            * Conservatively overestimate 1000 orders per block,
    //                              then 13149000000 orders in 5 years.
    //                            * 34-bits used for 5 years, assuming an excessive number of orders per block.
    //                            * Caveat Emptor on those who do not withdraw their proceeds in
    //                              an insufficient amount of time from order start.
    orderId = virtualOrders.nextOrderId++;
    virtualOrders.orderMap[orderId] = Order(
      _token0To1,
      uint112(sellingRateU112),
      uint128(BitPackingLib.unpackU128(ops.scaledProceeds, _token0To1)),
      _sender,
      ((_sender != _delegate) ? _delegate : C.NULL_ADDR),
      orderExpiry
    );
    orderIdMap[_sender].push(orderId);

    // Update order accounting (add the user's order amount to the global orders accounting):
    //
    // NOTE: The amount in (_amountInU112) is not the amount that will be swapped due to
    //       truncation. The amount that will be swapped is the number of blocks in the order
    //       multiplied by the order sales rate. The remainder is implicitly added to the pool
    //       reserves, augmenting LP rewards (Caveat Emptor Design Philosophy for Swap User).
    //
    // #unchecked
    //            Multiplication below is unchecked because sellingRate is maximum U112 and
    //            tradeBlocks is maximum U24 (STABLE_OBI * STABLE_MAX_INTERVALS = 13149000 blocks),
    //            the product of which will not overflow a U256. Also note the U112 limit is checked
    //            in the increment call.
    uint256 orderAmount = sellingRateU112 * tradeBlocks;
    slot1 = BitPackingLib.incrementPairU112(slot1, (_token0To1 ? orderAmount : 0), (_token0To1 ? 0 : orderAmount));
  }

  /// @notice Withdraws any collected Cron-Fi fees from the pool and resets the pool accounting of
  ///         Cron-Fi fees to zero for both Token0 and Token1.
  /// @param _sender must be the current fee address.
  /// @return token0OutU96 Amount of Token0 collected as Cron-Fi fees in Long-Term (LT) swaps.
  ///                      Min. = 0, Max = (2**96) - 1
  /// @return token1OutU96 Amount of Token1 collected as Cron-Fi fees in LT swaps.
  ///                      Min. = 0, Max = (2**96) - 1
  ///
  function _withdrawCronFees(address _sender) private returns (uint256 token0OutU96, uint256 token1OutU96) {
    // IMPORTANT: safety check to ensure user calling this is fee address:
    requireErrCode(feeAddr == _sender, CronErrors.SENDER_NOT_FEE_ADDRESS);

    // Optimization:
    //               The U96 Cron-Fi fees below are assigned to the output tokens in onExitPool and
    //               the pool's Cron-Fi fee counters in slot3 are cleared.
    //               A flag indicating they have been cleared in slot4 is also set. This reduces the
    //               gas used in calculating pool reserves for subsequent transactions (why read slot3
    //               if the values within are zero?). See function _calculateReserves for the use of
    //               this flag in slot4.
    //               The optimization is re-used here too (why read the Cron-Fi fees from slot3
    //               if they are zero).
    uint256 localSlot4 = slot4; // #savegas
    requireErrCode(
      BitPackingLib.unpackBit(localSlot4, C.S4_OFFSET_ZERO_CRONFI_FEES) == C.FALSE,
      CronErrors.NO_FEES_AVAILABLE
    );

    // Send the Cron-Fi fees out of the pool and zero our accounting of them:
    (slot3, token0OutU96, token1OutU96) = BitPackingLib.unpackAndClearPairU96(slot3);

    // Set the Zero Cron-Fi fees flag since we've cleared the Cron-Fi fees counters:
    slot4 = BitPackingLib.packBit(localSlot4, 1, C.S4_OFFSET_ZERO_CRONFI_FEES);

    emit FeeWithdraw(_sender, token0OutU96, token1OutU96);
  }

  /// @notice A wrapper around _withdrawLongTermSwap to mitigate stack depth limitations in that method.
  ///         See documentation of _withdrawLongTermSwap for more information.
  /// @param _orderId the order id to withdraw or cancel.
  /// @param _sender is the account performing the LT swap order withdrawl or cancellation.
  ///                For long term orders, a "delegate" may be specified, this address is able to
  ///                perform LT order withdraws and cancellations on behalf of the LT swap owner as
  ///                long as the recipient is the LT swap owner.
  /// @param _recipient the recipient must always be the original order owner (the address
  ///                   that issued the order) if a "delegate" address is performing the withdrawl or
  ///                   cancellation. If the order owner is performing the withdrawl or cancellation, the
  ///                   recipient can be set to whatever destination address desired.
  /// @param _cancel if true, cancel the order, otherwise if false, withdraw the order.
  /// @return token0OutU112 the amount of Token 0 remitted from the order as either proceeds or a refund.
  ///                       Min. = 0, Max. = (2**112)-1
  /// @return token1OutU112 the amount of Token 1 remitted from the order as either proceeds or a refund.
  ///                       Min. = 0, Max. = (2**112)-1
  ///
  function _withdrawLongTermSwapWrapper(
    uint256 _orderId,
    address _sender,
    address _recipient,
    bool _cancel
  ) private returns (uint256 token0OutU112, uint256 token1OutU112) {
    Order storage order = virtualOrders.orderMap[_orderId];

    requireErrCode(order.owner != C.NULL_ADDR, CronErrors.CLEARED_ORDER);
    if (_sender != order.owner) {
      requireErrCode(_recipient == order.owner, CronErrors.RECIPIENT_NOT_OWNER);
    }
    requireErrCode(
      (_sender == order.owner || _sender == order.delegate) && _sender != C.NULL_ADDR,
      CronErrors.SENDER_NOT_ORDER_OWNER_OR_DELEGATE
    );

    (bool token0To1, uint256 refundU112, uint256 proceedsU112) = _withdrawLongTermSwap(order, _cancel);

    emit WithdrawLongTermSwap(
      order.owner,
      (_cancel)
        ? ((token0To1) ? address(TOKEN0) : address(TOKEN1)) // sell token
        : C.NULL_ADDR,
      refundU112,
      (token0To1) ? address(TOKEN1) : address(TOKEN0), // buy token
      proceedsU112,
      _orderId,
      _sender
    );

    // NOTE: The execute virtual orders run prior to this function in function onExitPool updates the
    //       orders and proceeds amounts.
    token0OutU112 = (token0To1) ? refundU112 : proceedsU112;
    token1OutU112 = (token0To1) ? proceedsU112 : refundU112;
  }

  /// @notice This function performs a withdraw or a cancel of a Long-Term (LT) swap virtual order, which
  ///         remits proceeds and if cancelling, also refunds remaining order amounts.
  ///         Funds may be withdrawn from an order multiple times up until order expiry and the last of
  ///         the proceeds are withdrawn.
  ///         Orders may be cancelled up to order expiry. Both proceeds and remaining order funds will be
  ///         remitted upon cancellation.
  /// @param _order information of a specific LT swap order. See Order struct documentation in Structs.sol.
  /// @param _cancel indicates whether the virtual order is to be cancelled if true. If false, the order
  ///                proceeds are to be withdrawn.
  /// @return token0To1 is the direction of this LT swap. It is true if selling Token 0 for Token1, false
  ///                   otherwise.
  /// @return refundU112 is the amount of token to be refunded.
  ///                    Min. = 0, Max. = (2**112) - 1
  /// @return proceedsU112 is the amount of token already purchased in the LT swap to be remitted.
  ///                      Min. = 0, Max. = (2**112) - 1
  ///
  function _withdrawLongTermSwap(Order storage _order, bool _cancel)
    private
    returns (
      bool token0To1,
      uint256 refundU112,
      uint256 proceedsU112
    )
  {
    token0To1 = _order.token0To1;
    uint256 expiryBlock = _order.orderExpiry;
    uint256 salesRateU112 = _order.salesRate;

    // NOTE: There is no need to check if order is completed using orderExpiry
    //       or salesRate, because entire Order struct is zeroed/falsed on cancel
    //       or final withdraw. A 2nd cancel or withdraw attempt will revert with
    //       CronErrors.SENDER_NOT_ORDER_OWNER_OR_DELEGATE

    // #RISK: Although the NULL_ADDR technically owns zeroed cancelled orders,
    //        anyone owning that address would be able to withdraw nothing
    //        because the salesRateU112 (order.salesRate) is zerod after
    //        cancel.
    if (_cancel) {
      // Calculate unsold amount to refund:
      //
      // NOTE: Must use last virtual order block, not block.number to yield correct result
      //       when pool is paused.
      //
      // #unchecked
      //            The subtraction is unchecked because the error check confirms that expiryBlock
      //            is larger than lastVirtualOrderBlock, preventing underflow.
      //
      //            The multiplication is unchecked for overflow because the sales rate is
      //            computed from the amount in, which is checked by Balancer (BAL#526
      //            BALANCE_TOTAL_OVERFLOW) and known to be less than U112. The maximum
      //            difference between the expiry block and last virtual order block is
      //            constrained to be U18 (the MAX_ORDER_INTERVALS largest value,
      //            STABLE_MAX_INTERVALS), the product of which cannot exceed U130.
      requireErrCode(expiryBlock > virtualOrders.lastVirtualOrderBlock, CronErrors.CANT_CANCEL_COMPLETED_ORDER);
      refundU112 = (expiryBlock - virtualOrders.lastVirtualOrderBlock) * salesRateU112;
    }

    uint256 scaledProceedsU128 = BitPackingLib.unpackU128(
      (!_cancel && (block.number > expiryBlock))
        ? virtualOrders.scaledProceedsAtBlock[expiryBlock] // Expired or cancelled order - calculate scaled proceeds at expiryBlock.
        : virtualOrders.orderPools.scaledProceeds, // Unexpired order. Remit current proceeds.
      token0To1
    );
    proceedsU112 = _calculateProceeds(
      scaledProceedsU128,
      _order.scaledProceedsAtSubmissionU128,
      salesRateU112,
      token0To1
    );

    // CAREFUL: This must come after the calculation of proceedsU112
    if (!_cancel && (block.number <= expiryBlock)) {
      // Reset stake (sales rate) if not cancellation and not expired order

      // NOTE: Cast to uint128 below for scaledProceedsAtSubmissionU128 is safe because result of function unpackPairU128
      //       cannot execeed 128-bits (correct by construction, i.e. function pulls a 128-bit value from
      //       packed storage in 256-bit slot, value is checked for overflow when it was stored).
      _order.scaledProceedsAtSubmissionU128 = uint128(scaledProceedsU128);
    }

    requireErrCode(refundU112 > 0 || proceedsU112 > 0, CronErrors.NO_FUNDS_AVAILABLE);

    if (_cancel) {
      OrderPools storage ops = virtualOrders.orderPools;

      // Decrement the current and ending sales rates:
      ops.currentSalesRates = BitPackingLib.decrementPairU112(
        ops.currentSalesRates,
        token0To1 ? salesRateU112 : 0,
        token0To1 ? 0 : salesRateU112
      );
      ops.salesRatesEndingPerBlock[expiryBlock] = BitPackingLib.decrementPairU112(
        ops.salesRatesEndingPerBlock[expiryBlock],
        token0To1 ? salesRateU112 : 0,
        token0To1 ? 0 : salesRateU112
      );

      // Update the order accounting:
      slot1 = BitPackingLib.decrementPairU112(slot1, (token0To1 ? refundU112 : 0), (token0To1 ? 0 : refundU112));
    }

    // Update the proceed accounting:
    slot2 = BitPackingLib.decrementPairU112(slot2, (token0To1 ? 0 : proceedsU112), (token0To1 ? proceedsU112 : 0));

    if (_cancel || block.number > expiryBlock) {
      // Remove stake (sales rate) and clear order state for rebate and to indicate order complete:
      _order.token0To1 = false;
      _order.salesRate = 0;
      _order.scaledProceedsAtSubmissionU128 = 0;
      _order.owner = C.NULL_ADDR;
      _order.delegate = C.NULL_ADDR;
      _order.orderExpiry = 0;
    }
  }

  /// @notice This function performs a join given the amounts of Token 0 and Token 1 to add to the pool
  ///         along with the current virtual reserves.
  /// @param _recipient is the account designated to receive pool shares in the form of LP tokens when
  ///                   Joining the pool. Can be set to _sender if sender wishes to receive the tokens
  ///                   and Join Events.
  /// @param _token0InU112 is the amount of Token 0 to Join the pool with.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _token1InU112 is the amount of Token 1 to Join the pool with.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _token0ReserveU112 is the current Token 0 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @param _token1ReserveU112 is the current Token 1 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @return amountLP is the amount of Liquidity Provider (LP) tokens returned in exchange for the
  ///                  amounts of Token 0 and Token 1 provided to the pool.
  function _join(
    address _recipient,
    uint256 _token0InU112,
    uint256 _token1InU112,
    uint256 _token0ReserveU112,
    uint256 _token1ReserveU112
  ) private returns (uint256 amountLP) {
    requireErrCode(_recipient != C.NULL_ADDR, CronErrors.NULL_RECIPIENT_ON_JOIN);

    uint256 supplyLP = totalSupply();
    if (supplyLP == 0) {
      requireErrCode(
        (_token0InU112 > C.MINIMUM_LIQUIDITY) && (_token1InU112 > C.MINIMUM_LIQUIDITY),
        CronErrors.INSUFFICIENT_LIQUIDITY
      );

      // #unchecked
      //            The check of _token0InU112 and _token1InU112 ensure that both are less
      //            than 112-bit maximums. Multiplying two 112-bit numbers will not exceed
      //            224-bits, hence no checked mul operator here.
      //            The check that both _token0InU112 and _token1InU112 exceed
      //            C.MINIMUM_LIQUIDITY, means that the square root of their product cannot
      //            be less than C.MINIMUM_LIQUIDITY, hence no checked sub operator here.
      amountLP = sqrt(_token0InU112 * _token1InU112) - C.MINIMUM_LIQUIDITY;

      // Initial Join / Mint does not execute virtual orders, is only run once b/c of MINIMUM_LIQUIDITY,
      // thus a call to update the oracle resolves to simply setting the oracle timestamp:
      slot2 = BitPackingLib.packOracleTimeStampS2(slot2, block.timestamp);

      _mintPoolTokens(address(0), C.MINIMUM_LIQUIDITY); // Permanently locked for div / 0 safety.

      // Set the last virtual order block to the block number. This reduces the gas used
      // in virtual order execution iterations for the first call to execute virtual orders
      // now that the pool has liquidity.
      virtualOrders.lastVirtualOrderBlock = block.number;
    } else {
      amountLP = Math.min(
        _token0InU112.mul(supplyLP).divDown(_token0ReserveU112),
        _token1InU112.mul(supplyLP).divDown(_token1ReserveU112)
      );
    }

    requireErrCode(amountLP > 0, CronErrors.INSUFFICIENT_LIQUIDITY);
    _mintPoolTokens(_recipient, amountLP);
  }

  /// @notice This function performs an exit given the amount of Liquidity Provider (LP) tokens
  ///         to return to the pool along with the current virtual reserves.
  /// @param _sender the account performing the liquidity removal.
  /// @param _tokensLP the number of LP tokens to redeem in exchange for the pool's asset tokens.
  /// @param _token0ReserveU112 is the current Token 0 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @param _token1ReserveU112 is the current Token 1 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @return token0OutU112 is the amount of Token 0 returned by the pool in exchange for LP tokens.
  ///                       Min. = 0, Max. = (2**112) - 1
  /// @return token1OutU112 is the amount of Token 1 returned by the pool in exchange for LP tokens.
  ///                       Min. = 0, Max. = (2**112) - 1
  ///
  function _exit(
    address _sender,
    uint256 _tokensLP,
    uint256 _token0ReserveU112,
    uint256 _token1ReserveU112
  ) private returns (uint256 token0OutU112, uint256 token1OutU112) {
    // CAREFUL: Next line must preceed burning of pool liquidity provider tokens.
    uint256 lTotalSupply = totalSupply();
    _burnPoolTokens(_sender, _tokensLP);
    token0OutU112 = (_token0ReserveU112.mul(_tokensLP)).divDown(lTotalSupply);
    token1OutU112 = (_token1ReserveU112.mul(_tokensLP)).divDown(lTotalSupply);

    emit PoolExit(_sender, _tokensLP, token0OutU112, token1OutU112);
  }

  /// @notice Handles the updating of the stored Balancer fee multiplier and remittance of any collected
  ///         Balancer fees in onJoinPool and onExitPool function calls.
  /// @param _protocolFeeDU1F18 the newest Balancer Fee passed into the pool by the onJoinPool and
  ///                           onExitPool functions. This number is divided by 1e18 (C.ONE_DU1_18) to
  ///                           arrive at a fee multiplier between 0 and 1, inclusive, with 18
  ///                           fractional decimal digits.
  ///                           Min. = 0, Max. = 10**18
  /// @return dueProtocolFeeAmountsU96 the amount of Token 0 and Token 1 collected by the pool for
  ///                                  Balancer. Values are returned in the same array ordering that
  ///                                  IVault.getPoolTokens returns.
  ///                                  Min. = 0, Max. = (2**96) - 1
  ///
  function _handleBalancerFees(uint256 _protocolFeeDU1F18) private returns (uint256[] memory dueProtocolFeeAmountsU96) {
    uint256 localSlot4 = slot4;

    if (_protocolFeeDU1F18 <= C.ONE_DU1_18) {
      uint256 currentBalancerFee = BitPackingLib.unpackBalancerFeeS4(localSlot4);
      // #savegas: check before write
      if (currentBalancerFee != _protocolFeeDU1F18) {
        localSlot4 = BitPackingLib.packBalancerFeeS4(localSlot4, _protocolFeeDU1F18);
      }
    } else {
      // Ignore change and keep operating with old fee if new fee is too large but notify through logs.
      emit ProtocolFeeTooLarge(_protocolFeeDU1F18);
    }

    // Send the Balancer fees out of the pool and zero our accounting of them:
    dueProtocolFeeAmountsU96 = new uint256[](2);
    (slot4, dueProtocolFeeAmountsU96[C.INDEX_TOKEN0], dueProtocolFeeAmountsU96[C.INDEX_TOKEN1]) = BitPackingLib
      .unpackAndClearPairU96(localSlot4);
  }

  /// @notice Returns the TWAMM pool's reserves after the non-stateful execution of all virtual orders
  ///         up to the current block.
  ///         IMPORTANT - This function does not meaningfully modify state despite the lack of a "view"
  ///                     designator for state mutability. (The call to _triggerVaultReentrancyCheck
  ///                     Unfortunately prevents the "view" designator as meaningless value is written
  ///                     to state to trigger a reentracy check).
  ///         Runs virtual orders from the last virtual order block up to the current block to provide
  ///         visibility into the current accounting for the pool.
  ///         If the pool is paused, this function reflects the accounting values of the pool at
  ///         the last virtual order block (i.e. it does not execute virtual orders to deliver the result).
  /// @param _maxBlock a block to update virtual orders to. If less than or equal to the last virtual order
  ///                  block or greater than the current block, the value is set to the current
  ///                  block number.
  /// @param _paused is true to indicate the result should be returned as though the pool is in a paused
  ///                state where virtual orders are not executed and only withdraw, cancel and liquidations
  ///                are possible (check function isPaused to see if the pool is in that state). If false
  ///                then the virtual reserves are computed from virtual order execution to the specified
  ///                block.
  /// @return evoMem a struct containing the pool's virtual reserves for Token 0 and Token 1 along with
  ///                the changes to the Orders, Proceeds, and Fee accounting to the current state values
  ///                for Token 0 and Token 1. See the documentation of struct ExecVirtualOrdersMem for
  ///                details.
  ///                IMPORTANT Note above that struct values for token0ReserveU112 and token1ReserveU112
  ///                          are the virtual values of the reserves at the current block, whereas other
  ///                          values in the ExecVirtualOrdersMem struct are the DIFFERENCES from the
  ///                          state of their respective values and their virtual values.
  /// @return blockNumber The block that the virtual reserve values were computed at. Should
  ///                     match parameter _maxBlock, unless _maxBlock was not greater than the
  ///                     last virtual order block or less than or equal to the current block.
  ///
  function _getVirtualReserves(uint256 _maxBlock, bool _paused)
    private
    returns (ExecVirtualOrdersMem memory evoMem, uint256 blockNumber)
  {
    blockNumber = (virtualOrders.lastVirtualOrderBlock < _maxBlock && _maxBlock <= block.number)
      ? _maxBlock
      : block.number;

    _triggerVaultReentrancyCheck();
    (, uint256[] memory balancesU112, ) = VAULT.getPoolTokens(POOL_ID);

    evoMem = _getExecVirtualOrdersMem(balancesU112[C.INDEX_TOKEN0], balancesU112[C.INDEX_TOKEN1]);

    if (!_paused) {
      _executeVirtualOrdersView(evoMem, blockNumber);
    }
  }

  /// @notice Executes all virtual orders between current lastVirtualOrderBlock and blockNumber
  ///         also handles orders that expire at end of final block. This assumes that no orders
  ///         expire inside the given interval.
  ///
  /// @param _evoMem aggregated information for gas efficient virtual order execution. See
  ///                documentation in Structs.sol.
  /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned on
  ///                     multiples of the order block interval.
  /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See
  ///                 documentation in Structs.sol.
  /// @param _poolFeeLTFP is the pool fee charged for long term swaps in Fee Points (FP).
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000.
  ///
  function _executeVirtualTradesAndOrderExpiries(
    ExecVirtualOrdersMem memory _evoMem,
    uint256 _expiryBlock,
    LoopMem memory _loopMem,
    uint256 _poolFeeLTFP
  ) private view {
    // Determine how many blocks are in the current interval and compute
    // the amount of Token0 and Token1 being sold to the pool and any
    // fees. (Fees are collected as a percentage of tokens being sold to
    // the pool as opposed to tokens bought from the pool).
    //
    // #unchecked
    //            Subtraction below is unchecked because _expiryBlock is
    //            always greater than the lastVirtualOrderBlock by at least
    //            1, and up to a block interval (see function _getLoopMem
    //            where it's initialized).
    uint256 intervalBlocks = _expiryBlock - _loopMem.lastVirtualOrderBlock;

    uint256 currentSalesRate0U112 = _loopMem.currentSalesRate0U112; // #savegas
    uint256 currentSalesRate1U112 = _loopMem.currentSalesRate1U112; // #savegas

    // #unchecked
    //            The computation of token0In and token1In are not checked
    //            for overflow. The current sales rates will not exceed MAX_U112
    //            and the number of interval blocks is bounded to a maximum of
    //            1200 (VOLATILE_OBI), well within the safe range of MAX_U256.
    //
    //            Likewise calculation of grossFeeT0 and grossFeeT1 are not
    //            checked for overflow because token0In has a maximum of MAX_U123
    //            and it's multiplied by _poolFeeLTFP which has a maximum
    //            of 1000 (MAX_FEE_FP), well within the safe range of MAX_U256.
    //
    //            grossFeeT0 and grossFeeT1 thus have a practical maximum value of
    //              MAX_U112 * 1200 * 1000 / 100000 ---> 116-bits
    //
    // NOTE: division with rounding up is performed to favor the pool.
    uint256 token0In = currentSalesRate0U112 * intervalBlocks;
    uint256 grossFeeT0 = (token0In * _poolFeeLTFP).divUp(C.TOTAL_FP);
    //
    uint256 token1In = currentSalesRate1U112 * intervalBlocks;
    uint256 grossFeeT1 = (token1In * _poolFeeLTFP).divUp(C.TOTAL_FP);

    (uint256 lpFeeT0, uint256 lpFeeT1) = _updateFees(_evoMem, grossFeeT0, grossFeeT1);

    // Compute the amount of each token bought from the pool for the current
    // interval. Then update counts for the orders sold to the pool, the
    // proceeds bought from the pool, and add the LP fees into the reserves
    // after the swap is performed (LP fees are added in after the swap because
    // otherwise, they wouldn't be implicitly collected and the reserves wouldn't
    // grow and compensate the LPs).
    //
    // #unchecked
    //            The subtraction of grossFeeT0 and grossFeeT1 from token0In and
    //            token1In, respectively, are unchecked because gross fees are
    //            computed as a fraction less than one (multiplying by a numerator
    //            that is less than the denominator) of the token in values. The resulting
    //            gross fee values will always be less than their respective token in
    //            values.
    (uint256 token0Out, uint256 token1Out) = _computeVirtualReserves(
      _evoMem,
      token0In - grossFeeT0,
      token1In - grossFeeT1
    );

    // NOTE: For gas efficiency the order and proceed amounts are summed and then
    //       used to modify the global order and proceed accounting after all
    //       iterations are performed.
    //
    // #unchecked
    //            The incrementing of tokenNOrdersU112 below is unchecked because the
    //            order amounts in are checked by Balancer (BAL#526 BALANCE_TOTAL_OVERFLOW)
    //            when orders are taken in by the pool. Repeatedly adding them to a
    //            zero initialized value (tokenNOrdersU112) would take longer to overflow
    //            than this loop could run before it runs out of gas. At the end of the
    //            loop calling this method, these values are then subtracted from the order
    //            accounting and zeroed again for the next execution (the subtraction checks
    //            for underflow on a verified <= U112 value, see BitPackingLib.decrementPairU112).
    _evoMem.token0OrdersU112 += token0In;
    _evoMem.token1OrdersU112 += token1In;

    // The check below for values exceeding U112 cover the unchecked increment of tokenNProceedsU112
    // as safe for the duration of this loop. They also cover the call to _incrementScaledProceeds
    // which depends upon the tokenNOut values being <= U112.  (The two one sided active trade use
    // cases will never produce an output > U112, need to confirm the TWAMM approximation with
    // two active trades is similar, hence the check below.)
    requireErrCode(token0Out <= C.MAX_U112, CronErrors.OVERFLOW);
    requireErrCode(token1Out <= C.MAX_U112, CronErrors.OVERFLOW);
    _evoMem.token0ProceedsU112 += token0Out;
    _evoMem.token1ProceedsU112 += token1Out;

    // Compensate the LPs:
    //
    // #unchecked
    //            The incrementing below is unchecked because the lpFeeTN values are a fraction
    //            of the grossFeeTN values which themselves are a fraction of the tokenNIn values
    //            that are known to be limited to 112-bits by the Balancer check when the order is
    //            taken (BAL#526 BALANCE_TOTAL_OVERFLOW). (As discussed above the grossFeeTN values
    //            have a maximum practical value of 116-bits which won't result in a 256-bit overflow
    //            in the increment below before the loop ends).
    _evoMem.token0ReserveU112 += lpFeeT0;
    _evoMem.token1ReserveU112 += lpFeeT1;

    // Distribute proceeds to pools:
    _incrementScaledProceeds(_loopMem, token0Out, token1Out, currentSalesRate0U112, currentSalesRate1U112);

    _loopMem.lastVirtualOrderBlock = _expiryBlock;
  }

  /// @notice Read only execution of active virtual orders from the last virtual order block
  ///         in state to the current block number. Does not write updates or changes to state.
  /// @param _evoMem aggregated information for gas efficient virtual order exection. See
  ///                documentation in Structs.sol.
  /// @param _blockNumber is the block number to execute active virtual orders up to from
  ///                     the last virtual order block.
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000.
  /// @dev This method's utility comes from the values stored in _evoMem at the end of
  ///      calling it; fees, orders, proceeds and reserves will all reflect the most up-to-date
  ///      values for virtual orders at the current block.
  ///
  function _executeVirtualOrdersView(ExecVirtualOrdersMem memory _evoMem, uint256 _blockNumber) private view {
    uint256 poolFeeLTFP = BitPackingLib.unpackU10(slot1, C.S1_OFFSET_LONG_TERM_FEE_FP);
    (LoopMem memory loopMem, uint256 expiryBlock) = _getLoopMem();

    // Loop through active virtual orders preceeding the final block interval, performing
    // aggregate Long-Term (LT) swaps and updating sales rates in memory
    //
    uint256 prevLVOB = loopMem.lastVirtualOrderBlock;
    while (expiryBlock < _blockNumber) {
      _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP);

      _calculateOracleIncrement(_evoMem, prevLVOB, expiryBlock);
      prevLVOB = loopMem.lastVirtualOrderBlock;

      // Handle orders expiring at end of interval
      _decrementSalesRates(expiryBlock, loopMem);

      expiryBlock += ORDER_BLOCK_INTERVAL;
    }

    // Process the active virtual orders of the final block interval, performing
    // aggregate Long-Term (LT) swaps
    //
    if (loopMem.lastVirtualOrderBlock != _blockNumber) {
      expiryBlock = _blockNumber;
      _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP);

      _calculateOracleIncrement(_evoMem, prevLVOB, _blockNumber);
    }
  }

  /// @notice Increments the current scaled proceeds of an order pool with the normalized
  ///         proceeds of recent swaps when given the current scaled proceeds, token out amount
  ///         and sales rate of the order pools.
  /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See
  ///                 documentation in Structs.sol.
  /// @param _token0OutU112 The amount of Token 0 purchased in aggregate in an interval. This amount
  ///                       is normalized by the sales rate and then increments the scaled proceeds.
  /// @param _token1OutU112 The amount of Token 1 purchased in aggregate in an interval. This amount
  ///                       is normalized by the sales rate and then increments the scaled proceeds.
  /// @param _currentSalesRate0U112 The current amount of Token 0 sold per block to the order pool.
  /// @param _currentSalesRate1U112 The current amount of Token 1 sold per block to the order pool.
  ///
  function _incrementScaledProceeds(
    LoopMem memory _loopMem,
    uint256 _token0OutU112,
    uint256 _token1OutU112,
    uint256 _currentSalesRate0U112,
    uint256 _currentSalesRate1U112
  ) private view {
    // #overUnderFlowIntended The addition of scaledProceeds below is required to overflow a uint128
    //                        to work properly. This is because when a user withdraws or
    //                        cancels their order, the distance between the scaled proceeds
    //                        at the time of withdraw/cancel from when their order started is
    //                        used to determine their portion.  In other words it's not the
    //                        value of their scaled proceeds that determines their portion but
    //                        the distance between scaled proceeds at withdraw/cancel and order
    //                        start.  As long as the scaled proceeds doesn't overflow twice
    //                        during an order, they have not lost proceeds--overflowing twice
    //                        is highly unlikely because of the scaling by the number of decimal
    //                        places of the source token in the stored result. Consult Cron-Fi TWAMM
    //                        Numerical Analysis for a discussion on this matter.
    //
    if (_currentSalesRate0U112 != 0) {
      _loopMem.scaledProceeds0U128 = uint128(
        uint128(_loopMem.scaledProceeds0U128) +
          uint128((_token1OutU112 * ORDER_POOL0_PROCEEDS_SCALING) / _currentSalesRate0U112)
      );
    }

    if (_currentSalesRate1U112 != 0) {
      _loopMem.scaledProceeds1U128 = uint128(
        uint128(_loopMem.scaledProceeds1U128) +
          uint128((_token0OutU112 * ORDER_POOL1_PROCEEDS_SCALING) / _currentSalesRate1U112)
      );
    }
  }

  /// @notice Decrements the sales rates expiring in the specified block, _expiryBlock, from the
  ///         current sales rates.
  /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned on
  ///                     multiples of the order block interval.
  /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See
  ///                 documentation in Structs.sol.
  ///
  function _decrementSalesRates(uint256 _expiryBlock, LoopMem memory _loopMem) private view {
    // NOTE: Stored sales rates are correct by construction (the packPairU112 method checks for exceeding
    //       MAX_U112), thus these fetched values here are within the range of MAX_U112.
    (uint256 salesRateEndingPerBlock0U112, uint256 salesRateEndingPerBlock1U112) = BitPackingLib.unpackPairU112(
      virtualOrders.orderPools.salesRatesEndingPerBlock[_expiryBlock]
    );

    if (salesRateEndingPerBlock0U112 > 0) {
      requireErrCode(_loopMem.currentSalesRate0U112 >= salesRateEndingPerBlock0U112, CronErrors.UNDERFLOW);
      _loopMem.currentSalesRate0U112 -= salesRateEndingPerBlock0U112;
    }
    if (salesRateEndingPerBlock1U112 > 0) {
      requireErrCode(_loopMem.currentSalesRate1U112 >= salesRateEndingPerBlock1U112, CronErrors.UNDERFLOW);
      _loopMem.currentSalesRate1U112 -= salesRateEndingPerBlock1U112;
    }
  }

  /// @notice Converts the provided block number, _updateBlock, to a timestamp and returns the computed
  ///         time elapsed between the timestamp and the previous oracle timestamp.
  ///         If _updateBlock is the current block number, the block.timestamp is used for the timestamp.
  ///         Otherwise the timestamp is computed by subtracting the block time, 12s, multiplied by the
  ///         number of blocks between the current block, block.number, and the provided _updateBlock.
  /// @param _updateBlock A block number to compute a timestamp for and the elapsed time since the
  ///                     previous oracle timestamp.
  ///                     NOTE: Expects that all calling functions ensure that updateBlock <= block.number.
  /// @return timeElapsed The difference between the computed timestamp and the previous oracle timestamp.
  /// @return blockTimestamp The computed timestamp.
  ///
  function _getOracleTimeData(uint256 _updateBlock) private view returns (uint32 timeElapsed, uint32 blockTimestamp) {
    uint32 oracleTimeStamp = uint32(BitPackingLib.unpackOracleTimeStampS2(slot2));

    if (_updateBlock == block.number) {
      // _updateBlock is the current block number, use the current block timestamp:
      blockTimestamp = uint32(block.timestamp % 2**32);
    } else {
      // _update block is less than the current block, use the current block minus an approximate
      // amount of time based on the difference multiplied by the block time in seconds guaranteed
      // by the beacon chain, to generate a timestamp approximation between the current block and
      // previous last virtual order block:
      //
      // #unchecked:
      //             The subtraction of _updateBlock below is unchecked here b/c the calls to
      //             _updateOracle are guaranteed to either pass in a value less than or equal
      //             to the current block number.
      //
      //             The subtraction of the unelapsed blocks multiplied by the block time from
      //             the timestamp should be able to underflow with the modulus result being
      //             correct for the purposes herein (i.e. the nature of this entire system being
      //             based in differences makes that acceptable).
      blockTimestamp = uint32((block.timestamp - C.SECONDS_PER_BLOCK * (block.number - _updateBlock)) % 2**32);
    }

    // #overUnderFlowIntended
    //                        As documented in the Uniswap V2 whitepaper, the 32-bits allocated to
    //                        store the timestamp will overflow approximately every 136 years. As
    //                        long as users take 1 sample every 136 years, then the resulting underflow
    //                        that would occur in the following 32-bit subtraction will be appropriate
    //                        as it will yield the correct distance in seconds between the two samples.
    timeElapsed = blockTimestamp - oracleTimeStamp;
  }

  /// @notice Balancer custom pool specific safety checks.
  /// @param _sender The message sender. Must be the vault address.
  /// @param _poolId The pool ID in the calling function. Must be this pool.
  ///
  function _poolSafetyChecks(address _sender, bytes32 _poolId) private view {
    requireErrCode(_sender == address(VAULT), CronErrors.NON_VAULT_CALLER);
    requireErrCode(_poolId == POOL_ID, CronErrors.INCORRECT_POOL_ID);
  }

  /// @notice Gets an initialized instance of struct ExecVirtualOrdersMem. The
  ///         orders, proceeds and fees struct members are all initialized to zero.
  ///         The reserves are initialized to the difference between the
  ///         Balancer Vault's notion of the pool's token balances and the sum
  ///         of the orders, proceeds, and fee counters in state.
  ///         State is also used to configure the fee percentages, which are
  ///         explained in more detail below, see NOTE.
  /// @param _balance0U112 The Balancer Vault balance of Token 0 for this pool.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _balance1U112 The Balancer Vault balance of Token 1 for this pool.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @return evoMem A Struct for executing virtual orders across functions
  ///                efficiently (saves on gas use from continual accesses to
  ///                storage/state). See documentation for ExecVirtualOrdersMem
  ///                struct.
  /// @dev NOTE: This getter also sets struct members lpFeeU60, feeShareU60 and
  ///            feeShiftU3 based on current state. Explanations for these are
  ///            detailed in the documentation for the ExecVirtualOrdersMem struct.
  ///            For convenience though, note that when Cron-Fi fees are not being
  ///            collected (cronFeeEnabled == C.FALSE), feeShiftU3 and feeShareU60
  ///            are set to zero as they are not needed since Cron-Fi fees need not
  ///            be calculated. When Cron-Fi fees are collected, then the following
  ///            equations are applicable:
  ///
  ///                lpFeeU60 = ONE_DU1_18 - balancerFeeDU1F18                (1)
  ///
  ///                                  lpFeeU60
  ///                feeShareU60 = -----------------                          (2)
  ///                              1 + 2**feeShiftU3
  ///
  ///            The results of equations 1 & 2 above are used in the function
  ///            _updateFees to divide the gross fees between
  ///            Cron-Fi and the Liquidity Providers (LPs), efficiently. The
  ///            sum of those collected fees are then subtracted from the gross fees
  ///            and the remainder goes to Balancer.
  ///            A similar strategy is used when Cron-Fi fees are not collected,
  ///            however there is no need to split fees between Cron-Fi and the LPs
  ///            in that scenario, so feeShiftU3 and feeShareU60 are not needed.
  ///
  function _getExecVirtualOrdersMem(uint256 _balance0U112, uint256 _balance1U112)
    private
    view
    returns (ExecVirtualOrdersMem memory evoMem)
  {
    (uint256 token0ReserveU112, uint256 token1ReserveU112) = _calculateReserves(_balance0U112, _balance1U112);

    uint256 localSlot = slot4; // #savegas
    uint256 cronFeeEnabled = BitPackingLib.unpackBit(localSlot, C.S4_OFFSET_CRON_FEE_ENABLED);
    uint256 collectBalancerFees = BitPackingLib.unpackBit(localSlot, C.S4_OFFSET_COLLECT_BALANCER_FEES);
    uint256 balancerFeeDU1F18 = BitPackingLib.unpackBalancerFeeS4(localSlot);

    // #unchecked
    //            The subtraction below is unchecked because the Balancer Fee, balancerFeeDU1F18, is
    //            retrieved from storage that does not permit values > C.ONE_DU1_18 to be stored.
    //            See predicate in function _handleBalancerFees that ensures potential stored values
    //            are <= C.ONE_DU1_18.
    uint256 lpFeeU60 = (collectBalancerFees != C.FALSE) ? C.ONE_DU1_18 - balancerFeeDU1F18 : C.ONE_DU1_18;

    uint256 feeShiftU3;
    uint256 feeShareU60;
    if (cronFeeEnabled != C.FALSE) {
      feeShiftU3 = BitPackingLib.unpackFeeShiftS3(slot3);
      feeShareU60 = lpFeeU60 / (1 + 2**feeShiftU3);
    }

    evoMem = ExecVirtualOrdersMem(
      token0ReserveU112,
      token1ReserveU112,
      lpFeeU60,
      feeShareU60,
      feeShiftU3,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0
    );
  }

  /// @notice Constructs an execute virtual order loop memory struct from virtual orders
  ///         state and the order block interval.
  ///
  function _getLoopMem() private view returns (LoopMem memory loopMem, uint256 expiryBlock) {
    // #unchecked
    //            The subtraction of lvob from the mod of it by the ORDER_BLOCK_INTERVAL
    //            is unchecked because lvob will always be greater than it's modulus by
    //            ORDER_BLOCK_INTERVAL and can't underflow. This is because it is initialized
    //            in the constructor to block.number, which is much greater than the maximum
    //            value of ORDER_BLOCK_INTERVAL (1200, VOLATILE_OBI).
    //
    //            The addition of ORDER_BLOCK_INTERVAL is unchecked because it is
    //            at most adding 1200 (VOLATILE_OBI) to the previous result,
    //            which is unlikely to overflow unless that result is already near overflow
    //            because block.number is approaching 2**256 - 1.
    //
    uint256 lvob = virtualOrders.lastVirtualOrderBlock; // #savegas
    expiryBlock = lvob - (lvob % ORDER_BLOCK_INTERVAL) + ORDER_BLOCK_INTERVAL;
    loopMem.lastVirtualOrderBlock = lvob;

    (loopMem.currentSalesRate0U112, loopMem.currentSalesRate1U112) = BitPackingLib.unpackPairU112(
      virtualOrders.orderPools.currentSalesRates
    );

    (loopMem.scaledProceeds0U128, loopMem.scaledProceeds1U128) = BitPackingLib.unpackPairU128(
      virtualOrders.orderPools.scaledProceeds
    );
  }

  /// @notice Calculates the pool's reserves at the current state or Last Virtual Order Block (LVOB) (i.e.
  ///         not considering unexecuted virtual orders). The calculation is the difference between the
  ///         Balancer Vault notion of the pool's token balances and the sum of the pool's internal
  ///         accounting of orders, proceeds, and fees for both tokens.
  /// @param _balance0U112 The Balancer Vault balance of Token 0 for this pool.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _balance1U112 The Balancer Vault balance of Token 1 for this pool.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @return token0ReserveU112 The reserves of Token 0 in this pool as of the LVOB.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @return token1ReserveU112 The reserves of Token 0 in this pool as of the LVOB.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @dev NOTE: _balance0U112 and _balance1U112 can not exceed (2**112) - 1 according to Balancer
  ///            documentation and code comments. This was further confirmed in conversations with
  ///            Balancer's engineering team.
  ///            [https://dev.balancer.fi/deep-dive/guided-tour-of-balancer-vault/episode-2-joins#vault-poolbalances.sol-5]
  ///            That means that if this function does not revert, we are assured that token0ReserveU112
  ///            and token1ReserveU112 are < MAX_U112.
  /// @dev #RISK: If the _balance0U112 and/or _balance1U112 values exceed the sum of the respective
  ///             token accounting (orders, proceeds, fees), this function will revert, rendering the
  ///             pool unusable. Even if paused!
  ///             We have mitigated this risk using the onJoinPool function when JoinType.Reward is
  ///             specified. In this scenario, a pool problem will have been identified and the pool
  ///             will have been paused. Anyone could then reward the pool with sufficient liquidity
  ///             to ensure the difference no longer underflows, enabling withdraw, cancel, and exit
  ///             operations.
  ///
  function _calculateReserves(uint256 _balance0U112, uint256 _balance1U112)
    private
    view
    returns (uint256 token0ReserveU112, uint256 token1ReserveU112)
  {
    (uint256 token0OrdersU112, uint256 token1OrdersU112) = BitPackingLib.unpackPairU112(slot1);
    (uint256 token0ProceedsU112, uint256 token1ProceedsU112) = BitPackingLib.unpackPairU112(slot2);

    uint256 localSlot4 = slot4;
    (uint256 token0BalancerFeesU96, uint256 token1BalancerFeesU96) = BitPackingLib.unpackPairU96(localSlot4);

    // NOTE: Optimization - prevent un-necessary read of slot3 by checking whether the Cron-Fi Fees stored
    //       in slot3 are zero (in flag zeroCronFiFees stored in slot4):
    //
    uint256 token0CronFiFeesU96;
    uint256 token1CronFiFeesU96;
    uint256 zeroCronFiFees = BitPackingLib.unpackBit(localSlot4, C.S4_OFFSET_ZERO_CRONFI_FEES);
    if (zeroCronFiFees == C.FALSE) {
      (token0CronFiFeesU96, token1CronFiFeesU96) = BitPackingLib.unpackPairU96(slot3);
    }

    /// #unchecked
    ///            The orders, proceeds and fees summed below are correct by construction (that is to say,
    ///            their values cannot exceed 112-bits or 96-bits, because they are stored as only 112-bits
    ///            or 96-bits and checked at storage time to ensure these limits aren't exceeded).
    ///            The sum of four 112-bit numbers cannot exceed 116-bits, and thus the unchecked addition
    ///            because a 256-bit overflow is impossible.
    ///            Safe math is used for the subtraction though in case an unforeseen error results in
    ///            the sum of orders, proceeds and fees exceeding Balancer's notion of the the pool's total
    ///            balance of either token.
    token0ReserveU112 = _balance0U112.sub(
      token0OrdersU112 + token0ProceedsU112 + token0BalancerFeesU96 + token0CronFiFeesU96
    );
    token1ReserveU112 = _balance1U112.sub(
      token1OrdersU112 + token1ProceedsU112 + token1BalancerFeesU96 + token1CronFiFeesU96
    );
  }

  /// @notice Computes the virtual reserves and output amounts of the pool for active
  ///         virtual orders. Handles the no-active orders, swap of Token0 to Token1 only
  ///         active orders, Token1 to Token0 only active orders and concurrent swap between
  ///         Token0 and Token1 active orders.
  ///         Updated virtual reserves are returned in the _evoMem struct and output amounts
  ///         through the token0OutU112 and token1OutU112 variables.
  /// @param _evoMem aggregated information for gas efficient virtual order exection. See
  ///                documentation in Structs.sol.
  /// @param _token0InU112 amount of Token0 to sell to the pool in exchange for Token1 for active
  ///                  virtual orders swapping in this direction.
  /// @param _token1InU112 amount of Token1 to sell to the pool in exchange for Token0 for active
  ///                  virtual orders swapping in this direction.
  /// @return token0OutU112 amount of Token0 received from swapping _token1InU112 Token1 with the pool.
  /// @return token1OutU112 amount of Token1 received from swapping _token0InU112 Token0 with the pool.
  ///
  function _computeVirtualReserves(
    ExecVirtualOrdersMem memory _evoMem,
    uint256 _token0InU112,
    uint256 _token1InU112
  ) private pure returns (uint256 token0OutU112, uint256 token1OutU112) {
    if (_token0InU112 != 0 || _token1InU112 != 0) {
      uint256 token0ReserveU112 = _evoMem.token0ReserveU112; // #savegas #savesize
      uint256 token1ReserveU112 = _evoMem.token1ReserveU112;

      if (_token0InU112 == 0) {
        // For single pool selling, use CPAMM formula:
        //
        // #unchecked
        //            The increment of token1ReserveU112 is unchecked below because the
        //            _token1InU112 value is derived from the orders which are checked along
        //            with the reserves by Balancer not to exceed U112 (BAL#526
        //            BALANCE_TOTAL_OVERFLOW). Also, at the start of execute virtual orders
        //            cycles, the token1ReserveU112 is computed from a known U112 value (the
        //            Balancer Vault balance) by subtracting orders, proceeds and fees accounting.
        //
        //            The multiplication of token0ReserveU112 and _token1InU112 is similarly
        //            constrained to U112 because of the BAL#526 checks performed. The maximum
        //            product of these two values is therefore a U224, which will not overflow
        //            the U256 container (especially given the division by token1ReserveU112).
        //
        //            The subtraction of token0OutU112 from token0ReserveU112 is unchecked
        //            because token0OutU112 is derived from multiplying token0ReserveU112 by
        //            values that amount to being less than 1, thus reducing it in comparison
        //            to token0ReserveU112.
        token1ReserveU112 += _token1InU112;
        token0OutU112 = (token0ReserveU112 * _token1InU112) / token1ReserveU112;
        token0ReserveU112 = token0ReserveU112 - token0OutU112;
      } else if (_token1InU112 == 0) {
        // For single pool selling, use CPAMM formula:
        //
        // #unchecked
        //            The following 3 lines are unchecked using the same rationale presented
        //            above for trades in the opposite direction.
        token0ReserveU112 += _token0InU112;
        token1OutU112 = (token1ReserveU112 * _token0InU112) / token0ReserveU112;
        token1ReserveU112 = token1ReserveU112 - token1OutU112;
      } else {
        // When both pools sell, apply the TWAMM formula in the form of the FRAX Approximation
        //
        // #unchecked
        //            The addition of tokenNReserveU112 to _tokenNInU112 below is unchecked
        //            because both values are known to be <= U112 by Balancer overflow checks
        //            (BAL#526 BALANCE_TOTAL_OVERFLOW). Also as noted in the single-sided rationale
        //            above, the reserves are computed by subtracting order, proceeds and fees
        //            accounting from known U112 values at the start of any execute virtual orders
        //            cycle (the Balancer Vault balance).
        uint256 sum0 = token0ReserveU112 + _token0InU112;
        uint256 sum1 = token1ReserveU112 + _token1InU112;

        // NOTE: purposely using standard rounding (divDown = /) here to reduce operating error.
        //
        // #unchecked
        //            Multiplication of token0ReserveU112 and sum1 is unchecked because these
        //            values are known to max out at U112 and U113, the product of which is U225,
        //            much less than the U256 container they're stored in.
        //
        //            Multiplication by token0ReserveU112 and token1ReserveU112, values known to
        //            be within U112 by Balancer overflow checks and their construction at the start
        //            of the execute virtual orders cycle, cannot exceed a U224 value, thus safe
        //            from overflow.
        uint256 ammEndToken0 = (token1ReserveU112 * sum0) / sum1;
        uint256 ammEndToken1 = (token0ReserveU112 * sum1) / sum0;
        token0ReserveU112 = ammEndToken0;
        token1ReserveU112 = ammEndToken1;

        // #unchecked
        //            Both subtractions below are unchecked for underflow because they take the form:
        //
        //                     x * z
        //                z - -------
        //                       y
        //
        //                where x <= y
        //
        //
        //            Full explanation for token0OutU112 & token1OutU112:
        //
        //                token0OutU112 = sum0 - ammEndToken0
        //
        //                                        sum0 * token1ReserveU112
        //                              = sum0 - --------------------------
        //                                                 sum1
        //
        //                                            sum0 * token1ReserveU112
        //                              = sum0 - -----------------------------------
        //                                        token1ReserveU112 + _token1InU112
        //
        //                    where: token1ReserveU112 <= token1ReserveU112 + token1InU112
        //
        //
        //                token1OutU112 = sum1 - ammEndToken1
        //
        //                                        sum1 * token0ReserveU112
        //                token1OutU112 = sum1 - --------------------------
        //                                                  sum0
        //
        //                                              sum1 * token0ReserveU112
        //                              = sum1 - ------------------------------------
        //                                        (token0ReserveU112 + token0InU112)
        //
        //                    where: token0ReserveU112 <= token0ReserveU112 + token0InU112
        //
        token0OutU112 = sum0 - ammEndToken0;
        token1OutU112 = sum1 - ammEndToken1;
      }

      _evoMem.token0ReserveU112 = token0ReserveU112;
      _evoMem.token1ReserveU112 = token1ReserveU112;
    }
  }

  /// @notice Updates the fees collected for Balancer, Liquidity Providers (LPs) and Cron-Fi (if
  ///         enabled) in the provided struct, _evoMem, for later addition to the pool.
  /// @param _grossFeeT0 is the gross fee amount collected by the pool for performing LT swaps.
  ///                    Min. = 0, Max. = (2**116) - 1
  /// @param _grossFeeT1 is the gross fee amount collected by the pool for performing LT swaps.
  ///                    Min. = 0, Max. = (2**116) - 1
  ///
  /// @dev see _executeVirtualTradesAndOrderExpiries for analysis explaining
  ///      why _grossFeeT0 and _grossFeeT1 are limited to MAX_U116.
  ///
  function _updateFees(
    ExecVirtualOrdersMem memory _evoMem,
    uint256 _grossFeeT0,
    uint256 _grossFeeT1
  ) private pure returns (uint256 lpFeeT0, uint256 lpFeeT1) {
    uint256 feeShiftU3 = _evoMem.feeShiftU3; // #savegas #savesize
    uint256 lpFeeU60 = _evoMem.lpFeeU60; // #savegas #savesize

    if (feeShiftU3 == 0) {
      // Cron-Fi Fees Not Collected:

      // #unchecked
      //            Multiplication of the gross fees, _grossFeeT0 and _grossFeeT1,
      //            with lp fee, lpFeeU60, is unchecked because the gross fees
      //            max out at 116-bits and the lp fee, lpFeeU60, at 60-bits, the
      //            product of which has a theoretical maximum of 186-bits, far less
      //            than the 256-bit result container.
      //
      // NOTE: division with rounding down is performed to protect against underflow
      //       in subtraction below.
      lpFeeT0 = (_grossFeeT0 * lpFeeU60).divDown(C.DENOMINATOR_DU1_18);
      lpFeeT1 = (_grossFeeT1 * lpFeeU60).divDown(C.DENOMINATOR_DU1_18);

      // Accumulate fees for balancer
      // #unchecked
      //            The accumulation of gross fees for balancer involves unchecked
      //            subtraction because _grossFeeT0 and _grossFeeT1 will always
      //            be greater than lpFeeT0 and lpFeeT1, respectively. This is
      //            because lpFee0 and lpFee1 are derived from the gross fees
      //            by multipling them with a fraction less than one (multiplying
      //            by a numerator that is less than the denominator, C.DENOMINATOR_FP18).
      //
      //            Overflow is also not checked here in the addition because the
      //            values are clamped to 96-bits when stored in state and the
      //            theoretical maximum _grossFee of 116-bits when added to
      //            96-bits would not exceed the 256-bit intermediate container resolution.
      //
      // Predicate prevents unintended rounding error based balancer fee collection when
      // balancer fees are zero. (see CronV1Pool.sol::_getExecVirtualOrdersMem documentation).
      if (lpFeeU60 < C.ONE_DU1_18) {
        _evoMem.token0BalancerFeesU96 += _grossFeeT0 - lpFeeT0;
        _evoMem.token1BalancerFeesU96 += _grossFeeT1 - lpFeeT1;
      }
    } else {
      // Cron-Fi Fees Collected:
      //
      // NOTE: When the fee address is set, Cron-Fi splits the fees with the LPs
      //       and balancer.  The feeShareU60 value is 1/(1+2**feeShift) multiplied by the
      //       fee remaining from collecting the Balancer Protocol fee. This value is then
      //       aportioned with 1 share going to Cron-Fi and (2**feeShift) shares to the LPs.
      //       For example if the Balancer Protocol fee is half of all fees collected
      //       and feeShift=1, then 1/6 of the total collected fee goes to Cron-Fi and
      //       1/3 of the fees collected go to the LPs (the other 1/2 of collected fees
      //       going to Balancer).
      uint256 feeShareU60 = _evoMem.feeShareU60; // #savegas #savesize

      // #unchecked
      //            Multiplication of the gross fees, _grossFeeT0 and _grossFeeT1,
      //            with fee share, feeShareU60, is unchecked because the gross fees
      //            max out at 116-bits and the lp fee, feeShareU60, at 60-bits, the
      //            product of which has a theoretical maximum of 186-bits, far less
      //            than the 256-bit result container.
      //
      // NOTE: division with rounding down is performed to protect against underflow
      //       in subtraction below.
      uint256 feeShareT0 = (_grossFeeT0 * feeShareU60).divDown(C.DENOMINATOR_DU1_18);
      uint256 feeShareT1 = (_grossFeeT1 * feeShareU60).divDown(C.DENOMINATOR_DU1_18);

      // LPs get 2**_evoMem.feeShiftU3 of the fee shares, Cron Fi gets one share.
      //
      // #unchecked
      //            The left shifts of feeShareT0 and feeShareT1 below are unchecked
      //            because the maximum shift, feeShiftU3, is limited to 4 (see
      //            check in CronV1Pool.sol::setFeeShift. The worst case value of
      //            feeShareT0 and feeShareT1 is 186-bits before division by
      //            C.DENOMINATOR_DU1_18, well within the 256-bit container limit.
      lpFeeT0 = feeShareT0 << feeShiftU3;
      lpFeeT1 = feeShareT1 << feeShiftU3;

      // Accumulate fees for balancer
      // #unchecked
      //            The accumulation of fees for balancer involves unchecked
      //            subtraction because _grossFeeT0 and _grossFeeT1 will always
      //            be greater than lpFeeT0, lpFeeT1 feeShareT0, and feeShareT1 combined,
      //            respectively. This is because lpFee0, lpFee1, feeShareT0 and feeShareT1
      //            are derived from the gross fees by multipling them with a fraction less
      //            than or equal to one (multiplying by a numerator that is less than or equal
      //            to the denominator, C.DENOMINATOR_FP18, which rounds down). (The value of
      //            feeShareU60 is such that multiplying it by 2**feeShift and then adding it
      //            to itself will sum to the gross fee minus fees collected for balancer).
      //
      //            Overflow is also not checked here in the addition because the
      //            values are clamped to 96-bits when stored in state and the
      //            theortical maximum _grossFee of 116-bits when added to
      //            96-bits would not exceed the 256-bit intermediate container range.
      //
      // Predicate prevents unintended rounding error based balancer fee collection when
      // balancer fees are zero. (see CronV1Pool.sol::_getExecVirtualOrdersMem documentation).
      if (lpFeeU60 < C.ONE_DU1_18) {
        _evoMem.token0BalancerFeesU96 += (_grossFeeT0 - lpFeeT0) - feeShareT0;
        _evoMem.token1BalancerFeesU96 += (_grossFeeT1 - lpFeeT1) - feeShareT1;
      }

      // Accumulate fees for CronFi
      // #unchecked
      //            Similar to above, overflow is not checked in the addition because
      //            Cron-Fi fees are clamped to 96-bits when stored in state and the
      //            theoretical maximum _grossFee of 116-bits (which greatly exceeds
      //            the feeShareT0 and feeShareT1 values) when added to 96-bits would not
      //            exceed the 256-bit intermediate container range.
      _evoMem.token0CronFiFeesU96 += feeShareT0;
      _evoMem.token1CronFiFeesU96 += feeShareT1;
    }
  }

  /// @notice Calculate the oracle increment by incrementing the oracle change struct members
  ///         (token0OracleU256F112 and token1OracleU256F112) of struct _evoMem based on the
  ///         current reserve values and the time elapsed since the last reserve values.
  /// @param _evoMem aggregated information for gas efficient virtual order exection. See
  ///                documentation in Structs.sol.
  /// @param _lastVirtualOrderBlock is the ethereum block number before the last virtual
  ///                               orders were executed.
  /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned
  ///                     on multiples of the order block interval.
  function _calculateOracleIncrement(
    ExecVirtualOrdersMem memory _evoMem,
    uint256 _lastVirtualOrderBlock,
    uint256 _expiryBlock
  ) private pure {
    // #unchecked
    //            Overflow is not possible in the shift below: U256 > (U112 << 112).
    //            Token reserve values known to be U112 because computed from Balancer Vault values
    //            by subtracting accounting and funds going in/out of them all known to sum to
    //            less than U112 mathematically.
    //
    //            The subtraction of lastVirtualOrderBlock from _expiryBlock is unchecked below
    //            because the following functions that call this function ensure that _expiryBlock
    //            is greater than _lastVirtualOrderBlock in their call to _getLoopMem, which sets
    //            expiryBlock to the next block number divisible by order block interval after the
    //            last virtual order block:
    //                - executeVirtualOrdersToBlock
    //                - executeVirtualOrdersView
    //
    // #overUnderFlowIntended
    //                        The UQ256x112 incremented result relies upon and expects overflow
    //                        and is unchecked. The reason is that it is the difference between
    //                        price oracle samples over time that matters, not the absolute value.
    //                        As noted above, see the Uniswap V2 whitepaper.
    //                        That said, this is a partial increment that will be added to the
    //                        stored accumulation of values and is unlikely to overflow during
    //                        virtual order execution because both tokenXOracleU256F112 values
    //                        are initialized to zero at the start of execution (the time elapsed
    //                        would need to be on the order of 2 ** 32 to overflow the worst case
    //                        ratio of token reserves ((2 ** 112-1) / 1)--that means
    //                        (2**32-1/12s) = 357913941 blocks must elapse since the last oracle
    //                        update and the worst case reserves to overflow this incremental value
    //                        and cause an oracle price error.
    //
    uint256 timeElapsed = C.SECONDS_PER_BLOCK * (_expiryBlock - _lastVirtualOrderBlock);
    if (_evoMem.token0ReserveU112 > 0) {
      _evoMem.token0OracleU256F112 += ((_evoMem.token1ReserveU112 << 112) / _evoMem.token0ReserveU112) * timeElapsed;
    }
    if (_evoMem.token1ReserveU112 > 0) {
      _evoMem.token1OracleU256F112 += ((_evoMem.token0ReserveU112 << 112) / _evoMem.token1ReserveU112) * timeElapsed;
    }
  }

  /// @notice
  /// @param _token0InU112 is the amount of Token 0 to Join the pool with.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _token1InU112 is the amount of Token 1 to Join the pool with.
  ///                      Min. = 0, Max. = (2**112) - 1
  /// @param _token0MinU112 is the minimum price of Token 0 permitted.
  ///                       Min. = 0, Max. = (2**112) - 1
  /// @param _token1MinU112 is the minimum price of Token 1 permitted.
  ///                       Min. = 0, Max. = (2**112) - 1
  /// @param _token0ReserveU112 is the current Token 0 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  /// @param _token1ReserveU112 is the current Token 1 reserves of the pool.
  ///                           Min. = 0, Max. = (2**112) - 1
  ///
  function _joinMinimumCheck(
    uint256 _token0InU112,
    uint256 _token1InU112,
    uint256 _token0MinU112,
    uint256 _token1MinU112,
    uint256 _token0ReserveU112,
    uint256 _token1ReserveU112
  ) private pure {
    if (_token0ReserveU112 > 0 && _token1ReserveU112 > 0) {
      // #unchecked
      //            The multiplication below is unchecked for overflow because the product
      //            of two 112-bit values is less than 256-bits. (Reserves are correct by
      //            construction and Balancer ensures that the tokens in do not cause the
      //            pool to exceed 112-bits).
      uint256 nominalToken0 = (_token1InU112 * _token0ReserveU112) / _token1ReserveU112;
      uint256 nominalToken1 = (_token0InU112 * _token1ReserveU112) / _token0ReserveU112;
      requireErrCode(
        nominalToken0 >= _token0MinU112 && nominalToken1 >= _token1MinU112,
        CronErrors.MINIMUM_NOT_SATISFIED
      );
    }
  }
}

File 2 of 38 : Authentication.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

import "./BalancerErrors.sol";
import "./IAuthentication.sol";

/**
 * @dev Building block for performing access control on external functions.
 *
 * This contract is used via the `authenticate` modifier (or the `_authenticateCaller` function), which can be applied
 * to external functions to only make them callable by authorized accounts.
 *
 * Derived contracts must implement the `_canPerform` function, which holds the actual access control logic.
 */
abstract contract Authentication is IAuthentication {
    bytes32 private immutable _actionIdDisambiguator;

    /**
     * @dev The main purpose of the `actionIdDisambiguator` is to prevent accidental function selector collisions in
     * multi contract systems.
     *
     * There are two main uses for it:
     *  - if the contract is a singleton, any unique identifier can be used to make the associated action identifiers
     *    unique. The contract's own address is a good option.
     *  - if the contract belongs to a family that shares action identifiers for the same functions, an identifier
     *    shared by the entire family (and no other contract) should be used instead.
     */
    constructor(bytes32 actionIdDisambiguator) {
        _actionIdDisambiguator = actionIdDisambiguator;
    }

    /**
     * @dev Reverts unless the caller is allowed to call this function. Should only be applied to external functions.
     */
    modifier authenticate() {
        _authenticateCaller();
        _;
    }

    /**
     * @dev Reverts unless the caller is allowed to call the entry point function.
     */
    function _authenticateCaller() internal view {
        bytes32 actionId = getActionId(msg.sig);
        _require(_canPerform(actionId, msg.sender), Errors.SENDER_NOT_ALLOWED);
    }

    function getActionId(bytes4 selector) public view override returns (bytes32) {
        // Each external function is dynamically assigned an action identifier as the hash of the disambiguator and the
        // function selector. Disambiguation is necessary to avoid potential collisions in the function selectors of
        // multiple contracts.
        return keccak256(abi.encodePacked(_actionIdDisambiguator, selector));
    }

    function _canPerform(bytes32 actionId, address user) internal view virtual returns (bool);
}

File 3 of 38 : BalancerErrors.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

// solhint-disable

/**
 * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are
 * supported.
 */
function _require(bool condition, uint256 errorCode) pure {
    if (!condition) _revert(errorCode);
}

/**
 * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.
 */
function _revert(uint256 errorCode) pure {
    // We're going to dynamically create a revert string based on the error code, with the following format:
    // 'BAL#{errorCode}'
    // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).
    //
    // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a
    // number (8 to 16 bits) than the individual string characters.
    //
    // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a
    // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a
    // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.
    assembly {
        // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999
        // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for
        // the '0' character.

        let units := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let tenths := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let hundreds := add(mod(errorCode, 10), 0x30)

        // With the individual characters, we can now construct the full string. The "BAL#" part is a known constant
        // (0x42414c23): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the
        // characters to it, each shifted by a multiple of 8.
        // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits
        // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte
        // array).

        let revertReason := shl(200, add(0x42414c23000000, add(add(units, shl(8, tenths)), shl(16, hundreds))))

        // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded
        // message will have the following layout:
        // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]

        // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We
        // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.
        mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
        // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).
        mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
        // The string length is fixed: 7 characters.
        mstore(0x24, 7)
        // Finally, the string itself is stored.
        mstore(0x44, revertReason)

        // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of
        // the encoded message is therefore 4 + 32 + 32 + 32 = 100.
        revert(0, 100)
    }
}

library Errors {
    // Math
    uint256 internal constant ADD_OVERFLOW = 0;
    uint256 internal constant SUB_OVERFLOW = 1;
    uint256 internal constant SUB_UNDERFLOW = 2;
    uint256 internal constant MUL_OVERFLOW = 3;
    uint256 internal constant ZERO_DIVISION = 4;
    uint256 internal constant DIV_INTERNAL = 5;
    uint256 internal constant X_OUT_OF_BOUNDS = 6;
    uint256 internal constant Y_OUT_OF_BOUNDS = 7;
    uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;
    uint256 internal constant INVALID_EXPONENT = 9;

    // Input
    uint256 internal constant OUT_OF_BOUNDS = 100;
    uint256 internal constant UNSORTED_ARRAY = 101;
    uint256 internal constant UNSORTED_TOKENS = 102;
    uint256 internal constant INPUT_LENGTH_MISMATCH = 103;
    uint256 internal constant ZERO_TOKEN = 104;

    // Shared pools
    uint256 internal constant MIN_TOKENS = 200;
    uint256 internal constant MAX_TOKENS = 201;
    uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;
    uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;
    uint256 internal constant MINIMUM_BPT = 204;
    uint256 internal constant CALLER_NOT_VAULT = 205;
    uint256 internal constant UNINITIALIZED = 206;
    uint256 internal constant BPT_IN_MAX_AMOUNT = 207;
    uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;
    uint256 internal constant EXPIRED_PERMIT = 209;

    // Pools
    uint256 internal constant MIN_AMP = 300;
    uint256 internal constant MAX_AMP = 301;
    uint256 internal constant MIN_WEIGHT = 302;
    uint256 internal constant MAX_STABLE_TOKENS = 303;
    uint256 internal constant MAX_IN_RATIO = 304;
    uint256 internal constant MAX_OUT_RATIO = 305;
    uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;
    uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;
    uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;
    uint256 internal constant INVALID_TOKEN = 309;
    uint256 internal constant UNHANDLED_JOIN_KIND = 310;
    uint256 internal constant ZERO_INVARIANT = 311;
    uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;
    uint256 internal constant ORACLE_NOT_INITIALIZED = 313;
    uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;
    uint256 internal constant ORACLE_INVALID_INDEX = 315;
    uint256 internal constant ORACLE_BAD_SECS = 316;

    // Lib
    uint256 internal constant REENTRANCY = 400;
    uint256 internal constant SENDER_NOT_ALLOWED = 401;
    uint256 internal constant PAUSED = 402;
    uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;
    uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;
    uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;
    uint256 internal constant INSUFFICIENT_BALANCE = 406;
    uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;
    uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;
    uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;
    uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;
    uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;
    uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;
    uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;
    uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;
    uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;
    uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;
    uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;
    uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;
    uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;
    uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;
    uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;
    uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;
    uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;
    uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;
    uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;

    // Vault
    uint256 internal constant INVALID_POOL_ID = 500;
    uint256 internal constant CALLER_NOT_POOL = 501;
    uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;
    uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;
    uint256 internal constant INVALID_SIGNATURE = 504;
    uint256 internal constant EXIT_BELOW_MIN = 505;
    uint256 internal constant JOIN_ABOVE_MAX = 506;
    uint256 internal constant SWAP_LIMIT = 507;
    uint256 internal constant SWAP_DEADLINE = 508;
    uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;
    uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;
    uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;
    uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;
    uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;
    uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;
    uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;
    uint256 internal constant INSUFFICIENT_ETH = 516;
    uint256 internal constant UNALLOCATED_ETH = 517;
    uint256 internal constant ETH_TRANSFER = 518;
    uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;
    uint256 internal constant TOKENS_MISMATCH = 520;
    uint256 internal constant TOKEN_NOT_REGISTERED = 521;
    uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;
    uint256 internal constant TOKENS_ALREADY_SET = 523;
    uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;
    uint256 internal constant NONZERO_TOKEN_BALANCE = 525;
    uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;
    uint256 internal constant POOL_NO_TOKENS = 527;
    uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;

    // Fees
    uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;
    uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;
    uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;
}

File 4 of 38 : IAuthentication.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

interface IAuthentication {
    /**
     * @dev Returns the action identifier associated with the external function described by `selector`.
     */
    function getActionId(bytes4 selector) external view returns (bytes32);
}

File 5 of 38 : ISignaturesValidator.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

/**
 * @dev Interface for the SignatureValidator helper, used to support meta-transactions.
 */
interface ISignaturesValidator {
    /**
     * @dev Returns the EIP712 domain separator.
     */
    function getDomainSeparator() external view returns (bytes32);

    /**
     * @dev Returns the next nonce used by an address to sign messages.
     */
    function getNextNonce(address user) external view returns (uint256);
}

File 6 of 38 : ITemporarilyPausable.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

/**
 * @dev Interface for the TemporarilyPausable helper.
 */
interface ITemporarilyPausable {
    /**
     * @dev Emitted every time the pause state changes by `_setPaused`.
     */
    event PausedStateChanged(bool paused);

    /**
     * @dev Returns the current paused state.
     */
    function getPausedState()
        external
        view
        returns (
            bool paused,
            uint256 pauseWindowEndTime,
            uint256 bufferPeriodEndTime
        );
}

File 7 of 38 : InputHelpers.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

import "../openzeppelin/IERC20.sol";

import "./BalancerErrors.sol";

import "../../vault/interfaces/IAsset.sol";

library InputHelpers {
    function ensureInputLengthMatch(uint256 a, uint256 b) internal pure {
        _require(a == b, Errors.INPUT_LENGTH_MISMATCH);
    }

    function ensureInputLengthMatch(
        uint256 a,
        uint256 b,
        uint256 c
    ) internal pure {
        _require(a == b && b == c, Errors.INPUT_LENGTH_MISMATCH);
    }

    function ensureArrayIsSorted(IAsset[] memory array) internal pure {
        address[] memory addressArray;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            addressArray := array
        }
        ensureArrayIsSorted(addressArray);
    }

    function ensureArrayIsSorted(IERC20[] memory array) internal pure {
        address[] memory addressArray;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            addressArray := array
        }
        ensureArrayIsSorted(addressArray);
    }

    function ensureArrayIsSorted(address[] memory array) internal pure {
        if (array.length < 2) {
            return;
        }

        address previous = array[0];
        for (uint256 i = 1; i < array.length; ++i) {
            address current = array[i];
            _require(previous < current, Errors.UNSORTED_ARRAY);
            previous = current;
        }
    }
}

File 8 of 38 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../helpers/BalancerErrors.sol";

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow checks.
 * Adapted from OpenZeppelin's SafeMath library
 */
library Math {
    /**
     * @dev Returns the addition of two unsigned integers of 256 bits, reverting on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        _require(c >= a, Errors.ADD_OVERFLOW);
        return c;
    }

    /**
     * @dev Returns the addition of two signed integers, reverting on overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a + b;
        _require((b >= 0 && c >= a) || (b < 0 && c < a), Errors.ADD_OVERFLOW);
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers of 256 bits, reverting on overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        _require(b <= a, Errors.SUB_OVERFLOW);
        uint256 c = a - b;
        return c;
    }

    /**
     * @dev Returns the subtraction of two signed integers, reverting on overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a - b;
        _require((b >= 0 && c <= a) || (b < 0 && c > a), Errors.SUB_OVERFLOW);
        return c;
    }

    /**
     * @dev Returns the largest of two numbers of 256 bits.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers of 256 bits.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a * b;
        _require(a == 0 || c / a == b, Errors.MUL_OVERFLOW);
        return c;
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        _require(b != 0, Errors.ZERO_DIVISION);
        return a / b;
    }

    function divUp(uint256 a, uint256 b) internal pure returns (uint256) {
        _require(b != 0, Errors.ZERO_DIVISION);

        if (a == 0) {
            return 0;
        } else {
            return 1 + (a - 1) / b;
        }
    }
}

File 9 of 38 : EIP712.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _HASHED_NAME = keccak256(bytes(name));
        _HASHED_VERSION = keccak256(bytes(version));
        _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view virtual returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION, _getChainId(), address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
    }

    function _getChainId() private view returns (uint256 chainId) {
        // Silence state mutability warning without generating bytecode.
        // See https://github.com/ethereum/solidity/issues/10090#issuecomment-741789128 and
        // https://github.com/ethereum/solidity/issues/2691
        this;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            chainId := chainid()
        }
    }
}

File 10 of 38 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 11 of 38 : IERC20Permit.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens,
     * given `owner`'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 12 of 38 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

// Based on the ReentrancyGuard library from OpenZeppelin Contracts, altered to reduce bytecode size.
// Modifier code is inlined by the compiler, which causes its code to appear multiple times in the codebase. By using
// private functions, we achieve the same end result with slightly higher runtime gas costs, but reduced bytecode size.

pragma solidity ^0.7.0;

import "../helpers/BalancerErrors.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _enterNonReentrant();
        _;
        _exitNonReentrant();
    }

    function _enterNonReentrant() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        _require(_status != _ENTERED, Errors.REENTRANCY);

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _exitNonReentrant() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 13 of 38 : SafeERC20.sol
// SPDX-License-Identifier: MIT

// Based on the ReentrancyGuard library from OpenZeppelin Contracts, altered to reduce gas costs.
// The `safeTransfer` and `safeTransferFrom` functions assume that `token` is a contract (an account with code), and
// work differently from the OpenZeppelin version if it is not.

pragma solidity ^0.7.0;

import "../helpers/BalancerErrors.sol";

import "./IERC20.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(address(token), abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(address(token), abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     *
     * WARNING: `token` is assumed to be a contract: calls to EOAs will *not* revert.
     */
    function _callOptionalReturn(address token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.
        (bool success, bytes memory returndata) = token.call(data);

        // If the low-level call didn't succeed we return whatever was returned from it.
        assembly {
            if eq(success, 0) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }

        // Finally we check the returndata size is either zero or true - note that this check will always pass for EOAs
        _require(returndata.length == 0 || abi.decode(returndata, (bool)), Errors.SAFE_ERC20_CALL_FAILED);
    }
}

File 14 of 38 : BalancerPoolToken.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

import "../lib/math/Math.sol";
import "../lib/openzeppelin/IERC20.sol";
import "../lib/openzeppelin/IERC20Permit.sol";
import "../lib/openzeppelin/EIP712.sol";

/**
 * @title Highly opinionated token implementation
 * @author Balancer Labs
 * @dev
 * - Includes functions to increase and decrease allowance as a workaround
 *   for the well-known issue with `approve`:
 *   https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
 * - Allows for 'infinite allowance', where an allowance of 0xff..ff is not
 *   decreased by calls to transferFrom
 * - Lets a token holder use `transferFrom` to send their own tokens,
 *   without first setting allowance
 * - Emits 'Approval' events whenever allowance is changed by `transferFrom`
 */
contract BalancerPoolToken is IERC20, IERC20Permit, EIP712 {
    using Math for uint256;

    // State variables

    uint8 private constant _DECIMALS = 18;

    mapping(address => uint256) private _balance;
    mapping(address => mapping(address => uint256)) private _allowance;
    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    mapping(address => uint256) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private immutable _PERMIT_TYPE_HASH = keccak256(
        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
    );

    // Function declarations

    constructor(string memory tokenName, string memory tokenSymbol) EIP712(tokenName, "1") {
        _name = tokenName;
        _symbol = tokenSymbol;
    }

    // External functions

    function allowance(address owner, address spender) external view override returns (uint256) {
        return _allowance[owner][spender];
    }

    function balanceOf(address account) external view override returns (uint256) {
        return _balance[account];
    }

    function approve(address spender, uint256 amount) external override returns (bool) {
        _setAllowance(msg.sender, spender, amount);

        return true;
    }

    function increaseApproval(address spender, uint256 amount) external returns (bool) {
        _setAllowance(msg.sender, spender, _allowance[msg.sender][spender].add(amount));

        return true;
    }

    function decreaseApproval(address spender, uint256 amount) external returns (bool) {
        uint256 currentAllowance = _allowance[msg.sender][spender];

        if (amount >= currentAllowance) {
            _setAllowance(msg.sender, spender, 0);
        } else {
            _setAllowance(msg.sender, spender, currentAllowance.sub(amount));
        }

        return true;
    }

    function transfer(address recipient, uint256 amount) external override returns (bool) {
        _move(msg.sender, recipient, amount);

        return true;
    }

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external override returns (bool) {
        uint256 currentAllowance = _allowance[sender][msg.sender];
        _require(msg.sender == sender || currentAllowance >= amount, Errors.INSUFFICIENT_ALLOWANCE);

        _move(sender, recipient, amount);

        if (msg.sender != sender && currentAllowance != uint256(-1)) {
            // Because of the previous require, we know that if msg.sender != sender then currentAllowance >= amount
            _setAllowance(sender, msg.sender, currentAllowance - amount);
        }

        return true;
    }

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        // solhint-disable-next-line not-rely-on-time
        _require(block.timestamp <= deadline, Errors.EXPIRED_PERMIT);

        uint256 nonce = _nonces[owner];

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPE_HASH, owner, spender, value, nonce, deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ecrecover(hash, v, r, s);
        _require((signer != address(0)) && (signer == owner), Errors.INVALID_SIGNATURE);

        _nonces[owner] = nonce + 1;
        _setAllowance(owner, spender, value);
    }

    // Public functions

    function name() public view returns (string memory) {
        return _name;
    }

    function symbol() public view returns (string memory) {
        return _symbol;
    }

    function decimals() public pure returns (uint8) {
        return _DECIMALS;
    }

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function nonces(address owner) external view override returns (uint256) {
        return _nonces[owner];
    }

    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    // Internal functions

    function _mintPoolTokens(address recipient, uint256 amount) internal {
        _balance[recipient] = _balance[recipient].add(amount);
        _totalSupply = _totalSupply.add(amount);
        emit Transfer(address(0), recipient, amount);
    }

    function _burnPoolTokens(address sender, uint256 amount) internal {
        uint256 currentBalance = _balance[sender];
        _require(currentBalance >= amount, Errors.INSUFFICIENT_BALANCE);

        _balance[sender] = currentBalance - amount;
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(sender, address(0), amount);
    }

    function _move(
        address sender,
        address recipient,
        uint256 amount
    ) internal {
        uint256 currentBalance = _balance[sender];
        _require(currentBalance >= amount, Errors.INSUFFICIENT_BALANCE);
        // Prohibit transfers to the zero address to avoid confusion with the
        // Transfer event emitted by `_burnPoolTokens`
        _require(recipient != address(0), Errors.ERC20_TRANSFER_TO_ZERO_ADDRESS);

        _balance[sender] = currentBalance - amount;
        _balance[recipient] = _balance[recipient].add(amount);

        emit Transfer(sender, recipient, amount);
    }

    // Private functions

    function _setAllowance(
        address owner,
        address spender,
        uint256 amount
    ) private {
        _allowance[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

File 15 of 38 : ProtocolFeesCollector.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "../lib/openzeppelin/IERC20.sol";
import "../lib/helpers/InputHelpers.sol";
import "../lib/helpers/Authentication.sol";
import "../lib/openzeppelin/ReentrancyGuard.sol";
import "../lib/openzeppelin/SafeERC20.sol";

import "./interfaces/IVault.sol";
import "./interfaces/IAuthorizer.sol";

/**
 * @dev This an auxiliary contract to the Vault, deployed by it during construction. It offloads some of the tasks the
 * Vault performs to reduce its overall bytecode size.
 *
 * The current values for all protocol fee percentages are stored here, and any tokens charged as protocol fees are
 * sent to this contract, where they may be withdrawn by authorized entities. All authorization tasks are delegated
 * to the Vault's own authorizer.
 */
contract ProtocolFeesCollector is Authentication, ReentrancyGuard {
    using SafeERC20 for IERC20;

    // Absolute maximum fee percentages (1e18 = 100%, 1e16 = 1%).
    uint256 private constant _MAX_PROTOCOL_SWAP_FEE_PERCENTAGE = 50e16; // 50%
    uint256 private constant _MAX_PROTOCOL_FLASH_LOAN_FEE_PERCENTAGE = 1e16; // 1%

    IVault public immutable vault;

    // All fee percentages are 18-decimal fixed point numbers.

    // The swap fee is charged whenever a swap occurs, as a percentage of the fee charged by the Pool. These are not
    // actually charged on each individual swap: the `Vault` relies on the Pools being honest and reporting fees due
    // when users join and exit them.
    uint256 private _swapFeePercentage;

    // The flash loan fee is charged whenever a flash loan occurs, as a percentage of the tokens lent.
    uint256 private _flashLoanFeePercentage;

    event SwapFeePercentageChanged(uint256 newSwapFeePercentage);
    event FlashLoanFeePercentageChanged(uint256 newFlashLoanFeePercentage);

    constructor(IVault _vault)
        // The ProtocolFeesCollector is a singleton, so it simply uses its own address to disambiguate action
        // identifiers.
        Authentication(bytes32(uint256(address(this))))
    {
        vault = _vault;
    }

    function withdrawCollectedFees(
        IERC20[] calldata tokens,
        uint256[] calldata amounts,
        address recipient
    ) external nonReentrant authenticate {
        InputHelpers.ensureInputLengthMatch(tokens.length, amounts.length);

        for (uint256 i = 0; i < tokens.length; ++i) {
            IERC20 token = tokens[i];
            uint256 amount = amounts[i];
            token.safeTransfer(recipient, amount);
        }
    }

    // authenticate disabled for testing close to production
    // function setSwapFeePercentage(uint256 newSwapFeePercentage) external authenticate {
    function setSwapFeePercentage(uint256 newSwapFeePercentage) external {
        _require(newSwapFeePercentage <= _MAX_PROTOCOL_SWAP_FEE_PERCENTAGE, Errors.SWAP_FEE_PERCENTAGE_TOO_HIGH);
        _swapFeePercentage = newSwapFeePercentage;
        emit SwapFeePercentageChanged(newSwapFeePercentage);
    }

    function setFlashLoanFeePercentage(uint256 newFlashLoanFeePercentage) external authenticate {
        _require(
            newFlashLoanFeePercentage <= _MAX_PROTOCOL_FLASH_LOAN_FEE_PERCENTAGE,
            Errors.FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH
        );
        _flashLoanFeePercentage = newFlashLoanFeePercentage;
        emit FlashLoanFeePercentageChanged(newFlashLoanFeePercentage);
    }

    function getSwapFeePercentage() external view returns (uint256) {
        return _swapFeePercentage;
    }

    function getFlashLoanFeePercentage() external view returns (uint256) {
        return _flashLoanFeePercentage;
    }

    function getCollectedFeeAmounts(IERC20[] memory tokens) external view returns (uint256[] memory feeAmounts) {
        feeAmounts = new uint256[](tokens.length);
        for (uint256 i = 0; i < tokens.length; ++i) {
            feeAmounts[i] = tokens[i].balanceOf(address(this));
        }
    }

    function getAuthorizer() external view returns (IAuthorizer) {
        return _getAuthorizer();
    }

    function _canPerform(bytes32 actionId, address account) internal view override returns (bool) {
        return _getAuthorizer().canPerform(actionId, account, address(this));
    }

    function _getAuthorizer() internal view returns (IAuthorizer) {
        return vault.getAuthorizer();
    }
}

File 16 of 38 : IAsset.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

/**
 * @dev This is an empty interface used to represent either ERC20-conforming token contracts or ETH (using the zero
 * address sentinel value). We're just relying on the fact that `interface` can be used to declare new address-like
 * types.
 *
 * This concept is unrelated to a Pool's Asset Managers.
 */
interface IAsset {
    // solhint-disable-previous-line no-empty-blocks
}

File 17 of 38 : IAuthorizer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

interface IAuthorizer {
    /**
     * @dev Returns true if `account` can perform the action described by `actionId` in the contract `where`.
     */
    function canPerform(
        bytes32 actionId,
        address account,
        address where
    ) external view returns (bool);
}

File 18 of 38 : IBasePool.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "./IVault.sol";
import "./IPoolSwapStructs.sol";

/**
 * @dev Interface for adding and removing liquidity that all Pool contracts should implement. Note that this is not
 * the complete Pool contract interface, as it is missing the swap hooks. Pool contracts should also inherit from
 * either IGeneralPool or IMinimalSwapInfoPool
 */
interface IBasePool is IPoolSwapStructs {
    /**
     * @dev Called by the Vault when a user calls `IVault.joinPool` to add liquidity to this Pool. Returns how many of
     * each registered token the user should provide, as well as the amount of protocol fees the Pool owes to the Vault.
     * The Vault will then take tokens from `sender` and add them to the Pool's balances, as well as collect
     * the reported amount in protocol fees, which the pool should calculate based on `protocolSwapFeePercentage`.
     *
     * Protocol fees are reported and charged on join events so that the Pool is free of debt whenever new users join.
     *
     * `sender` is the account performing the join (from which tokens will be withdrawn), and `recipient` is the account
     * designated to receive any benefits (typically pool shares). `currentBalances` contains the total balances
     * for each token the Pool registered in the Vault, in the same order that `IVault.getPoolTokens` would return.
     *
     * `lastChangeBlock` is the last block in which *any* of the Pool's registered tokens last changed its total
     * balance.
     *
     * `userData` contains any pool-specific instructions needed to perform the calculations, such as the type of
     * join (e.g., proportional given an amount of pool shares, single-asset, multi-asset, etc.)
     *
     * Contracts implementing this function should check that the caller is indeed the Vault before performing any
     * state-changing operations, such as minting pool shares.
     */
    function onJoinPool(
        bytes32 poolId,
        address sender,
        address recipient,
        uint256[] memory balances,
        uint256 lastChangeBlock,
        uint256 protocolSwapFeePercentage,
        bytes memory userData
    ) external returns (uint256[] memory amountsIn, uint256[] memory dueProtocolFeeAmounts);

    /**
     * @dev Called by the Vault when a user calls `IVault.exitPool` to remove liquidity from this Pool. Returns how many
     * tokens the Vault should deduct from the Pool's balances, as well as the amount of protocol fees the Pool owes
     * to the Vault. The Vault will then take tokens from the Pool's balances and send them to `recipient`,
     * as well as collect the reported amount in protocol fees, which the Pool should calculate based on
     * `protocolSwapFeePercentage`.
     *
     * Protocol fees are charged on exit events to guarantee that users exiting the Pool have paid their share.
     *
     * `sender` is the account performing the exit (typically the pool shareholder), and `recipient` is the account
     * to which the Vault will send the proceeds. `currentBalances` contains the total token balances for each token
     * the Pool registered in the Vault, in the same order that `IVault.getPoolTokens` would return.
     *
     * `lastChangeBlock` is the last block in which *any* of the Pool's registered tokens last changed its total
     * balance.
     *
     * `userData` contains any pool-specific instructions needed to perform the calculations, such as the type of
     * exit (e.g., proportional given an amount of pool shares, single-asset, multi-asset, etc.)
     *
     * Contracts implementing this function should check that the caller is indeed the Vault before performing any
     * state-changing operations, such as burning pool shares.
     */
    function onExitPool(
        bytes32 poolId,
        address sender,
        address recipient,
        uint256[] memory balances,
        uint256 lastChangeBlock,
        uint256 protocolSwapFeePercentage,
        bytes memory userData
    ) external returns (uint256[] memory amountsOut, uint256[] memory dueProtocolFeeAmounts);
}

File 19 of 38 : IFlashLoanRecipient.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

// Inspired by Aave Protocol's IFlashLoanReceiver.

import "../../lib/openzeppelin/IERC20.sol";

interface IFlashLoanRecipient {
    /**
     * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
     *
     * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
     * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
     * Vault, or else the entire flash loan will revert.
     *
     * `userData` is the same value passed in the `IVault.flashLoan` call.
     */
    function receiveFlashLoan(
        IERC20[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    ) external;
}

File 20 of 38 : IMinimalSwapInfoPool.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "./IBasePool.sol";

/**
 * @dev Pool contracts with the MinimalSwapInfo or TwoToken specialization settings should implement this interface.
 *
 * This is called by the Vault when a user calls `IVault.swap` or `IVault.batchSwap` to swap with this Pool.
 * Returns the number of tokens the Pool will grant to the user in a 'given in' swap, or that the user will grant
 * to the pool in a 'given out' swap.
 *
 * This can often be implemented by a `view` function, since many pricing algorithms don't need to track state
 * changes in swaps. However, contracts implementing this in non-view functions should check that the caller is
 * indeed the Vault.
 */
interface IMinimalSwapInfoPool is IBasePool {
    function onSwap(
        SwapRequest memory swapRequest,
        uint256 currentBalanceTokenIn,
        uint256 currentBalanceTokenOut
    ) external returns (uint256 amount);
}

File 21 of 38 : IPoolSwapStructs.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "../../lib/openzeppelin/IERC20.sol";

import "./IVault.sol";

interface IPoolSwapStructs {
    // This is not really an interface - it just defines common structs used by other interfaces: IGeneralPool and
    // IMinimalSwapInfoPool.
    //
    // This data structure represents a request for a token swap, where `kind` indicates the swap type ('given in' or
    // 'given out') which indicates whether or not the amount sent by the pool is known.
    //
    // The pool receives `tokenIn` and sends `tokenOut`. `amount` is the number of `tokenIn` tokens the pool will take
    // in, or the number of `tokenOut` tokens the Pool will send out, depending on the given swap `kind`.
    //
    // All other fields are not strictly necessary for most swaps, but are provided to support advanced scenarios in
    // some Pools.
    //
    // `poolId` is the ID of the Pool involved in the swap - this is useful for Pool contracts that implement more than
    // one Pool.
    //
    // The meaning of `lastChangeBlock` depends on the Pool specialization:
    //  - Two Token or Minimal Swap Info: the last block in which either `tokenIn` or `tokenOut` changed its total
    //    balance.
    //  - General: the last block in which *any* of the Pool's registered tokens changed its total balance.
    //
    // `from` is the origin address for the funds the Pool receives, and `to` is the destination address
    // where the Pool sends the outgoing tokens.
    //
    // `userData` is extra data provided by the caller - typically a signature from a trusted party.
    struct SwapRequest {
        IVault.SwapKind kind;
        IERC20 tokenIn;
        IERC20 tokenOut;
        uint256 amount;
        // Misc data
        bytes32 poolId;
        uint256 lastChangeBlock;
        address from;
        address to;
        bytes userData;
    }
}

File 22 of 38 : IVault.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma experimental ABIEncoderV2;

import "../../lib/openzeppelin/IERC20.sol";

import "./IWETH.sol";
import "./IAsset.sol";
import "./IAuthorizer.sol";
import "./IFlashLoanRecipient.sol";
import "../ProtocolFeesCollector.sol";

import "../../lib/helpers/ISignaturesValidator.sol";
import "../../lib/helpers/ITemporarilyPausable.sol";

pragma solidity ^0.7.0;

/**
 * @dev Full external interface for the Vault core contract - no external or public methods exist in the contract that
 * don't override one of these declarations.
 */
interface IVault is ISignaturesValidator, ITemporarilyPausable {
    // Generalities about the Vault:
    //
    // - Whenever documentation refers to 'tokens', it strictly refers to ERC20-compliant token contracts. Tokens are
    // transferred out of the Vault by calling the `IERC20.transfer` function, and transferred in by calling
    // `IERC20.transferFrom`. In these cases, the sender must have previously allowed the Vault to use their tokens by
    // calling `IERC20.approve`. The only deviation from the ERC20 standard that is supported is functions not returning
    // a boolean value: in these scenarios, a non-reverting call is assumed to be successful.
    //
    // - All non-view functions in the Vault are non-reentrant: calling them while another one is mid-execution (e.g.
    // while execution control is transferred to a token contract during a swap) will result in a revert. View
    // functions can be called in a re-reentrant way, but doing so might cause them to return inconsistent results.
    // Contracts calling view functions in the Vault must make sure the Vault has not already been entered.
    //
    // - View functions revert if referring to either unregistered Pools, or unregistered tokens for registered Pools.

    // Authorizer
    //
    // Some system actions are permissioned, like setting and collecting protocol fees. This permissioning system exists
    // outside of the Vault in the Authorizer contract: the Vault simply calls the Authorizer to check if the caller
    // can perform a given action.

    /**
     * @dev Returns the Vault's Authorizer.
     */
    function getAuthorizer() external view returns (IAuthorizer);

    /**
     * @dev Sets a new Authorizer for the Vault. The caller must be allowed by the current Authorizer to do this.
     *
     * Emits an `AuthorizerChanged` event.
     */
    function setAuthorizer(IAuthorizer newAuthorizer) external;

    /**
     * @dev Emitted when a new authorizer is set by `setAuthorizer`.
     */
    event AuthorizerChanged(IAuthorizer indexed newAuthorizer);

    // Relayers
    //
    // Additionally, it is possible for an account to perform certain actions on behalf of another one, using their
    // Vault ERC20 allowance and Internal Balance. These accounts are said to be 'relayers' for these Vault functions,
    // and are expected to be smart contracts with sound authentication mechanisms. For an account to be able to wield
    // this power, two things must occur:
    //  - The Authorizer must grant the account the permission to be a relayer for the relevant Vault function. This
    //    means that Balancer governance must approve each individual contract to act as a relayer for the intended
    //    functions.
    //  - Each user must approve the relayer to act on their behalf.
    // This double protection means users cannot be tricked into approving malicious relayers (because they will not
    // have been allowed by the Authorizer via governance), nor can malicious relayers approved by a compromised
    // Authorizer or governance drain user funds, since they would also need to be approved by each individual user.

    /**
     * @dev Returns true if `user` has approved `relayer` to act as a relayer for them.
     */
    function hasApprovedRelayer(address user, address relayer) external view returns (bool);

    /**
     * @dev Allows `relayer` to act as a relayer for `sender` if `approved` is true, and disallows it otherwise.
     *
     * Emits a `RelayerApprovalChanged` event.
     */
    function setRelayerApproval(
        address sender,
        address relayer,
        bool approved
    ) external;

    /**
     * @dev Emitted every time a relayer is approved or disapproved by `setRelayerApproval`.
     */
    event RelayerApprovalChanged(address indexed relayer, address indexed sender, bool approved);

    // Internal Balance
    //
    // Users can deposit tokens into the Vault, where they are allocated to their Internal Balance, and later
    // transferred or withdrawn. It can also be used as a source of tokens when joining Pools, as a destination
    // when exiting them, and as either when performing swaps. This usage of Internal Balance results in greatly reduced
    // gas costs when compared to relying on plain ERC20 transfers, leading to large savings for frequent users.
    //
    // Internal Balance management features batching, which means a single contract call can be used to perform multiple
    // operations of different kinds, with different senders and recipients, at once.

    /**
     * @dev Returns `user`'s Internal Balance for a set of tokens.
     */
    function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory);

    /**
     * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)
     * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as
     * it lets integrators reuse a user's Vault allowance.
     *
     * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.
     */
    function manageUserBalance(UserBalanceOp[] memory ops) external payable;

    /**
     * @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received
     without manual WETH wrapping or unwrapping.
     */
    struct UserBalanceOp {
        UserBalanceOpKind kind;
        IAsset asset;
        uint256 amount;
        address sender;
        address payable recipient;
    }

    // There are four possible operations in `manageUserBalance`:
    //
    // - DEPOSIT_INTERNAL
    // Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding
    // `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`.
    //
    // ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped
    // and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is
    // relevant for relayers).
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - WITHDRAW_INTERNAL
    // Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`.
    //
    // ETH can be used by passing the ETH sentinel value as the asset. This will deduct WETH instead, unwrap it and send
    // it to the recipient as ETH.
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - TRANSFER_INTERNAL
    // Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`.
    //
    // Reverts if the ETH sentinel value is passed.
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - TRANSFER_EXTERNAL
    // Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by
    // relayers, as it lets them reuse a user's Vault allowance.
    //
    // Reverts if the ETH sentinel value is passed.
    //
    // Emits an `ExternalBalanceTransfer` event.

    enum UserBalanceOpKind { DEPOSIT_INTERNAL, WITHDRAW_INTERNAL, TRANSFER_INTERNAL, TRANSFER_EXTERNAL }

    /**
     * @dev Emitted when a user's Internal Balance changes, either from calls to `manageUserBalance`, or through
     * interacting with Pools using Internal Balance.
     *
     * Because Internal Balance works exclusively with ERC20 tokens, ETH deposits and withdrawals will use the WETH
     * address.
     */
    event InternalBalanceChanged(address indexed user, IERC20 indexed token, int256 delta);

    /**
     * @dev Emitted when a user's Vault ERC20 allowance is used by the Vault to transfer tokens to an external account.
     */
    event ExternalBalanceTransfer(IERC20 indexed token, address indexed sender, address recipient, uint256 amount);

    // Pools
    //
    // There are three specialization settings for Pools, which allow for cheaper swaps at the cost of reduced
    // functionality:
    //
    //  - General: no specialization, suited for all Pools. IGeneralPool is used for swap request callbacks, passing the
    // balance of all tokens in the Pool. These Pools have the largest swap costs (because of the extra storage reads),
    // which increase with the number of registered tokens.
    //
    //  - Minimal Swap Info: IMinimalSwapInfoPool is used instead of IGeneralPool, which saves gas by only passing the
    // balance of the two tokens involved in the swap. This is suitable for some pricing algorithms, like the weighted
    // constant product one popularized by Balancer V1. Swap costs are smaller compared to general Pools, and are
    // independent of the number of registered tokens.
    //
    //  - Two Token: only allows two tokens to be registered. This achieves the lowest possible swap gas cost. Like
    // minimal swap info Pools, these are called via IMinimalSwapInfoPool.

    enum PoolSpecialization { GENERAL, MINIMAL_SWAP_INFO, TWO_TOKEN }

    /**
     * @dev Registers the caller account as a Pool with a given specialization setting. Returns the Pool's ID, which
     * is used in all Pool-related functions. Pools cannot be deregistered, nor can the Pool's specialization be
     * changed.
     *
     * The caller is expected to be a smart contract that implements either `IGeneralPool` or `IMinimalSwapInfoPool`,
     * depending on the chosen specialization setting. This contract is known as the Pool's contract.
     *
     * Note that the same contract may register itself as multiple Pools with unique Pool IDs, or in other words,
     * multiple Pools may share the same contract.
     *
     * Emits a `PoolRegistered` event.
     */
    function registerPool(PoolSpecialization specialization) external returns (bytes32);

    /**
     * @dev Emitted when a Pool is registered by calling `registerPool`.
     */
    event PoolRegistered(bytes32 indexed poolId, address indexed poolAddress, PoolSpecialization specialization);

    /**
     * @dev Returns a Pool's contract address and specialization setting.
     */
    function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);

    /**
     * @dev Registers `tokens` for the `poolId` Pool. Must be called by the Pool's contract.
     *
     * Pools can only interact with tokens they have registered. Users join a Pool by transferring registered tokens,
     * exit by receiving registered tokens, and can only swap registered tokens.
     *
     * Each token can only be registered once. For Pools with the Two Token specialization, `tokens` must have a length
     * of two, that is, both tokens must be registered in the same `registerTokens` call, and they must be sorted in
     * ascending order.
     *
     * The `tokens` and `assetManagers` arrays must have the same length, and each entry in these indicates the Asset
     * Manager for the corresponding token. Asset Managers can manage a Pool's tokens via `managePoolBalance`,
     * depositing and withdrawing them directly, and can even set their balance to arbitrary amounts. They are therefore
     * expected to be highly secured smart contracts with sound design principles, and the decision to register an
     * Asset Manager should not be made lightly.
     *
     * Pools can choose not to assign an Asset Manager to a given token by passing in the zero address. Once an Asset
     * Manager is set, it cannot be changed except by deregistering the associated token and registering again with a
     * different Asset Manager.
     *
     * Emits a `TokensRegistered` event.
     */
    function registerTokens(
        bytes32 poolId,
        IERC20[] memory tokens,
        address[] memory assetManagers
    ) external;

    /**
     * @dev Emitted when a Pool registers tokens by calling `registerTokens`.
     */
    event TokensRegistered(bytes32 indexed poolId, IERC20[] tokens, address[] assetManagers);

    /**
     * @dev Deregisters `tokens` for the `poolId` Pool. Must be called by the Pool's contract.
     *
     * Only registered tokens (via `registerTokens`) can be deregistered. Additionally, they must have zero total
     * balance. For Pools with the Two Token specialization, `tokens` must have a length of two, that is, both tokens
     * must be deregistered in the same `deregisterTokens` call.
     *
     * A deregistered token can be re-registered later on, possibly with a different Asset Manager.
     *
     * Emits a `TokensDeregistered` event.
     */
    function deregisterTokens(bytes32 poolId, IERC20[] memory tokens) external;

    /**
     * @dev Emitted when a Pool deregisters tokens by calling `deregisterTokens`.
     */
    event TokensDeregistered(bytes32 indexed poolId, IERC20[] tokens);

    /**
     * @dev Returns detailed information for a Pool's registered token.
     *
     * `cash` is the number of tokens the Vault currently holds for the Pool. `managed` is the number of tokens
     * withdrawn and held outside the Vault by the Pool's token Asset Manager. The Pool's total balance for `token`
     * equals the sum of `cash` and `managed`.
     *
     * Internally, `cash` and `managed` are stored using 112 bits. No action can ever cause a Pool's token `cash`,
     * `managed` or `total` balance to be greater than 2^112 - 1.
     *
     * `lastChangeBlock` is the number of the block in which `token`'s total balance was last modified (via either a
     * join, exit, swap, or Asset Manager update). This value is useful to avoid so-called 'sandwich attacks', for
     * example when developing price oracles. A change of zero (e.g. caused by a swap with amount zero) is considered a
     * change for this purpose, and will update `lastChangeBlock`.
     *
     * `assetManager` is the Pool's token Asset Manager.
     */
    function getPoolTokenInfo(bytes32 poolId, IERC20 token)
        external
        view
        returns (
            uint256 cash,
            uint256 managed,
            uint256 lastChangeBlock,
            address assetManager
        );

    /**
     * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of
     * the tokens' `balances` changed.
     *
     * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all
     * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.
     *
     * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same
     * order as passed to `registerTokens`.
     *
     * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are
     * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`
     * instead.
     */
    function getPoolTokens(bytes32 poolId)
        external
        view
        returns (
            IERC20[] memory tokens,
            uint256[] memory balances,
            uint256 lastChangeBlock
        );

    /**
     * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will
     * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized
     * Pool shares.
     *
     * If the caller is not `sender`, it must be an authorized relayer for them.
     *
     * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount
     * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces
     * these maximums.
     *
     * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable
     * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the
     * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent
     * back to the caller (not the sender, which is important for relayers).
     *
     * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when
     * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be
     * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final
     * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.
     *
     * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only
     * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be
     * withdrawn from Internal Balance: attempting to do so will trigger a revert.
     *
     * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement
     * their own custom logic. This typically requires additional information from the user (such as the expected number
     * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed
     * directly to the Pool's contract, as is `recipient`.
     *
     * Emits a `PoolBalanceChanged` event.
     */
    function joinPool(
        bytes32 poolId,
        address sender,
        address recipient,
        JoinPoolRequest memory request
    ) external payable;

    struct JoinPoolRequest {
        IAsset[] assets;
        uint256[] maxAmountsIn;
        bytes userData;
        bool fromInternalBalance;
    }

    /**
     * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will
     * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized
     * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see
     * `getPoolTokenInfo`).
     *
     * If the caller is not `sender`, it must be an authorized relayer for them.
     *
     * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum
     * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:
     * it just enforces these minimums.
     *
     * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To
     * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead
     * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.
     *
     * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when
     * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must
     * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the
     * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.
     *
     * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,
     * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to
     * do so will trigger a revert.
     *
     * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the
     * `tokens` array. This array must match the Pool's registered tokens.
     *
     * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement
     * their own custom logic. This typically requires additional information from the user (such as the expected number
     * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and
     * passed directly to the Pool's contract.
     *
     * Emits a `PoolBalanceChanged` event.
     */
    function exitPool(
        bytes32 poolId,
        address sender,
        address payable recipient,
        ExitPoolRequest memory request
    ) external;

    struct ExitPoolRequest {
        IAsset[] assets;
        uint256[] minAmountsOut;
        bytes userData;
        bool toInternalBalance;
    }

    /**
     * @dev Emitted when a user joins or exits a Pool by calling `joinPool` or `exitPool`, respectively.
     */
    event PoolBalanceChanged(
        bytes32 indexed poolId,
        address indexed liquidityProvider,
        IERC20[] tokens,
        int256[] deltas,
        uint256[] protocolFeeAmounts
    );

    enum PoolBalanceChangeKind { JOIN, EXIT }

    // Swaps
    //
    // Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this,
    // they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be
    // aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote.
    //
    // The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence.
    // In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'),
    // and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out').
    // More complex swaps, such as one token in to multiple tokens out can be achieved by batching together
    // individual swaps.
    //
    // There are two swap kinds:
    //  - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the
    // `onSwap` hook) the amount of tokens out (to send to the recipient).
    //  - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines
    // (via the `onSwap` hook) the amount of tokens in (to receive from the sender).
    //
    // Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with
    // the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated
    // tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended
    // swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at
    // the final intended token.
    //
    // In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal
    // Balance) after all individual swaps have been completed, and the net token balance change computed. This makes
    // certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost
    // much less gas than they would otherwise.
    //
    // It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple
    // Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only
    // updating the Pool's internal accounting).
    //
    // To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token
    // involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the
    // minimum amount of tokens to receive (by passing a negative value) is specified.
    //
    // Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after
    // this point in time (e.g. if the transaction failed to be included in a block promptly).
    //
    // If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do
    // the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be
    // passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the
    // same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers).
    //
    // Finally, Internal Balance can be used when either sending or receiving tokens.

    enum SwapKind { GIVEN_IN, GIVEN_OUT }

    /**
     * @dev Performs a swap with a single Pool.
     *
     * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens
     * taken from the Pool, which must be greater than or equal to `limit`.
     *
     * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens
     * sent to the Pool, which must be less than or equal to `limit`.
     *
     * Internal Balance usage and the recipient are determined by the `funds` struct.
     *
     * Emits a `Swap` event.
     */
    function swap(
        SingleSwap memory singleSwap,
        FundManagement memory funds,
        uint256 limit,
        uint256 deadline
    ) external payable returns (uint256);

    /**
     * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on
     * the `kind` value.
     *
     * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).
     * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.
     *
     * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
     * used to extend swap behavior.
     */
    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        IAsset assetIn;
        IAsset assetOut;
        uint256 amount;
        bytes userData;
    }

    /**
     * @dev Performs a series of swaps with one or multiple Pools. In each individual swap, the caller determines either
     * the amount of tokens sent to or received from the Pool, depending on the `kind` value.
     *
     * Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the
     * Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at
     * the same index in the `assets` array.
     *
     * Swaps are executed sequentially, in the order specified by the `swaps` array. Each array element describes a
     * Pool, the token to be sent to this Pool, the token to receive from it, and an amount that is either `amountIn` or
     * `amountOut` depending on the swap kind.
     *
     * Multihop swaps can be executed by passing an `amount` value of zero for a swap. This will cause the amount in/out
     * of the previous swap to be used as the amount in for the current one. In a 'given in' swap, 'tokenIn' must equal
     * the previous swap's `tokenOut`. For a 'given out' swap, `tokenOut` must equal the previous swap's `tokenIn`.
     *
     * The `assets` array contains the addresses of all assets involved in the swaps. These are either token addresses,
     * or the IAsset sentinel value for ETH (the zero address). Each entry in the `swaps` array specifies tokens in and
     * out by referencing an index in `assets`. Note that Pools never interact with ETH directly: it will be wrapped to
     * or unwrapped from WETH by the Vault.
     *
     * Internal Balance usage, sender, and recipient are determined by the `funds` struct. The `limits` array specifies
     * the minimum or maximum amount of each token the vault is allowed to transfer.
     *
     * `batchSwap` can be used to make a single swap, like `swap` does, but doing so requires more gas than the
     * equivalent `swap` call.
     *
     * Emits `Swap` events.
     */
    function batchSwap(
        SwapKind kind,
        BatchSwapStep[] memory swaps,
        IAsset[] memory assets,
        FundManagement memory funds,
        int256[] memory limits,
        uint256 deadline
    ) external payable returns (int256[] memory);

    /**
     * @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the
     * `assets` array passed to that function, and ETH assets are converted to WETH.
     *
     * If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out
     * from the previous swap, depending on the swap kind.
     *
     * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
     * used to extend swap behavior.
     */
    struct BatchSwapStep {
        bytes32 poolId;
        uint256 assetInIndex;
        uint256 assetOutIndex;
        uint256 amount;
        bytes userData;
    }

    /**
     * @dev Emitted for each individual swap performed by `swap` or `batchSwap`.
     */
    event Swap(
        bytes32 indexed poolId,
        IERC20 indexed tokenIn,
        IERC20 indexed tokenOut,
        uint256 amountIn,
        uint256 amountOut
    );

    /**
     * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the
     * `recipient` account.
     *
     * If the caller is not `sender`, it must be an authorized relayer for them.
     *
     * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20
     * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender`
     * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of
     * `joinPool`.
     *
     * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of
     * transferred. This matches the behavior of `exitPool`.
     *
     * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a
     * revert.
     */
    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }

    /**
     * @dev Simulates a call to `batchSwap`, returning an array of Vault asset deltas. Calls to `swap` cannot be
     * simulated directly, but an equivalent `batchSwap` call can and will yield the exact same result.
     *
     * Each element in the array corresponds to the asset at the same index, and indicates the number of tokens (or ETH)
     * the Vault would take from the sender (if positive) or send to the recipient (if negative). The arguments it
     * receives are the same that an equivalent `batchSwap` call would receive.
     *
     * Unlike `batchSwap`, this function performs no checks on the sender or recipient field in the `funds` struct.
     * This makes it suitable to be called by off-chain applications via eth_call without needing to hold tokens,
     * approve them for the Vault, or even know a user's address.
     *
     * Note that this function is not 'view' (due to implementation details): the client code must explicitly execute
     * eth_call instead of eth_sendTransaction.
     */
    function queryBatchSwap(
        SwapKind kind,
        BatchSwapStep[] memory swaps,
        IAsset[] memory assets,
        FundManagement memory funds
    ) external returns (int256[] memory assetDeltas);

    // Flash Loans

    /**
     * @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it,
     * and then reverting unless the tokens plus a proportional protocol fee have been returned.
     *
     * The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount
     * for each token contract. `tokens` must be sorted in ascending order.
     *
     * The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the
     * `receiveFlashLoan` call.
     *
     * Emits `FlashLoan` events.
     */
    function flashLoan(
        IFlashLoanRecipient recipient,
        IERC20[] memory tokens,
        uint256[] memory amounts,
        bytes memory userData
    ) external;

    /**
     * @dev Emitted for each individual flash loan performed by `flashLoan`.
     */
    event FlashLoan(IFlashLoanRecipient indexed recipient, IERC20 indexed token, uint256 amount, uint256 feeAmount);

    // Asset Management
    //
    // Each token registered for a Pool can be assigned an Asset Manager, which is able to freely withdraw the Pool's
    // tokens from the Vault, deposit them, or assign arbitrary values to its `managed` balance (see
    // `getPoolTokenInfo`). This makes them extremely powerful and dangerous. Even if an Asset Manager only directly
    // controls one of the tokens in a Pool, a malicious manager could set that token's balance to manipulate the
    // prices of the other tokens, and then drain the Pool with swaps. The risk of using Asset Managers is therefore
    // not constrained to the tokens they are managing, but extends to the entire Pool's holdings.
    //
    // However, a properly designed Asset Manager smart contract can be safely used for the Pool's benefit,
    // for example by lending unused tokens out for interest, or using them to participate in voting protocols.
    //
    // This concept is unrelated to the IAsset interface.

    /**
     * @dev Performs a set of Pool balance operations, which may be either withdrawals, deposits or updates.
     *
     * Pool Balance management features batching, which means a single contract call can be used to perform multiple
     * operations of different kinds, with different Pools and tokens, at once.
     *
     * For each operation, the caller must be registered as the Asset Manager for `token` in `poolId`.
     */
    function managePoolBalance(PoolBalanceOp[] memory ops) external;

    struct PoolBalanceOp {
        PoolBalanceOpKind kind;
        bytes32 poolId;
        IERC20 token;
        uint256 amount;
    }

    /**
     * Withdrawals decrease the Pool's cash, but increase its managed balance, leaving the total balance unchanged.
     *
     * Deposits increase the Pool's cash, but decrease its managed balance, leaving the total balance unchanged.
     *
     * Updates don't affect the Pool's cash balance, but because the managed balance changes, it does alter the total.
     * The external amount can be either increased or decreased by this call (i.e., reporting a gain or a loss).
     */
    enum PoolBalanceOpKind { WITHDRAW, DEPOSIT, UPDATE }

    /**
     * @dev Emitted when a Pool's token Asset Manager alters its balance via `managePoolBalance`.
     */
    event PoolBalanceManaged(
        bytes32 indexed poolId,
        address indexed assetManager,
        IERC20 indexed token,
        int256 cashDelta,
        int256 managedDelta
    );

    // Protocol Fees
    //
    // Some operations cause the Vault to collect tokens in the form of protocol fees, which can then be withdrawn by
    // permissioned accounts.
    //
    // There are two kinds of protocol fees:
    //
    //  - flash loan fees: charged on all flash loans, as a percentage of the amounts lent.
    //
    //  - swap fees: a percentage of the fees charged by Pools when performing swaps. For a number of reasons, including
    // swap gas costs and interface simplicity, protocol swap fees are not charged on each individual swap. Rather,
    // Pools are expected to keep track of how much they have charged in swap fees, and pay any outstanding debts to the
    // Vault when they are joined or exited. This prevents users from joining a Pool with unpaid debt, as well as
    // exiting a Pool in debt without first paying their share.

    /**
     * @dev Returns the current protocol fee module.
     */
    function getProtocolFeesCollector() external view returns (ProtocolFeesCollector);

    /**
     * @dev Safety mechanism to pause most Vault operations in the event of an emergency - typically detection of an
     * error in some part of the system.
     *
     * The Vault can only be paused during an initial time period, after which pausing is forever disabled.
     *
     * While the contract is paused, the following features are disabled:
     * - depositing and transferring internal balance
     * - transferring external balance (using the Vault's allowance)
     * - swaps
     * - joining Pools
     * - Asset Manager interactions
     *
     * Internal Balance can still be withdrawn, and Pools exited.
     */
    function setPaused(bool paused) external;

    /**
     * @dev Returns the Vault's WETH instance.
     */
    function WETH() external view returns (IWETH);
    // solhint-disable-previous-line func-name-mixedcase
}

File 23 of 38 : IWETH.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

import "../../lib/openzeppelin/IERC20.sol";

/**
 * @dev Interface for the WETH token contract used internally for wrapping and unwrapping, to support
 * sending and receiving ETH in joins, swaps, and internal balance deposits and withdrawals.
 */
interface IWETH is IERC20 {
    function deposit() external payable;

    function withdraw(uint256 amount) external;
}

File 24 of 38 : IArbitrageurList.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.7.6;

/// @notice Interface for managing list of addresses permitted to perform preferred rate
///         arbitrage swaps on Cron-Fi TWAMM V1.0.
///
interface IArbitrageurList {
  /// @param sender is the address that called the function changing list owner permissions.
  /// @param listOwner is the address to change list owner permissions on.
  /// @param permission is true if the address specified in listOwner is granted list owner
  ///        permissions. Is false otherwise.
  ///
  event ListOwnerPermissions(address indexed sender, address indexed listOwner, bool indexed permission);

  /// @param sender is the address that called the function changing arbitrageur permissions.
  /// @param arbitrageurs is a list of addresses to change arbitrage permissions on.
  /// @param permission is true if the addresses specified in arbitrageurs is granted
  ///        arbitrage permissions. Is false otherwise.
  ///
  event ArbitrageurPermissions(address indexed sender, address[] arbitrageurs, bool indexed permission);

  /// @param sender is the address that called the function changing the next list address.
  /// @param nextListAddress is the address the return value of the nextList function is set to.
  ///
  event NextList(address indexed sender, address indexed nextListAddress);

  /// @notice Returns true if the provide address is permitted the preferred
  ///         arbitrage rate in the partner swap method of a Cron-Fi TWAMM pool.
  ///         Returns false otherwise.
  /// @param _address the address to check for arbitrage rate permissions.
  ///
  function isArbitrageur(address _address) external returns (bool);

  /// @notice Returns the address of the next contract implementing the next list of arbitrageurs.
  ///         If the return value is the NULL address, address(0), then the TWAMM contract's update
  ///         list method will keep the existing address it is storing to check for arbitrage permissions.
  ///
  function nextList() external returns (address);
}

File 25 of 38 : ICronV1Pool.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

pragma experimental ABIEncoderV2;

import { ICronV1FactoryOwnerActions } from "./pool/ICronV1FactoryOwnerActions.sol";
import { ICronV1PoolAdminActions } from "./pool/ICronV1PoolAdminActions.sol";
import { ICronV1PoolArbitrageurActions } from "./pool/ICronV1PoolArbitrageurActions.sol";
import { ICronV1PoolEnums } from "./pool/ICronV1PoolEnums.sol";
import { ICronV1PoolEvents } from "./pool/ICronV1PoolEvents.sol";
import { ICronV1PoolHelpers } from "./pool/ICronV1PoolHelpers.sol";
import { IERC20 } from "../balancer-core-v2/lib/openzeppelin/IERC20.sol";

interface ICronV1Pool is
  ICronV1FactoryOwnerActions,
  ICronV1PoolAdminActions,
  ICronV1PoolArbitrageurActions,
  ICronV1PoolEnums,
  ICronV1PoolEvents,
  ICronV1PoolHelpers,
  IERC20
{}

File 26 of 38 : ICronV1PoolFactory.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.7.6;

import { ICronV1Pool } from "../interfaces/ICronV1Pool.sol";

interface ICronV1PoolFactory {
  /// @notice This event tracks pool creations from this factory
  /// @param pool the address of the pool
  /// @param token0 The token 0 in this pool
  /// @param token1 The token 1 in this pool
  /// @param poolType The poolType set for this pool
  event CronV1PoolCreated(
    address indexed pool,
    address indexed token0,
    address indexed token1,
    ICronV1Pool.PoolType poolType
  );

  /// @notice This event tracks pool being set from this factory
  /// @param pool the address of the pool
  /// @param token0 The token 0 in this pool
  /// @param token1 The token 1 in this pool
  /// @param poolType The poolType set for this pool
  event CronV1PoolSet(
    address indexed pool,
    address indexed token0,
    address indexed token1,
    ICronV1Pool.PoolType poolType
  );

  /// @notice This event tracks pool deletions from this factory
  /// @param pool the address of the pool
  /// @param token0 The token 0 in this pool
  /// @param token1 The token 1 in this pool
  /// @param poolType The poolType set for this pool
  event CronV1PoolRemoved(
    address indexed pool,
    address indexed token0,
    address indexed token1,
    ICronV1Pool.PoolType poolType
  );

  /// @notice This event tracks pool creations from this factory
  /// @param oldAdmin the address of the previous admin
  /// @param newAdmin the address of the new admin
  event OwnerChanged(address indexed oldAdmin, address indexed newAdmin);

  // Functions
  function create(
    address _token0,
    address _token1,
    string memory _name,
    string memory _symbol,
    uint256 _poolType
  ) external returns (address);

  function set(
    address _token0,
    address _token1,
    uint256 _poolType,
    address _pool
  ) external;

  function remove(
    address _token0,
    address _token1,
    uint256 _poolType
  ) external;

  function transferOwnership(
    address _newOwner,
    bool _direct,
    bool _renounce
  ) external;

  function claimOwnership() external;

  function owner() external view returns (address);

  function pendingOwner() external view returns (address);

  function getPool(
    address _token0,
    address _token1,
    uint256 _poolType
  ) external view returns (address pool);
}

File 27 of 38 : IERC20Decimals.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.7.6;

import { IERC20 } from "../balancer-core-v2/lib/openzeppelin/IERC20.sol";

interface IERC20Decimals is IERC20 {
  // Non standard but almost all erc20 have this
  function decimals() external view returns (uint8);
}

File 28 of 38 : Structs.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.7.6;

/// @dev Conventions in the methods, variables and constants are as follows:
///
///      Prefixes:
///
///      - In constants, the prefix "Sn", where 1 <= n <= 4, denotes which slot the constant
///        pertains too. There are four storage slots that are bitpacked. For example,
///        "S2_OFFSET_ORACLE_TIMESTAMP" refers to the offset of the oracle timestamp in bit-
///        packed storage slot 2.
///
///      Suffixes:
///
///      - The suffix of a variable name denotes the type contained within the variable.
///        For instance "uint256 _incrementU96" is a 256-bit unsigned container representing
///        the 96-bit value "_increment".
///        In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is
///        representing a 19 digit decimal value with 18 fractional digits. In this scenario,
///        the D=Decimal, U=Unsigned, F=Fractional.
///        Finally, "uint128 valueU128F64" is a 128-bit container representing a 128-bit value
///        with 64 fractional bits.
///
///      - The suffix of a function name denotes what slot it is proprietary too as a
///        matter of convention. While unchecked at run-time or by the compiler, the naming
///        convention easily aids in understanding what slot a packed value is stored within.
///        For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If
///        the value of slot 2 were passed to this method, the unpacked value would be
///        incorrect.

//
// Structs Related to Virtual Orders
////////////////////////////////////////////////////////////////////////////////

/// @notice Virtual Order details for a single user's Long-Term (LT) swap. An LT swap from
///         Token0 to Token1 is described as a user selling Token0 to the pool to buy Token1
///         from the pool.  Vice-versa if the swap is from Token1 to Token0.
/// @member token0To1 Swap direction, true swapping Token0 for Token1. False otherwise.
/// @member salesRate Amount of token sold to the pool per block for LT swap duration.
/// @member scaledProceedsAtSubmissionU128 The normalized proceeds of the pool for the token
///                                        being purchased at the block the order is
///                                        submitted. For example, for an LT swap of Token0
///                                        for Token1, this value would be the normalized
///                                        proceeds of Token1 for the pool. The normalized
///                                        value is also scaled for precision reasons.
///                                        Min. = 0, Max. = (2**128) - 1
/// @member owner The address issuing the LT swap virtual order; exclusively able to cancel or
///               withdraw the order.
/// @member delegate Is an address that is able to withdraw or cancel the LT swap on behalf
///                  of owner account, as long as the recipient specified is the owner
///                  account.
/// @member orderExpiry is the block in which this order expires.
struct Order {
  bool token0To1;
  uint112 salesRate;
  uint128 scaledProceedsAtSubmissionU128;
  address owner;
  address delegate;
  uint256 orderExpiry;
}

/// @notice This struct abstracts two order pools representing pooled Long-Term (LT) swaps in
///         each swap direction along with the current proceeds and a mapping of the sales
///         rate of each token at the end of a block. This allows the grouping of swaps in
///         the two swap directions for gas efficient execution when virutal orders are
///         executed. It is an adaptation of the staking algorithm desribed here:
///         - https://uploads-ssl.webflow.com/5ad71ffeb79acc67c8bcdaba/5ad8d1193a40977462982470_scalable-reward-distribution-paper.pdf
/// @member currentSalesRates stores the current sales rate of both Token0 and Token1 per block
///                           as 112-bit numbers packed into the 256-bit container. Token0
///                           occupies bits 224 downto 113 and Token1 bits 112 downto 1.
/// @member scaledProceeds stores the normalized, scaled, proceeds of each order pool together as
///                        128-bit numbers packed into the 256-bit container. Scaled proceeds0
///                        occupies bits 256 downto 129 and scaled proceeds1 occupies
///                        bits 128 downto 1.
///                        WARNING: Scaled proceeds0 and scaled proceeds1 described above are
///                        not the proceeds of Token0 and Token1 as would be expected, but rather
///                        the proceeds of order pool 0 and order pool 1 respectively. This means
///                        that scaled proceeds0 is actually the amount of Token1 obtained for
///                        users selling Token0 to the pool and vice-versa for proceeds1.
/// @member salesRateEndingPerBlock is a mapping of a block number to the sales rates of Token0
///                                 and Token1 expiring on that block number for each order pool.
///                                 The 112-bit sales rates are stored in a single 256-bit slot
///                                 together for efficiency. The sales rate for Token0 occupies
///                                 bits 224 downto 113 while the sales rate for Token1 occupies
///                                 bits 112 townto 1.
///
struct OrderPools {
  uint256 currentSalesRates;
  uint256 scaledProceeds;
  mapping(uint256 => uint256) salesRatesEndingPerBlock;
}

/// @notice This struct contains the order pool data for virtual orders comprising of sales of
///         Token0 for Token1 and vice-versa over multiple blocks. Each order pool is stored
///         herein, tracking the current sales rates and proceeds along with expiring sales
///         rates.
///         This struct also stores the scaled proceeds at each block, allowing an individual
///         user's proceeds to be calculated for a given interval. Each user's order is stored
///         with a mapping to their order id and the most recently executed virtual order block
///         and next order id are also stored herein.
/// @member orderPools is a struct containing the sale rate and proceeds for each of the two
///                    order pools along with expiring orders sales rates mapped by block.
/// @member scaledProceedsAtBlock is a mapping of a block number to the normalized, scaled,
///                               proceeds of each order pool together as 128-bit numbers packed
///                               into the 256-bit container. Scaled proceeds0 occupies
///                               bits 256 downto 129 and scaled proceeds1 occupies
///                               bits 128 downto 1.
///                               WARNING: Scaled proceeds0 and scaled proceeds1 described above are
///                               not the proceeds of Token0 and Token1 as would be expected, but rather
///                               the proceeds of order pool 0 and order pool 1 respectively. This means
///                               that scaled proceeds0 is actually the amount of Token1 obtained for
///                               users selling Token0 to the pool and vice-versa for proceeds1.
/// @dev The values contained in scaledProceedsAtBlock are always increasing and are expected to
///      overflow. Their difference when measured between two blocks, determines the proceeds in a
///      particular time-interval. A user's sales rate multiplying that amount determines the user's
///      share of the proceeds (scaledProceeds are normalized by the total sales rate and scaled up for
///      maintaining precision). The subtraction of the two points is also expected to underflow.
/// @member orderMap maps a particular order id to information about that order.
/// @member lastVirtualOrderBlock The ethereum block number before the last virtual orders were executed.
/// @member nextOrderId Is the next order id issued when a user places a Long-Term swap virtual order.
///
struct VirtualOrders {
  OrderPools orderPools;
  mapping(uint256 => uint256) scaledProceedsAtBlock;
  mapping(uint256 => Order) orderMap;
  uint256 lastVirtualOrderBlock;
  uint256 nextOrderId;
}

//
// Structs Related to Other Pool Features
////////////////////////////////////////////////////////////////////////////////

/// @notice The cumulative prices of Token0 and Token1 as of the start of the
///         last executed block (the timestamp of which can be fetched using
///         getOracleTimeStamp).
/// @member token0U256F112 The cumulative price of Token0 measured in amount of
///                        Token1 seconds.
/// @member token1U256F112 The cumulative price of Token1 measured in amount of
///                        Token0 seconds.
/// @dev These values have 112 fractional bits and are expected to overflow.
///      Behavior is identical to the price oracle introduced in Uniswap V2 with
///      similar limitations and vulnerabilities.
/// @dev The average price over an interval can be obtained by sampling these
///      values and their measurement times (see getOracleTimeStamp) and
///      computing the difference over the given interval.
struct PriceOracle {
  uint256 token0U256F112;
  uint256 token1U256F112;
}

//
// Structs for Gas Efficiency / Stack Depth Limitations
////////////////////////////////////////////////////////////////////////////////

/// @notice Struct for executing virtual orders across functions efficiently.
/// @member token0ReserveU112 reserves of Token0 in the TWAMM pool.
/// @member token1ReserveU112 reserves of Token1 in the TWAMM pool.
/// @member lpFeeU60 This is the portion of fees to be distributed to Liquidity Providers
///                  (LPs) after Balancer's portion is collected. The portioning is based
///                  on fractions of 10**18 and the value is computed by subtracting
///                  Balancer's portion from 10**18. If Cron-Fi fees are being collected
///                  this value is used to compute the fee share, feeShareU60.
/// @member feeShareU60 If Cron-Fi fees are being collected, this amount represents a
///                     single share of the fees remaining after Balancer's portion. A
///                     single share goes to Cron-Fi and multiples of a single share go
///                     to the Liquidity Providers (LPs) based on the fee shift value,
///                     feeShiftU3.
/// @member feeShiftU3 If Cron-Fi fees are being collected, this represents the amount of
///                    bits shifted to partition fees between Liquidity Providers (LPs)
///                    and Cron-Fi. For example, if this is 1, then 2 shares of fees
///                    collected after Balancer's portion go to the LPs and 1 share goes
///                    to Cron-Fi. If it is 2, then 4 shares go to the LPs and 1 share
///                    goes to Cron-Fi.
/// @member orderPool0ProceedsScaling is the amount to scale proceeds of order pool 0 (Long-
///                                   Term swaps of Token 0 to Token 1) based on the number
///                                   of decimal places in Token 0.
/// @member orderPool0ProceedsScaling is the amount to scale proceeds of order pool 1 (Long-
///                                   Term swaps of Token 1 to Token 0) based on the number
///                                   of decimal places in Token 1.
/// @member token0BalancerFeesU96 Balancer fees collected for Token0-->Token1 swaps.
/// @member token1BalancerFeesU96 Balancer fees collected for Token1-->Token0 swaps.
/// @member token0CronFiFeesU96 Cron-Fi fees collected for Token0-->Token1 Long-Term swaps.
/// @member token1CronFiFeesU96 Cron-Fi fees collected for Token1-->Token0 Long-Term swaps.
/// @member token0OrdersU112 Amount of Token0 sold to the pool in virtual orders.
/// @member token1OrdersU112 Amount of Token1 sold to the pool in virtual orders.
/// @member token0ProceedsU112 Amount of Token0 bought from the pool in virtual orders.
/// @member token1ProceedsU112 Amount of Token1 bought from the pool in virtual orders.
/// @member token0OracleU256F112 The computed increment for the price oracle for Token 0.
/// @member token1OracleU256F112 The computed increment for the price oracle for Token 1.
/// @member oracleTimeStampU32 The oracle time stamp.
///
struct ExecVirtualOrdersMem {
  uint256 token0ReserveU112;
  uint256 token1ReserveU112;
  uint256 lpFeeU60;
  uint256 feeShareU60;
  uint256 feeShiftU3;
  uint256 token0BalancerFeesU96;
  uint256 token1BalancerFeesU96;
  uint256 token0CronFiFeesU96;
  uint256 token1CronFiFeesU96;
  uint256 token0OrdersU112;
  uint256 token1OrdersU112;
  uint256 token0ProceedsU112;
  uint256 token1ProceedsU112;
  uint256 token0OracleU256F112;
  uint256 token1OracleU256F112;
}

/// @notice Struct for executing the virtual order loop efficiently (reduce
///         storage reads/writes). Advantages increase when pool is inactive
///         for longer multiples of the Order Block Interval.
/// @member lastVirtualOrderBlock The ethereum block number before the last virtual orders were
///                               executed.
/// @member scaledProceeds0U128 The normalized scaled proceeds of order pool 0 in Token1. For
///                             example, for an LT swap of Token0 for Token1, this value
///                             would be the normalized proceeds of Token1 for the pool. The
///                             normalized value is also scaled for precision reasons.
///                             Min. = 0, Max. = (2**128) - 1
/// @member scaledProceeds1U128 The normalized scaled proceeds of order pool 1 in Token0.
///                             Min. = 0, Max. = (2**128) - 1
/// @member currentSalesRate0U112 The current sales rate of Token0 per block.
///                               Min. = 0, Max. = (2**112) - 1
/// @member currentSalesRate1U112 The current sales rate of Token1 per block.
///                               Min. = 0, Max. = (2**112) - 1
///
struct LoopMem {
  // Block Numbers:
  uint256 lastVirtualOrderBlock;
  // Order Pool Items:
  uint256 scaledProceeds0U128;
  uint256 scaledProceeds1U128;
  uint256 currentSalesRate0U112;
  uint256 currentSalesRate1U112;
}

File 29 of 38 : ICronV1FactoryOwnerActions.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

interface ICronV1FactoryOwnerActions {
  function setAdminStatus(address _admin, bool _status) external;

  function setFeeAddress(address _feeDestination) external;

  function setFeeShift(uint256 _feeShift) external;

  function setCollectBalancerFees(bool _collectValue) external;
}

File 30 of 38 : ICronV1PoolAdminActions.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

interface ICronV1PoolAdminActions {
  function setPause(bool _pauseValue) external;

  function setParameter(uint256 _paramTypeU, uint256 _value) external;

  function setArbitragePartner(address _arbPartner, address _arbitrageList) external;
}

File 31 of 38 : ICronV1PoolArbitrageurActions.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

interface ICronV1PoolArbitrageurActions {
  function updateArbitrageList() external returns (address);

  function executeVirtualOrdersToBlock(uint256 _maxBlock) external;
}

File 32 of 38 : ICronV1PoolEnums.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

interface ICronV1PoolEnums {
  /// @notice Enumeration for the type of TWAMM pool created; the type determines the default fees and the immutable block
  ///         interval that the pool will operate with for it's lifetime. Each enumeration value is described in more
  ///         detail below (Fee Points = FP):
  ///
  ///           Stable:
  ///
  ///             - Intended for pool tokens that trade frequently; features lower fees and more frequent Long-Term order
  ///               expiries in exchange for higher gas use.
  ///             Short-Term Swap Fee  = 10 FP (0.010%)
  ///             Arbitrageur Swap Fee =  5 FP (0.005%)
  ///             Long-Term Swap Fee   = 30 FP (0.030%)
  ///             Order Block Interval = 75 blocks (~15 minutes)
  ///
  ///           Liquid:
  ///
  ///             - The middle ground setting between tokens that trade frequently and those that trade infrequently with
  ///               low-liquidity. Mid-range fees and order expiry frequency.
  ///             Short-Term Swap Fee  =  50 FP (0.050%)
  ///             Arbitrageur Swap Fee =  25 FP (0.025%)
  ///             Long-Term Swap Fee   = 150 FP (0.150%)
  ///             Order Block Interval = 300 blocks (~1 hour)
  ///
  ///           Volatile:
  ///
  ///             - Intended for pool tokens that trade infrequently with low-liquidity; features higher fees and less
  ///               frequent Long-Term order expiries in exchange for reduced gas use.
  ///             Short-Term Swap Fee  = 100 FP (0.100%)
  ///             Arbitrageur Swap Fee =  50 FP (0.050%)
  ///             Long-Term Swap Fee   = 300 FP (0.300%)
  ///             Order Block Interval = 1200 blocks (~ 4 hours)
  ///
  enum PoolType {
    Stable, // 0
    Liquid, // 1
    Volatile // 2
  }

  /// @notice Enumeration for functionality when joining the pool:
  ///         - Join performs the standard Join/Mint functionality, taking the provided tokens in exchange for
  ///           pool Liquidity Provider (LP) tokens.
  ///         - Reward performs a donation of the provided tokens to the pool with no LP tokens provided in return.
  ///
  enum JoinType {
    Join, // 0
    Reward // 1
  }

  /// @notice Enumeration for functionality when swapping with the pool:
  ///         - RegularSwap performs a standard swap of the specified token for its opposing token using the Constant
  ///           Product Automated Market Maker (CPAMM) formula.
  ///         - LongTermSwap performs a swap of the spcified token for its opposing token over more than one block.
  ///         - PartnerSwap performs a reduced fee RegularSwap with registered arbitrage partner's arbitrageurs.
  ///
  enum SwapType {
    RegularSwap, // 0
    LongTermSwap, // 1
    PartnerSwap // 2
  }

  /// @notice Enumeration for functionality when exiting the pool:
  ///         - Exit performs a standard exit or burn functionality, taking provided LP tokens in exchange for the
  ///           proportional amount of pool tokens.
  ///         - Withdraw performs a Long-Term swap order proceeds withdrawl.
  ///         - Cancel performs a Long-Term swap order cancellation, remitting proceeds and refunding unspent deposits.
  ///         - FeeWithdraw performs a withdraw of Cron-Fi fees to the fee address if enabled.
  ///
  enum ExitType {
    Exit, // 0
    Withdraw, // 1
    Cancel, // 2
    FeeWithdraw // 3
  }

  /// @notice Enumeration for shared parameterization setting function to specify parameter being set:
  ///         - SwapFeeFP is the short term swap fee in Fee Points (FP).
  ///         - PartnerFeeFP is the arbitrage partner swap fee in FP.
  ///         - LongSwapFeeFP is the Long-Term swap fee in FP.
  /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000.
  ///
  enum ParamType {
    // Slot 1:
    SwapFeeFP, // 0
    PartnerFeeFP, // 1
    LongSwapFeeFP // 2
  }

  /// @notice Enumeration for shared event log for boolean parameter state changes. The event
  ///         BoolParameterChange will contain one of the following enum values to indicate a
  ///         change to the respective one--the pool's paused state or collection of balancer fees.
  ///
  enum BoolParamType {
    Paused, // 0
    CollectBalancerFees // 1
  }
}

File 33 of 38 : ICronV1PoolEvents.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

import { ICronV1PoolEnums } from "./ICronV1PoolEnums.sol";

interface ICronV1PoolEvents is ICronV1PoolEnums {
  /// @notice ShortTermSwap event is emitted for Short-Term (ST) swap transactions and
  ///         arbitrage partner ST swap transactions. To differentiate, examine the value of
  ///         swapType in the emitted event.
  ///
  event ShortTermSwap(
    address indexed sender,
    address indexed tokenIn,
    uint256 amountIn,
    uint256 amountOut,
    uint256 swapType
  );

  /// @notice LongTermSwap event is emitted when Long-Term (LT) swaps transaction are issued to
  ///         the pool.
  ///
  event LongTermSwap(
    address indexed sender,
    address indexed delegate,
    address indexed tokenIn,
    uint256 amountIn,
    uint256 intervals,
    uint256 orderId
  );

  /// @notice PoolJoin events are emitted for Join/Mint and Reward transactions. A Reward
  ///         transaction can be identified from a Join/Mint transaction by examining the
  ///         emitted event's poolTokenAmt to see if is zero.
  ///
  event PoolJoin(
    address indexed sender,
    address indexed recipient,
    uint256 token0In,
    uint256 token1In,
    uint256 poolTokenAmt
  );

  /// @notice WithdrawLongTermSwap events are emitted when an LT swap order is withdrawn or cancelled
  ///         in a transaction. To differentiate between the two, only a cancellation will have non-zero
  ///         values for refundOut.
  ///
  event WithdrawLongTermSwap(
    address indexed owner,
    address indexed refundToken,
    uint256 refundOut,
    address indexed proceedsToken,
    uint256 proceedsOut,
    uint256 orderId,
    address sender
  );

  /// @notice FeeWithdraw events are emitted when Cron-Fi fees are withdrawn from the pool.
  ///
  event FeeWithdraw(address indexed sender, uint256 token0Out, uint256 token1Out);

  /// @notice PoolExit events are emitted when a Liquidity Provider (LP) redeems LP tokens for
  ///         their share of tokens remaining in the pool.
  ///
  event PoolExit(address indexed sender, uint256 poolTokenAmt, uint256 token0Out, uint256 token1Out);

  /// @notice AdministratorStatusChange events are emitted when an administrator address, admin,
  ///         is given administrator privileges (status == true) or when they are taken away
  ///         (status == false).
  ///
  event AdministratorStatusChange(address indexed sender, address indexed admin, bool status);

  /// @notice ProtocolFeeTooLarge is emitted if the protocol fee passed in by balancer ever exceeds
  ///         1e18 (in which case the change is ignored and fees continue with the last good value).
  ///
  event ProtocolFeeTooLarge(uint256 suggestedProtocolFee);

  /// @notice ParameterChange is emitted when a parameter value is changed to value. Consult the
  ///         enum ParmType for the parameter undergoing change.
  ///
  event ParameterChange(address indexed sender, ParamType paramType, uint256 value);

  /// @notice FeeAddressChange is emitted when the fee address, feeAddress, is changed.
  ///
  event FeeAddressChange(address indexed sender, address indexed feeAddress);

  /// @notice FeeShiftChange is emitted when the fee shift, feeShift is changed.
  ///
  event FeeShiftChange(address indexed sender, uint256 feeShift);

  /// @notice BoolParameterChange is emitted when a boolean value parameter is changed. Consult the
  ///         enum BoolParmType for the parameter undergoing change.
  ///
  event BoolParameterChange(address indexed sender, BoolParamType boolParam, bool value);

  /// @notice UpdatedArbitragePartner is emitted when an arbitrage partner's arbitrageur list is
  ///         updated to a new contract address.
  ///
  event UpdatedArbitragePartner(address indexed sender, address partner, address list);

  /// @notice UpdatedArbitrageList is emitted when an arbitrage partner's updates their arbitrageur
  ///         list is to a new contract address through the updateArbitrageList function.
  ///
  event UpdatedArbitrageList(address indexed partner, address indexed oldList, address indexed newList);

  /// @notice ExecuteVirtualOrdersEvent is emitted on calls to executeVirtualOrdersToBlock.
  ///
  event ExecuteVirtualOrdersEvent(address indexed sender, uint256 block);
}

File 34 of 38 : ICronV1PoolHelpers.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;

pragma experimental ABIEncoderV2;

import { ICronV1PoolEnums } from "./ICronV1PoolEnums.sol";
import { Order, PriceOracle, ExecVirtualOrdersMem } from "../Structs.sol";

interface ICronV1PoolHelpers {
  function getVirtualPriceOracle(uint256 _maxBlock)
    external
    returns (
      uint256 timestamp,
      uint256 token0U256F112,
      uint256 token1U256F112,
      uint256 blockNumber
    );

  function getVirtualReserves(uint256 _maxBlock, bool _paused)
    external
    returns (
      uint256 blockNumber,
      uint256 token0ReserveU112,
      uint256 token1ReserveU112,
      uint256 token0OrdersU112,
      uint256 token1OrdersU112,
      uint256 token0ProceedsU112,
      uint256 token1ProceedsU112,
      uint256 token0BalancerFeesU96,
      uint256 token1BalancerFeesU96,
      uint256 token0CronFiFeesU96,
      uint256 token1CronFiFeesU96
    );

  // solhint-disable-next-line func-name-mixedcase
  function POOL_ID() external view returns (bytes32);

  // solhint-disable-next-line func-name-mixedcase
  function POOL_TYPE() external view returns (ICronV1PoolEnums.PoolType);

  function getPriceOracle()
    external
    view
    returns (
      uint256 timestamp,
      uint256 token0U256F112,
      uint256 token1U256F112
    );

  function getOrderIds(
    address _owner,
    uint256 _offset,
    uint256 _maxResults
  )
    external
    view
    returns (
      uint256[] memory orderIds,
      uint256 numResults,
      uint256 totalResults
    );

  function getOrder(uint256 _orderId) external view returns (Order memory order);

  function getOrderIdCount() external view returns (uint256 nextOrderId);

  function getSalesRates() external view returns (uint256 salesRate0U112, uint256 salesRate1U112);

  function getLastVirtualOrderBlock() external view returns (uint256 lastVirtualOrderBlock);

  function getSalesRatesEndingPerBlock(uint256 _blockNumber)
    external
    view
    returns (uint256 salesRateEndingPerBlock0U112, uint256 salesRateEndingPerBlock1U112);

  function getShortTermFeePoints() external view returns (uint256);

  function getPartnerFeePoints() external view returns (uint256);

  function getLongTermFeePoints() external view returns (uint256);

  function getOrderAmounts() external view returns (uint256 orders0U112, uint256 orders1U112);

  function getProceedAmounts() external view returns (uint256 proceeds0U112, uint256 proceeds1U112);

  function getFeeShift() external view returns (uint256);

  function getCronFeeAmounts() external view returns (uint256 cronFee0U96, uint256 cronFee1U96);

  function isPaused() external view returns (bool);

  function isCollectingCronFees() external view returns (bool);

  function isCollectingBalancerFees() external view returns (bool);

  function getBalancerFee() external view returns (uint256);

  function getBalancerFeeAmounts() external view returns (uint256 balFee0U96, uint256 balFee1U96);
}

File 35 of 38 : BitPacking.sol
// (c) Copyright 2023, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.7.6;

import { C } from "./Constants.sol";
import { requireErrCode, CronErrors } from "./Errors.sol";

/// @notice Library for bit-packing generic and specific values pertinent to Cron-Fi TWAMM
///         into storage-slots efficiently (both for gas use and contract size).
///
/// @dev Many custom representations are used herein (i.e. non native word lengths) and there
///      are a number of explicit checks against the maximum of these non native word lengths.
///      Furthermore there are unchecked operations (this code targets Solidity 0.7.x which
///      didn't yet feature implicit arithmetic checks or have the 'unchecked' block feature)
///      in this library for reasons of efficiency or desired overflow. Wherever they appear
///      they will be documented and accompanied with one of the following tags:
///        - #unchecked
///        - #overUnderFlowIntended
///      Identified risks will be accompanied and described with the following tag:
///        - #RISK
///
/// @dev Generic shifting methods were eschewed because of their additional gas use.
/// @dev Conventions in the methods below are as follows:
///
///      Suffixes:
///
///      - The suffix of a variable name denotes the type contained within the variable.
///        For instance "uint256 _incrementU96" is a 256-bit unsigned container representing
///        a 96-bit value, _increment.
///        In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is
///        representing a 19 digit decimal value with 18 fractional digits. In this scenario,
///        the D=Decimal, U=Unsigned, F=Fractional.
///
///      - The suffix of a function name denotes what slot it is proprietary too as a
///        matter of convention. While unchecked at run-time or by the compiler, the naming
///        convention easily aids in understanding what slot a packed value is stored within.
///        For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If
///        the value of slot 2 were passed to this method, the unpacked value would be
///        incorrect.
///
///      Bit-Numbering:
///
///      - Bits are counted starting with the least-significant bit (LSB) from 1. Thus for
///        a 256-bit slot, the most-significant bit (MSB) is bit 256 and the LSB is bit 1.
///
///
///      Offsets:
///
///      - Offsets are the distance from the LSB to the desired LSB of the word being
///        placed within a slot. For instance, to store an 8-bit word in a 256-bit slot
///        at bits 16 down to 9, an offset of 8-bits should be specified.
///
///
///      Pairs
///
///      - The following methods which operate upon pairs follow the convention that a
///        pair consists of two same sized words, with word0 stored above word1 within
///        a slot. For example, a pair of 96-bit words will be stored with word0
///        occupying bits 192 downto 97 and word1 occupying bits 96 downto 1. The following
///        diagram depicts this scenario:
///
///              bit-256     bit-192      bit-96      bit-1
///                 |           |            |           |
///                 v           v            v           v
///
///           MSB < I    ???   II   word0   II   word1   I > LSB
///
///                            ^            ^
///                            |            |
///                         bit-193      bit-97
///
library BitPackingLib {
  //
  // Generic Packing Functions
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Packs bit _bitU1 into the provided 256-bit slot, _slot, at location _offsetU8
  ///         bits from the provided slot's LSB, bit 1.
  /// @param _slot A 256-bit container to pack bit _bitU1 within.
  /// @param _bitU1 A 1-bit value to pack into the provided slot.
  ///               Min. = 0, Max. = 1.
  /// @param _offsetU8 The distance in bits from the provided slot's LSB to store _bitU1 at.
  ///                  Min. = 0, Max. = 255.
  /// @dev WARNING: No checks of _offsetU8 are performed for efficiency!
  /// @return slot The modified slot containing _bitU1 at bit position _offsetU8 + 1.
  ///
  function packBit(
    uint256 _slot,
    uint256 _bitU1,
    uint256 _offsetU8
  ) internal pure returns (uint256 slot) {
    requireErrCode(_bitU1 <= C.MAX_U1, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    uint256 mask = C.MAX_U1 << _offsetU8;
    slot = (_bitU1 != 0) ? (_slot | mask) : (_slot & (~mask));
  }

  /// @notice Unpacks bitU1 from the provided 256-bit slot, _slot, at location _offsetU8
  ///         bits from the provided slot's LSB, bit 1.
  /// @param _slot A 256-bit container to unpack bitU1 from.
  /// @param _offsetU8 The distance in bits from the provided slot's LSB to unpack bitU1 from.
  ///                  Min. = 0, Max. = 255.
  /// @dev WARNING: No checks of _offsetU8 are performed for efficiency!
  /// @return bitU1 The 1-bit value unpacked from the provided slot.
  ///               Min. = 0, Max. = 1.
  ///
  function unpackBit(uint256 _slot, uint256 _offsetU8) internal pure returns (uint256 bitU1) {
    bitU1 = ((_slot >> _offsetU8) & C.MAX_U1);
  }

  /// @notice Packs ten-bit word, _wordU10, into the provided 256-bit slot, _slot, at location
  ///         _offsetU8 bits from the provided slot's LSB, bit 1.
  /// @param _slot A 256-bit container to pack bit _wordU10 within.
  /// @param _wordU10 A ten-bit word to pack into the provided slot.
  ///                 Min. = 0, Max. = (2**10)-1.
  /// @param _offsetU8 The distance in bits from the provided slot's LSB to store _bitU1 at.
  ///                  Min. = 0, Max. = 255.
  /// @dev WARNING: No checks of _offsetU8 are performed for efficiency!
  /// @return slot The modified slot containing _wordU10 at bit position _offsetU8 + 1.
  ///
  function packU10(
    uint256 _slot,
    uint256 _wordU10,
    uint256 _offsetU8
  ) internal pure returns (uint256 slot) {
    requireErrCode(_wordU10 <= C.MAX_U10, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    uint256 clearMask = ~(C.MAX_U10 << _offsetU8);
    uint256 setMask = _wordU10 << _offsetU8;
    slot = (_slot & clearMask) | setMask;
  }

  /// @notice Unpacks wordU10 from the provided 256-bit slot, _slot, at location _offsetU8
  ///         bits from the provided slot's LSB, bit 1.
  /// @param _slot A 256-bit container to unpack wordU10 from.
  /// @param _offsetU8 The distance in bits from the provided slot's LSB to unpack wordU10 from.
  ///                  Min. = 0, Max. = 255.
  /// @dev WARNING: No checks of _offsetU8 are performed for efficiency!
  /// @return wordU10 The ten-bit word unpacked from the provided slot.
  ///                  Min. = 0, Max. = (2**10)-1.
  function unpackU10(uint256 _slot, uint256 _offsetU8) internal pure returns (uint256 wordU10) {
    wordU10 = (_slot >> _offsetU8) & C.MAX_U10;
  }

  /// @notice Increments the 96-bit words, word0 and/or word1, stored within the provided
  ///         256-bit slot, _slot, by the values provided in _increment0U96 and _increment1U96
  ///         respectively. Importantly, if the increment results in overflow, the value
  ///         will "clamp" to the maximum value (2**96)-1.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 96-bit words, word0 and word1.
  /// @param _increment0U96 The amount to increment word0 by.
  ///                       Min. = 0, Max. = (2**96)-1.
  /// @param _increment1U96 The amount to increment word1 by.
  ///                       Min. = 0, Max. = (2**96)-1.
  /// @return slot The modified slot containing incremented values of word0 and/or word1.
  ///
  function incrementPairWithClampU96(
    uint256 _slot,
    uint256 _increment0U96,
    uint256 _increment1U96
  ) internal pure returns (uint256 slot) {
    uint256 word0U96 = ((_slot >> 96) & C.MAX_U96);
    uint256 word1U96 = (_slot & C.MAX_U96);

    if (_increment0U96 > 0) {
      requireErrCode(C.MAX_U256 - word0U96 >= _increment0U96, CronErrors.OVERFLOW);
      // #unchecked
      //            safe from overflow in increment below because of check above.
      word0U96 += _increment0U96;
      // Clamp the resulting value to (2**96)+1 on overflow of U96 beyond MAX_U96:
      if (word0U96 > C.MAX_U96) {
        word0U96 = C.MAX_U96;
      }
    }

    if (_increment1U96 > 0) {
      requireErrCode(C.MAX_U256 - word1U96 >= _increment1U96, CronErrors.OVERFLOW);
      // #unchecked
      //            safe from overflow in increment below because of check above.
      word1U96 += _increment1U96;
      // Clamp the resulting value to (2**96)+1 on overflow of U96 beyond MAX_U96:
      if (word1U96 > C.MAX_U96) {
        word1U96 = C.MAX_U96;
      }
    }

    // NOTE: No need create set masks as _value*U96 above checked against MAX_U96/clamped.
    slot = (_slot & C.CLEAR_MASK_PAIR_U96) | (word0U96 << 96) | word1U96;
  }

  /// @notice Unpacks the two 96-bit values, word0 and word1, from the provided slot, _slot,
  ///         returning them along with the provided slot modified to clear the values
  ///         or word0 and word1 to zero.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 96-bit words, word0 and word1.
  /// @return slot The modified slot containing cleared values of word0 and word1.
  /// @return word0U96 The value of word0 prior to clearing it.
  ///                  Min. = 0, Max. = (2**96)-1.
  /// @return word1U96 The value of word1 prior to clearing it.
  ///                  Min. = 0, Max. = (2**96)-1.
  ///
  function unpackAndClearPairU96(uint256 _slot)
    internal
    pure
    returns (
      uint256 slot,
      uint256 word0U96,
      uint256 word1U96
    )
  {
    word0U96 = (_slot >> 96) & C.MAX_U96;
    word1U96 = _slot & C.MAX_U96;

    slot = _slot & C.CLEAR_MASK_PAIR_U96;
  }

  /// @notice Unpacks and returns the two 96-bit values, word0 and word1, from the provided slot.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 96-bit words, word0 and word1.
  /// @return word0U96 The value of word0.
  ///                  Min. = 0, Max. = (2**96)-1.
  /// @return word1U96 The value of word1.
  ///                  Min. = 0, Max. = (2**96)-1.
  ///
  function unpackPairU96(uint256 _slot) internal pure returns (uint256 word0U96, uint256 word1U96) {
    word0U96 = (_slot >> 96) & C.MAX_U96;
    word1U96 = _slot & C.MAX_U96;
  }

  /// @notice Packs the two provided 112-bit words, word0 and word1, into the provided 256-bit
  ///         slot, _slot.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1.
  /// @param _word0U112 The value of word0 to pack.
  ///                   Min. = 0, Max. = (2**112)-1.
  /// @param _word1U112 The value of word1 to pack.
  ///                   Min. = 0, Max. = (2**112)-1.
  /// @return slot The modified slot containing the values of word0 and word1.
  ///
  function packPairU112(
    uint256 _slot,
    uint256 _word0U112,
    uint256 _word1U112
  ) internal pure returns (uint256 slot) {
    requireErrCode(_word0U112 <= C.MAX_U112, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);
    requireErrCode(_word1U112 <= C.MAX_U112, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    slot = (_slot & C.CLEAR_MASK_PAIR_U112) | (_word0U112 << 112) | _word1U112;
  }

  /// @notice Unpacks and returns the two 112-bit values, word0 and word1, from the provided slot.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1.
  /// @return word0U112 The value of word0.
  ///                  Min. = 0, Max. = (2**112)-1.
  /// @return word1U112 The value of word1.
  ///                  Min. = 0, Max. = (2**112)-1.
  ///
  function unpackPairU112(uint256 _slot) internal pure returns (uint256 word0U112, uint256 word1U112) {
    word0U112 = (_slot >> 112) & C.MAX_U112;
    word1U112 = _slot & C.MAX_U112;
  }

  /// @notice Increments the 112-bit words, word0 and/or word1, stored within the provided
  ///         256-bit slot, _slot, by the values provided in _increment0U112 and _increment1U112
  ///         respectively. Errors on overflow.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1.
  /// @param _increment0U112 The amount to increment word0 by.
  ///                        Min. = 0, Max. = (2**112)-1.
  /// @param _increment1U112 The amount to increment word1 by.
  ///                        Min. = 0, Max. = (2**112)-1.
  /// @return slot The modified slot containing incremented values of word0 and/or word1.
  ///
  function incrementPairU112(
    uint256 _slot,
    uint256 _increment0U112,
    uint256 _increment1U112
  ) internal pure returns (uint256 slot) {
    uint256 word0U112 = ((_slot >> 112) & C.MAX_U112);
    uint256 word1U112 = (_slot & C.MAX_U112);

    if (_increment0U112 > 0) {
      requireErrCode(C.MAX_U112 - word0U112 >= _increment0U112, CronErrors.OVERFLOW);
      // #unchecked
      //            safe from overflow in increment below because of check above.
      word0U112 += _increment0U112;
    }
    if (_increment1U112 > 0) {
      requireErrCode(C.MAX_U112 - word1U112 >= _increment1U112, CronErrors.OVERFLOW);
      // #unchecked
      //            safe from overflow in increment below because of check above.
      word1U112 += _increment1U112;
    }

    // NOTE: No need to create set masks as _value*U112 above checked against MAX_U112.
    slot = (_slot & C.CLEAR_MASK_PAIR_U112) | (word0U112 << 112) | word1U112;
  }

  /// @notice Decrements the 112-bit words, word0 and/or word1, stored within the provided
  ///         256-bit slot, _slot, by the values provided in _decrement0U112 and
  ///         _decrement1U112 respectively. Errors on underflow.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1.
  /// @param _decrement0U112 The amount to decrement word0 by.
  ///                        Min. = 0, Max. = (2**112)-1.
  /// @param _decrement1U112 The amount to decrement word1 by.
  ///                        Min. = 0, Max. = (2**112)-1.
  /// @return slot The modified slot containing decremented values of word0 and/or word1.
  ///
  function decrementPairU112(
    uint256 _slot,
    uint256 _decrement0U112,
    uint256 _decrement1U112
  ) internal pure returns (uint256 slot) {
    uint256 word0U112 = ((_slot >> 112) & C.MAX_U112);
    uint256 word1U112 = (_slot & C.MAX_U112);

    if (_decrement0U112 > 0) {
      requireErrCode(word0U112 >= _decrement0U112, CronErrors.UNDERFLOW);
      // #unchecked
      //            safe from underflow in decrement below because of check above.
      word0U112 -= _decrement0U112;
    }
    if (_decrement1U112 > 0) {
      requireErrCode(word1U112 >= _decrement1U112, CronErrors.UNDERFLOW);
      // #unchecked
      //            safe from underflow in decrement below because of check above.
      word1U112 -= _decrement1U112;
    }

    // NOTE: No need to create set masks as _value*U112 above both at most MAX_U112 (correct by
    //       construction--checked at creation/pack) and operation is subtraction with underflow
    //       checked;
    slot = (_slot & C.CLEAR_MASK_PAIR_U112) | (word0U112 << 112) | word1U112;
  }

  /// @notice Unpacks and returns the specified 128-bit values, word0 or word1, from the provided slot,
  ///         depending on the value of isWord0.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 128-bit words, word0 and word1.
  /// @param _isWord0 Instructs this method to unpack the upper 128-bits corresponding to word0 when true.
  ///                 Otherwise the lower 128-bits, word1 are unpacked.
  /// @return wordU128 The value of word0.
  ///                  Min. = 0, Max. = (2**128)-1.
  ///
  function unpackU128(uint256 _slot, bool _isWord0) internal pure returns (uint256 wordU128) {
    wordU128 = _isWord0 ? _slot >> 128 : _slot & C.MAX_U128;
  }

  /// @notice Packs the two provided 128-bit words, word0 and word1, into a 256-bit slot.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the slot.
  /// @param _word0U128 The value of word0 to pack.
  ///                   Min. = 0, Max. = (2**128)-1.
  /// @param _word1U128 The value of word1 to pack.
  ///                   Min. = 0, Max. = (2**128)-1.
  /// @return slot A slot containing the 128-bit values word0 and word1.
  ///
  function packPairU128(uint256 _word0U128, uint256 _word1U128) internal pure returns (uint256 slot) {
    requireErrCode(_word0U128 <= C.MAX_U128, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);
    requireErrCode(_word1U128 <= C.MAX_U128, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    slot = (_word0U128 << 128) | _word1U128;
  }

  /// @notice Unpacks and returns the two 128-bit values, word0 and word1, from the provided slot.
  /// @dev    See the section on Pairs in the notes on Conventions to understand how the two
  ///         words are stored within the provided slot.
  /// @param _slot A 256-bit container holding two 128-bit words, word0 and word1.
  /// @return word0U128 The value of word0.
  ///                   Min. = 0, Max. = (2**128)-1.
  /// @return word1U128 The value of word1.
  ///                   Min. = 0, Max. = (2**128)-1.
  ///
  function unpackPairU128(uint256 _slot) internal pure returns (uint256 word0U128, uint256 word1U128) {
    word0U128 = _slot >> 128;
    word1U128 = _slot & C.MAX_U128;
  }

  //
  // Slot 2 Specific Packing Functions
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Packs the 32-bit oracle time stamp, _oracleTimeStampU32, into the provided 256-bit slot.
  /// @param _slot A 256-bit container to pack the oracle time stamp within.
  /// @param _oracleTimeStampU32 The 32-bit oracle time stamp.
  ///                            Min. = 0, Max. = (2**32)-1.
  /// @return slot The modified slot containing the oracle time stamp.
  ///
  function packOracleTimeStampS2(uint256 _slot, uint256 _oracleTimeStampU32) internal pure returns (uint256 slot) {
    requireErrCode(_oracleTimeStampU32 <= C.MAX_U32, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    uint256 setMask = _oracleTimeStampU32 << C.S2_OFFSET_ORACLE_TIMESTAMP;
    slot = (_slot & C.CLEAR_MASK_ORACLE_TIMESTAMP) | setMask;
  }

  /// @notice Unpacks the 32-bit oracle time stamp, oracleTimeStampU32, from the provided 256-bit slot,
  /// @param _slot A 256-bit container to unpack the oracle time stamp from.
  /// @return oracleTimeStampU32 The 32-bit oracle time stamp.
  ///                            Min. = 0, Max. = (2**32)-1.
  function unpackOracleTimeStampS2(uint256 _slot) internal pure returns (uint256 oracleTimeStampU32) {
    oracleTimeStampU32 = (_slot >> C.S2_OFFSET_ORACLE_TIMESTAMP) & C.MAX_U32;
  }

  //
  // Slot 3 Specific Packing Functions
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Packs the 3-bit fee shift, _feeShiftU3, into the provided 256-bit slot.
  /// @param _slot A 256-bit container to pack the fee shift into.
  /// @param _feeShiftU3 The 3-bit fee shift.
  ///                    Min. = 0, Max. = 7.
  /// @return slot The modified slot containing the new fee shift value.
  ///
  function packFeeShiftS3(uint256 _slot, uint256 _feeShiftU3) internal pure returns (uint256 slot) {
    requireErrCode(_feeShiftU3 <= C.MAX_U3, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    uint256 setMask = _feeShiftU3 << C.S3_OFFSET_FEE_SHIFT_U3;
    slot = (_slot & C.CLEAR_MASK_FEE_SHIFT) | setMask;
  }

  /// @notice Unpacks the 3-bit fee shift, feeShiftU3, from the provided 256-bit slot,
  /// @param _slot A 256-bit container to unpack the fee shift from.
  /// @return feeShiftU3 The 3-bit fee shift.
  ///                    Min. = 0, Max. = 7.
  function unpackFeeShiftS3(uint256 _slot) internal pure returns (uint256 feeShiftU3) {
    feeShiftU3 = (_slot >> C.S3_OFFSET_FEE_SHIFT_U3) & C.MAX_U3;
  }

  //
  // Slot 4 Specific Packing Functions
  ////////////////////////////////////////////////////////////////////////////////

  /// @notice Packs the balancer fee, _balancerFeeDU1F18, into the provided 256-bit slot.
  /// @param _slot A 256-bit container to pack the balancer fee into.
  /// @param _balancerFeeDU1F18 The balancer fee representing a 19 decimal digit
  ///                           value with 18 fractional digits, NOT TO EXCEED 10**19.
  ///                           Min. = 0, Max. = 10**19.
  /// @return slot The modified slot containing the new balancer fee value.
  ///
  function packBalancerFeeS4(uint256 _slot, uint256 _balancerFeeDU1F18) internal pure returns (uint256 slot) {
    requireErrCode(_balancerFeeDU1F18 <= C.MAX_U60, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ);

    uint256 setMask = _balancerFeeDU1F18 << C.S4_OFFSET_BALANCER_FEE;
    slot = (_slot & C.CLEAR_MASK_BALANCER_FEE) | setMask;
  }

  /// @notice Unpacks the 60-bit balancer fee representation, balancerFeeDU1F18, from the
  ///         provided 256-bit slot,
  /// @param _slot A 256-bit container to unpack the balancer fee from.
  /// @return balancerFeeDU1F18 The 60-bit balancer fee representing a 19 decimal digit value
  ///                           with 18 fractional digits.
  ///                           Min. = 0, Max. = (2**60)-1.
  function unpackBalancerFeeS4(uint256 _slot) internal pure returns (uint256 balancerFeeDU1F18) {
    balancerFeeDU1F18 = (_slot >> C.S4_OFFSET_BALANCER_FEE) & C.MAX_U60;
  }
}

File 36 of 38 : Constants.sol
// (c) Copyright 2023, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.7.6;

/// @notice Library of constants used throughout the implementation.
///
/// @dev Conventions in the methods, variables and constants are as follows:
///
///      Prefixes:
///
///      - In constants, the prefix "Sn", where 1 <= n <= 4, denotes which slot the constant
///        pertains too. There are four storage slots that are bitpacked. For example,
///        "S2_OFFSET_ORACLE_TIMESTAMP" refers to the offset of the oracle timestamp in bit-
///        packed storage slot 2.
///
///      Suffixes:
///
///      - The suffix of a variable name denotes the type contained within the variable.
///        For instance "uint256 _incrementU96" is a 256-bit unsigned container representing
///        the 96-bit value "_increment".
///        In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is
///        representing a 19 digit decimal value with 18 fractional digits. In this scenario,
///        the D=Decimal, U=Unsigned, F=Fractional.
///        Finally, "uint128 valueU128F64" is a 128-bit container representing a 128-bit value
///        with 64 fractional bits.
///
///      - The suffix of a function name denotes what slot it is proprietary too as a
///        matter of convention. While unchecked at run-time or by the compiler, the naming
///        convention easily aids in understanding what slot a packed value is stored within.
///        For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If
///        the value of slot 2 were passed to this method, the unpacked value would be
///        incorrect.
///
library C {
  //
  // Factory owner and default pool admin address
  ////////////////////////////////////////////////////////////////////////////////
  address internal constant CRON_DEPLOYER_ADMIN = 0xe122Eff60083bC550ACbf31E7d8197A58d436b39;

  //
  // General constants
  ////////////////////////////////////////////////////////////////////////////////
  address internal constant NULL_ADDR = address(0);

  uint256 internal constant FALSE = 0;

  uint256 internal constant MAX_U256 = type(uint256).max;
  uint256 internal constant MAX_U128 = type(uint128).max;
  uint256 internal constant MAX_U112 = type(uint112).max;
  uint256 internal constant MAX_U96 = type(uint96).max;
  uint256 internal constant MAX_U64 = type(uint64).max;
  uint256 internal constant MAX_U60 = 2**60 - 1;
  uint256 internal constant MAX_U32 = type(uint32).max;
  uint256 internal constant MAX_U24 = type(uint24).max;
  uint256 internal constant MAX_U20 = 0xFFFFF;
  uint256 internal constant MAX_U16 = type(uint16).max;
  uint256 internal constant MAX_U10 = 0x3FF;
  uint256 internal constant MAX_U8 = type(uint8).max;
  uint256 internal constant MAX_U3 = 0x7;
  uint256 internal constant MAX_U1 = 0x1;

  uint256 internal constant ONE_DU1_18 = 10**18;
  uint256 internal constant DENOMINATOR_DU1_18 = 10**18;

  uint256 internal constant SECONDS_PER_BLOCK = 12;

  //
  // Array Index constants
  ////////////////////////////////////////////////////////////////////////////////
  uint256 internal constant INDEX_TOKEN0 = 0;
  uint256 internal constant INDEX_TOKEN1 = 1;

  //
  // Bit-Packing constants
  //
  // Dev: Bit-offsets below are the offset from the first bit. For example to get
  //      to bit 250, the offset 249 is used. (The first bit is counted as bit 1).
  ////////////////////////////////////////////////////////////////////////////////

  // Masks:
  uint256 internal constant CLEAR_MASK_PAIR_U96 = ~((MAX_U96 << 96) | MAX_U96);
  uint256 internal constant CLEAR_MASK_PAIR_U112 = ~((MAX_U112 << 112) | MAX_U112);
  uint256 internal constant CLEAR_MASK_ORACLE_TIMESTAMP = ~(MAX_U32 << S2_OFFSET_ORACLE_TIMESTAMP);
  uint256 internal constant CLEAR_MASK_FEE_SHIFT = ~(MAX_U3 << S3_OFFSET_FEE_SHIFT_U3);
  uint256 internal constant CLEAR_MASK_BALANCER_FEE = ~(MAX_U60 << S4_OFFSET_BALANCER_FEE);

  // Slot 1 Offsets:
  uint256 internal constant S1_OFFSET_SHORT_TERM_FEE_FP = 244; // Bits 254-245;
  uint256 internal constant S1_OFFSET_PARTNER_FEE_FP = 234; // Bits 244-235;
  uint256 internal constant S1_OFFSET_LONG_TERM_FEE_FP = 224; // Bits 234-225;

  // Slot 2 Offsets:
  uint256 internal constant S2_OFFSET_ORACLE_TIMESTAMP = 224; // Bits 256-225;

  // Slot 3 Offsets:
  uint256 internal constant S3_OFFSET_FEE_SHIFT_U3 = 222; // Bits 225-223

  // Slot 4 Offsets:
  uint256 internal constant S4_OFFSET_PAUSE = 255; // Bit 256
  uint256 internal constant S4_OFFSET_CRON_FEE_ENABLED = 254; // Bit 255
  uint256 internal constant S4_OFFSET_COLLECT_BALANCER_FEES = 253; // Bit 254
  uint256 internal constant S4_OFFSET_ZERO_CRONFI_FEES = 252; // Bit 253
  uint256 internal constant S4_OFFSET_BALANCER_FEE = 192; // Bits 252-193;

  //
  // Scaling constants
  ////////////////////////////////////////////////////////////////////////////////
  //
  uint256 internal constant MAX_DECIMALS = 22;
  uint256 internal constant MIN_DECIMALS = 2;

  //
  // Pool Specific constants
  ////////////////////////////////////////////////////////////////////////////////
  uint256 internal constant MINIMUM_LIQUIDITY = 10**3;

  uint16 internal constant STABLE_OBI = 75; // ~15m @ 12s/block
  uint16 internal constant LIQUID_OBI = 300; // ~60m @ 12s/block
  uint16 internal constant VOLATILE_OBI = 1200; // ~240m @ 12s/block

  // Maximum long-term swap (5 years, 13149000 blocks @ 12s/block).
  // - Numbers below are 13149000 / OBI (rounded down where noted):
  uint24 internal constant STABLE_MAX_INTERVALS = 175320;
  uint24 internal constant LIQUID_MAX_INTERVALS = 43830;
  uint24 internal constant VOLATILE_MAX_INTERVALS = 10957; // Rounded down from 10957.5

  //
  // Fees constants
  ////////////////////////////////////////////////////////////////////////////////
  //       FP = Total Fee Points
  //       ST = Short-Term Swap
  //       LT = Long-Term Swap
  //       LP = Liquidity Provider
  //       CF = Cron Fi
  //
  // NOTE: Mult-by these constants requires Max. 14-bits (~13.3 bits) headroom to prevent overflow.
  //
  uint256 internal constant TOTAL_FP = 100000;
  uint256 internal constant MAX_FEE_FP = 1000; // 1.000%

  // Short Term Swap Payouts:
  // ----------------------------------------
  uint16 internal constant STABLE_ST_FEE_FP = 10; // 0.010%
  uint16 internal constant LIQUID_ST_FEE_FP = 50; // 0.050%
  uint16 internal constant VOLATILE_ST_FEE_FP = 100; // 0.100%

  // Partner Swap Payouts:
  // ----------------------------------------
  uint16 internal constant STABLE_ST_PARTNER_FEE_FP = 5; // 0.005%
  uint16 internal constant LIQUID_ST_PARTNER_FEE_FP = 25; // 0.025%
  uint16 internal constant VOLATILE_ST_PARTNER_FEE_FP = 50; // 0.050%

  // Long Term Swap Payouts
  // ----------------------------------------
  uint16 internal constant STABLE_LT_FEE_FP = 30; // 0.030%
  uint16 internal constant LIQUID_LT_FEE_FP = 150; // 0.150%
  uint16 internal constant VOLATILE_LT_FEE_FP = 300; // 0.300%

  uint8 internal constant DEFAULT_FEE_SHIFT = 1; // 66% LP to 33% CronFi
}

File 37 of 38 : Errors.sol
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// NOTE: Adapted from Balancer's BalancerErrors.sol code.

pragma solidity ^0.7.6;

/// @dev Conventions in the methods below are as follows:
///
///      Suffixes:
///
///      - The suffix of a variable name denotes the type contained within the variable.
///        For instance "uint256 _incrementU96" is a 256-bit unsigned container representing
///        a 96-bit value, _increment.
///        In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is
///        representing a 19 digit decimal value with 18 fractional digits. In this scenario,
///        the D=Decimal, U=Unsigned, F=Fractional.
///
///      - The suffix of a function name denotes what slot it is proprietary too as a
///        matter of convention. While unchecked at run-time or by the compiler, the naming
///        convention easily aids in understanding what slot a packed value is stored within.
///        For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If
///        the value of slot 2 were passed to this method, the unpacked value would be
///        incorrect.

/// @notice Reverts if the specified condition is not true with the provided error code.
/// @param _condition A condition to test; must resolve to true to not revert.
/// @param _errorCodeD3 An 3 digit decimal error code to present if the condition
///                     resolves to false.
///                     Min. = 0, Max. = 999.
/// @dev WARNING: No checks of _errorCodeD3 are performed for efficiency!
///
// solhint-disable-next-line func-visibility
function requireErrCode(bool _condition, uint256 _errorCodeD3) pure {
  if (!_condition) {
    // We're going to dynamically create a revert string based on the error code, with the following format:
    // 'CFI#{errorCode}'
    // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).
    //
    // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a
    // number (8 to 16 bits) than the individual string characters.
    //
    // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a
    // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a
    // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.
    // solhint-disable-next-line no-inline-assembly
    assembly {
      // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999
      // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for
      // the '0' character.

      let units := add(mod(_errorCodeD3, 10), 0x30)

      _errorCodeD3 := div(_errorCodeD3, 10)
      let tenths := add(mod(_errorCodeD3, 10), 0x30)

      _errorCodeD3 := div(_errorCodeD3, 10)
      let hundreds := add(mod(_errorCodeD3, 10), 0x30)

      // With the individual characters, we can now construct the full string. The "CFI#" part is a known constant
      // (0x43464923): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the
      // characters to it, each shifted by a multiple of 8.
      // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits
      // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte
      // array).

      let revertReason := shl(200, add(0x43464923000000, add(add(units, shl(8, tenths)), shl(16, hundreds))))

      // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded
      // message will have the following layout:
      // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]

      // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We
      // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.
      mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
      // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).
      mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
      // The string length is fixed: 7 characters.
      mstore(0x24, 7)
      // Finally, the string itself is stored.
      mstore(0x44, revertReason)

      // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of
      // the encoded message is therefore 4 + 32 + 32 + 32 = 100.
      revert(0, 100)
    }
  }
}

library CronErrors {
  //
  // Permissions
  ////////////////////////////////////////////////////////////////////////////////

  uint256 internal constant SENDER_NOT_FACTORY = 0;
  uint256 internal constant SENDER_NOT_FACTORY_OWNER = 1;
  uint256 internal constant SENDER_NOT_ADMIN = 2;
  uint256 internal constant SENDER_NOT_ARBITRAGE_PARTNER = 3;
  uint256 internal constant NON_VAULT_CALLER = 4;
  uint256 internal constant SENDER_NOT_PARTNER = 5;
  uint256 internal constant SENDER_NOT_FEE_ADDRESS = 7;
  uint256 internal constant SENDER_NOT_ORDER_OWNER_OR_DELEGATE = 8;
  uint256 internal constant CANNOT_TRANSFER_TO_SELF_OR_NULL = 9;
  uint256 internal constant RECIPIENT_NOT_OWNER = 10;
  // A cleared order can be one that:
  //   - was cancelled
  //   - was withdrawn after expiry
  //   - never existed (i.e. empty blockchain state in the future)
  uint256 internal constant CLEARED_ORDER = 11;

  //
  // Modifiers
  ////////////////////////////////////////////////////////////////////////////////

  uint256 internal constant POOL_PAUSED = 100;

  //
  // Configuration & Parameterization
  ////////////////////////////////////////////////////////////////////////////////

  uint256 internal constant UNSUPPORTED_SWAP_KIND = 201;
  uint256 internal constant INSUFFICIENT_LIQUIDITY = 204;
  uint256 internal constant INCORRECT_POOL_ID = 206;
  uint256 internal constant ZERO_SALES_RATE = 208;
  uint256 internal constant NO_FUNDS_AVAILABLE = 212;
  uint256 internal constant MAX_ORDER_LENGTH_EXCEEDED = 223;
  uint256 internal constant NO_FEES_AVAILABLE = 224;
  uint256 internal constant UNSUPPORTED_TOKEN_DECIMALS = 225;
  uint256 internal constant NULL_RECIPIENT_ON_JOIN = 226;
  uint256 internal constant CANT_CANCEL_COMPLETED_ORDER = 227;
  uint256 internal constant MINIMUM_NOT_SATISFIED = 228;

  //
  // General
  ////////////////////////////////////////////////////////////////////////////////

  uint256 internal constant VALUE_EXCEEDS_CONTAINER_SZ = 400;
  uint256 internal constant OVERFLOW = 401;
  uint256 internal constant UNDERFLOW = 402;
  uint256 internal constant PARAM_ERROR = 403;

  //
  // Factory
  ////////////////////////////////////////////////////////////////////////////////

  uint256 internal constant ZERO_TOKEN_ADDRESSES = 500;
  uint256 internal constant IDENTICAL_TOKEN_ADDRESSES = 501;
  uint256 internal constant EXISTING_POOL = 502;
  uint256 internal constant INVALID_FACTORY_OWNER = 503;
  uint256 internal constant INVALID_PENDING_OWNER = 504;
  uint256 internal constant NON_EXISTING_POOL = 505;

  //
  // Periphery Relayer
  ////////////////////////////////////////////////////////////////////////////////
  uint256 internal constant P_ETH_TRANSFER = 600;
  uint256 internal constant P_NULL_USER_ADDRESS = 602;
  uint256 internal constant P_INSUFFICIENT_LIQUIDITY = 603;
  uint256 internal constant P_INSUFFICIENT_TOKEN_A_USER_BALANCE = 604;
  uint256 internal constant P_INSUFFICIENT_TOKEN_B_USER_BALANCE = 605;
  uint256 internal constant P_INVALID_POOL_TOKEN_AMOUNT = 606;
  uint256 internal constant P_INSUFFICIENT_POOL_TOKEN_USER_BALANCE = 607;
  uint256 internal constant P_INVALID_INTERVAL_AMOUNT = 608;
  uint256 internal constant P_DELEGATE_WITHDRAW_RECIPIENT_NOT_OWNER = 609;
  uint256 internal constant P_INVALID_OR_EXPIRED_ORDER_ID = 610;
  uint256 internal constant P_WITHDRAW_BY_ORDER_OR_DELEGATE_ONLY = 611;
  uint256 internal constant P_DELEGATE_CANCEL_RECIPIENT_NOT_OWNER = 612;
  uint256 internal constant P_CANCEL_BY_ORDER_OR_DELEGATE_ONLY = 613;
  uint256 internal constant P_INVALID_TOKEN_IN_ADDRESS = 614;
  uint256 internal constant P_INVALID_TOKEN_OUT_ADDRESS = 615;
  uint256 internal constant P_INVALID_POOL_TYPE = 616;
  uint256 internal constant P_NON_EXISTING_POOL = 617;
  uint256 internal constant P_INVALID_POOL_ADDRESS = 618;
  uint256 internal constant P_INVALID_AMOUNT_IN = 619;
  uint256 internal constant P_INSUFFICIENT_TOKEN_IN_USER_BALANCE = 620;
  uint256 internal constant P_POOL_HAS_NO_LIQUIDITY = 621;
  uint256 internal constant P_MAX_ORDER_LENGTH_EXCEEDED = 622;
  uint256 internal constant P_NOT_IMPLEMENTED = 624;
  uint256 internal constant P_MULTICALL_NOT_SUPPORTED = 625;
}

File 38 of 38 : Misc.sol
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved
//
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.7.6;

/// @notice Square-root function for providing initial liquidity.
/// @dev    Sourced from Uniswap V2 library:
///           - https://github.com/Uniswap/v2-core/blob/master/contracts/libraries/Math.sol
/// @dev    Based on Babylonian Method:
///           - https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
/// @param _value Is a number to approximate the square root of.
/// @return root The approximate square root of _value using the Babylonian Method.
///
// solhint-disable-next-line func-visibility
function sqrt(uint256 _value) pure returns (uint256 root) {
  if (_value > 3) {
    root = _value;
    uint256 iteration = _value / 2 + 1;
    while (iteration < root) {
      root = iteration;
      iteration = (_value / iteration + iteration) / 2;
    }
  } else if (_value != 0) {
    root = 1;
  }
}

Settings
{
  "remappings": [
    "@balancer-labs/=node_modules/@balancer-labs/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@rari-capital/=node_modules/@rari-capital/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "hardhat/=node_modules/hardhat/",
    "prb-math/=node_modules/prb-math/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 575
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "istanbul",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20","name":"_token0Inst","type":"address"},{"internalType":"contract IERC20","name":"_token1Inst","type":"address"},{"internalType":"contract IVault","name":"_vaultInst","type":"address"},{"internalType":"string","name":"_poolName","type":"string"},{"internalType":"string","name":"_poolSymbol","type":"string"},{"internalType":"enum ICronV1PoolEnums.PoolType","name":"_poolType","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AdministratorStatusChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"enum ICronV1PoolEnums.BoolParamType","name":"boolParam","type":"uint8"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"BoolParameterChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"}],"name":"ExecuteVirtualOrdersEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"feeAddress","type":"address"}],"name":"FeeAddressChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeShift","type":"uint256"}],"name":"FeeShiftChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"token0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Out","type":"uint256"}],"name":"FeeWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"intervals","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"LongTermSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"enum ICronV1PoolEnums.ParamType","name":"paramType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ParameterChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolTokenAmt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Out","type":"uint256"}],"name":"PoolExit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"token0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"poolTokenAmt","type":"uint256"}],"name":"PoolJoin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"suggestedProtocolFee","type":"uint256"}],"name":"ProtocolFeeTooLarge","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"swapType","type":"uint256"}],"name":"ShortTermSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"partner","type":"address"},{"indexed":true,"internalType":"address","name":"oldList","type":"address"},{"indexed":true,"internalType":"address","name":"newList","type":"address"}],"name":"UpdatedArbitrageList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"partner","type":"address"},{"indexed":false,"internalType":"address","name":"list","type":"address"}],"name":"UpdatedArbitragePartner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"refundToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"proceedsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"proceedsOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"WithdrawLongTermSwap","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_ID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_TYPE","outputs":[{"internalType":"enum ICronV1PoolEnums.PoolType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"decreaseApproval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxBlock","type":"uint256"}],"name":"executeVirtualOrdersToBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBalancerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalancerFeeAmounts","outputs":[{"internalType":"uint256","name":"balFee0U96","type":"uint256"},{"internalType":"uint256","name":"balFee1U96","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCronFeeAmounts","outputs":[{"internalType":"uint256","name":"cronFee0U96","type":"uint256"},{"internalType":"uint256","name":"cronFee1U96","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeShift","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastVirtualOrderBlock","outputs":[{"internalType":"uint256","name":"lastVirtualOrderBlock","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLongTermFeePoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderId","type":"uint256"}],"name":"getOrder","outputs":[{"components":[{"internalType":"bool","name":"token0To1","type":"bool"},{"internalType":"uint112","name":"salesRate","type":"uint112"},{"internalType":"uint128","name":"scaledProceedsAtSubmissionU128","type":"uint128"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint256","name":"orderExpiry","type":"uint256"}],"internalType":"struct Order","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOrderAmounts","outputs":[{"internalType":"uint256","name":"orders0U112","type":"uint256"},{"internalType":"uint256","name":"orders1U112","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOrderIdCount","outputs":[{"internalType":"uint256","name":"nextOrderId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_offset","type":"uint256"},{"internalType":"uint256","name":"_maxResults","type":"uint256"}],"name":"getOrderIds","outputs":[{"internalType":"uint256[]","name":"orderIds","type":"uint256[]"},{"internalType":"uint256","name":"numResults","type":"uint256"},{"internalType":"uint256","name":"totalResults","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPartnerFeePoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPriceOracle","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"token0U256F112","type":"uint256"},{"internalType":"uint256","name":"token1U256F112","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProceedAmounts","outputs":[{"internalType":"uint256","name":"proceeds0U112","type":"uint256"},{"internalType":"uint256","name":"proceeds1U112","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSalesRates","outputs":[{"internalType":"uint256","name":"salesRate0U112","type":"uint256"},{"internalType":"uint256","name":"salesRate1U112","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"getSalesRatesEndingPerBlock","outputs":[{"internalType":"uint256","name":"salesRateEndingPerBlock0U112","type":"uint256"},{"internalType":"uint256","name":"salesRateEndingPerBlock1U112","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getShortTermFeePoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxBlock","type":"uint256"}],"name":"getVirtualPriceOracle","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"token0U256F112","type":"uint256"},{"internalType":"uint256","name":"token1U256F112","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxBlock","type":"uint256"},{"internalType":"bool","name":"_paused","type":"bool"}],"name":"getVirtualReserves","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"token0ReserveU112","type":"uint256"},{"internalType":"uint256","name":"token1ReserveU112","type":"uint256"},{"internalType":"uint256","name":"token0OrdersU112","type":"uint256"},{"internalType":"uint256","name":"token1OrdersU112","type":"uint256"},{"internalType":"uint256","name":"token0ProceedsU112","type":"uint256"},{"internalType":"uint256","name":"token1ProceedsU112","type":"uint256"},{"internalType":"uint256","name":"token0BalancerFeesU96","type":"uint256"},{"internalType":"uint256","name":"token1BalancerFeesU96","type":"uint256"},{"internalType":"uint256","name":"token0CronFiFeesU96","type":"uint256"},{"internalType":"uint256","name":"token1CronFiFeesU96","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseApproval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isCollectingBalancerFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCollectingCronFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_poolId","type":"bytes32"},{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256[]","name":"_currentBalancesU112","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"_protocolFeeDU1F18","type":"uint256"},{"internalType":"bytes","name":"_userData","type":"bytes"}],"name":"onExitPool","outputs":[{"internalType":"uint256[]","name":"amountsOutU112","type":"uint256[]"},{"internalType":"uint256[]","name":"dueProtocolFeeAmountsU96","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_poolId","type":"bytes32"},{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256[]","name":"_currentBalancesU112","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"_protocolFeeDU1F18","type":"uint256"},{"internalType":"bytes","name":"_userData","type":"bytes"}],"name":"onJoinPool","outputs":[{"internalType":"uint256[]","name":"amountsInU112","type":"uint256[]"},{"internalType":"uint256[]","name":"dueProtocolFeeAmountsU96","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum IVault.SwapKind","name":"kind","type":"uint8"},{"internalType":"contract IERC20","name":"tokenIn","type":"address"},{"internalType":"contract IERC20","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint256","name":"lastChangeBlock","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"userData","type":"bytes"}],"internalType":"struct IPoolSwapStructs.SwapRequest","name":"_swapRequest","type":"tuple"},{"internalType":"uint256","name":"_currentBalanceTokenInU112","type":"uint256"},{"internalType":"uint256","name":"_currentBalanceTokenOutU112","type":"uint256"}],"name":"onSwap","outputs":[{"internalType":"uint256","name":"amountOutU112","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setAdminStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_arbPartner","type":"address"},{"internalType":"address","name":"_arbitrageList","type":"address"}],"name":"setArbitragePartner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_collectBalancerFee","type":"bool"}],"name":"setCollectBalancerFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeDestination","type":"address"}],"name":"setFeeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_feeShift","type":"uint256"}],"name":"setFeeShift","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_paramTypeU","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setParameter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_pauseValue","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateArbitrageList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061032b5760003560e01c80638705fcd4116101b2578063d505accf116100f9578063e0b20d6c116100a2578063ee7293561161007c578063ee72935614610658578063f449dbc814610660578063fa6c4b4f14610668578063fca513a81461067b5761032b565b8063e0b20d6c14610640578063e0d7d0e914610648578063e84b1f3f146106505761032b565b8063dd62ed3e116100d3578063dd62ed3e14610612578063ddba751414610625578063df0f11af1461062d5761032b565b8063d505accf146105d9578063d5c096c4146105ec578063d73dd623146105ff5761032b565b8063b187bd261161015b578063bedb86fb11610135578063bedb86fb14610584578063bf37843c14610597578063d09ef241146105b95761032b565b8063b187bd261461056c578063b4456b3614610574578063bc36419f1461057c5761032b565b8063a511eb7c1161018c578063a511eb7c1461053e578063a6c449c114610546578063a9059cbb146105595761032b565b80638705fcd41461051057806395d89b41146105235780639d2c110c1461052b5761032b565b80633644e5151161027657806374f3b0091161021f5780637ca2c357116101f95780637ca2c357146104ed5780637ecebe00146104f55780637f6de94b146105085761032b565b806374f3b009146104b157806377f53c2f146104d257806378b45070146104da5761032b565b8063648da85911610250578063648da85914610476578063661884631461048b57806370a082311461049e5761032b565b80633644e515146104315780634f5bee44146104395780634ff643211461044c5761032b565b806323b872dd116102d857806330c55ddb116102b257806330c55ddb146103ff57806330c5ee4614610407578063313ce5671461041c5761032b565b806323b872dd146103c357806327ca08a2146103d65780633023f7c8146103e95761032b565b806318160ddd1161030957806318160ddd146103835780631822a4211461039857806319ebefa7146103a05761032b565b806306fdde0314610330578063095ea7b31461034e57806312ee63f31461036e575b600080fd5b610338610692565b604051610345919061462c565b60405180910390f35b61036161035c366004614082565b610729565b60405161034591906145d1565b61038161037c366004614455565b610740565b005b61038b610854565b60405161034591906145dc565b61038b61085a565b6103b36103ae3660046143af565b61086e565b604051610345949392919061472e565b6103616103d1366004613fa0565b6108de565b6103816103e43660046141ad565b61095f565b6103f16109d9565b6040516103459291906146e6565b6103616109f0565b61040f610a07565b60405161034591906144d3565b610424610b22565b6040516103459190614797565b61038b610b27565b610381610447366004614055565b610b31565b61045f61045a366004614431565b610ba7565b6040516103459b9a99989796959493929190614749565b61047e610c8e565b6040516103459190614619565b610361610499366004614082565b610cb2565b61038b6104ac366004613f30565b610d0c565b6104c46104bf3660046141e5565b610d2b565b604051610345929190614587565b61038b610ea4565b6103816104e83660046143af565b610eaa565b61038b610f18565b61038b610503366004613f30565b610f27565b6103f1610f42565b61038161051e366004613f30565b610f50565b610338610fe4565b61038b6105393660046142b8565b611045565b6103f16112fd565b6103f16105543660046143af565b611315565b610361610567366004614082565b611339565b610361611346565b61038b611356565b6103f1611363565b6103816105923660046141ad565b611371565b6105aa6105a53660046140ad565b6113d8565b604051610345939291906145ac565b6105cc6105c73660046143af565b6114a8565b604051610345919061467f565b6103816105e7366004613fe0565b611530565b6104c46105fa3660046141e5565b6116a0565b61036161060d366004614082565b611864565b61038b610620366004613f68565b61189a565b6103616118c5565b61038161063b366004613f68565b6118d5565b61038b611958565b61038b611965565b61038b611989565b6103f161198f565b61038b61199d565b6103816106763660046143af565b6119ac565b610683611b02565b604051610345939291906146f4565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561071e5780601f106106f35761010080835404028352916020019161071e565b820191906000526020600020905b81548152906001019060200180831161070157829003601f168201915b505050505090505b90565b6000610736338484611b9d565b5060015b92915050565b610748611bff565b610750611c1f565b600082600281111561075e57fe5b905060008082600281111561076f57fe5b141561077d575060f46107b1565b600182600281111561078b57fe5b1415610799575060ea6107b1565b60028260028111156107a757fe5b14156107b1575060e05b8260028360028111156107c057fe5b111580156107cd57508115155b80156107db57506103e88411155b156107f5576107ed600e548284611b24565b600e55610802565b6108026000610193611c38565b336001600160a01b03167f225ac322492a1d8feb500fcd29c839e1e846186ea09a2f37d3bb8974f4e85f45848360405161083d929190614602565b60405180910390a2505050610850611c90565b5050565b60025490565b6000610869600e5460e0611c97565b905090565b60008060008061087c613ce1565b610887866000611c9f565b600c54909350909150156108d65760006108a083611de5565b60125460135463ffffffff928316995090975095509091508116156108d457816101a0015185019450816101c00151840193505b505b509193509193565b6001600160a01b0383166000818152600160209081526040808320338085529252822054919261091c9114806109145750838210155b610197611e31565b610927858585611e3f565b336001600160a01b0386161480159061094257506000198114155b15610954576109548533858403611b9d565b506001949350505050565b610967611f09565b61096f611c1f565b61098e60115482610981576000610984565b60015b60ff1660fd611b6c565b60115560405133907f9d7e9829e0e608d50be70896ea553fe5ba9db33c11a836495412b8b8481aa4dd906109c69060019085906145e5565b60405180910390a26109d6611c90565b50565b6000806109e7600f54611fb8565b90939092509050565b600080610a0060115460fd611fcd565b1415905090565b6000610a11611fd4565b610a19611c1f565b336000908152601560205260408120546001600160a01b0316805b809250826001600160a01b03166355fcff3c6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610a7257600080fd5b505af1158015610a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aaa9190613f4c565b90506001600160a01b038116610a34573360008181526015602052604080822080546001600160a01b0319166001600160a01b03888116918217909255915191939086169290917fe0e07986073e388ace1090c16866621627f1f1299f7aa008589d4d45b61034ab9190a45090915050610726611c90565b601290565b6000610869611ffa565b610b39611f09565b610b41611c1f565b6001600160a01b03821660008181526014602052604090819020805460ff19168415151790555133907fe8b9bbf829bebf34c09fc3a715a8e78509552d2d567e7682e24229f1c166930590610b979085906145d1565b60405180910390a3610850611c90565b6000806000806000806000806000806000610bc0613ce1565b610bca8e8e611c9f565b809d50819250505080600001519a5080602001519950600080610bee600e54611fb8565b9150915082610120015182039a5082610140015181039950600080610c14600f54611fb8565b9150915084610160015182019a5084610180015181019950600080610c3a6011546120b8565b915091508660a0015182019a508660c0015181019950600080610c5e6010546120b8565b915091508860e0015182019a50886101000151810199505050505050505050509295989b509295989b9093969950565b7f000000000000000000000000000000000000000000000000000000000000000181565b3360009081526001602090815260408083206001600160a01b0386168452909152812054808310610cee57610ce933856000611b9d565b610d02565b610d023385610cfd84876120cd565b611b9d565b5060019392505050565b6001600160a01b0381166000908152602081905260409020545b919050565b606080610d38338b6120e3565b60008080610d4886880188614455565b92509050806003811115610d5857fe5b925060009050806003846003811115610d6d57fe5b1415610d8657610d7c8d61214f565b9092509050610e34565b600080610dbc8d600081518110610d9957fe5b60200260200101518e600181518110610dae57fe5b6020026020010151436121fb565b90925090506000866003811115610dcf57fe5b1415610de757610de18f8684846122e3565b90945092505b5060019050846003811115610df857fe5b1480610e0f57506002846003811115610e0d57fe5b145b15610e3457610e2e838e8e6002886003811115610e2857fe5b14612371565b90925090505b60408051600280825260608201835290916020830190803683370190505095508186600081518110610e6257fe5b6020026020010181815250508086600181518110610e7c57fe5b602002602001018181525050610e9189612574565b9450505050509850989650505050505050565b600c5490565b610eb2611f09565b610eba611c1f565b610ed760018210158015610ecf575060048211155b610193611c38565b610ee360105482611b48565b60105560405133907f5acdc353c39f707c917be1fcdfd9f0ef222602d718c114688a00abd2fc80e8fd906109c69084906145dc565b6000610869600e5460f4611c97565b6001600160a01b031660009081526005602052604090205490565b6000806109e76010546120b8565b610f58611f09565b610f60611c1f565b601680546001600160a01b0319166001600160a01b038316908117909155600090610f8c576000610f8f565b60015b60ff169050610fa26011548260fe611b6c565b6011556040516001600160a01b0383169033907fb5c9ea0e9f459f0f9f5e75158bb0d1e656d758ede76198f5843b070d1676e51790600090a3506109d6611c90565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561071e5780601f106106f35761010080835404028352916020019161071e565b600061104f61264f565b611085336001600160a01b037f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c816146004611c38565b602084015160c085015160608601516110ae6000885160018111156110a657fe5b1460c9611c38565b6000808861010001518060200190518101906110ca9190614476565b9150915060008260028111156110dc57fe5b905060007f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b0316876001600160a01b031614905060008061113d83611128578b61112a565b8c5b84611135578d611137565b8c5b436121fb565b9092509050600184600281111561115057fe5b14156111cc5760e08d0151600061116a8a83878c8b612662565b90508a6001600160a01b0316826001600160a01b03168b6001600160a01b03167f5aba2dae7ef407628bfbaf988767303f2c5ac508d40c737736efe9536c52fbeb8c8b866040516111bd939291906146f4565b60405180910390a450506112ed565b60028460028111156111da57fe5b1415611280576001600160a01b038086166000908152601560205260409081902054905163387f517760e01b815291169061127e90829063387f517790611225908d906004016144d3565b602060405180830381600087803b15801561123f57600080fd5b505af1158015611253573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127791906141c9565b6005611c38565b505b61129b83600086600281111561129257fe5b1489858561292c565b9950886001600160a01b0316886001600160a01b03167f37e41badd72aca9bedfbfa5f3c99a2b3b408e7b966e604063a37af67cd081d10898d8a6040516112e4939291906146f4565b60405180910390a35b5050505050505050509392505050565b6007546001600160701b03607082901c811692911690565b600081815260096020526040812054819061132f90611fb8565b9094909350915050565b6000610736338484611e3f565b600080610a0060115460ff611fcd565b6000610869601154612a06565b6000806109e7600e54611fb8565b611379611bff565b611381611c1f565b6113a060115482611393576000611396565b60015b60ff1660ff611b6c565b60115560405133907f9d7e9829e0e608d50be70896ea553fe5ba9db33c11a836495412b8b8481aa4dd906109c69060009085906145e5565b6001600160a01b03831660009081526017602052604081208054606092918285156114035785611405565b825b90508067ffffffffffffffff8111801561141e57600080fd5b50604051908082528060200260200182016040528015611448578160200160208202803683370190505b509450865b818510801561145b57508381105b1561149c5782818154811061146c57fe5b906000526020600020015486868060010197508151811061148957fe5b602090810291909101015260010161144d565b50505093509350939050565b6114b0613d57565b506000908152600b6020908152604091829020825160c081018452815460ff81161515825261010081046001600160701b031693820193909352600160781b9092046001600160801b03169282019290925260018201546001600160a01b039081166060830152600283015416608082015260039091015460a082015290565b61153e8442111560d1611e31565b6001600160a01b0380881660008181526005602090815260408083205481517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98185015280830195909552948b166060850152608084018a905260a0840185905260c08085018a90528151808603909101815260e090940190528251920191909120906115ca82612a16565b9050600060018288888860405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b5050604051601f190151915061166a90506001600160a01b0382161580159061166257508b6001600160a01b0316826001600160a01b0316145b6101f8611e31565b6001600160a01b038b1660009081526005602052604090206001850190556116938b8b8b611b9d565b5050505050505050505050565b6060806116ad338b6120e3565b60006060816116be868801886143c7565b909650925090508060018111156116d157fe5b9250506000846000815181106116e357fe5b602002602001015190506000856001815181106116fc57fe5b602002602001015190506000611710611346565b6117de576000808087600181111561172457fe5b1480156117375750611734610854565b15155b8061174d5750600187600181111561174b57fe5b145b1561177d576117778e60008151811061176257fe5b60200260200101518f600181518110610dae57fe5b90925090505b600087600181111561178b57fe5b14156117d7576117c78585886000815181106117a357fe5b6020026020010151896001815181106117b857fe5b60200260200101518686612a62565b6117d48f86868585612abc565b92505b50506117f8565b6117f860008660018111156117ef57fe5b14156064611c38565b8c6001600160a01b03168e6001600160a01b03167f54f5163abddef0c7b641204242ef436658d731009e7630bd815e3d52383b0c7085858560405161183f939291906146f4565b60405180910390a36118508a612574565b955050505050509850989650505050505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610736918590610cfd9086612b80565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080610a0060115460fe611fcd565b6118dd611bff565b6118e5611c1f565b6001600160a01b038281166000908152601560205260409081902080546001600160a01b031916928416929092179091555133907f14298ee83383fc0fa7913c34ea36b3209010c03d0a5a09c606462f80a5f25e589061194890859085906144e7565b60405180910390a2610850611c90565b6000610869601054612b99565b7f6910c4e32d425a834fb61e983c8083a84b0ebd0100020000000000000000053281565b600d5490565b6000806109e76011546120b8565b6000610869600e5460ea611c97565b6119b4611c1f565b6119bc612ba2565b604051631f29a8cd60e31b81526000906001600160a01b037f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8169063f94d466890611a2b907f6910c4e32d425a834fb61e983c8083a84b0ebd01000200000000000000000532906004016145dc565b60006040518083038186803b158015611a4357600080fd5b505afa158015611a57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a7f91908101906140e1565b50915050611ab681600081518110611a9357fe5b602002602001015182600181518110611aa857fe5b6020026020010151846121fb565b5050336001600160a01b03167f9b6574d1f0a54e6f2d8510751cd8764d42de16aa67970a9bc544cb1028bcdb2683604051611af191906145dc565b60405180910390a2506109d6611c90565b6000806000611b12600f54612cb2565b60125460135491959094509092509050565b6000611b376103ff841115610190611c38565b506103ff811b1992909216911b1790565b6000611b5a6007831115610190611c38565b5060de1b600760de1b19919091161790565b6000611b7e6001841115610190611c38565b6001821b83611b905780198516611b94565b8085175b95945050505050565b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b33600090815260146020526040902054611c1d9060ff166002611c38565b565b611c3160026006541415610190611e31565b6002600655565b816108505762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b6001600655565b1c6103ff1690565b611ca7613ce1565b600083600760050154108015611cbd5750438411155b611cc75743611cc9565b835b9050611cd3612ba2565b604051631f29a8cd60e31b81526000906001600160a01b037f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8169063f94d466890611d42907f6910c4e32d425a834fb61e983c8083a84b0ebd01000200000000000000000532906004016145dc565b60006040518083038186803b158015611d5a57600080fd5b505afa158015611d6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d9691908101906140e1565b50915050611dcc81600081518110611daa57fe5b602002602001015182600181518110611dbf57fe5b6020026020010151612cb8565b925083611ddd57611ddd8383612dcb565b509250929050565b6000806000611df5600f54612cb2565b905043841415611e0e5764010000000042069150611e27565b640100000000844303600c02420381611e2357fe5b0691505b8103939092509050565b816108505761085081612e6b565b6001600160a01b038316600090815260208190526040902054611e6782821015610196611e31565b611e7e6001600160a01b0384161515610199611e31565b6001600160a01b03808516600090815260208190526040808220858503905591851681522054611eae9083612b80565b6001600160a01b038085166000818152602081815260409182902094909455805186815290519193928816927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a350505050565b611c1d7f000000000000000000000000d64c9cd98949c07f3c85730a37c13f4e78f35e776001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f6557600080fd5b505afa158015611f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9d9190613f4c565b6001600160a01b0316336001600160a01b0316146001611c38565b6001600160701b03607082901c811692911690565b1c60011690565b33600090815260156020526040902054611c1d906001600160a01b031615156003611c38565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f9acfa59f48dbcd893b8810bbbadf63c8db2800909d26867aa59b1561606e72737fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6612067612ebe565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b031681526020019550505050505060405160208183030381529060405280519060200120905090565b6001600160601b03606082901c811692911690565b60006120dd838311156001611e31565b50900390565b6121227f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c86001600160a01b0316836001600160a01b0316146004611c38565b6108507f6910c4e32d425a834fb61e983c8083a84b0ebd01000200000000000000000532821460ce611c38565b6016546000908190612170906001600160a01b038581169116146007611c38565b60115461218b60006121838360fc611fcd565b1460e0611c38565b612196601054612ec2565b601092909255935091506121ad81600160fc611b6c565b6011556040516001600160a01b038516907f5c170c563ea37bd3580cc3b1c6d0d9e35c29182d2e26ce550917bdb7ef34bbcd906121ed90869086906146e6565b60405180910390a250915091565b600080826007600501541080156122125750438311155b61221a574392505b60115460006122298787612cb8565b9050612233611346565b6122ce576122418186612ef3565b6080810151156122745761225882600060fc611b6c565b91506122706010548260e00151836101000151612fee565b6010555b612287828260a001518360c00151612fee565b6011819055506122a4600e5482610120015183610140015161309c565b600e819055506122c1600f54826101600151836101800151613102565b600f556122ce818661317b565b80516020909101519097909650945050505050565b60008060006122f0610854565b90506122fc87876131d2565b6123108161230a878961326d565b90613291565b92506123208161230a868961326d565b9150866001600160a01b03167f998379a72dc64811ce41782d18dd7759dd22de4dd43049986376fada953be82987858560405161235f939291906146f4565b60405180910390a25094509492505050565b6000848152600b6020819052604082206001810154839261239d916001600160a01b0316151590611c38565b60018101546001600160a01b038781169116146123d25760018101546123d2906001600160a01b03878116911614600a611c38565b600181015461241e906001600160a01b0388811691161480612403575060028201546001600160a01b038881169116145b801561241757506001600160a01b03871615155b6008611c38565b600080600061242d84886132b1565b9250925092508261245e577f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0612480565b7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc63935b6001600160a01b0316876124955760006124e2565b836124c0577f000000000000000000000000ae78736cd615f374d3085123a210448e74fc63936124e2565b7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca05b6001600160a01b03168560010160009054906101000a90046001600160a01b03166001600160a01b03167f4ad2915cd8cef57cf8d82983f8bf82308eb03cc31daaa0945f04baf7f2b2611585858f8f604051612541949392919061470a565b60405180910390a4826125545780612556565b815b9550826125635781612565565b805b94505050505094509492505050565b601154606090670de0b6b3a764000083116125b057600061259482612a06565b90508381146125aa576125a782856134c5565b91505b506125e8565b7fc527124565765c9f70b694614157f3cbebdb44c48483db5817d4cc5fa55e6926836040516125df91906145dc565b60405180910390a15b604080516002808252606082018352909160208301908036833701905050915061261181612ec2565b601160008660008151811061262257fe5b602002602001018760018151811061263657fe5b6020908102919091010193909352509190525550919050565b611c1d61265a611346565b156064611c38565b60006126977f000000000000000000000000000000000000000000000000000000000000ab3662ffffff1683111560df611c38565b60007f000000000000000000000000000000000000000000000000000000000000012c61ffff1643816126c657fe5b0643908103915061ffff7f000000000000000000000000000000000000000000000000000000000000012c1660018501028201908103600081878161270757fe5b0490506127186000821160d0611c38565b60078054612741908a61272c57600061272e565b835b8b612739578461273c565b60005b613102565b81556000848152600282016020526040902054612764908a61272c57600061272e565b81600201600086815260200190815260200160002081905550600760060160008154809291906001019190505595506040518060c001604052808a15158152602001836001600160701b031681526020016127c383600101548c61350b565b6001600160801b031681526020018c6001600160a01b031681526020018b6001600160a01b03168d6001600160a01b03161415612801576000612803565b8b5b6001600160a01b03908116825260209182018790526000898152600b835260408082208551815487870151888501516001600160801b0316600160781b027fff00000000000000000000000000000000ffffffffffffffffffffffffffffff6001600160701b03909216610100026effffffffffffffffffffffffffff001994151560ff19909416939093179390931691909117161781556060860151600180830180549287166001600160a01b03199384161790556080880151600284018054918816919093161790915560a090960151600390910155918f168152601783529081208054938401815581522001869055600e5482840290612919908b61290c57600061290e565b825b8c612739578361273c565b600e5550949a9950505050505050505050565b600080612949600e54876129415760ea612944565b60f45b611c97565b9050600061295c868302620186a0613291565b601154909150600061296f8260fd611fcd565b905080156129c757600061298283612a06565b9050600061299a858302670de0b6b3a7640000613291565b90506129c1848d6129ac5760006129ae565b825b8e6129b957836129bc565b60005b612fee565b60115550505b8288036000818c6129d857886129da565b895b0190506129f681838e6129ed578b6129ef565b8a5b0290613291565b9c9b505050505050505050505050565b60c01c670fffffffffffffff1690565b6000612a20611ffa565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b600082118015612a725750600081115b15612ab45760008183870281612a8457fe5b04905060008383890281612a9457fe5b049050612ab1868310158015612aaa5750858210155b60e4611c38565b50505b505050505050565b6000612ad46001600160a01b038716151560e2611c38565b6000612ade610854565b905080612b3757612b016103e887118015612afa57506103e886115b60cc611c38565b6103e8612b0f868802613529565b039150612b1e600f544261357a565b600f55612b2e60006103e86135b8565b43600c55612b5e565b612b5b612b488561230a898561326d565b612b568561230a898661326d565b613649565b91505b612b6c6000831160cc611c38565b612b7687836135b8565b5095945050505050565b6000828201612b928482101583611e31565b9392505050565b60de1c60071690565b604080516001808252818301909252600091816020015b612bc1613d8c565b815260200190600190039081612bb9579050509050600181600081518110612be557fe5b6020026020010151600001906003811115612bfc57fe5b90816003811115612c0957fe5b815250503081600081518110612c1b57fe5b60209081029190910101516001600160a01b039182166060909101526040516303a38fa160e21b81527f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c890911690630e8e3e8490612c7d908490600401614501565b600060405180830381600087803b158015612c9757600080fd5b505af1158015612cab573d6000803e3d6000fd5b5050505050565b60e01c90565b612cc0613ce1565b600080612ccd858561365f565b60115491935091506000612ce28260fe611fcd565b90506000612cf18360fd611fcd565b90506000612cfe84612a06565b9050600082612d1557670de0b6b3a7640000612d21565b81670de0b6b3a7640000035b90506000808515612d4c57612d37601054612b99565b91508160020a6001018381612d4857fe5b0490505b604051806101e001604052808a81526020018981526020018481526020018281526020018381526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815250995050505050505050505092915050565b6000612dda600e5460e0611c97565b9050600080612de76136f7565b815191935091505b84821015612e4957612e0386838587613772565b612e0e86828461386c565b508151612e1b82846138d8565b7f000000000000000000000000000000000000000000000000000000000000012c61ffff1682019150612def565b82518514612ab457849150612e6086838587613772565b612ab486828761386c565b62461bcd60e51b6000908152602060045260076024526642414c23000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b4690565b77ffffffffffffffffffffffffffffffffffffffffffffffff198116916001600160601b03606083901c8116921690565b6000612f02600e5460e0611c97565b9050600080612f0f6136f7565b815191935091505b84821015612f7b57612f2b86838587613772565b612f3686828461386c565b508151612f4382846138d8565b612f4d8383613946565b7f000000000000000000000000000000000000000000000000000000000000012c61ffff1682019150612f17565b82518514612fb157849150612f9286838587613772565b612f9d86828761386c565b612fa782846138d8565b612fb18383613946565b8251600c5560608301516080840151612fcc9160009161396b565b60075560208301516040840151612fe391906139b2565b600855505050505050565b60006001600160601b03606085901c811690851684156130395761301b8583600019031015610191611c38565b908401906001600160601b03821115613039576001600160601b0391505b831561306d576130528482600019031015610191611c38565b83016001600160601b0381111561306d57506001600160601b035b77ffffffffffffffffffffffffffffffffffffffffffffffff199590951660609190911b179093179392505050565b60006001600160701b03607085901c811690851684156130cb576130c585831015610192611c38565b84820391505b83156130e4576130e084821015610192611c38565b8390035b6001600160e01b03199590951660709190911b179093179392505050565b60006001600160701b03607085901c8116908516841561313b5761313685836001600160701b038016031015610191611c38565b908401905b83156130e45761315b84826001600160701b038016031015610191611c38565b83016001600160e01b03199590951660709190911b179093179392505050565b60008061318783611de5565b909250905063ffffffff8216156131b7576101a08401516012805490910190556101c08401516013805490910190555b6131c9600f548263ffffffff1661357a565b600f5550505050565b6001600160a01b0382166000908152602081905260409020546131fa82821015610196611e31565b6001600160a01b0383166000908152602081905260409020828203905560025461322490836120cd565b6002556040805183815290516000916001600160a01b038616917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3505050565b6000828202612b9284158061328a57508385838161328757fe5b04145b6003611e31565b60006132a08215156004611e31565b8183816132a957fe5b049392505050565b8154600383015460ff821691600091829161010090046001600160701b031685156132f057600c546132e690831160e3611c38565b600c548203810293505b60006133248715801561330257508343115b61330e5760085461331e565b6000848152600a60205260409020545b8761350b565b8854909150613346908290600160781b90046001600160801b031684896139eb565b9350861580156133565750824311155b156133935787547fff00000000000000000000000000000000ffffffffffffffffffffffffffffff16600160781b6001600160801b038316021788555b6133ac60008611806133a55750600085115b60d4611c38565b861561343457600780546133db90886133c65760006133c8565b845b896133d357856133d6565b60005b61309c565b815560008481526002820160205260409020546133fe90886133c65760006133c8565b6000858152600283016020526040902055600e5461342f9088613422576000613424565b875b896133d357886133d6565b600e55505b61345a600f54876134455785613448565b60005b886134545760006133d6565b8661309c565b600f55868061346857508243115b156134bb5787547fff000000000000000000000000000000000000000000000000000000000000001688556001880180546001600160a01b03199081169091556002890180549091169055600060038901555b5050509250925092565b60006134de670fffffffffffffff831115610190611c38565b5060c01b7ff000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b600081613521576001600160801b038316612b92565b505060801c90565b6000600382111561356c575080600160028204015b818110156135665780915060028182858161355557fe5b04018161355e57fe5b04905061353e565b50610d26565b8115610d2657506001919050565b600061358f63ffffffff831115610190611c38565b5060e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b6001600160a01b0382166000908152602081905260409020546135db9082612b80565b6001600160a01b0383166000908152602081905260409020556002546136019082612b80565b6002556040805182815290516001600160a01b038416916000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b60008183106136585781612b92565b5090919050565b600080600080613670600e54611fb8565b91509150600080613682600f54611fb8565b6011549193509150600080613696836120b8565b9150915060008060006136aa8660fc611fcd565b9050806136c2576136bc6010546120b8565b90935091505b6136d28e8b8a01870185016120cd565b9b506136e48d8a8901860184016120cd565b9a50505050505050505050509250929050565b6136ff613dbc565b600c5460009061ffff7f000000000000000000000000000000000000000000000000000000000000012c1680828161373357fe5b838652600754919006830391909101925061374d90611fb8565b6080850152606084015260085461376390613a5b565b60408501526020840152509091565b815160608301516080840151918503918183026000613796868302620186a0613a6f565b905082850260006137ac888302620186a0613a6f565b90506000806137bc8d8685613aa2565b915091506000806137d28f888a03878903613ba2565b91509150878f610120018181510191508181525050858f6101400181815101915081815250506138106001600160701b038016831115610191611c38565b6138266001600160701b03821115610191611c38565b6101608f018051830190526101808f018051820190528e5184018f5260208f018051840190526138598d83838d8d613c45565b5050509990985250505050505050505050565b8251600c8383030290156138a1578351602085015182919060701b8161388e57fe5b0402846101a00181815101915081815250505b6020840151156138d2576020840151845182919060701b816138bf57fe5b0402846101c00181815101915081815250505b50505050565b60008281526009602052604081205481906138f290611fb8565b9092509050811561391c576139108284606001511015610192611c38565b60608301805183900390525b80156138d2576139358184608001511015610192611c38565b608083018051829003905250505050565b613958826020015183604001516139b2565b6000918252600a60205260409091205550565b60006139836001600160701b03841115610190611c38565b6139996001600160701b03831115610190611c38565b506001600160e01b03199290921660709190911b171790565b60006139ca6001600160801b03841115610190611c38565b6139e06001600160801b03831115610190611c38565b5060809190911b1790565b60006001600160801b038486031682613a24577f0000000000000000000000000000000000000000000000008ac7230489e80000613a46565b7f0000000000000000000000000000000000000000000000008ac7230489e800005b84820281613a5057fe5b049695505050505050565b608081901c916001600160801b0390911690565b6000613a7e8215156004611e31565b82613a8b5750600061073a565b816001840381613a9757fe5b04600101905061073a565b60808301516040840151600091829181613b1057613aca868202670de0b6b3a7640000613291565b9350613ae0858202670de0b6b3a7640000613291565b9250670de0b6b3a7640000811015613b0b5760a08701805185880301905260c0870180518487030190525b613b98565b60608701516000613b2b888302670de0b6b3a7640000613291565b90506000613b43888402670de0b6b3a7640000613291565b82861b975080861b96509050670de0b6b3a7640000841015613b7e5760a08a018051888b0384900301905260c08a018051878a038390030190525b60e08a018051909201909152610100890180519091019052505b5050935093915050565b60008083151580613bb257508215155b15613c3d578451602086015185613bdd5784018082860281613bd057fe5b0493508382039150613c34565b84613bfc57908501908181870281613bf157fe5b049250829003613c34565b81860181860160008184840281613c0f57fe5b04905060008383870281613c1f57fe5b94839003985090930491829003955093509150505b90865260208601525b935093915050565b8115613c9057817f0000000000000000000000000000000000000000000000008ac7230489e80000840281613c7657fe5b048560200151016001600160801b03168560200181815250505b8015612cab57807f0000000000000000000000000000000000000000000000008ac7230489e80000850281613cc157fe5b048560400151016001600160801b03168560400181815250505050505050565b604051806101e001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8035610d26816147f1565b600082601f830112613e06578081fd5b81356020613e1b613e16836147c9565b6147a5565b8281528181019085830183850287018401881015613e37578586fd5b855b85811015613e5557813584529284019290840190600101613e39565b5090979650505050505050565b600082601f830112613e72578081fd5b81516020613e82613e16836147c9565b8281528181019085830183850287018401881015613e9e578586fd5b855b85811015613e5557815184529284019290840190600101613ea0565b600082601f830112613ecc578081fd5b813567ffffffffffffffff811115613ee057fe5b613ef3601f8201601f19166020016147a5565b818152846020838601011115613f07578283fd5b816020850160208301379081016020019190915292915050565b803560028110610d2657600080fd5b600060208284031215613f41578081fd5b8135612b92816147f1565b600060208284031215613f5d578081fd5b8151612b92816147f1565b60008060408385031215613f7a578081fd5b8235613f85816147f1565b91506020830135613f95816147f1565b809150509250929050565b600080600060608486031215613fb4578081fd5b8335613fbf816147f1565b92506020840135613fcf816147f1565b929592945050506040919091013590565b600080600080600080600060e0888a031215613ffa578485fd5b8735614005816147f1565b96506020880135614015816147f1565b95506040880135945060608801359350608088013560ff81168114614038578384fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215614067578182fd5b8235614072816147f1565b91506020830135613f9581614806565b60008060408385031215614094578182fd5b823561409f816147f1565b946020939093013593505050565b6000806000606084860312156140c1578081fd5b83356140cc816147f1565b95602085013595506040909401359392505050565b6000806000606084860312156140f5578081fd5b835167ffffffffffffffff8082111561410c578283fd5b818601915086601f83011261411f578283fd5b8151602061412f613e16836147c9565b82815281810190858301838502870184018c101561414b578788fd5b8796505b84871015614176578051614162816147f1565b83526001969096019591830191830161414f565b509189015191975090935050508082111561418f578283fd5b5061419c86828701613e62565b925050604084015190509250925092565b6000602082840312156141be578081fd5b8135612b9281614806565b6000602082840312156141da578081fd5b8151612b9281614806565b60008060008060008060008060e0898b031215614200578182fd5b883597506020890135614212816147f1565b96506040890135614222816147f1565b9550606089013567ffffffffffffffff8082111561423e578384fd5b61424a8c838d01613df6565b965060808b0135955060a08b0135945060c08b013591508082111561426d578384fd5b818b0191508b601f830112614280578384fd5b81358181111561428e578485fd5b8c602082850101111561429f578485fd5b6020830194508093505050509295985092959890939650565b6000806000606084860312156142cc578081fd5b833567ffffffffffffffff808211156142e3578283fd5b81860191506101208083890312156142f9578384fd5b614302816147a5565b905061430d83613f21565b815261431b60208401613deb565b602082015261432c60408401613deb565b6040820152606083013560608201526080830135608082015260a083013560a082015261435b60c08401613deb565b60c082015261436c60e08401613deb565b60e08201526101008084013583811115614384578586fd5b6143908a828701613ebc565b9183019190915250976020870135975060409096013595945050505050565b6000602082840312156143c0578081fd5b5035919050565b6000806000606084860312156143db578081fd5b83359250602084013567ffffffffffffffff808211156143f9578283fd5b61440587838801613df6565b9350604086013591508082111561441a578283fd5b5061442786828701613df6565b9150509250925092565b60008060408385031215614443578182fd5b823591506020830135613f9581614806565b60008060408385031215614467578182fd5b50508035926020909101359150565b60008060408385031215614488578182fd5b505080516020909101519092909150565b6000815180845260208085019450808401835b838110156144c8578151875295820195908201906001016144ac565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b602080825282518282018190526000919060409081850190868401855b8281101561457a57815180516004811061453457fe5b8552808701516001600160a01b03908116888701528682015187870152606080830151821690870152608091820151169085015260a0909301929085019060010161451e565b5091979650505050505050565b60006040825261459a6040830185614499565b8281036020840152611b948185614499565b6000606082526145bf6060830186614499565b60208301949094525060400152919050565b901515815260200190565b90815260200190565b60408101600284106145f357fe5b92815290151560209091015290565b6040810161460f846147e7565b9281526020015290565b60208101614626836147e7565b91905290565b6000602080835283518082850152825b818110156146585785810183015185820160400152820161463c565b818111156146695783604083870101525b50601f01601f1916929092016040019392505050565b600060c0820190508251151582526001600160701b0360208401511660208301526001600160801b03604084015116604083015260608301516001600160a01b038082166060850152806080860151166080850152505060a083015160a083015292915050565b918252602082015260400190565b9283526020830191909152604082015260600190565b938452602084019290925260408301526001600160a01b0316606082015260800190565b93845260208401929092526040830152606082015260800190565b9a8b5260208b019990995260408a01979097526060890195909552608088019390935260a087019190915260c086015260e08501526101008401526101208301526101408201526101600190565b60ff91909116815260200190565b60405181810167ffffffffffffffff811182821017156147c157fe5b604052919050565b600067ffffffffffffffff8211156147dd57fe5b5060209081020190565b600381106109d657fe5b6001600160a01b03811681146109d657600080fd5b80151581146109d657600080fdfea2646970667358221220e5d32adddd65b1ba95d5b33b5251a34884a7f14f2bef5dd0fca9d439a916328864736f6c63430007060033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Txn 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.