ETH Price: $3,494.35 (+2.52%)

Contract

0x5ad2fED59E8DF461c6164c31B4267Efb7cBaF9C0
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Multicall178546052023-08-06 8:03:47507 days ago1691309027IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0024922513.50924095
Multicall178545992023-08-06 8:02:35507 days ago1691308955IN
0x5ad2fED5...b7cBaF9C0
0.01 ETH0.0038619112.86284062
Multicall178537792023-08-06 5:17:23507 days ago1691299043IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0024027211.76047006
Multicall178536492023-08-06 4:51:23507 days ago1691297483IN
0x5ad2fED5...b7cBaF9C0
0.1 ETH0.0033207913.44853388
Multicall178535692023-08-06 4:35:23507 days ago1691296523IN
0x5ad2fED5...b7cBaF9C0
0.15 ETH0.004909611.71541169
Multicall178351532023-08-03 14:46:35509 days ago1691073995IN
0x5ad2fED5...b7cBaF9C0
0.02 ETH0.0123549346.80076849
Multicall178337932023-08-03 10:11:59509 days ago1691057519IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0026430912.93573676
Multicall178088042023-07-30 22:25:11513 days ago1690755911IN
0x5ad2fED5...b7cBaF9C0
0.02 ETH0.0037423514.17610749
Multicall178075302023-07-30 18:07:59513 days ago1690740479IN
0x5ad2fED5...b7cBaF9C0
0.0054 ETH0.003872214.66601632
Multicall178047022023-07-30 8:37:11513 days ago1690706231IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.003710418.15931953
Multicall177774352023-07-26 13:05:11517 days ago1690376711IN
0x5ad2fED5...b7cBaF9C0
0.03 ETH0.012346838.90743089
Multicall177747092023-07-26 3:55:47518 days ago1690343747IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0043842622.62974329
Multicall177530822023-07-23 3:17:23521 days ago1690082243IN
0x5ad2fED5...b7cBaF9C0
0.004 ETH0.0034774913.17283639
Multicall177516012023-07-22 22:19:23521 days ago1690064363IN
0x5ad2fED5...b7cBaF9C0
0.02355 ETH0.0034738114.06824962
Multicall177422522023-07-21 14:56:47522 days ago1689951407IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0075208136.80883298
Multicall177109302023-07-17 5:34:23527 days ago1689572063IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0027638118.24529875
Multicall177108682023-07-17 5:21:35527 days ago1689571295IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0020230217.9677093
Multicall177069312023-07-16 16:06:59527 days ago1689523619IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0042814820.95468671
Multicall176943612023-07-14 21:27:23529 days ago1689370043IN
0x5ad2fED5...b7cBaF9C0
0.01 ETH0.0047670715.02265541
Multicall176939772023-07-14 20:09:35529 days ago1689365375IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0046508824
Multicall176694392023-07-11 9:19:23532 days ago1689067163IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0024365915.54467974
Multicall176694322023-07-11 9:17:59532 days ago1689067079IN
0x5ad2fED5...b7cBaF9C0
0.008 ETH0.0040909815.49675999
Multicall176691572023-07-11 8:22:11533 days ago1689063731IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0034737917
Multicall176667302023-07-11 0:11:11533 days ago1689034271IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0047537223.26550615
Multicall176204012023-07-04 11:55:59539 days ago1688471759IN
0x5ad2fED5...b7cBaF9C0
0 ETH0.0030144413.50696591
View all transactions

Latest 23 internal transactions

Advanced mode:
Parent Transaction Hash Block
From
To
178545992023-08-06 8:02:35507 days ago1691308955
0x5ad2fED5...b7cBaF9C0
0.01 ETH
178536492023-08-06 4:51:23507 days ago1691297483
0x5ad2fED5...b7cBaF9C0
0.1 ETH
178535692023-08-06 4:35:23507 days ago1691296523
0x5ad2fED5...b7cBaF9C0
0.15 ETH
178351532023-08-03 14:46:35509 days ago1691073995
0x5ad2fED5...b7cBaF9C0
0.02 ETH
178088042023-07-30 22:25:11513 days ago1690755911
0x5ad2fED5...b7cBaF9C0
0.02 ETH
178075302023-07-30 18:07:59513 days ago1690740479
0x5ad2fED5...b7cBaF9C0
0.0054 ETH
177774352023-07-26 13:05:11517 days ago1690376711
0x5ad2fED5...b7cBaF9C0
0.03 ETH
177530822023-07-23 3:17:23521 days ago1690082243
0x5ad2fED5...b7cBaF9C0
0.004 ETH
177516012023-07-22 22:19:23521 days ago1690064363
0x5ad2fED5...b7cBaF9C0
0.02355 ETH
176943612023-07-14 21:27:23529 days ago1689370043
0x5ad2fED5...b7cBaF9C0
0.01 ETH
176694322023-07-11 9:17:59532 days ago1689067079
0x5ad2fED5...b7cBaF9C0
0.008 ETH
176203582023-07-04 11:47:23539 days ago1688471243
0x5ad2fED5...b7cBaF9C0
0.002555 ETH
176156032023-07-03 19:46:35540 days ago1688413595
0x5ad2fED5...b7cBaF9C0
0.0194297 ETH
176073502023-07-02 15:56:35541 days ago1688313395
0x5ad2fED5...b7cBaF9C0
0.01 ETH
175644092023-06-26 15:12:23547 days ago1687792343
0x5ad2fED5...b7cBaF9C0
0.03 ETH
175631382023-06-26 10:55:11547 days ago1687776911
0x5ad2fED5...b7cBaF9C0
0.0027 ETH
175364982023-06-22 16:59:35551 days ago1687453175
0x5ad2fED5...b7cBaF9C0
1.47792987 ETH
175127712023-06-19 9:01:59554 days ago1687165319
0x5ad2fED5...b7cBaF9C0
0.005 ETH
175019432023-06-17 20:35:11556 days ago1687034111
0x5ad2fED5...b7cBaF9C0
0.021 ETH
174995322023-06-17 12:29:11556 days ago1687004951
0x5ad2fED5...b7cBaF9C0
0.01 ETH
174229642023-06-06 17:44:47567 days ago1686073487
0x5ad2fED5...b7cBaF9C0
0.05 ETH
173688282023-05-30 2:37:35575 days ago1685414255
0x5ad2fED5...b7cBaF9C0
0.02982515 ETH
170947632023-04-21 12:27:35613 days ago1682080055  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DCAHubCompanion

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 9999 runs

Other Settings:
default evmVersion
File 1 of 34 : DCAHubCompanion.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import './DCAHubCompanionLibrariesHandler.sol';
import './DCAHubCompanionHubProxyHandler.sol';
import '../utils/BaseCompanion.sol';

contract DCAHubCompanion is DCAHubCompanionLibrariesHandler, DCAHubCompanionHubProxyHandler, BaseCompanion, IDCAHubCompanion {
  constructor(address _swapperRegistry, address _governor) BaseCompanion(_swapperRegistry, _governor) {}
}

File 2 of 34 : DCAHubCompanionLibrariesHandler.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '../libraries/InputBuilding.sol';
import '../libraries/SecondsUntilNextSwap.sol';
import '../interfaces/IDCAHubCompanion.sol';

abstract contract DCAHubCompanionLibrariesHandler is IDCAHubCompanionLibrariesHandler {
  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function getNextSwapInfo(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability,
    bytes calldata _oracleData
  ) external view returns (IDCAHub.SwapInfo memory) {
    (address[] memory _tokens, IDCAHub.PairIndexes[] memory _indexes) = InputBuilding.buildGetNextSwapInfoInput(_pairs);
    return _hub.getNextSwapInfo(_tokens, _indexes, _calculatePrivilegedAvailability, _oracleData);
  }

  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function legacyGetNextSwapInfo(ILegacyDCAHub _hub, Pair[] calldata _pairs) external view returns (ILegacyDCAHub.SwapInfo memory) {
    (address[] memory _tokens, IDCAHub.PairIndexes[] memory _indexes) = InputBuilding.buildGetNextSwapInfoInput(_pairs);
    return _hub.getNextSwapInfo(_tokens, _indexes);
  }

  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function secondsUntilNextSwap(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability
  ) external view returns (uint256[] memory) {
    return SecondsUntilNextSwap.secondsUntilNextSwap(_hub, _pairs, _calculatePrivilegedAvailability);
  }
}

File 3 of 34 : DCAHubCompanionHubProxyHandler.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '../interfaces/IDCAHubCompanion.sol';

/// @dev All public functions are payable, so that they can be multicalled together with other payable functions when msg.value > 0
abstract contract DCAHubCompanionHubProxyHandler is IDCAHubCompanionHubProxyHandler {
  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function permissionPermit(
    IDCAPermissionManager _permissionManager,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    uint256 _tokenId,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _permissionManager.permissionPermit(_permissions, _tokenId, _deadline, _v, _r, _s);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function multiPermissionPermit(
    IDCAPermissionManager _permissionManager,
    IDCAPermissionManager.PositionPermissions[] calldata _permissions,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _permissionManager.multiPermissionPermit(_permissions, _deadline, _v, _r, _s);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function deposit(
    IDCAHub _hub,
    address _from,
    address _to,
    uint256 _amount,
    uint32 _amountOfSwaps,
    uint32 _swapInterval,
    address _owner,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    bytes calldata _miscellaneous
  ) public payable virtual returns (uint256 _positionId) {
    _approveHub(address(_from), _hub, _amount);
    _positionId = _miscellaneous.length > 0
      ? _hub.deposit(_from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions, _miscellaneous)
      : _hub.deposit(_from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function depositWithBalanceOnContract(
    IDCAHub _hub,
    address _from,
    address _to,
    uint32 _amountOfSwaps,
    uint32 _swapInterval,
    address _owner,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    bytes calldata _miscellaneous
  ) external payable returns (uint256 _positionId) {
    uint256 _amount = IERC20(_from).balanceOf(address(this));
    return deposit(_hub, _from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions, _miscellaneous);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function withdrawSwapped(
    IDCAHub _hub,
    uint256 _positionId,
    address _recipient
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.WITHDRAW) returns (uint256 _swapped) {
    _swapped = _hub.withdrawSwapped(_positionId, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function withdrawSwappedMany(
    IDCAHub _hub,
    IDCAHub.PositionSet[] calldata _positions,
    address _recipient
  ) external payable returns (uint256[] memory _withdrawn) {
    for (uint256 i = 0; i < _positions.length; ) {
      uint256[] memory _positionIds = _positions[i].positionIds;
      for (uint256 j = 0; j < _positionIds.length; ) {
        _checkPermissionOrFail(_hub, _positionIds[j], IDCAPermissionManager.Permission.WITHDRAW);
        unchecked {
          j++;
        }
      }
      unchecked {
        i++;
      }
    }
    _withdrawn = _hub.withdrawSwappedMany(_positions, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function increasePosition(
    IDCAHub _hub,
    uint256 _positionId,
    uint256 _amount,
    uint32 _newSwaps
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.INCREASE) {
    IERC20Metadata _from = _hub.userPosition(_positionId).from;
    _approveHub(address(_from), _hub, _amount);
    _hub.increasePosition(_positionId, _amount, _newSwaps);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function increasePositionWithBalanceOnContract(
    IDCAHub _hub,
    uint256 _positionId,
    uint32 _newSwaps
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.INCREASE) {
    IERC20Metadata _from = _hub.userPosition(_positionId).from;
    uint256 _amount = _from.balanceOf(address(this));
    _approveHub(address(_from), _hub, _amount);
    _hub.increasePosition(_positionId, _amount, _newSwaps);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function reducePosition(
    IDCAHub _hub,
    uint256 _positionId,
    uint256 _amount,
    uint32 _newSwaps,
    address _recipient
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.REDUCE) {
    _hub.reducePosition(_positionId, _amount, _newSwaps, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function terminate(
    IDCAHub _hub,
    uint256 _positionId,
    address _recipientUnswapped,
    address _recipientSwapped
  )
    external
    payable
    verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.TERMINATE)
    returns (uint256 _unswapped, uint256 _swapped)
  {
    (_unswapped, _swapped) = _hub.terminate(_positionId, _recipientUnswapped, _recipientSwapped);
  }

  function _approveHub(
    address _token,
    IDCAHub _hub,
    uint256 _amount
  ) internal {
    uint256 _allowance = IERC20(_token).allowance(address(this), address(_hub));
    if (_allowance < _amount) {
      if (_allowance > 0) {
        IERC20(_token).approve(address(_hub), 0); // We do this because some tokens (like USDT) fail if we don't
      }
      IERC20(_token).approve(address(_hub), type(uint256).max);
    }
  }

  function _checkPermissionOrFail(
    IDCAHub _hub,
    uint256 _positionId,
    IDCAPermissionManager.Permission _permission
  ) internal view {
    if (!_hub.permissionManager().hasPermission(_positionId, msg.sender, _permission)) revert UnauthorizedCaller();
  }

  modifier verifyPermission(
    IDCAHub _hub,
    uint256 _positionId,
    IDCAPermissionManager.Permission _permission
  ) {
    _checkPermissionOrFail(_hub, _positionId, _permission);
    _;
  }
}

File 4 of 34 : BaseCompanion.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@mean-finance/swappers/solidity/contracts/extensions/GetBalances.sol';
import '@mean-finance/swappers/solidity/contracts/extensions/RevokableWithGovernor.sol';
import '@mean-finance/swappers/solidity/contracts/extensions/RunSwap.sol';
import '@mean-finance/swappers/solidity/contracts/extensions/PayableMulticall.sol';
import '@mean-finance/swappers/solidity/contracts/extensions/TokenPermit.sol';

/**
 * @notice This contract will work as base companion for all our contracts. It will extend the capabilities of our companion
 *         contracts so that they can execute multicalls, swaps, revokes and more
 * @dev All public functions are payable, so that they can be multicalled together with other payable functions when msg.value > 0
 */
abstract contract BaseCompanion is RunSwap, RevokableWithGovernor, GetBalances, PayableMulticall, TokenPermit {
  constructor(address _swapperRegistry, address _governor) SwapAdapter(_swapperRegistry) Governable(_governor) {}

  /**
   * @notice Sends the specified amount of the given token to the recipient
   * @param _token The token to transfer
   * @param _amount The amount to transfer
   * @param _recipient The recipient of the token balance
   */
  function sendToRecipient(
    address _token,
    uint256 _amount,
    address _recipient
  ) external payable {
    _sendToRecipient(_token, _amount, _recipient);
  }

  /**
   * @notice Takes the given amount of tokens from the caller and transfers it to this contract
   * @param _token The token to take
   * @param _amount The amount to take
   */
  function takeFromCaller(IERC20 _token, uint256 _amount) external payable {
    _takeFromMsgSender(_token, _amount);
  }

  /**
   * @notice Checks if the contract has any balance of the given token, and if it does,
   *         it sends it to the given recipient
   * @param _token The token to check
   * @param _recipient The recipient of the token balance
   */
  function sendBalanceOnContractToRecipient(address _token, address _recipient) external payable {
    _sendBalanceOnContractToRecipient(_token, _recipient);
  }
}

File 5 of 34 : InputBuilding.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '../interfaces/ISharedTypes.sol';

/// @title Input Building Library
/// @notice Provides functions to build input for swap related actions
/// @dev Please note that these functions are very expensive. Ideally, these would be used for off-chain purposes
library InputBuilding {
  /// @notice Takes a list of pairs and returns the input necessary to check the next swap
  /// @dev Even though this function allows it, the DCAHub will fail if duplicated pairs are used
  /// @return _tokens A sorted list of all the tokens involved in the swap
  /// @return _pairsToSwap A sorted list of indexes that represent the pairs involved in the swap
  function buildGetNextSwapInfoInput(Pair[] calldata _pairs)
    internal
    pure
    returns (address[] memory _tokens, IDCAHub.PairIndexes[] memory _pairsToSwap)
  {
    (_tokens, _pairsToSwap, ) = buildSwapInput(_pairs, new IDCAHub.AmountOfToken[](0));
  }

  /// @notice Takes a list of pairs and a list of tokens to borrow and returns the input necessary to execute a swap
  /// @dev Even though this function allows it, the DCAHub will fail if duplicated pairs are used
  /// @return _tokens A sorted list of all the tokens involved in the swap
  /// @return _pairsToSwap A sorted list of indexes that represent the pairs involved in the swap
  /// @return _borrow A list of amounts to borrow, based on the sorted token list
  function buildSwapInput(Pair[] calldata _pairs, IDCAHub.AmountOfToken[] memory _toBorrow)
    internal
    pure
    returns (
      address[] memory _tokens,
      IDCAHub.PairIndexes[] memory _pairsToSwap,
      uint256[] memory _borrow
    )
  {
    _tokens = _calculateUniqueTokens(_pairs, _toBorrow);
    _pairsToSwap = _calculatePairIndexes(_pairs, _tokens);
    _borrow = _calculateTokensToBorrow(_toBorrow, _tokens);
  }

  /// @dev Given a list of token pairs and tokens to borrow, returns a list of all the tokens involved, sorted
  function _calculateUniqueTokens(Pair[] memory _pairs, IDCAHub.AmountOfToken[] memory _toBorrow)
    private
    pure
    returns (address[] memory _tokens)
  {
    uint256 _uniqueTokens;
    address[] memory _tokensPlaceholder = new address[](_pairs.length * 2 + _toBorrow.length);

    // Load tokens in pairs onto placeholder
    for (uint256 i; i < _pairs.length; i++) {
      bool _foundA = false;
      bool _foundB = false;
      for (uint256 j; j < _uniqueTokens && !(_foundA && _foundB); j++) {
        if (!_foundA && _tokensPlaceholder[j] == _pairs[i].tokenA) _foundA = true;
        if (!_foundB && _tokensPlaceholder[j] == _pairs[i].tokenB) _foundB = true;
      }

      if (!_foundA) _tokensPlaceholder[_uniqueTokens++] = _pairs[i].tokenA;
      if (!_foundB) _tokensPlaceholder[_uniqueTokens++] = _pairs[i].tokenB;
    }

    // Load tokens to borrow onto placeholder
    for (uint256 i; i < _toBorrow.length; i++) {
      bool _found = false;
      for (uint256 j; j < _uniqueTokens && !_found; j++) {
        if (_tokensPlaceholder[j] == _toBorrow[i].token) _found = true;
      }
      if (!_found) _tokensPlaceholder[_uniqueTokens++] = _toBorrow[i].token;
    }

    // Load sorted into new array
    _tokens = new address[](_uniqueTokens);
    for (uint256 i; i < _uniqueTokens; i++) {
      address _token = _tokensPlaceholder[i];

      // Find index where the token should be
      uint256 _tokenIndex;
      while (_tokens[_tokenIndex] < _token && _tokens[_tokenIndex] != address(0)) _tokenIndex++;

      // Move everything one place back
      for (uint256 j = i; j > _tokenIndex; j--) {
        _tokens[j] = _tokens[j - 1];
      }

      // Set token on the correct index
      _tokens[_tokenIndex] = _token;
    }
  }

  /// @dev Given a list of pairs, and a list of sorted tokens, it translates the first list into indexes of the second list. This list of indexes will
  /// be sorted. For example, if pairs are [{ tokenA, tokenB }, { tokenC, tokenB }] and tokens are: [ tokenA, tokenB, tokenC ], the following is returned
  /// [ { 0, 1 }, { 1, 1 }, { 1, 2 } ]
  function _calculatePairIndexes(Pair[] calldata _pairs, address[] memory _tokens)
    private
    pure
    returns (IDCAHub.PairIndexes[] memory _pairIndexes)
  {
    _pairIndexes = new IDCAHub.PairIndexes[](_pairs.length);
    uint256 _count;

    for (uint8 i; i < _tokens.length; i++) {
      for (uint8 j = i + 1; j < _tokens.length; j++) {
        for (uint256 k; k < _pairs.length; k++) {
          if (
            (_tokens[i] == _pairs[k].tokenA && _tokens[j] == _pairs[k].tokenB) ||
            (_tokens[i] == _pairs[k].tokenB && _tokens[j] == _pairs[k].tokenA)
          ) {
            _pairIndexes[_count++] = IDCAHubSwapHandler.PairIndexes({indexTokenA: i, indexTokenB: j});
          }
        }
      }
    }
  }

  /// @dev Given a list of tokens to borrow and a list of sorted tokens, it translated the first list into a list of amounts, sorted by the indexed of
  /// the seconds list. For example, if `toBorrow` are [{ tokenA, 100 }, { tokenC, 200 }, { tokenB, 500 }] and tokens are [ tokenA, tokenB, tokenC], the
  /// following is returned [100, 500, 200]
  function _calculateTokensToBorrow(IDCAHub.AmountOfToken[] memory _toBorrow, address[] memory _tokens)
    private
    pure
    returns (uint256[] memory _borrow)
  {
    _borrow = new uint256[](_tokens.length);

    for (uint256 i; i < _toBorrow.length; i++) {
      uint256 j;
      while (_tokens[j] != _toBorrow[i].token) j++;
      _borrow[j] = _toBorrow[i].amount;
    }
  }
}

File 6 of 34 : SecondsUntilNextSwap.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '@mean-finance/dca-v2-core/contracts/libraries/TokenSorting.sol';
import '@mean-finance/dca-v2-core/contracts/libraries/Intervals.sol';
import '../interfaces/ISharedTypes.sol';

/**
 * @title Seconds Until Next Swap Library
 * @notice Provides functions to calculate how long users have to wait until a pair's next swap is available
 */
library SecondsUntilNextSwap {
  /**
   * @notice Returns how many seconds left until the next swap is available for a specific pair
   * @dev _tokenA and _tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param _hub The address of the DCA Hub
   * @param _tokenA One of the pair's tokens
   * @param _tokenB The other of the pair's tokens
   * @param _calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   * @return The amount of seconds until next swap. Returns 0 if a swap can already be executed and max(uint256) if there is nothing to swap
   */
  function secondsUntilNextSwap(
    IDCAHub _hub,
    address _tokenA,
    address _tokenB,
    bool _calculatePrivilegedAvailability
  ) internal view returns (uint256) {
    (address __tokenA, address __tokenB) = TokenSorting.sortTokens(_tokenA, _tokenB);
    bytes1 _activeIntervals = _hub.activeSwapIntervals(__tokenA, __tokenB);
    bytes1 _mask = 0x01;
    uint256 _smallerIntervalBlocking;
    while (_activeIntervals >= _mask && _mask > 0) {
      if (_activeIntervals & _mask == _mask) {
        (, uint224 _nextAmountToSwapAToB, uint32 _lastSwappedAt, uint224 _nextAmountToSwapBToA) = _hub.swapData(_tokenA, _tokenB, _mask);
        uint32 _swapInterval = Intervals.maskToInterval(_mask);
        uint256 _nextAvailable = ((_lastSwappedAt / _swapInterval) + 1) * _swapInterval;
        if (!_calculatePrivilegedAvailability) {
          // If the caller does not have privileges, then they will have to wait a little more to execute swaps
          _nextAvailable += _swapInterval / 3;
        }
        if (_nextAmountToSwapAToB > 0 || _nextAmountToSwapBToA > 0) {
          if (_nextAvailable <= block.timestamp) {
            return _smallerIntervalBlocking;
          } else {
            return _nextAvailable - block.timestamp;
          }
        } else if (_nextAvailable > block.timestamp) {
          _smallerIntervalBlocking = _smallerIntervalBlocking == 0 ? _nextAvailable - block.timestamp : _smallerIntervalBlocking;
        }
      }
      _mask <<= 1;
    }
    return type(uint256).max;
  }

  /**
   * @notice Returns how many seconds left until the next swap is available for a list of pairs
   * @dev Tokens in pairs may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param _hub The address of the DCA Hub
   * @param _pairs Pairs to check
   * @return _seconds The amount of seconds until next swap for each of the pairs
   * @param _calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   */
  function secondsUntilNextSwap(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability
  ) internal view returns (uint256[] memory _seconds) {
    _seconds = new uint256[](_pairs.length);
    for (uint256 i; i < _pairs.length; i++) {
      _seconds[i] = secondsUntilNextSwap(_hub, _pairs[i].tokenA, _pairs[i].tokenB, _calculatePrivilegedAvailability);
    }
  }
}

File 7 of 34 : IDCAHubCompanion.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAPermissionManager.sol';
import './ILegacyDCAHub.sol';
import './ISharedTypes.sol';

/**
 * @notice This contract exposes many utils that are also available through libraries. The idea is to make
 *         these functions available here, so others don't need to deploy new contracts
 */
interface IDCAHubCompanionLibrariesHandler {
  /**
   * @notice Takes a list of pairs and returns how it would look like to execute a swap for all of them
   * @dev Please note that this function is very expensive. Ideally, it would be used for off-chain purposes
   * @param hub The address of the DCAHub
   * @param pairs The pairs to be involved in the swap
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the next swap information for privileged and non-privileged accounts
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return How executing a swap for all the given pairs would look like
   */
  function getNextSwapInfo(
    IDCAHub hub,
    Pair[] calldata pairs,
    bool calculatePrivilegedAvailability,
    bytes calldata oracleData
  ) external view returns (IDCAHub.SwapInfo memory);

  /**
   * @notice Takes a list of pairs and returns how it would look like to execute a swap for all of them
   * @dev Please note that this function is very expensive. Ideally, it would be used for off-chain purposes
   * @param hub The address of the DCAHub
   * @param pairs The pairs to be involved in the swap
   * @return How executing a swap for all the given pairs would look like
   */
  function legacyGetNextSwapInfo(ILegacyDCAHub hub, Pair[] calldata pairs) external view returns (ILegacyDCAHub.SwapInfo memory);

  /**
   * @notice Returns how many seconds left until the next swap is available for a list of pairs
   * @dev Tokens in pairs may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param hub The address of the DCAHub
   * @param pairs Pairs to check
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   * @return The amount of seconds until next swap for each of the pairs
   */
  function secondsUntilNextSwap(
    IDCAHub hub,
    Pair[] calldata pairs,
    bool calculatePrivilegedAvailability
  ) external view returns (uint256[] memory);
}

interface IDCAHubCompanionHubProxyHandler {
  /// @notice Thrown when a user tries operate on a position that they don't have access to
  error UnauthorizedCaller();

  /**
   * @notice Creates a new position
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param miscellaneous Bytes that will be emitted, and associated with the position. If empty, no event will be emitted
   * @return positionId The id of the created position
   */
  function deposit(
    IDCAHub hub,
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external payable returns (uint256 positionId);

  /**
   * @notice Creates a new position using the entire balance available on the contract
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param miscellaneous Bytes that will be emitted, and associated with the position. If empty, no event will be emitted
   * @return positionId The id of the created position
   */
  function depositWithBalanceOnContract(
    IDCAHub hub,
    address from,
    address to,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external payable returns (uint256 positionId);

  /**
   * @notice Call the hub and withdraws all swapped tokens from a position to a recipient
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param recipient The address to withdraw swapped tokens to
   * @return swapped How much was withdrawn
   */
  function withdrawSwapped(
    IDCAHub hub,
    uint256 positionId,
    address recipient
  ) external payable returns (uint256 swapped);

  /**
   * @notice Call the hub and withdraws all swapped tokens from multiple positions
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positions A list positions, grouped by `to` token
   * @param recipient The address to withdraw swapped tokens to
   * @return withdrawn How much was withdrawn for each token
   */
  function withdrawSwappedMany(
    IDCAHub hub,
    IDCAHub.PositionSet[] calldata positions,
    address recipient
  ) external payable returns (uint256[] memory withdrawn);

  /**
   * @notice Call the hub and takes the unswapped balance, adds the new deposited funds and modifies the position so that
   * it is executed in `newSwaps` swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param amount Amount of funds to add to the position
   * @param newSwaps The new amount of swaps
   */
  function increasePosition(
    IDCAHub hub,
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps
  ) external payable;

  /**
   * @notice Call the hub and takes the unswapped balance, adds the Companion's current balance and modifies the position so that
   * it is executed in `newSwaps` swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param newSwaps The new amount of swaps
   */
  function increasePositionWithBalanceOnContract(
    IDCAHub hub,
    uint256 positionId,
    uint32 newSwaps
  ) external payable;

  /**
   * @notice Call the hub and withdraws the specified amount from the unswapped balance and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param amount Amount of funds to withdraw from the position
   * @param newSwaps The new amount of swaps
   * @param recipient The address to send tokens to
   */
  function reducePosition(
    IDCAHub hub,
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps,
    address recipient
  ) external payable;

  /**
   * @notice Calls the hub and terminates the position and sends all unswapped and swapped balance to the specified recipients
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param recipientUnswapped The address to withdraw unswapped tokens to
   * @param recipientSwapped The address to withdraw swapped tokens to
   * @return unswapped The unswapped balance sent to `recipientUnswapped`
   * @return swapped The swapped balance sent to `recipientSwapped`
   */
  function terminate(
    IDCAHub hub,
    uint256 positionId,
    address recipientUnswapped,
    address recipientSwapped
  ) external payable returns (uint256 unswapped, uint256 swapped);

  /**
   * @notice Calls the permission manager and sets multiple permissions via signature
   * @param permissionManager The address of the permission manager
   * @param permissions The permissions to set
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function multiPermissionPermit(
    IDCAPermissionManager permissionManager,
    IDCAPermissionManager.PositionPermissions[] calldata permissions,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable;

  /**
   * @notice Calls the permission manager and sets permissions via signature
   * @param permissionManager The address of the permission manager
   * @param permissions The permissions to set
   * @param tokenId The token's id
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permissionPermit(
    IDCAPermissionManager permissionManager,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable;
}

interface IDCAHubCompanion is IDCAHubCompanionLibrariesHandler, IDCAHubCompanionHubProxyHandler {}

File 8 of 34 : IDCAHub.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@mean-finance/oracles/solidity/interfaces/ITokenPriceOracle.sol';
import './IDCAPermissionManager.sol';

/**
 * @title The interface for all state related queries
 * @notice These methods allow users to read the hubs's current values
 */
interface IDCAHubParameters {
  /**
   * @notice Returns how much will the amount to swap differ from the previous swap. f.e. if the returned value is -100, then the amount to swap will be 100 less than the swap just before it
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @param swapNumber The swap number to check
   * @return swapDeltaAToB How much less of token A will the following swap require
   * @return swapDeltaBToA How much less of token B will the following swap require
   */
  function swapAmountDelta(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask,
    uint32 swapNumber
  ) external view returns (uint128 swapDeltaAToB, uint128 swapDeltaBToA);

  /**
   * @notice Returns the sum of the ratios reported in all swaps executed until the given swap number
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @param swapNumber The swap number to check
   * @return accumRatioAToB The sum of all ratios from A to B
   * @return accumRatioBToA The sum of all ratios from B to A
   */
  function accumRatio(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask,
    uint32 swapNumber
  ) external view returns (uint256 accumRatioAToB, uint256 accumRatioBToA);

  /**
   * @notice Returns swapping information about a specific pair
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @return performedSwaps How many swaps have been executed
   * @return nextAmountToSwapAToB How much of token A will be swapped on the next swap
   * @return lastSwappedAt Timestamp of the last swap
   * @return nextAmountToSwapBToA How much of token B will be swapped on the next swap
   */
  function swapData(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask
  )
    external
    view
    returns (
      uint32 performedSwaps,
      uint224 nextAmountToSwapAToB,
      uint32 lastSwappedAt,
      uint224 nextAmountToSwapBToA
    );

  /**
   * @notice Returns the byte representation of the set of actice swap intervals for the given pair
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA The smaller of the pair's token
   * @param tokenB The other of the pair's token
   * @return The byte representation of the set of actice swap intervals
   */
  function activeSwapIntervals(address tokenA, address tokenB) external view returns (bytes1);

  /**
   * @notice Returns how much of the hub's token balance belongs to the platform
   * @param token The token to check
   * @return The amount that belongs to the platform
   */
  function platformBalance(address token) external view returns (uint256);
}

/**
 * @title The interface for all position related matters
 * @notice These methods allow users to create, modify and terminate their positions
 */
interface IDCAHubPositionHandler {
  /// @notice The position of a certain user
  struct UserPosition {
    // The token that the user deposited and will be swapped in exchange for "to"
    IERC20Metadata from;
    // The token that the user will get in exchange for their "from" tokens in each swap
    IERC20Metadata to;
    // How frequently the position's swaps should be executed
    uint32 swapInterval;
    // How many swaps were executed since deposit, last modification, or last withdraw
    uint32 swapsExecuted;
    // How many "to" tokens can currently be withdrawn
    uint256 swapped;
    // How many swaps left the position has to execute
    uint32 swapsLeft;
    // How many "from" tokens there are left to swap
    uint256 remaining;
    // How many "from" tokens need to be traded in each swap
    uint120 rate;
  }

  /// @notice A list of positions that all have the same `to` token
  struct PositionSet {
    // The `to` token
    address token;
    // The position ids
    uint256[] positionIds;
  }

  /**
   * @notice Emitted when a position is terminated
   * @param user The address of the user that terminated the position
   * @param recipientUnswapped The address of the user that will receive the unswapped tokens
   * @param recipientSwapped The address of the user that will receive the swapped tokens
   * @param positionId The id of the position that was terminated
   * @param returnedUnswapped How many "from" tokens were returned to the caller
   * @param returnedSwapped How many "to" tokens were returned to the caller
   */
  event Terminated(
    address indexed user,
    address indexed recipientUnswapped,
    address indexed recipientSwapped,
    uint256 positionId,
    uint256 returnedUnswapped,
    uint256 returnedSwapped
  );

  /**
   * @notice Emitted when a position is created
   * @param depositor The address of the user that creates the position
   * @param owner The address of the user that will own the position
   * @param positionId The id of the position that was created
   * @param fromToken The address of the "from" token
   * @param toToken The address of the "to" token
   * @param swapInterval How frequently the position's swaps should be executed
   * @param rate How many "from" tokens need to be traded in each swap
   * @param startingSwap The number of the swap when the position will be executed for the first time
   * @param lastSwap The number of the swap when the position will be executed for the last time
   * @param permissions The permissions defined for the position
   */
  event Deposited(
    address indexed depositor,
    address indexed owner,
    uint256 positionId,
    address fromToken,
    address toToken,
    uint32 swapInterval,
    uint120 rate,
    uint32 startingSwap,
    uint32 lastSwap,
    IDCAPermissionManager.PermissionSet[] permissions
  );

  /**
   * @notice Emitted when a position is created and extra data is provided
   * @param positionId The id of the position that was created
   * @param data The extra data that was provided
   */
  event Miscellaneous(uint256 positionId, bytes data);

  /**
   * @notice Emitted when a user withdraws all swapped tokens from a position
   * @param withdrawer The address of the user that executed the withdraw
   * @param recipient The address of the user that will receive the withdrawn tokens
   * @param positionId The id of the position that was affected
   * @param token The address of the withdrawn tokens. It's the same as the position's "to" token
   * @param amount The amount that was withdrawn
   */
  event Withdrew(address indexed withdrawer, address indexed recipient, uint256 positionId, address token, uint256 amount);

  /**
   * @notice Emitted when a user withdraws all swapped tokens from many positions
   * @param withdrawer The address of the user that executed the withdraws
   * @param recipient The address of the user that will receive the withdrawn tokens
   * @param positions The positions to withdraw from
   * @param withdrew The total amount that was withdrawn from each token
   */
  event WithdrewMany(address indexed withdrawer, address indexed recipient, PositionSet[] positions, uint256[] withdrew);

  /**
   * @notice Emitted when a position is modified
   * @param user The address of the user that modified the position
   * @param positionId The id of the position that was modified
   * @param rate How many "from" tokens need to be traded in each swap
   * @param startingSwap The number of the swap when the position will be executed for the first time
   * @param lastSwap The number of the swap when the position will be executed for the last time
   */
  event Modified(address indexed user, uint256 positionId, uint120 rate, uint32 startingSwap, uint32 lastSwap);

  /// @notice Thrown when a user tries to create a position with the same `from` & `to`
  error InvalidToken();

  /// @notice Thrown when a user tries to create a position with a swap interval that is not allowed
  error IntervalNotAllowed();

  /// @notice Thrown when a user tries operate on a position that doesn't exist (it might have been already terminated)
  error InvalidPosition();

  /// @notice Thrown when a user tries operate on a position that they don't have access to
  error UnauthorizedCaller();

  /// @notice Thrown when a user tries to create a position with zero swaps
  error ZeroSwaps();

  /// @notice Thrown when a user tries to create a position with zero funds
  error ZeroAmount();

  /// @notice Thrown when a user tries to withdraw a position whose `to` token doesn't match the specified one
  error PositionDoesNotMatchToken();

  /// @notice Thrown when a user tries create or modify a position with an amount too big
  error AmountTooBig();

  /**
   * @notice Returns the permission manager contract
   * @return The contract itself
   */
  function permissionManager() external view returns (IDCAPermissionManager);

  /**
   * @notice Returns total created positions
   * @return The total created positions
   */
  function totalCreatedPositions() external view returns (uint256);

  /**
   * @notice Returns a user position
   * @param positionId The id of the position
   * @return position The position itself
   */
  function userPosition(uint256 positionId) external view returns (UserPosition memory position);

  /**
   * @notice Creates a new position
   * @dev Will revert:
   *      - With ZeroAddress if from, to or owner are zero
   *      - With InvalidToken if from == to
   *      - With ZeroAmount if amount is zero
   *      - With AmountTooBig if amount is too big
   *      - With ZeroSwaps if amountOfSwaps is zero
   *      - With IntervalNotAllowed if swapInterval is not allowed
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param permissions Extra permissions to add to the position. Can be empty
   * @return positionId The id of the created position
   */
  function deposit(
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions
  ) external returns (uint256 positionId);

  /**
   * @notice Creates a new position
   * @dev Will revert:
   *      - With ZeroAddress if from, to or owner are zero
   *      - With InvalidToken if from == to
   *      - With ZeroAmount if amount is zero
   *      - With AmountTooBig if amount is too big
   *      - With ZeroSwaps if amountOfSwaps is zero
   *      - With IntervalNotAllowed if swapInterval is not allowed
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param permissions Extra permissions to add to the position. Can be empty
   * @param miscellaneous Bytes that will be emitted, and associated with the position
   * @return positionId The id of the created position
   */
  function deposit(
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external returns (uint256 positionId);

  /**
   * @notice Withdraws all swapped tokens from a position to a recipient
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroAddress if recipient is zero
   * @param positionId The position's id
   * @param recipient The address to withdraw swapped tokens to
   * @return swapped How much was withdrawn
   */
  function withdrawSwapped(uint256 positionId, address recipient) external returns (uint256 swapped);

  /**
   * @notice Withdraws all swapped tokens from multiple positions
   * @dev Will revert:
   *      - With InvalidPosition if any of the position ids are invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position to any of the given positions
   *      - With ZeroAddress if recipient is zero
   *      - With PositionDoesNotMatchToken if any of the positions do not match the token in their position set
   * @param positions A list positions, grouped by `to` token
   * @param recipient The address to withdraw swapped tokens to
   * @return withdrawn How much was withdrawn for each token
   */
  function withdrawSwappedMany(PositionSet[] calldata positions, address recipient) external returns (uint256[] memory withdrawn);

  /**
   * @notice Takes the unswapped balance, adds the new deposited funds and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With AmountTooBig if amount is too big
   * @param positionId The position's id
   * @param amount Amount of funds to add to the position
   * @param newSwaps The new amount of swaps
   */
  function increasePosition(
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps
  ) external;

  /**
   * @notice Withdraws the specified amount from the unswapped balance and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroSwaps if newSwaps is zero and amount is not the total unswapped balance
   * @param positionId The position's id
   * @param amount Amount of funds to withdraw from the position
   * @param newSwaps The new amount of swaps
   * @param recipient The address to send tokens to
   */
  function reducePosition(
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps,
    address recipient
  ) external;

  /**
   * @notice Terminates the position and sends all unswapped and swapped balance to the specified recipients
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroAddress if recipientUnswapped or recipientSwapped is zero
   * @param positionId The position's id
   * @param recipientUnswapped The address to withdraw unswapped tokens to
   * @param recipientSwapped The address to withdraw swapped tokens to
   * @return unswapped The unswapped balance sent to `recipientUnswapped`
   * @return swapped The swapped balance sent to `recipientSwapped`
   */
  function terminate(
    uint256 positionId,
    address recipientUnswapped,
    address recipientSwapped
  ) external returns (uint256 unswapped, uint256 swapped);
}

/**
 * @title The interface for all swap related matters
 * @notice These methods allow users to get information about the next swap, and how to execute it
 */
interface IDCAHubSwapHandler {
  /// @notice Information about a swap
  struct SwapInfo {
    // The tokens involved in the swap
    TokenInSwap[] tokens;
    // The pairs involved in the swap
    PairInSwap[] pairs;
  }

  /// @notice Information about a token's role in a swap
  struct TokenInSwap {
    // The token's address
    address token;
    // How much will be given of this token as a reward
    uint256 reward;
    // How much of this token needs to be provided by swapper
    uint256 toProvide;
    // How much of this token will be paid to the platform
    uint256 platformFee;
  }

  /// @notice Information about a pair in a swap
  struct PairInSwap {
    // The address of one of the tokens
    address tokenA;
    // The address of the other token
    address tokenB;
    // The total amount of token A swapped in this pair
    uint256 totalAmountToSwapTokenA;
    // The total amount of token B swapped in this pair
    uint256 totalAmountToSwapTokenB;
    // How much is 1 unit of token A when converted to B
    uint256 ratioAToB;
    // How much is 1 unit of token B when converted to A
    uint256 ratioBToA;
    // The swap intervals involved in the swap, represented as a byte
    bytes1 intervalsInSwap;
  }

  /// @notice A pair of tokens, represented by their indexes in an array
  struct PairIndexes {
    // The index of the token A
    uint8 indexTokenA;
    // The index of the token B
    uint8 indexTokenB;
  }

  /**
   * @notice Emitted when a swap is executed
   * @param sender The address of the user that initiated the swap
   * @param rewardRecipient The address that received the reward
   * @param callbackHandler The address that executed the callback
   * @param swapInformation All information related to the swap
   * @param borrowed How much was borrowed
   * @param fee The swap fee at the moment of the swap
   */
  event Swapped(
    address indexed sender,
    address indexed rewardRecipient,
    address indexed callbackHandler,
    SwapInfo swapInformation,
    uint256[] borrowed,
    uint32 fee
  );

  /// @notice Thrown when pairs indexes are not sorted correctly
  error InvalidPairs();

  /// @notice Thrown when trying to execute a swap, but there is nothing to swap
  error NoSwapsToExecute();

  /**
   * @notice Returns all information related to the next swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   * @param tokens The tokens involved in the next swap
   * @param pairs The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the next swap information for privileged and non-privileged accounts
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return swapInformation The information about the next swap
   */
  function getNextSwapInfo(
    address[] calldata tokens,
    PairIndexes[] calldata pairs,
    bool calculatePrivilegedAvailability,
    bytes calldata oracleData
  ) external view returns (SwapInfo memory swapInformation);

  /**
   * @notice Executes a flash swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   *      - With Paused if swaps are paused by protocol
   *      - With NoSwapsToExecute if there are no swaps to execute for the given pairs
   *      - With LiquidityNotReturned if the required tokens were not back during the callback
   * @param tokens The tokens involved in the next swap
   * @param pairsToSwap The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param rewardRecipient The address to send the reward to
   * @param callbackHandler Address to call for callback (and send the borrowed tokens to)
   * @param borrow How much to borrow of each of the tokens in tokens. The amount must match the position of the token in the tokens array
   * @param callbackData Bytes to send to the caller during the callback
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return Information about the executed swap
   */
  function swap(
    address[] calldata tokens,
    PairIndexes[] calldata pairsToSwap,
    address rewardRecipient,
    address callbackHandler,
    uint256[] calldata borrow,
    bytes calldata callbackData,
    bytes calldata oracleData
  ) external returns (SwapInfo memory);
}

/**
 * @title The interface for handling all configuration
 * @notice This contract will manage configuration that affects all pairs, swappers, etc
 */
interface IDCAHubConfigHandler {
  /**
   * @notice Emitted when a new oracle is set
   * @param oracle The new oracle contract
   */
  event OracleSet(ITokenPriceOracle oracle);

  /**
   * @notice Emitted when a new swap fee is set
   * @param feeSet The new swap fee
   */
  event SwapFeeSet(uint32 feeSet);

  /**
   * @notice Emitted when new swap intervals are allowed
   * @param swapIntervals The new swap intervals
   */
  event SwapIntervalsAllowed(uint32[] swapIntervals);

  /**
   * @notice Emitted when some swap intervals are no longer allowed
   * @param swapIntervals The swap intervals that are no longer allowed
   */
  event SwapIntervalsForbidden(uint32[] swapIntervals);

  /**
   * @notice Emitted when a new platform fee ratio is set
   * @param platformFeeRatio The new platform fee ratio
   */
  event PlatformFeeRatioSet(uint16 platformFeeRatio);

  /**
   * @notice Emitted when allowed states of tokens are updated
   * @param tokens Array of updated tokens
   * @param allowed Array of new allow state per token were allowed[i] is the updated state of tokens[i]
   */
  event TokensAllowedUpdated(address[] tokens, bool[] allowed);

  /// @notice Thrown when trying to interact with an unallowed token
  error UnallowedToken();

  /// @notice Thrown when set allowed tokens input is not valid
  error InvalidAllowedTokensInput();

  /// @notice Thrown when trying to set a fee higher than the maximum allowed
  error HighFee();

  /// @notice Thrown when trying to set a fee that is not multiple of 100
  error InvalidFee();

  /// @notice Thrown when trying to set a fee ratio that is higher that the maximum allowed
  error HighPlatformFeeRatio();

  /**
   * @notice Returns the max fee ratio that can be set
   * @dev Cannot be modified
   * @return The maximum possible value
   */
  // solhint-disable-next-line func-name-mixedcase
  function MAX_PLATFORM_FEE_RATIO() external view returns (uint16);

  /**
   * @notice Returns the fee charged on swaps
   * @return swapFee The fee itself
   */
  function swapFee() external view returns (uint32 swapFee);

  /**
   * @notice Returns the price oracle contract
   * @return oracle The contract itself
   */
  function oracle() external view returns (ITokenPriceOracle oracle);

  /**
   * @notice Returns how much will the platform take from the fees collected in swaps
   * @return The current ratio
   */
  function platformFeeRatio() external view returns (uint16);

  /**
   * @notice Returns the max fee that can be set for swaps
   * @dev Cannot be modified
   * @return maxFee The maximum possible fee
   */
  // solhint-disable-next-line func-name-mixedcase
  function MAX_FEE() external view returns (uint32 maxFee);

  /**
   * @notice Returns a byte that represents allowed swap intervals
   * @return allowedSwapIntervals The allowed swap intervals
   */
  function allowedSwapIntervals() external view returns (bytes1 allowedSwapIntervals);

  /**
   * @notice Returns if a token is currently allowed or not
   * @return Allowed state of token
   */
  function allowedTokens(address token) external view returns (bool);

  /**
   * @notice Returns token's magnitude (10**decimals)
   * @return Stored magnitude for token
   */
  function tokenMagnitude(address token) external view returns (uint120);

  /**
   * @notice Returns whether swaps and deposits are currently paused
   * @return isPaused Whether swaps and deposits are currently paused
   */
  function paused() external view returns (bool isPaused);

  /**
   * @notice Sets a new swap fee
   * @dev Will revert with HighFee if the fee is higher than the maximum
   * @dev Will revert with InvalidFee if the fee is not multiple of 100
   * @param fee The new swap fee
   */
  function setSwapFee(uint32 fee) external;

  /**
   * @notice Sets a new price oracle
   * @dev Will revert with ZeroAddress if the zero address is passed
   * @param oracle The new oracle contract
   */
  function setOracle(ITokenPriceOracle oracle) external;

  /**
   * @notice Sets a new platform fee ratio
   * @dev Will revert with HighPlatformFeeRatio if given ratio is too high
   * @param platformFeeRatio The new ratio
   */
  function setPlatformFeeRatio(uint16 platformFeeRatio) external;

  /**
   * @notice Adds new swap intervals to the allowed list
   * @param swapIntervals The new swap intervals
   */
  function addSwapIntervalsToAllowedList(uint32[] calldata swapIntervals) external;

  /**
   * @notice Removes some swap intervals from the allowed list
   * @param swapIntervals The swap intervals to remove
   */
  function removeSwapIntervalsFromAllowedList(uint32[] calldata swapIntervals) external;

  /// @notice Pauses all swaps and deposits
  function pause() external;

  /// @notice Unpauses all swaps and deposits
  function unpause() external;
}

/**
 * @title The interface for handling platform related actions
 * @notice This contract will handle all actions that affect the platform in some way
 */
interface IDCAHubPlatformHandler {
  /**
   * @notice Emitted when someone withdraws from the paltform balance
   * @param sender The address of the user that initiated the withdraw
   * @param recipient The address that received the withdraw
   * @param amounts The tokens (and the amount) that were withdrawn
   */
  event WithdrewFromPlatform(address indexed sender, address indexed recipient, IDCAHub.AmountOfToken[] amounts);

  /**
   * @notice Withdraws tokens from the platform balance
   * @param amounts The amounts to withdraw
   * @param recipient The address that will receive the tokens
   */
  function withdrawFromPlatformBalance(IDCAHub.AmountOfToken[] calldata amounts, address recipient) external;
}

interface IDCAHub is IDCAHubParameters, IDCAHubConfigHandler, IDCAHubSwapHandler, IDCAHubPositionHandler, IDCAHubPlatformHandler {
  /// @notice Specifies an amount of a token. For example to determine how much to borrow from certain tokens
  struct AmountOfToken {
    // The tokens' address
    address token;
    // How much to borrow or withdraw of the specified token
    uint256 amount;
  }

  /// @notice Thrown when one of the parameters is a zero address
  error ZeroAddress();

  /// @notice Thrown when the expected liquidity is not returned in flash swaps
  error LiquidityNotReturned();

  /// @notice Thrown when a list of token pairs is not sorted, or if there are duplicates
  error InvalidTokens();
}

File 9 of 34 : ISharedTypes.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/// @notice A pair of tokens
struct Pair {
  address tokenA;
  address tokenB;
}

File 10 of 34 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 11 of 34 : ITokenPriceOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/**
 * @title The interface for an oracle that provides price quotes
 * @notice These methods allow users to add support for pairs, and then ask for quotes
 */
interface ITokenPriceOracle {
  /// @notice Thrown when trying to add support for a pair that cannot be supported
  error PairCannotBeSupported(address tokenA, address tokenB);

  /// @notice Thrown when trying to execute a quote with a pair that isn't supported yet
  error PairNotSupportedYet(address tokenA, address tokenB);

  /**
   * @notice Returns whether this oracle can support the given pair of tokens
   * @dev tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @return Whether the given pair of tokens can be supported by the oracle
   */
  function canSupportPair(address tokenA, address tokenB) external view returns (bool);

  /**
   * @notice Returns whether this oracle is already supporting the given pair of tokens
   * @dev tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @return Whether the given pair of tokens is already being supported by the oracle
   */
  function isPairAlreadySupported(address tokenA, address tokenB) external view returns (bool);

  /**
   * @notice Returns a quote, based on the given tokens and amount
   * @dev Will revert if pair isn't supported
   * @param tokenIn The token that will be provided
   * @param amountIn The amount that will be provided
   * @param tokenOut The token we would like to quote
   * @param data Custom data that the oracle might need to operate
   * @return amountOut How much `tokenOut` will be returned in exchange for `amountIn` amount of `tokenIn`
   */
  function quote(
    address tokenIn,
    uint256 amountIn,
    address tokenOut,
    bytes calldata data
  ) external view returns (uint256 amountOut);

  /**
   * @notice Add or reconfigures the support for a given pair. This function will let the oracle take some actions
   *         to configure the pair, in preparation for future quotes. Can be called many times in order to let the oracle
   *         re-configure for a new context
   * @dev Will revert if pair cannot be supported. tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @param data Custom data that the oracle might need to operate
   */
  function addOrModifySupportForPair(
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external;

  /**
   * @notice Adds support for a given pair if the oracle didn't support it already. If called for a pair that is already supported,
   *         then nothing will happen. This function will let the oracle take some actions to configure the pair, in preparation
   *         for future quotes
   * @dev Will revert if pair cannot be supported. tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @param data Custom data that the oracle might need to operate
   */
  function addSupportForPairIfNeeded(
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external;
}

File 12 of 34 : IDCAPermissionManager.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@mean-finance/nft-descriptors/solidity/interfaces/IDCAHubPositionDescriptor.sol';

interface IERC721BasicEnumerable {
  /**
   * @notice Count NFTs tracked by this contract
   * @return A count of valid NFTs tracked by this contract, where each one of
   *         them has an assigned and queryable owner not equal to the zero address
   */
  function totalSupply() external view returns (uint256);
}

/**
 * @title The interface for all permission related matters
 * @notice These methods allow users to set and remove permissions to their positions
 */
interface IDCAPermissionManager is IERC721, IERC721BasicEnumerable {
  /// @notice Set of possible permissions
  enum Permission {
    INCREASE,
    REDUCE,
    WITHDRAW,
    TERMINATE
  }

  /// @notice A set of permissions for a specific operator
  struct PermissionSet {
    // The address of the operator
    address operator;
    // The permissions given to the overator
    Permission[] permissions;
  }

  /// @notice A collection of permissions sets for a specific position
  struct PositionPermissions {
    // The id of the token
    uint256 tokenId;
    // The permissions to assign to the position
    PermissionSet[] permissionSets;
  }

  /**
   * @notice Emitted when permissions for a token are modified
   * @param tokenId The id of the token
   * @param permissions The set of permissions that were updated
   */
  event Modified(uint256 tokenId, PermissionSet[] permissions);

  /**
   * @notice Emitted when the address for a new descritor is set
   * @param descriptor The new descriptor contract
   */
  event NFTDescriptorSet(IDCAHubPositionDescriptor descriptor);

  /// @notice Thrown when a user tries to set the hub, once it was already set
  error HubAlreadySet();

  /// @notice Thrown when a user provides a zero address when they shouldn't
  error ZeroAddress();

  /// @notice Thrown when a user calls a method that can only be executed by the hub
  error OnlyHubCanExecute();

  /// @notice Thrown when a user tries to modify permissions for a token they do not own
  error NotOwner();

  /// @notice Thrown when a user tries to execute a permit with an expired deadline
  error ExpiredDeadline();

  /// @notice Thrown when a user tries to execute a permit with an invalid signature
  error InvalidSignature();

  /**
   * @notice The permit typehash used in the permit signature
   * @return The typehash for the permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the permission permit signature
   * @return The typehash for the permission permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMISSION_PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the multi permission permit signature
   * @return The typehash for the multi permission permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function MULTI_PERMISSION_PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the permission permit signature
   * @return The typehash for the permission set
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMISSION_SET_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the multi permission permit signature
   * @return The typehash for the position permissions
   */
  // solhint-disable-next-line func-name-mixedcase
  function POSITION_PERMISSIONS_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The domain separator used in the permit signature
   * @return The domain seperator used in encoding of permit signature
   */
  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  /**
   * @notice Returns the NFT descriptor contract
   * @return The contract for the NFT descriptor
   */
  function nftDescriptor() external returns (IDCAHubPositionDescriptor);

  /**
   * @notice Returns the address of the DCA Hub
   * @return The address of the DCA Hub
   */
  function hub() external returns (address);

  /**
   * @notice Returns the next nonce to use for a given user
   * @param user The address of the user
   * @return nonce The next nonce to use
   */
  function nonces(address user) external returns (uint256 nonce);

  /**
   * @notice Returns whether the given address has the permission for the given token
   * @param id The id of the token to check
   * @param account The address of the user to check
   * @param permission The permission to check
   * @return Whether the user has the permission or not
   */
  function hasPermission(
    uint256 id,
    address account,
    Permission permission
  ) external view returns (bool);

  /**
   * @notice Returns whether the given address has the permissions for the given token
   * @param id The id of the token to check
   * @param account The address of the user to check
   * @param permissions The permissions to check
   * @return hasPermissions Whether the user has each permission or not
   */
  function hasPermissions(
    uint256 id,
    address account,
    Permission[] calldata permissions
  ) external view returns (bool[] memory hasPermissions);

  /**
   * @notice Sets the address for the hub
   * @dev Can only be successfully executed once. Once it's set, it can be modified again
   *      Will revert:
   *      - With ZeroAddress if address is zero
   *      - With HubAlreadySet if the hub has already been set
   * @param hub The address to set for the hub
   */
  function setHub(address hub) external;

  /**
   * @notice Mints a new NFT with the given id, and sets the permissions for it
   * @dev Will revert with OnlyHubCanExecute if the caller is not the hub
   * @param id The id of the new NFT
   * @param owner The owner of the new NFT
   * @param permissions Permissions to set for the new NFT
   */
  function mint(
    uint256 id,
    address owner,
    PermissionSet[] calldata permissions
  ) external;

  /**
   * @notice Burns the NFT with the given id, and clears all permissions
   * @dev Will revert with OnlyHubCanExecute if the caller is not the hub
   * @param id The token's id
   */
  function burn(uint256 id) external;

  /**
   * @notice Sets new permissions for the given position
   * @dev Will revert with NotOwner if the caller is not the token's owner.
   *      Operators that are not part of the given permission sets do not see their permissions modified.
   *      In order to remove permissions to an operator, provide an empty list of permissions for them
   * @param id The token's id
   * @param permissions A list of permission sets
   */
  function modify(uint256 id, PermissionSet[] calldata permissions) external;

  /**
   * @notice Sets new permissions for the given positions
   * @dev This is basically the same as executing multiple `modify`
   * @param permissions A list of position permissions to set
   */
  function modifyMany(PositionPermissions[] calldata permissions) external;

  /**
   * @notice Approves spending of a specific token ID by spender via signature
   * @param spender The account that is being approved
   * @param tokenId The ID of the token that is being approved for spending
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permit(
    address spender,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets permissions via signature
   * @dev This method works similarly to `modifyMany`, but instead of being executed by the owner, it can be set by signature
   * @param permissions The permissions to set for the different positions
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function multiPermissionPermit(
    PositionPermissions[] calldata permissions,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets permissions via signature
   * @dev This method works similarly to `modify`, but instead of being executed by the owner, it can be set my signature
   * @param permissions The permissions to set
   * @param tokenId The token's id
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permissionPermit(
    PermissionSet[] calldata permissions,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets a new NFT descriptor
   * @dev Will revert with ZeroAddress if address is zero
   * @param descriptor The new NFT descriptor contract
   */
  function setNFTDescriptor(IDCAHubPositionDescriptor descriptor) external;
}

File 13 of 34 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 14 of 34 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 15 of 34 : IDCAHubPositionDescriptor.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/**
 * @title The interface for generating a description for a position in a DCA Hub
 * @notice Contracts that implement this interface must return a base64 JSON with the entire description
 */
interface IDCAHubPositionDescriptor {
  /**
   * @notice Generates a positions's description, both the JSON and the image inside
   * @param hub The address of the DCA Hub
   * @param positionId The token/position id
   * @return description The position's description
   */
  function tokenURI(address hub, uint256 positionId) external view returns (string memory description);
}

File 16 of 34 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 17 of 34 : TokenSorting.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >0.6;

/// @title TokenSorting library
/// @notice Provides functions to sort tokens easily
library TokenSorting {
  /// @notice Takes two tokens, and returns them sorted
  /// @param _tokenA One of the tokens
  /// @param _tokenB The other token
  /// @return __tokenA The first of the tokens
  /// @return __tokenB The second of the tokens
  function sortTokens(address _tokenA, address _tokenB) internal pure returns (address __tokenA, address __tokenB) {
    (__tokenA, __tokenB) = _tokenA < _tokenB ? (_tokenA, _tokenB) : (_tokenB, _tokenA);
  }
}

File 18 of 34 : Intervals.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/// @title Intervals library
/// @notice Provides functions to easily convert from swap intervals to their byte representation and viceversa
library Intervals {
  /// @notice Thrown when a user tries convert and invalid interval to a byte representation
  error InvalidInterval();

  /// @notice Thrown when a user tries convert and invalid byte representation to an interval
  error InvalidMask();

  /// @notice Takes a swap interval and returns its byte representation
  /// @dev Will revert with InvalidInterval if the swap interval is not valid
  /// @param _swapInterval The swap interval
  /// @return The interval's byte representation
  function intervalToMask(uint32 _swapInterval) internal pure returns (bytes1) {
    if (_swapInterval == 1 minutes) return 0x01;
    if (_swapInterval == 5 minutes) return 0x02;
    if (_swapInterval == 15 minutes) return 0x04;
    if (_swapInterval == 30 minutes) return 0x08;
    if (_swapInterval == 1 hours) return 0x10;
    if (_swapInterval == 4 hours) return 0x20;
    if (_swapInterval == 1 days) return 0x40;
    if (_swapInterval == 1 weeks) return 0x80;
    revert InvalidInterval();
  }

  /// @notice Takes a byte representation of a swap interval and returns the swap interval
  /// @dev Will revert with InvalidMask if the byte representation is not valid
  /// @param _mask The byte representation
  /// @return The swap interval
  function maskToInterval(bytes1 _mask) internal pure returns (uint32) {
    if (_mask == 0x01) return 1 minutes;
    if (_mask == 0x02) return 5 minutes;
    if (_mask == 0x04) return 15 minutes;
    if (_mask == 0x08) return 30 minutes;
    if (_mask == 0x10) return 1 hours;
    if (_mask == 0x20) return 4 hours;
    if (_mask == 0x40) return 1 days;
    if (_mask == 0x80) return 1 weeks;
    revert InvalidMask();
  }

  /// @notice Takes a byte representation of a set of swap intervals and returns which ones are in the set
  /// @dev Will always return an array of length 8, with zeros at the end if there are less than 8 intervals
  /// @param _byte The byte representation
  /// @return _intervals The swap intervals in the set
  function intervalsInByte(bytes1 _byte) internal pure returns (uint32[] memory _intervals) {
    _intervals = new uint32[](8);
    uint8 _index;
    bytes1 _mask = 0x01;
    while (_byte >= _mask && _mask > 0) {
      if (_byte & _mask != 0) {
        _intervals[_index++] = maskToInterval(_mask);
      }
      _mask <<= 1;
    }
  }
}

File 19 of 34 : ILegacyDCAHub.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';

interface ILegacyDCAHub {
  /// @notice Information about a swap
  struct SwapInfo {
    // The tokens involved in the swap
    TokenInSwap[] tokens;
    // The pairs involved in the swap
    PairInSwap[] pairs;
  }

  /// @notice Information about a token's role in a swap
  struct TokenInSwap {
    // The token's address
    address token;
    // How much will be given of this token as a reward
    uint256 reward;
    // How much of this token needs to be provided by swapper
    uint256 toProvide;
    // How much of this token will be paid to the platform
    uint256 platformFee;
  }

  /// @notice Information about a pair in a swap
  struct PairInSwap {
    // The address of one of the tokens
    address tokenA;
    // The address of the other token
    address tokenB;
    // How much is 1 unit of token A when converted to B
    uint256 ratioAToB;
    // How much is 1 unit of token B when converted to A
    uint256 ratioBToA;
    // The swap intervals involved in the swap, represented as a byte
    bytes1 intervalsInSwap;
  }

  /**
   * @notice Returns all information related to the next swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   * @param tokens The tokens involved in the next swap
   * @param pairs The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @return swapInformation The information about the next swap
   */
  function getNextSwapInfo(address[] calldata tokens, IDCAHub.PairIndexes[] calldata pairs)
    external
    view
    returns (SwapInfo memory swapInformation);

  /**
   * @notice Executes a flash swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   *      - With Paused if swaps are paused by protocol
   *      - With NoSwapsToExecute if there are no swaps to execute for the given pairs
   *      - With LiquidityNotReturned if the required tokens were not back during the callback
   * @param tokens The tokens involved in the next swap
   * @param pairsToSwap The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param rewardRecipient The address to send the reward to
   * @param callbackHandler Address to call for callback (and send the borrowed tokens to)
   * @param borrow How much to borrow of each of the tokens in tokens. The amount must match the position of the token in the tokens array
   * @param callbackData Bytes to send to the caller during the callback
   * @return Information about the executed swap
   */
  function swap(
    address[] calldata tokens,
    IDCAHub.PairIndexes[] calldata pairsToSwap,
    address rewardRecipient,
    address callbackHandler,
    uint256[] calldata borrow,
    bytes calldata callbackData
  ) external returns (SwapInfo memory);
}

File 20 of 34 : GetBalances.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import './Shared.sol';
import '../SwapAdapter.sol';

abstract contract GetBalances is SwapAdapter {
  /// @notice The balance of a given token
  struct TokenBalance {
    address token;
    uint256 balance;
  }

  /**
   * @notice Returns the balance of each of the given tokens
   * @dev Meant to be used for off-chain queries
   * @param _tokens The tokens to check the balance for, can be ERC20s or the protocol token
   * @return _balances The balances for the given tokens
   */
  function getBalances(address[] calldata _tokens) external view returns (TokenBalance[] memory _balances) {
    _balances = new TokenBalance[](_tokens.length);
    for (uint256 i = 0; i < _tokens.length; ) {
      uint256 _balance = _tokens[i] == PROTOCOL_TOKEN ? address(this).balance : IERC20(_tokens[i]).balanceOf(address(this));
      _balances[i] = TokenBalance({token: _tokens[i], balance: _balance});
      unchecked {
        i++;
      }
    }
  }
}

File 21 of 34 : RevokableWithGovernor.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '../utils/Governable.sol';
import '../SwapAdapter.sol';

abstract contract RevokableWithGovernor is SwapAdapter, Governable {
  /**
   * @notice Revokes ERC20 allowances for the given spenders
   * @dev Can only be called by the governor
   * @param _revokeActions The spenders and tokens to revoke
   */
  function revokeAllowances(RevokeAction[] calldata _revokeActions) external onlyGovernor {
    _revokeAllowances(_revokeActions);
  }
}

File 22 of 34 : RunSwap.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '../SwapAdapter.sol';

abstract contract RunSwap is SwapAdapter {
  /// @notice The parameters to execute the call
  struct RunSwapParams {
    // The swapper that will execute the call
    address swapper;
    // The account that needs to be approved for token transfers
    address allowanceTarget;
    // The actual swap execution
    bytes swapData;
    // The token that will be swapped
    address tokenIn;
    // The amount of "token in" that will be spent
    uint256 amountIn;
  }

  /**
   * @notice Executes a swap with the given swapper. The input tokens are expected to be on the contract before
   *         this function is executed. If the swap doesn't include a transfer, then the swapped tokens will be left
   *         on the contract
   * @dev This function can only be executed with swappers that are allowlisted
   * @param _parameters The parameters for the swap
   */
  function runSwap(RunSwapParams calldata _parameters) public payable virtual onlyAllowlisted(_parameters.swapper) {
    if (_parameters.tokenIn == PROTOCOL_TOKEN) {
      _executeSwap(_parameters.swapper, _parameters.swapData, _parameters.amountIn);
    } else {
      _maxApproveSpenderIfNeeded(
        IERC20(_parameters.tokenIn),
        _parameters.allowanceTarget,
        _parameters.swapper == _parameters.allowanceTarget, // If target is a swapper, then it's ok as allowance target
        _parameters.amountIn
      );
      _executeSwap(_parameters.swapper, _parameters.swapData, 0);
    }
  }
}

File 23 of 34 : PayableMulticall.sol
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/utils/Address.sol';

/**
 * @dev Adding this contract will enable batching calls. This is basically the same as Open Zeppelin's
 *      Multicall contract, but we have made it payable. It supports both payable and non payable
 *      functions. However, if `msg.value` is not zero, then non payable functions cannot be called.
 *      Any contract that uses this Multicall version should be very careful when using msg.value.
 *      For more context, read: https://github.com/Uniswap/v3-periphery/issues/52
 */
abstract contract PayableMulticall {
  /**
   * @notice Receives and executes a batch of function calls on this contract.
   * @param _data A list of different function calls to execute
   * @return _results The result of executing each of those calls
   */
  function multicall(bytes[] calldata _data) external payable returns (bytes[] memory _results) {
    _results = new bytes[](_data.length);
    for (uint256 i = 0; i < _data.length; ) {
      _results[i] = Address.functionDelegateCall(address(this), _data[i]);
      unchecked {
        i++;
      }
    }
    return _results;
  }
}

File 24 of 34 : TokenPermit.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol';

abstract contract TokenPermit {
  /**
   * @notice Executes a permit on a ERC20 token that supports it
   * @param _token The token that will execute the permit
   * @param _owner The account that signed the permite
   * @param _spender The account that is being approved
   * @param _value The amount that is being approved
   * @param _v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param _r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param _s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permit(
    IERC20Permit _token,
    address _owner,
    address _spender,
    uint256 _value,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _token.permit(_owner, _spender, _value, _deadline, _v, _r, _s);
  }
}

File 25 of 34 : SwapAdapter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '../interfaces/ISwapAdapter.sol';

abstract contract SwapAdapter is ISwapAdapter {
  using SafeERC20 for IERC20;
  using Address for address;
  using Address for address payable;

  /// @inheritdoc ISwapAdapter
  address public constant PROTOCOL_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  /// @inheritdoc ISwapAdapter
  ISwapperRegistry public immutable SWAPPER_REGISTRY;

  constructor(address _swapperRegistry) {
    if (_swapperRegistry == address(0)) revert ZeroAddress();
    SWAPPER_REGISTRY = ISwapperRegistry(_swapperRegistry);
  }

  receive() external payable {}

  /**
   * @notice Takes the given amount of tokens from the caller
   * @param _token The token to check
   * @param _amount The amount to take
   */
  function _takeFromMsgSender(IERC20 _token, uint256 _amount) internal virtual {
    _token.safeTransferFrom(msg.sender, address(this), _amount);
  }

  /**
   * @notice Checks if the given spender has enough allowance, and approves the max amount
   *         if it doesn't
   * @param _token The token to check
   * @param _spender The spender to check
   * @param _minAllowance The min allowance. If the spender has over this amount, then no extra approve is needed
   */
  function _maxApproveSpenderIfNeeded(
    IERC20 _token,
    address _spender,
    bool _alreadyValidatedSpender,
    uint256 _minAllowance
  ) internal virtual {
    if (_spender != address(0)) {
      uint256 _allowance = _token.allowance(address(this), _spender);
      if (_allowance < _minAllowance) {
        if (!_alreadyValidatedSpender && !SWAPPER_REGISTRY.isValidAllowanceTarget(_spender)) {
          revert InvalidAllowanceTarget(_spender);
        }
        if (_allowance > 0) {
          _token.approve(_spender, 0); // We do this because some tokens (like USDT) fail if we don't
        }
        _token.approve(_spender, type(uint256).max);
      }
    }
  }

  /**
   * @notice Executes a swap for the given swapper
   * @param _swapper The actual swapper
   * @param _swapData The swap execution data
   */
  function _executeSwap(
    address _swapper,
    bytes calldata _swapData,
    uint256 _value
  ) internal virtual {
    _swapper.functionCallWithValue(_swapData, _value);
  }

  /**
   * @notice Checks if the contract has any balance of the given token, and if it does,
   *         it sends it to the given recipient
   * @param _token The token to check
   * @param _recipient The recipient of the token balance
   */
  function _sendBalanceOnContractToRecipient(address _token, address _recipient) internal virtual {
    uint256 _balance = _token == PROTOCOL_TOKEN ? address(this).balance : IERC20(_token).balanceOf(address(this));
    if (_balance > 0) {
      _sendToRecipient(_token, _balance, _recipient);
    }
  }

  /**
   * @notice Transfers the given amount of tokens from the contract to the recipient
   * @param _token The token to check
   * @param _amount The amount to send
   * @param _recipient The recipient
   */
  function _sendToRecipient(
    address _token,
    uint256 _amount,
    address _recipient
  ) internal virtual {
    if (_recipient == address(0)) _recipient = msg.sender;
    if (_token == PROTOCOL_TOKEN) {
      payable(_recipient).sendValue(_amount);
    } else {
      IERC20(_token).safeTransfer(_recipient, _amount);
    }
  }

  /**
   * @notice Checks if given swapper is allowlisted, and fails if it isn't
   * @param _swapper The swapper to check
   */
  function _assertSwapperIsAllowlisted(address _swapper) internal view {
    if (!SWAPPER_REGISTRY.isSwapperAllowlisted(_swapper)) revert SwapperNotAllowlisted(_swapper);
  }

  /**
   * @notice Revokes ERC20 allowances for the given spenders
   * @dev If exposed, then it should be permissioned
   * @param _revokeActions The spenders and tokens to revoke
   */
  function _revokeAllowances(RevokeAction[] calldata _revokeActions) internal virtual {
    for (uint256 i = 0; i < _revokeActions.length; ) {
      RevokeAction memory _action = _revokeActions[i];
      for (uint256 j = 0; j < _action.tokens.length; ) {
        _action.tokens[j].approve(_action.spender, 0);
        unchecked {
          j++;
        }
      }
      unchecked {
        i++;
      }
    }
  }

  modifier onlyAllowlisted(address _swapper) {
    _assertSwapperIsAllowlisted(_swapper);
    _;
  }
}

File 26 of 34 : Shared.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

/// @notice An amount to take form the caller
struct TakeFromCaller {
  // The token that will be taken from the caller
  IERC20 token;
  // The amount that will be taken
  uint256 amount;
}

/// @notice An allowance to provide for the swaps to work
struct Allowance {
  // The token that should be approved
  IERC20 token;
  // The spender
  address allowanceTarget;
  // The minimum allowance needed
  uint256 minAllowance;
}

/// @notice A swap to execute
struct Swap {
  // The index of the swapper in the list of swappers
  uint8 swapperIndex;
  // The data to send to the swapper
  bytes swapData;
}

/// @notice A token that was left on the contract and should be transferred out
struct TransferOutBalance {
  // The token to transfer
  address token;
  // The recipient of those tokens
  address recipient;
}

/// @notice Context necessary for the swap execution
struct SwapContext {
  // The index of the swapper that should execute each swap. This might look strange but it's way cheaper than alternatives
  uint8 swapperIndex;
  // The ETH/MATIC/BNB to send as part of the swap
  uint256 value;
}

File 27 of 34 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 28 of 34 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.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 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 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. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 29 of 34 : ISwapAdapter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/interfaces/IERC20.sol';
import './ISwapperRegistry.sol';

/**
 * @notice This abstract contract will give contracts that implement it swapping capabilities. It will
 *         take a swapper and a swap's data, and if the swapper is valid, it will execute the swap
 */
interface ISwapAdapter {
  /// @notice Describes how the allowance should be revoked for the given spender
  struct RevokeAction {
    address spender;
    IERC20[] tokens;
  }

  /// @notice Thrown when one of the parameters is a zero address
  error ZeroAddress();

  /**
   * @notice Thrown when trying to execute a swap with a swapper that is not allowlisted
   * @param swapper The swapper that was not allowlisted
   */
  error SwapperNotAllowlisted(address swapper);

  /// @notice Thrown when the allowance target is not allowed by the swapper registry
  error InvalidAllowanceTarget(address spender);

  /**
   * @notice Returns the address of the swapper registry
   * @dev Cannot be modified
   * @return The address of the swapper registry
   */
  function SWAPPER_REGISTRY() external view returns (ISwapperRegistry);

  /**
   * @notice Returns the address of the protocol token
   * @dev Cannot be modified
   * @return The address of the protocol token;
   */
  function PROTOCOL_TOKEN() external view returns (address);
}

File 30 of 34 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.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 31 of 34 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

File 32 of 34 : ISwapperRegistry.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/**
 * @notice This contract will act as a registry to allowlist swappers. Different contracts from the Mean
 *         ecosystem will ask this contract if an address is a valid swapper or not
 *         In some cases, swappers have supplementary allowance targets that need ERC20 approvals. We
 *         will also track those here
 */
interface ISwapperRegistry {
  /// @notice Thrown when one of the parameters is a zero address
  error ZeroAddress();

  /// @notice Thrown when trying to remove an account from the swappers list, when it wasn't there before
  error AccountIsNotSwapper(address account);

  /**
   * @notice Thrown when trying to remove an account from the supplementary allowance target list,
   *         when it wasn't there before
   */
  error AccountIsNotSupplementaryAllowanceTarget(address account);

  /// @notice Thrown when trying to mark an account as swapper or allowance target, but it already has a role assigned
  error AccountAlreadyHasRole(address account);

  /**
   * @notice Emitted when swappers are removed from the allowlist
   * @param swappers The swappers that were removed
   */
  event RemoveSwappersFromAllowlist(address[] swappers);

  /**
   * @notice Emitted when new swappers are added to the allowlist
   * @param swappers The swappers that were added
   */
  event AllowedSwappers(address[] swappers);

  /**
   * @notice Emitted when new supplementary allowance targets are are added to the allowlist
   * @param allowanceTargets The allowance targets that were added
   */
  event AllowedSupplementaryAllowanceTargets(address[] allowanceTargets);

  /**
   * @notice Emitted when supplementary allowance targets are removed from the allowlist
   * @param allowanceTargets The allowance targets that were removed
   */
  event RemovedAllowanceTargetsFromAllowlist(address[] allowanceTargets);

  /**
   * @notice Returns whether a given account is allowlisted for swaps
   * @param account The address to check
   * @return Whether it is allowlisted for swaps
   */
  function isSwapperAllowlisted(address account) external view returns (bool);

  /**
   * @notice Returns whether a given account is a valid allowance target. This would be true
   *         if the account is either a swapper, or a supplementary allowance target
   * @param account The address to check
   * @return Whether it is a valid allowance target
   */
  function isValidAllowanceTarget(address account) external view returns (bool);

  /**
   * @notice Adds a list of swappers to the allowlist
   * @dev Can only be called by users with the admin role
   * @param swappers The list of swappers to add
   */
  function allowSwappers(address[] calldata swappers) external;

  /**
   * @notice Removes the given swappers from the allowlist
   * @dev Can only be called by users with the admin role
   * @param swappers The list of swappers to remove
   */
  function removeSwappersFromAllowlist(address[] calldata swappers) external;

  /**
   * @notice Adds a list of supplementary allowance targets to the allowlist
   * @dev Can only be called by users with the admin role
   * @param allowanceTargets The list of allowance targets to add
   */
  function allowSupplementaryAllowanceTargets(address[] calldata allowanceTargets) external;

  /**
   * @notice Removes the given allowance targets from the allowlist
   * @dev Can only be called by users with the admin role
   * @param allowanceTargets The list of allowance targets to remove
   */
  function removeSupplementaryAllowanceTargetsFromAllowlist(address[] calldata allowanceTargets) external;
}

File 33 of 34 : Governable.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '../../interfaces/utils/IGovernable.sol';

/**
 * @notice This contract is meant to be used in other contracts. By using this contract,
 *         a specific address will be given a "governor" role, which basically will be able to
 *         control certains aspects of the contract. There are other contracts that do the same,
 *         but this contract forces a new governor to accept the role before it's transferred.
 *         This is a basically a safety measure to prevent losing access to the contract.
 */
abstract contract Governable is IGovernable {
  /// @inheritdoc IGovernable
  address public governor;

  /// @inheritdoc IGovernable
  address public pendingGovernor;

  constructor(address _governor) {
    if (_governor == address(0)) revert GovernorIsZeroAddress();
    governor = _governor;
  }

  /// @inheritdoc IGovernable
  function isGovernor(address _account) public view returns (bool) {
    return _account == governor;
  }

  /// @inheritdoc IGovernable
  function isPendingGovernor(address _account) public view returns (bool) {
    return _account == pendingGovernor;
  }

  /// @inheritdoc IGovernable
  function setPendingGovernor(address _pendingGovernor) external onlyGovernor {
    pendingGovernor = _pendingGovernor;
    emit PendingGovernorSet(_pendingGovernor);
  }

  /// @inheritdoc IGovernable
  function acceptPendingGovernor() external onlyPendingGovernor {
    governor = pendingGovernor;
    pendingGovernor = address(0);
    emit PendingGovernorAccepted();
  }

  modifier onlyGovernor() {
    if (!isGovernor(msg.sender)) revert OnlyGovernor();
    _;
  }

  modifier onlyPendingGovernor() {
    if (!isPendingGovernor(msg.sender)) revert OnlyPendingGovernor();
    _;
  }
}

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

/**
 * @title A contract that manages a "governor" role
 */
interface IGovernable {
  /// @notice Thrown when trying to set the zero address as governor
  error GovernorIsZeroAddress();

  /// @notice Thrown when trying to execute an action that only the governor an execute
  error OnlyGovernor();

  /// @notice Thrown when trying to execute an action that only the pending governor an execute
  error OnlyPendingGovernor();

  /**
   * @notice Emitted when a new pending governor is set
   * @param newPendingGovernor The new pending governor
   */
  event PendingGovernorSet(address newPendingGovernor);

  /**
   * @notice Emitted when the pending governor accepts the role and becomes the governor
   */
  event PendingGovernorAccepted();

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

  /**
   * @notice Returns the address of the pending governor
   * @return The address of the pending governor
   */
  function pendingGovernor() external view returns (address);

  /**
   * @notice Returns whether the given account is the current governor
   * @param account The account to check
   * @return Whether it is the current governor or not
   */
  function isGovernor(address account) external view returns (bool);

  /**
   * @notice Returns whether the given account is the pending governor
   * @param account The account to check
   * @return Whether it is the pending governor or not
   */
  function isPendingGovernor(address account) external view returns (bool);

  /**
   * @notice Sets a new pending governor
   * @dev Only the current governor can execute this action
   * @param pendingGovernor The new pending governor
   */
  function setPendingGovernor(address pendingGovernor) external;

  /**
   * @notice Sets the pending governor as the governor
   * @dev Only the pending governor can execute this action
   */
  function acceptPendingGovernor() external;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 9999
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_swapperRegistry","type":"address"},{"internalType":"address","name":"_governor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"GovernorIsZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"InvalidAllowanceTarget","type":"error"},{"inputs":[],"name":"InvalidMask","type":"error"},{"inputs":[],"name":"OnlyGovernor","type":"error"},{"inputs":[],"name":"OnlyPendingGovernor","type":"error"},{"inputs":[{"internalType":"address","name":"swapper","type":"address"}],"name":"SwapperNotAllowlisted","type":"error"},{"inputs":[],"name":"UnauthorizedCaller","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[],"name":"PendingGovernorAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPendingGovernor","type":"address"}],"name":"PendingGovernorSet","type":"event"},{"inputs":[],"name":"PROTOCOL_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWAPPER_REGISTRY","outputs":[{"internalType":"contract ISwapperRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptPendingGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_amountOfSwaps","type":"uint32"},{"internalType":"uint32","name":"_swapInterval","type":"uint32"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"bytes","name":"_miscellaneous","type":"bytes"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint32","name":"_amountOfSwaps","type":"uint32"},{"internalType":"uint32","name":"_swapInterval","type":"uint32"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"bytes","name":"_miscellaneous","type":"bytes"}],"name":"depositWithBalanceOnContract","outputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getBalances","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct GetBalances.TokenBalance[]","name":"_balances","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"internalType":"struct Pair[]","name":"_pairs","type":"tuple[]"},{"internalType":"bool","name":"_calculatePrivilegedAvailability","type":"bool"},{"internalType":"bytes","name":"_oracleData","type":"bytes"}],"name":"getNextSwapInfo","outputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"toProvide","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"}],"internalType":"struct IDCAHubSwapHandler.TokenInSwap[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"totalAmountToSwapTokenA","type":"uint256"},{"internalType":"uint256","name":"totalAmountToSwapTokenB","type":"uint256"},{"internalType":"uint256","name":"ratioAToB","type":"uint256"},{"internalType":"uint256","name":"ratioBToA","type":"uint256"},{"internalType":"bytes1","name":"intervalsInSwap","type":"bytes1"}],"internalType":"struct IDCAHubSwapHandler.PairInSwap[]","name":"pairs","type":"tuple[]"}],"internalType":"struct IDCAHubSwapHandler.SwapInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"}],"name":"increasePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"}],"name":"increasePositionWithBalanceOnContract","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isGovernor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isPendingGovernor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILegacyDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"internalType":"struct Pair[]","name":"_pairs","type":"tuple[]"}],"name":"legacyGetNextSwapInfo","outputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"toProvide","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"}],"internalType":"struct ILegacyDCAHub.TokenInSwap[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"ratioAToB","type":"uint256"},{"internalType":"uint256","name":"ratioBToA","type":"uint256"},{"internalType":"bytes1","name":"intervalsInSwap","type":"bytes1"}],"internalType":"struct ILegacyDCAHub.PairInSwap[]","name":"pairs","type":"tuple[]"}],"internalType":"struct ILegacyDCAHub.SwapInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAPermissionManager","name":"_permissionManager","type":"address"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"permissionSets","type":"tuple[]"}],"internalType":"struct IDCAPermissionManager.PositionPermissions[]","name":"_permissions","type":"tuple[]"},{"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":"multiPermissionPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"_results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"pendingGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAPermissionManager","name":"_permissionManager","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"uint256","name":"_tokenId","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":"permissionPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20Permit","name":"_token","type":"address"},{"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":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"reducePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"internalType":"struct ISwapAdapter.RevokeAction[]","name":"_revokeActions","type":"tuple[]"}],"name":"revokeAllowances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"swapper","type":"address"},{"internalType":"address","name":"allowanceTarget","type":"address"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"internalType":"struct RunSwap.RunSwapParams","name":"_parameters","type":"tuple"}],"name":"runSwap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"internalType":"struct Pair[]","name":"_pairs","type":"tuple[]"},{"internalType":"bool","name":"_calculatePrivilegedAvailability","type":"bool"}],"name":"secondsUntilNextSwap","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"sendBalanceOnContractToRecipient","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"sendToRecipient","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingGovernor","type":"address"}],"name":"setPendingGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"takeFromCaller","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"address","name":"_recipientUnswapped","type":"address"},{"internalType":"address","name":"_recipientSwapped","type":"address"}],"name":"terminate","outputs":[{"internalType":"uint256","name":"_unswapped","type":"uint256"},{"internalType":"uint256","name":"_swapped","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawSwapped","outputs":[{"internalType":"uint256","name":"_swapped","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256[]","name":"positionIds","type":"uint256[]"}],"internalType":"struct IDCAHubPositionHandler.PositionSet[]","name":"_positions","type":"tuple[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawSwappedMany","outputs":[{"internalType":"uint256[]","name":"_withdrawn","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b5060405162005469380380620054698339810160408190526200003491620000d6565b818180826001600160a01b038116620000605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0390811660805281166200008e5760405163e6250e3360e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055506200010e92505050565b80516001600160a01b0381168114620000d157600080fd5b919050565b60008060408385031215620000ea57600080fd5b620000f583620000b9565b91506200010560208401620000b9565b90509250929050565b608051615331620001386000396000818161041b01528181611a360152611c1c01526153316000f3fe6080604052600436106101c65760003560e01c80635f963dcf116100f7578063d339056d11610095578063e43581b811610064578063e43581b8146104e2578063e9c0407114610511578063f235757f14610531578063fa0c95551461055157600080fd5b8063d339056d1461045d578063db8266de14610470578063df08aed514610483578063e3056a34146104c257600080fd5b806389352328116100d157806389352328146103d6578063ac9650d8146103e9578063b3b48afb14610409578063bb2871a51461043d57600080fd5b80635f963dcf1461038857806362dd9af3146103b057806385f44632146103c357600080fd5b8063340b532f1161016457806340c5710c1161013e57806340c5710c146102f3578063484b3577146103065780635684c27514610333578063585cc6a51461036057600080fd5b8063340b532f146102ad57806335ac2a50146102c05780633a79d674146102e057600080fd5b806313f6986d116101a057806313f6986d146102375780631f66925c1461024c5780631f8b479d1461026d5780632d2ae1c11461028057600080fd5b806305ce20d6146101d25780630c340a24146101e757806312b17e351461022457600080fd5b366101cd57005b600080fd5b6101e56101e03660046135fa565b610564565b005b3480156101f357600080fd5b50600054610207906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6101e5610232366004613644565b610698565b34801561024357600080fd5b506101e56106a6565b61025f61025a36600461367b565b610752565b60405190815260200161021b565b6101e561027b36600461371a565b6107fa565b34801561028c57600080fd5b506102a061029b3660046137a6565b61084b565b60405161021b91906137e8565b6101e56102bb366004613840565b610a29565b6102d36102ce366004613879565b610a33565b60405161021b91906138d6565b6101e56102ee36600461367b565b610b82565b6101e561030136600461391a565b610b92565b34801561031257600080fd5b506103266103213660046139e6565b610d15565b60405161021b9190613a7d565b34801561033f57600080fd5b5061035361034e366004613bac565b610dda565b60405161021b9190613c01565b34801561036c57600080fd5b5061020773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61039b610396366004613d0d565b610e98565b6040805192835260208301919091520161021b565b6101e56103be366004613d55565b610f4d565b6101e56103d1366004613dd8565b610fd7565b61025f6103e4366004613e13565b6110c8565b6103fc6103f73660046137a6565b611173565b60405161021b9190613f44565b34801561041557600080fd5b506102077f000000000000000000000000000000000000000000000000000000000000000081565b34801561044957600080fd5b506102d3610458366004613fc4565b61125c565b6101e561046b366004614021565b611272565b6101e561047e3660046140a2565b6112e6565b34801561048f57600080fd5b506104b261049e366004614101565b6001546001600160a01b0391821691161490565b604051901515815260200161021b565b3480156104ce57600080fd5b50600154610207906001600160a01b031681565b3480156104ee57600080fd5b506104b26104fd366004614101565b6000546001600160a01b0391821691161490565b34801561051d57600080fd5b506101e561052c3660046137a6565b611356565b34801561053d57600080fd5b506101e561054c366004614101565b6113a4565b61025f61055f36600461411e565b611454565b8383600061057383838361159b565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018790526000906001600160a01b03891690635b6fd01d9060240161010060405180830381865afa1580156105d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f99190614310565b5190506106078189886116a3565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b0389169063ded700a6906064015b600060405180830381600087803b15801561067657600080fd5b505af115801561068a573d6000803e3d6000fd5b505050505050505050505050565b6106a28282611863565b5050565b6001546001600160a01b031633146106ea576040517f9ba0305d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018054600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b0384161782559091169091556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd769190a1565b60008383600261076383838361159b565b6040517f17621890000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0386811660248301528816906317621890906044016020604051808303816000875af11580156107cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ef91906143cc565b979650505050505050565b6040517f4faa38830000000000000000000000000000000000000000000000000000000081526001600160a01b03891690634faa38839061065c908a908a908a908a908a908a908a90600401614579565b60608167ffffffffffffffff8111156108665761086661420c565b6040519080825280602002602001820160405280156108ab57816020015b60408051808201909152600080825260208201528152602001906001900390816108845790505b50905060005b82811015610a2257600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8585848181106108e2576108e26145ba565b90506020020160208101906108f79190614101565b6001600160a01b0316146109b457848483818110610917576109176145ba565b905060200201602081019061092c9190614101565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561098b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109af91906143cc565b6109b6565b475b905060405180604001604052808686858181106109d5576109d56145ba565b90506020020160208101906109ea9190614101565b6001600160a01b0316815260200182815250838381518110610a0e57610a0e6145ba565b6020908102919091010152506001016108b1565b5092915050565b6106a28282611878565b606060005b83811015610ae8576000858583818110610a5457610a546145ba565b9050602002810190610a6691906145e9565b610a74906020810190614627565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394505050505b8151811015610ade57610ad688838381518110610ac757610ac76145ba565b6020026020010151600261159b565b600101610aa8565b5050600101610a38565b506040517f480b37960000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063480b379690610b329087908790879060040161468f565b6000604051808303816000875af1158015610b51573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b7991908101906147a7565b95945050505050565b610b8d838383611935565b505050565b82826000610ba183838361159b565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018690526000906001600160a01b03881690635b6fd01d9060240161010060405180830381865afa158015610c03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c279190614310565b516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610caf91906143cc565b9050610cbc8289836116a3565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810182905263ffffffff871660448201526001600160a01b0389169063ded700a69060640161065c565b6040805180820190915260608082526020820152600080610d36888861199d565b6040517f4997cdc300000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038a1690634997cdc390610d8890859085908b908b908b906004016148e3565b600060405180830381865afa158015610da5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610dcd9190810190614a13565b9998505050505050505050565b6040805180820190915260608082526020820152600080610dfb858561199d565b6040517fd2d95b2d00000000000000000000000000000000000000000000000000000000815291935091506001600160a01b0387169063d2d95b2d90610e479085908590600401614b7e565b600060405180830381865afa158015610e64573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e8c9190810190614ba3565b925050505b9392505050565b60008085856003610eaa83838361159b565b6040517f72ada4c5000000000000000000000000000000000000000000000000000000008152600481018990526001600160a01b03888116602483015287811660448301528a16906372ada4c59060640160408051808303816000875af1158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190614ce7565b909a909950975050505050505050565b6040517f3dff05870000000000000000000000000000000000000000000000000000000081526001600160a01b03881690633dff058790610f9c90899089908990899089908990600401614d0b565b600060405180830381600087803b158015610fb657600080fd5b505af1158015610fca573d6000803e3d6000fd5b5050505050505050505050565b610fe46020820182614101565b610fed816119fe565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6110126080840160608501614101565b6001600160a01b031603611047576106a26110306020840184614101565b61103d6040850185614dc8565b8560800135611aea565b6110a461105a6080840160608501614101565b61106a6040850160208601614101565b61107a6040860160208701614101565b6001600160a01b03166110906020870187614101565b6001600160a01b0316148560800135611b35565b6106a26110b46020840184614101565b6110c16040850185614dc8565b6000611aea565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038c16906370a0823190602401602060405180830381865afa15801561112a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114e91906143cc565b90506111638c8c8c848d8d8d8d8d8d8d611454565b9c9b505050505050505050505050565b60608167ffffffffffffffff81111561118e5761118e61420c565b6040519080825280602002602001820160405280156111c157816020015b60608152602001906001900390816111ac5790505b50905060005b82811015610a2257611231308585848181106111e5576111e56145ba565b90506020028101906111f79190614dc8565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611df492505050565b828281518110611243576112436145ba565b60209081029190910101526001016111c7565b92915050565b6060610b7985858585611e19565b949350505050565b6040517fd505accf0000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301528781166024830152604482018790526064820186905260ff8516608483015260a4820184905260c4820183905289169063d505accf9060e40161065c565b848460016112f583838361159b565b6040517ff1accf39000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b03858116606483015289169063f1accf399060840161065c565b6000546001600160a01b0316331461139a576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106a28282611eff565b6000546001600160a01b031633146113e8576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def9060200160405180910390a150565b60006114618b8d8b6116a3565b81611501576040517f6b29e1bd0000000000000000000000000000000000000000000000000000000081526001600160a01b038d1690636b29e1bd906114b9908e908e908e908e908e908e908e908e90600401614e2d565b6020604051808303816000875af11580156114d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fc91906143cc565b611163565b6040517fb2b513c10000000000000000000000000000000000000000000000000000000081526001600160a01b038d169063b2b513c190611558908e908e908e908e908e908e908e908e908e908e90600401614e8c565b6020604051808303816000875af1158015611577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116391906143cc565b826001600160a01b031663cc7a20496040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115fd9190614f03565b6001600160a01b031663823abfd98333846040518463ffffffff1660e01b815260040161162c93929190614f20565b602060405180830381865afa158015611649573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166d9190614f43565b610b8d576040517f5c427cd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa15801561170c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173091906143cc565b90508181101561185d5780156117ce576040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526000602483015285169063095ea7b3906044016020604051808303816000875af11580156117a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117cc9190614f43565b505b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152600019602483015285169063095ea7b3906044016020604051808303816000875af1158015611837573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185b9190614f43565b505b50505050565b6106a26001600160a01b03831633308461200d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611924576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156118fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191f91906143cc565b611926565b475b90508015610b8d57610b8d8382845b6001600160a01b0381166119465750335b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b0384160161198957610b8d6001600160a01b038216836120be565b610b8d6001600160a01b038416828461220b565b6060806119f2848460006040519080825280602002602001820160405280156119ec57816020015b60408051808201909152600080825260208201528152602001906001900390816119c55790505b50612254565b50909590945092505050565b6040517fcb00d85f0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063cb00d85f90602401602060405180830381865afa158015611a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa19190614f43565b611ae7576040517f220d24fd0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b50565b61185b83838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050506001600160a01b038716919050836122d9565b6001600160a01b0383161561185d576040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384811660248301526000919086169063dd62ed3e90604401602060405180830381865afa158015611bad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd191906143cc565b90508181101561185b5782158015611c8957506040517f344cb9cd0000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063344cb9cd90602401602060405180830381865afa158015611c63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c879190614f43565b155b15611ccb576040517f1f0b33c30000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401611ade565b8015611d5f576040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301526000602483015286169063095ea7b3906044016020604051808303816000875af1158015611d39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5d9190614f43565b505b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152600019602483015286169063095ea7b3906044016020604051808303816000875af1158015611dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dec9190614f43565b505050505050565b6060610e9183836040518060600160405280602781526020016152d5602791396122ff565b60608267ffffffffffffffff811115611e3457611e3461420c565b604051908082528060200260200182016040528015611e5d578160200160208202803683370190505b50905060005b83811015611ef657611ec786868684818110611e8157611e816145ba565b611e979260206040909202019081019150614101565b878785818110611ea957611ea96145ba565b9050604002016020016020810190611ec19190614101565b86612403565b828281518110611ed957611ed96145ba565b602090810291909101015280611eee81614f8f565b915050611e63565b50949350505050565b60005b81811015610b8d576000838383818110611f1e57611f1e6145ba565b9050602002810190611f3091906145e9565b611f3990614fa9565b905060005b8160200151518110156120035781602001518181518110611f6157611f616145ba565b602090810291909101015182516040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201526000602482015291169063095ea7b3906044016020604051808303816000875af1158015611fd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffa9190614f43565b50600101611f3e565b5050600101611f02565b6040516001600160a01b038085166024830152831660448201526064810182905261185d9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261277a565b80471015612128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401611ade565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612175576040519150601f19603f3d011682016040523d82523d6000602084013e61217a565b606091505b5050905080610b8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401611ade565b6040516001600160a01b038316602482015260448101829052610b8d9084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161205a565b60608060606122b58686808060200260200160405190810160405280939291908181526020016000905b828210156122aa5761229b60408302860136819003810190615061565b8152602001906001019061227e565b505050505085612879565b92506122c2868685612d5d565b91506122ce8484612ff8565b905093509350939050565b606061126a8484846040518060600160405280602981526020016152ac602991396130fe565b60606001600160a01b0384163b612398576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401611ade565b600080856001600160a01b0316856040516123b391906150c0565b600060405180830381855af49150503d80600081146123ee576040519150601f19603f3d011682016040523d82523d6000602084013e6123f3565b606091505b5091509150610e8c82828661326b565b600080600061241286866132be565b6040517f582cf84b0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152808316602483015292945090925060009189169063582cf84b90604401602060405180830381865afa158015612482573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a691906150d2565b90507f010000000000000000000000000000000000000000000000000000000000000060005b7fff000000000000000000000000000000000000000000000000000000000000008083169084161080159061252257507fff00000000000000000000000000000000000000000000000000000000000000821615155b1561276957817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168284167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19160361273b576040517f808ba8e00000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015289811660248301527fff000000000000000000000000000000000000000000000000000000000000008416604483015260009182918291908e169063808ba8e090606401608060405180830381865afa15801561260b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262f9190615119565b935093509350506000612641866132ef565b90506000816126508186615171565b61265b9060016151bb565b61266591906151d8565b63ffffffff1690508b6126905761267d600383615171565b61268d9063ffffffff1682615204565b90505b6000857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1611806126db57506000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16115b15612714574281116126f957859a505050505050505050505061126a565b6127034282615217565b9a505050505050505050505061126a565b428111156127355785156127285785612732565b6127324282615217565b95505b50505050505b6001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b91506124cc565b506000199998505050505050505050565b60006127cf826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135b49092919063ffffffff16565b805190915015610b8d57808060200190518101906127ed9190614f43565b610b8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611ade565b606060008083518551600261288e919061522a565b6128989190615204565b67ffffffffffffffff8111156128b0576128b061420c565b6040519080825280602002602001820160405280156128d9578160200160208202803683370190505b50905060005b8551811015612ab35760008060005b858110801561290457508280156129025750815b155b156129d9578215801561295e5750888481518110612924576129246145ba565b6020026020010151600001516001600160a01b031685828151811061294b5761294b6145ba565b60200260200101516001600160a01b0316145b1561296857600192505b811580156129bd5750888481518110612983576129836145ba565b6020026020010151602001516001600160a01b03168582815181106129aa576129aa6145ba565b60200260200101516001600160a01b0316145b156129c757600191505b806129d181614f8f565b9150506128ee565b5081612a3c578783815181106129f1576129f16145ba565b602002602001015160000151848680612a0990614f8f565b975081518110612a1b57612a1b6145ba565b60200260200101906001600160a01b031690816001600160a01b0316815250505b80612a9e57878381518110612a5357612a536145ba565b602002602001015160200151848680612a6b90614f8f565b975081518110612a7d57612a7d6145ba565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50508080612aab90614f8f565b9150506128df565b5060005b8451811015612bb2576000805b8481108015612ad1575081155b15612b3c57868381518110612ae857612ae86145ba565b6020026020010151600001516001600160a01b0316848281518110612b0f57612b0f6145ba565b60200260200101516001600160a01b031603612b2a57600191505b80612b3481614f8f565b915050612ac4565b5080612b9f57858281518110612b5457612b546145ba565b602002602001015160000151838580612b6c90614f8f565b965081518110612b7e57612b7e6145ba565b60200260200101906001600160a01b031690816001600160a01b0316815250505b5080612baa81614f8f565b915050612ab7565b508167ffffffffffffffff811115612bcc57612bcc61420c565b604051908082528060200260200182016040528015612bf5578160200160208202803683370190505b50925060005b82811015612d54576000828281518110612c1757612c176145ba565b6020026020010151905060005b816001600160a01b0316868281518110612c4057612c406145ba565b60200260200101516001600160a01b0316108015612c8a575060006001600160a01b0316868281518110612c7657612c766145ba565b60200260200101516001600160a01b031614155b15612ca15780612c9981614f8f565b915050612c24565b825b81811115612d0b5786612cb7600183615217565b81518110612cc757612cc76145ba565b6020026020010151878281518110612ce157612ce16145ba565b6001600160a01b039092166020928302919091019091015280612d0381615249565b915050612ca3565b5081868281518110612d1f57612d1f6145ba565b60200260200101906001600160a01b031690816001600160a01b03168152505050508080612d4c90614f8f565b915050612bfb565b50505092915050565b60608267ffffffffffffffff811115612d7857612d7861420c565b604051908082528060200260200182016040528015612dbd57816020015b6040805180820190915260008082526020820152815260200190600190039081612d965790505b5090506000805b83518160ff161015612fef576000612ddd826001615260565b90505b84518160ff161015612fdc5760005b86811015612fc957878782818110612e0957612e096145ba565b612e1f9260206040909202019081019150614101565b6001600160a01b0316868460ff1681518110612e3d57612e3d6145ba565b60200260200101516001600160a01b0316148015612eb15750878782818110612e6857612e686145ba565b9050604002016020016020810190612e809190614101565b6001600160a01b0316868360ff1681518110612e9e57612e9e6145ba565b60200260200101516001600160a01b0316145b80612f715750878782818110612ec957612ec96145ba565b9050604002016020016020810190612ee19190614101565b6001600160a01b0316868460ff1681518110612eff57612eff6145ba565b60200260200101516001600160a01b0316148015612f715750878782818110612f2a57612f2a6145ba565b612f409260206040909202019081019150614101565b6001600160a01b0316868360ff1681518110612f5e57612f5e6145ba565b60200260200101516001600160a01b0316145b15612fb7576040805180820190915260ff8085168252831660208201528585612f9981614f8f565b965081518110612fab57612fab6145ba565b60200260200101819052505b80612fc181614f8f565b915050612def565b5080612fd481615279565b915050612de0565b5080612fe781615279565b915050612dc4565b50509392505050565b6060815167ffffffffffffffff8111156130145761301461420c565b60405190808252806020026020018201604052801561303d578160200160208202803683370190505b50905060005b8351811015610a225760005b848281518110613061576130616145ba565b6020026020010151600001516001600160a01b0316848281518110613088576130886145ba565b60200260200101516001600160a01b0316146130b057806130a881614f8f565b91505061304f565b8482815181106130c2576130c26145ba565b6020026020010151602001518382815181106130e0576130e06145ba565b602090810291909101015250806130f681614f8f565b915050613043565b606082471015613190576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401611ade565b6001600160a01b0385163b613201576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611ade565b600080866001600160a01b0316858760405161321d91906150c0565b60006040518083038185875af1925050503d806000811461325a576040519150601f19603f3d011682016040523d82523d6000602084013e61325f565b606091505b50915091506107ef8282865b6060831561327a575081610e91565b82511561328a5782518084602001fd5b816040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ade9190615298565b600080826001600160a01b0316846001600160a01b0316106132e15782846132e4565b83835b909590945092505050565b60007fff0000000000000000000000000000000000000000000000000000000000000082167f0100000000000000000000000000000000000000000000000000000000000000036133425750603c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f020000000000000000000000000000000000000000000000000000000000000003613394575061012c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0400000000000000000000000000000000000000000000000000000000000000036133e65750610384919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0800000000000000000000000000000000000000000000000000000000000000036134385750610708919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f10000000000000000000000000000000000000000000000000000000000000000361348a5750610e10919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f2000000000000000000000000000000000000000000000000000000000000000036134dc5750613840919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f40000000000000000000000000000000000000000000000000000000000000000361352f575062015180919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f800000000000000000000000000000000000000000000000000000000000000003613582575062093a80919050565b6040517fbf3cad0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606061126a84846000856130fe565b6001600160a01b0381168114611ae757600080fd5b63ffffffff81168114611ae757600080fd5b80356135f5816135d8565b919050565b6000806000806080858703121561361057600080fd5b843561361b816135c3565b935060208501359250604085013591506060850135613639816135d8565b939692955090935050565b6000806040838503121561365757600080fd5b8235613662816135c3565b946020939093013593505050565b80356135f5816135c3565b60008060006060848603121561369057600080fd5b833561369b816135c3565b92506020840135915060408401356136b2816135c3565b809150509250925092565b60008083601f8401126136cf57600080fd5b50813567ffffffffffffffff8111156136e757600080fd5b6020830191508360208260051b850101111561370257600080fd5b9250929050565b803560ff811681146135f557600080fd5b60008060008060008060008060e0898b03121561373657600080fd5b8835613741816135c3565b9750602089013567ffffffffffffffff81111561375d57600080fd5b6137698b828c016136bd565b909850965050604089013594506060890135935061378960808a01613709565b925060a0890135915060c089013590509295985092959890939650565b600080602083850312156137b957600080fd5b823567ffffffffffffffff8111156137d057600080fd5b6137dc858286016136bd565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b8281101561383357815180516001600160a01b03168552860151868501529284019290850190600101613805565b5091979650505050505050565b6000806040838503121561385357600080fd5b823561385e816135c3565b9150602083013561386e816135c3565b809150509250929050565b6000806000806060858703121561388f57600080fd5b843561389a816135c3565b9350602085013567ffffffffffffffff8111156138b657600080fd5b6138c2878288016136bd565b9094509250506040850135613639816135c3565b6020808252825182820181905260009190848201906040850190845b8181101561390e578351835292840192918401916001016138f2565b50909695505050505050565b60008060006060848603121561392f57600080fd5b833561393a816135c3565b92506020840135915060408401356136b2816135d8565b60008083601f84011261396357600080fd5b50813567ffffffffffffffff81111561397b57600080fd5b6020830191508360208260061b850101111561370257600080fd5b8015158114611ae757600080fd5b60008083601f8401126139b657600080fd5b50813567ffffffffffffffff8111156139ce57600080fd5b60208301915083602082850101111561370257600080fd5b600080600080600080608087890312156139ff57600080fd5b8635613a0a816135c3565b9550602087013567ffffffffffffffff80821115613a2757600080fd5b613a338a838b01613951565b909750955060408901359150613a4882613996565b90935060608801359080821115613a5e57600080fd5b50613a6b89828a016139a4565b979a9699509497509295939492505050565b600060208083526060808401855160408085880152828251808552608094508489019150868401935060005b81811015613af657613ae68386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613aa9565b505088860151888203601f1901838a0152805180835290870193506000918701905b80831015613b9e57845180516001600160a01b03908116845289820151168984015284810151858401528781015188840152868101518784015260a0808201519084015260c0908101517fff000000000000000000000000000000000000000000000000000000000000001690830152938701936001929092019160e090910190613b18565b509998505050505050505050565b600080600060408486031215613bc157600080fd5b8335613bcc816135c3565b9250602084013567ffffffffffffffff811115613be857600080fd5b613bf486828701613951565b9497909650939450505050565b600060208083526060808401855160408085880152828251808552608094508489019150868401935060005b81811015613c7a57613c6a8386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613c2d565b505088860151888203601f1901838a0152805180835290870193506000918701905b80831015613b9e57845180516001600160a01b039081168452898201511689840152848101518584015287810151888401528601517fff000000000000000000000000000000000000000000000000000000000000001686830152938701936001929092019160a090910190613c9c565b60008060008060808587031215613d2357600080fd5b8435613d2e816135c3565b9350602085013592506040850135613d45816135c3565b91506060850135613639816135c3565b600080600080600080600060c0888a031215613d7057600080fd5b8735613d7b816135c3565b9650602088013567ffffffffffffffff811115613d9757600080fd5b613da38a828b016136bd565b90975095505060408801359350613dbc60608901613709565b92506080880135915060a0880135905092959891949750929550565b600060208284031215613dea57600080fd5b813567ffffffffffffffff811115613e0157600080fd5b820160a08185031215610e9157600080fd5b6000806000806000806000806000806101008b8d031215613e3357600080fd5b8a35613e3e816135c3565b995060208b0135613e4e816135c3565b985060408b0135613e5e816135c3565b975060608b0135613e6e816135d8565b965060808b0135613e7e816135d8565b9550613e8c60a08c01613670565b945060c08b013567ffffffffffffffff80821115613ea957600080fd5b613eb58e838f016136bd565b909650945060e08d0135915080821115613ece57600080fd5b50613edb8d828e016139a4565b915080935050809150509295989b9194979a5092959850565b60005b83811015613f0f578181015183820152602001613ef7565b50506000910152565b60008151808452613f30816020860160208601613ef4565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613fb7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613fa5858351613f18565b94509285019290850190600101613f6b565b5092979650505050505050565b60008060008060608587031215613fda57600080fd5b8435613fe5816135c3565b9350602085013567ffffffffffffffff81111561400157600080fd5b61400d87828801613951565b909450925050604085013561363981613996565b600080600080600080600080610100898b03121561403e57600080fd5b8835614049816135c3565b97506020890135614059816135c3565b96506040890135614069816135c3565b9550606089013594506080890135935061408560a08a01613709565b925060c0890135915060e089013590509295985092959890939650565b600080600080600060a086880312156140ba57600080fd5b85356140c5816135c3565b9450602086013593506040860135925060608601356140e3816135d8565b915060808601356140f3816135c3565b809150509295509295909350565b60006020828403121561411357600080fd5b8135610e91816135c3565b60008060008060008060008060008060006101208c8e03121561414057600080fd5b61414a8c356135c3565b8b359a5061415b60208d01356135c3565b60208c0135995061416e60408d01613670565b985060608c0135975061418360808d016135ea565b965061419160a08d016135ea565b955061419f60c08d01613670565b945067ffffffffffffffff8060e08e013511156141bb57600080fd5b6141cb8e60e08f01358f016136bd565b90955093506101008d01358110156141e257600080fd5b506141f48d6101008e01358e016139a4565b81935080925050509295989b509295989b9093969950565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561425e5761425e61420c565b60405290565b60405160e0810167ffffffffffffffff8111828210171561425e5761425e61420c565b60405160a0810167ffffffffffffffff8111828210171561425e5761425e61420c565b604051601f8201601f1916810167ffffffffffffffff811182821017156142d3576142d361420c565b604052919050565b80516135f5816135c3565b80516135f5816135d8565b80516effffffffffffffffffffffffffffff811681146135f557600080fd5b600061010080838503121561432457600080fd5b6040519081019067ffffffffffffffff821181831017156143475761434761420c565b8160405283519150614358826135c3565b818152614367602085016142db565b6020820152614378604085016142e6565b6040820152614389606085016142e6565b6060820152608084015160808201526143a460a085016142e6565b60a082015260c084015160c08201526143bf60e085016142f1565b60e0820152949350505050565b6000602082840312156143de57600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261441a57600080fd5b830160208101925035905067ffffffffffffffff81111561443a57600080fd5b8060051b360382131561370257600080fd5b60048110614483577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126144bb57600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b878110156138335782840389526144f58288614487565b60408086018235614505816135c3565b6001600160a01b0316875261451c838901846143e5565b888a019390935290829052909150606086019060005b838110156145645781356004811061454957600080fd5b614553848261444c565b509188019190880190600101614532565b505099860199945050908401906001016144de565b60c08152600061458d60c08301898b6144c4565b602083019790975250604081019490945260ff929092166060840152608083015260a09091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261461d57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261465c57600080fd5b83018035915067ffffffffffffffff82111561467757600080fd5b6020019150600581901b360382131561370257600080fd5b60408082528181018490526000906060808401600587811b860183018986805b8b811015614765577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a85030186526146e8838e614487565b80356146f3816135c3565b6001600160a01b03168552602061470c828201836143e5565b92508a82880152828b8801527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115614744578485fd5b91871b918281888c01375096870196940187019392909201916001016146af565b5050506001600160a01b0388166020880152945061126a9350505050565b600067ffffffffffffffff82111561479d5761479d61420c565b5060051b60200190565b600060208083850312156147ba57600080fd5b825167ffffffffffffffff8111156147d157600080fd5b8301601f810185136147e257600080fd5b80516147f56147f082614783565b6142aa565b81815260059190911b8201830190838101908783111561481457600080fd5b928401925b828410156107ef57835182529284019290840190614819565b600081518084526020808501945080840160005b8381101561486b5781516001600160a01b031687529582019590820190600101614846565b509495945050505050565b600081518084526020808501945080840160005b8381101561486b578151805160ff90811689529084015116838801526040909601959082019060010161488a565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6080815260006148f66080830188614832565b82810360208401526149088188614876565b9050851515604084015282810360608401526149258185876148b8565b98975050505050505050565b600061493f6147f084614783565b8381529050602080820190600785901b84018681111561495e57600080fd5b845b818110156149d857608080828a03121561497a5760008081fd5b604080519182019167ffffffffffffffff8311818410171561499e5761499e61420c565b9181528251916149ad836135c3565b9182528285015185830152808301519082015260608083015190820152845292820192608001614960565b505050509392505050565b80517fff00000000000000000000000000000000000000000000000000000000000000811681146135f557600080fd5b60006020808385031215614a2657600080fd5b825167ffffffffffffffff80821115614a3e57600080fd5b81850191506040808388031215614a5457600080fd5b614a5c61423b565b835183811115614a6b57600080fd5b8401601f81018913614a7c57600080fd5b614a8a898251888401614931565b8252508484015183811115614a9e57600080fd5b80850194505087601f850112614ab357600080fd5b83519250614ac36147f084614783565b83815260e0938402850186019386820191908a861115614ae257600080fd5b958701955b85871015614b6b5780878c031215614aff5760008081fd5b614b07614264565b8751614b12816135c3565b815287890151614b21816135c3565b818a01528786015186820152606080890151908201526080808901519082015260a0808901519082015260c0614b58818a016149e3565b9082015283529586019591870191614ae7565b5095820195909552979650505050505050565b604081526000614b916040830185614832565b8281036020840152610b798185614876565b60006020808385031215614bb657600080fd5b825167ffffffffffffffff80821115614bce57600080fd5b81850191506040808388031215614be457600080fd5b614bec61423b565b835183811115614bfb57600080fd5b8401601f81018913614c0c57600080fd5b614c1a898251888401614931565b8252508484015183811115614c2e57600080fd5b80850194505087601f850112614c4357600080fd5b83519250614c536147f084614783565b83815260a0938402850186019386820191908a861115614c7257600080fd5b958701955b85871015614b6b5780878c031215614c8f5760008081fd5b614c97614287565b8751614ca2816135c3565b815287890151614cb1816135c3565b818a01528786015186820152606080890151908201526080614cd4818a016149e3565b9082015283529586019591870191614c77565b60008060408385031215614cfa57600080fd5b505080516020909101519092909150565b60a08082528101869052600060c0600588901b8301810190830189835b8a811015614d9f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40868503018352614d61828d614487565b6040813586526020614d75818401846143e5565b93508282890152614d8983890185836144c4565b9750509485019493909301925050600101614d28565b5050506020830187905260ff861660408401529050606082019390935260800152949350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614dfd57600080fd5b83018035915067ffffffffffffffff821115614e1857600080fd5b60200191503681900382131561370257600080fd5b60006001600160a01b03808b168352808a16602084015288604084015263ffffffff808916606085015280881660808501525080861660a08401525060e060c0830152614e7e60e0830184866144c4565b9a9950505050505050505050565b6001600160a01b038b811682528a81166020830152604082018a905263ffffffff898116606084015288166080830152861660a082015261010060c08201819052600090614edd83820187896144c4565b905082810360e0840152614ef28185876148b8565b9d9c50505050505050505050505050565b600060208284031215614f1557600080fd5b8151610e91816135c3565b8381526001600160a01b03831660208201526060810161126a604083018461444c565b600060208284031215614f5557600080fd5b8151610e9181613996565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006000198203614fa257614fa2614f60565b5060010190565b600060408236031215614fbb57600080fd5b614fc361423b565b8235614fce816135c3565b815260208381013567ffffffffffffffff811115614feb57600080fd5b840136601f820112614ffc57600080fd5b803561500a6147f082614783565b81815260059190911b8201830190838101903683111561502957600080fd5b928401925b82841015615050578335615041816135c3565b8252928401929084019061502e565b938501939093525091949350505050565b60006040828403121561507357600080fd5b6040516040810181811067ffffffffffffffff821117156150965761509661420c565b60405282356150a4816135c3565b815260208301356150b4816135c3565b60208201529392505050565b6000825161461d818460208701613ef4565b6000602082840312156150e457600080fd5b610e91826149e3565b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811681146135f557600080fd5b6000806000806080858703121561512f57600080fd5b845161513a816135d8565b9350615148602086016150ed565b92506040850151615158816135d8565b9150615166606086016150ed565b905092959194509250565b600063ffffffff808416806151af577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b63ffffffff818116838216019080821115610a2257610a22614f60565b600063ffffffff808316818516818304811182151516156151fb576151fb614f60565b02949350505050565b8082018082111561125657611256614f60565b8181038181111561125657611256614f60565b600081600019048311821515161561524457615244614f60565b500290565b60008161525857615258614f60565b506000190190565b60ff818116838216019081111561125657611256614f60565b600060ff821660ff810361528f5761528f614f60565b60010192915050565b602081526000610e916020830184613f1856fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220c76d8001a5c6954cc4414f5a78ee7f4ca6cbae120c7c9e6ae4337c2b67b01cc264736f6c63430008100033000000000000000000000000d6c8fd8100252f0a314407c26e7a47286f7fda24000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4

Deployed Bytecode

0x6080604052600436106101c65760003560e01c80635f963dcf116100f7578063d339056d11610095578063e43581b811610064578063e43581b8146104e2578063e9c0407114610511578063f235757f14610531578063fa0c95551461055157600080fd5b8063d339056d1461045d578063db8266de14610470578063df08aed514610483578063e3056a34146104c257600080fd5b806389352328116100d157806389352328146103d6578063ac9650d8146103e9578063b3b48afb14610409578063bb2871a51461043d57600080fd5b80635f963dcf1461038857806362dd9af3146103b057806385f44632146103c357600080fd5b8063340b532f1161016457806340c5710c1161013e57806340c5710c146102f3578063484b3577146103065780635684c27514610333578063585cc6a51461036057600080fd5b8063340b532f146102ad57806335ac2a50146102c05780633a79d674146102e057600080fd5b806313f6986d116101a057806313f6986d146102375780631f66925c1461024c5780631f8b479d1461026d5780632d2ae1c11461028057600080fd5b806305ce20d6146101d25780630c340a24146101e757806312b17e351461022457600080fd5b366101cd57005b600080fd5b6101e56101e03660046135fa565b610564565b005b3480156101f357600080fd5b50600054610207906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6101e5610232366004613644565b610698565b34801561024357600080fd5b506101e56106a6565b61025f61025a36600461367b565b610752565b60405190815260200161021b565b6101e561027b36600461371a565b6107fa565b34801561028c57600080fd5b506102a061029b3660046137a6565b61084b565b60405161021b91906137e8565b6101e56102bb366004613840565b610a29565b6102d36102ce366004613879565b610a33565b60405161021b91906138d6565b6101e56102ee36600461367b565b610b82565b6101e561030136600461391a565b610b92565b34801561031257600080fd5b506103266103213660046139e6565b610d15565b60405161021b9190613a7d565b34801561033f57600080fd5b5061035361034e366004613bac565b610dda565b60405161021b9190613c01565b34801561036c57600080fd5b5061020773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61039b610396366004613d0d565b610e98565b6040805192835260208301919091520161021b565b6101e56103be366004613d55565b610f4d565b6101e56103d1366004613dd8565b610fd7565b61025f6103e4366004613e13565b6110c8565b6103fc6103f73660046137a6565b611173565b60405161021b9190613f44565b34801561041557600080fd5b506102077f000000000000000000000000d6c8fd8100252f0a314407c26e7a47286f7fda2481565b34801561044957600080fd5b506102d3610458366004613fc4565b61125c565b6101e561046b366004614021565b611272565b6101e561047e3660046140a2565b6112e6565b34801561048f57600080fd5b506104b261049e366004614101565b6001546001600160a01b0391821691161490565b604051901515815260200161021b565b3480156104ce57600080fd5b50600154610207906001600160a01b031681565b3480156104ee57600080fd5b506104b26104fd366004614101565b6000546001600160a01b0391821691161490565b34801561051d57600080fd5b506101e561052c3660046137a6565b611356565b34801561053d57600080fd5b506101e561054c366004614101565b6113a4565b61025f61055f36600461411e565b611454565b8383600061057383838361159b565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018790526000906001600160a01b03891690635b6fd01d9060240161010060405180830381865afa1580156105d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f99190614310565b5190506106078189886116a3565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b0389169063ded700a6906064015b600060405180830381600087803b15801561067657600080fd5b505af115801561068a573d6000803e3d6000fd5b505050505050505050505050565b6106a28282611863565b5050565b6001546001600160a01b031633146106ea576040517f9ba0305d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018054600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b0384161782559091169091556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd769190a1565b60008383600261076383838361159b565b6040517f17621890000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0386811660248301528816906317621890906044016020604051808303816000875af11580156107cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ef91906143cc565b979650505050505050565b6040517f4faa38830000000000000000000000000000000000000000000000000000000081526001600160a01b03891690634faa38839061065c908a908a908a908a908a908a908a90600401614579565b60608167ffffffffffffffff8111156108665761086661420c565b6040519080825280602002602001820160405280156108ab57816020015b60408051808201909152600080825260208201528152602001906001900390816108845790505b50905060005b82811015610a2257600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8585848181106108e2576108e26145ba565b90506020020160208101906108f79190614101565b6001600160a01b0316146109b457848483818110610917576109176145ba565b905060200201602081019061092c9190614101565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561098b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109af91906143cc565b6109b6565b475b905060405180604001604052808686858181106109d5576109d56145ba565b90506020020160208101906109ea9190614101565b6001600160a01b0316815260200182815250838381518110610a0e57610a0e6145ba565b6020908102919091010152506001016108b1565b5092915050565b6106a28282611878565b606060005b83811015610ae8576000858583818110610a5457610a546145ba565b9050602002810190610a6691906145e9565b610a74906020810190614627565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394505050505b8151811015610ade57610ad688838381518110610ac757610ac76145ba565b6020026020010151600261159b565b600101610aa8565b5050600101610a38565b506040517f480b37960000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063480b379690610b329087908790879060040161468f565b6000604051808303816000875af1158015610b51573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b7991908101906147a7565b95945050505050565b610b8d838383611935565b505050565b82826000610ba183838361159b565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018690526000906001600160a01b03881690635b6fd01d9060240161010060405180830381865afa158015610c03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c279190614310565b516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610caf91906143cc565b9050610cbc8289836116a3565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810182905263ffffffff871660448201526001600160a01b0389169063ded700a69060640161065c565b6040805180820190915260608082526020820152600080610d36888861199d565b6040517f4997cdc300000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038a1690634997cdc390610d8890859085908b908b908b906004016148e3565b600060405180830381865afa158015610da5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610dcd9190810190614a13565b9998505050505050505050565b6040805180820190915260608082526020820152600080610dfb858561199d565b6040517fd2d95b2d00000000000000000000000000000000000000000000000000000000815291935091506001600160a01b0387169063d2d95b2d90610e479085908590600401614b7e565b600060405180830381865afa158015610e64573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e8c9190810190614ba3565b925050505b9392505050565b60008085856003610eaa83838361159b565b6040517f72ada4c5000000000000000000000000000000000000000000000000000000008152600481018990526001600160a01b03888116602483015287811660448301528a16906372ada4c59060640160408051808303816000875af1158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190614ce7565b909a909950975050505050505050565b6040517f3dff05870000000000000000000000000000000000000000000000000000000081526001600160a01b03881690633dff058790610f9c90899089908990899089908990600401614d0b565b600060405180830381600087803b158015610fb657600080fd5b505af1158015610fca573d6000803e3d6000fd5b5050505050505050505050565b610fe46020820182614101565b610fed816119fe565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6110126080840160608501614101565b6001600160a01b031603611047576106a26110306020840184614101565b61103d6040850185614dc8565b8560800135611aea565b6110a461105a6080840160608501614101565b61106a6040850160208601614101565b61107a6040860160208701614101565b6001600160a01b03166110906020870187614101565b6001600160a01b0316148560800135611b35565b6106a26110b46020840184614101565b6110c16040850185614dc8565b6000611aea565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038c16906370a0823190602401602060405180830381865afa15801561112a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114e91906143cc565b90506111638c8c8c848d8d8d8d8d8d8d611454565b9c9b505050505050505050505050565b60608167ffffffffffffffff81111561118e5761118e61420c565b6040519080825280602002602001820160405280156111c157816020015b60608152602001906001900390816111ac5790505b50905060005b82811015610a2257611231308585848181106111e5576111e56145ba565b90506020028101906111f79190614dc8565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611df492505050565b828281518110611243576112436145ba565b60209081029190910101526001016111c7565b92915050565b6060610b7985858585611e19565b949350505050565b6040517fd505accf0000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301528781166024830152604482018790526064820186905260ff8516608483015260a4820184905260c4820183905289169063d505accf9060e40161065c565b848460016112f583838361159b565b6040517ff1accf39000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b03858116606483015289169063f1accf399060840161065c565b6000546001600160a01b0316331461139a576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106a28282611eff565b6000546001600160a01b031633146113e8576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def9060200160405180910390a150565b60006114618b8d8b6116a3565b81611501576040517f6b29e1bd0000000000000000000000000000000000000000000000000000000081526001600160a01b038d1690636b29e1bd906114b9908e908e908e908e908e908e908e908e90600401614e2d565b6020604051808303816000875af11580156114d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fc91906143cc565b611163565b6040517fb2b513c10000000000000000000000000000000000000000000000000000000081526001600160a01b038d169063b2b513c190611558908e908e908e908e908e908e908e908e908e908e90600401614e8c565b6020604051808303816000875af1158015611577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116391906143cc565b826001600160a01b031663cc7a20496040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115fd9190614f03565b6001600160a01b031663823abfd98333846040518463ffffffff1660e01b815260040161162c93929190614f20565b602060405180830381865afa158015611649573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166d9190614f43565b610b8d576040517f5c427cd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa15801561170c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173091906143cc565b90508181101561185d5780156117ce576040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526000602483015285169063095ea7b3906044016020604051808303816000875af11580156117a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117cc9190614f43565b505b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152600019602483015285169063095ea7b3906044016020604051808303816000875af1158015611837573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185b9190614f43565b505b50505050565b6106a26001600160a01b03831633308461200d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611924576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156118fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191f91906143cc565b611926565b475b90508015610b8d57610b8d8382845b6001600160a01b0381166119465750335b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b0384160161198957610b8d6001600160a01b038216836120be565b610b8d6001600160a01b038416828461220b565b6060806119f2848460006040519080825280602002602001820160405280156119ec57816020015b60408051808201909152600080825260208201528152602001906001900390816119c55790505b50612254565b50909590945092505050565b6040517fcb00d85f0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301527f000000000000000000000000d6c8fd8100252f0a314407c26e7a47286f7fda24169063cb00d85f90602401602060405180830381865afa158015611a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa19190614f43565b611ae7576040517f220d24fd0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b50565b61185b83838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050506001600160a01b038716919050836122d9565b6001600160a01b0383161561185d576040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384811660248301526000919086169063dd62ed3e90604401602060405180830381865afa158015611bad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd191906143cc565b90508181101561185b5782158015611c8957506040517f344cb9cd0000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f000000000000000000000000d6c8fd8100252f0a314407c26e7a47286f7fda24169063344cb9cd90602401602060405180830381865afa158015611c63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c879190614f43565b155b15611ccb576040517f1f0b33c30000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401611ade565b8015611d5f576040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301526000602483015286169063095ea7b3906044016020604051808303816000875af1158015611d39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5d9190614f43565b505b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152600019602483015286169063095ea7b3906044016020604051808303816000875af1158015611dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dec9190614f43565b505050505050565b6060610e9183836040518060600160405280602781526020016152d5602791396122ff565b60608267ffffffffffffffff811115611e3457611e3461420c565b604051908082528060200260200182016040528015611e5d578160200160208202803683370190505b50905060005b83811015611ef657611ec786868684818110611e8157611e816145ba565b611e979260206040909202019081019150614101565b878785818110611ea957611ea96145ba565b9050604002016020016020810190611ec19190614101565b86612403565b828281518110611ed957611ed96145ba565b602090810291909101015280611eee81614f8f565b915050611e63565b50949350505050565b60005b81811015610b8d576000838383818110611f1e57611f1e6145ba565b9050602002810190611f3091906145e9565b611f3990614fa9565b905060005b8160200151518110156120035781602001518181518110611f6157611f616145ba565b602090810291909101015182516040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201526000602482015291169063095ea7b3906044016020604051808303816000875af1158015611fd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffa9190614f43565b50600101611f3e565b5050600101611f02565b6040516001600160a01b038085166024830152831660448201526064810182905261185d9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261277a565b80471015612128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401611ade565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612175576040519150601f19603f3d011682016040523d82523d6000602084013e61217a565b606091505b5050905080610b8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401611ade565b6040516001600160a01b038316602482015260448101829052610b8d9084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161205a565b60608060606122b58686808060200260200160405190810160405280939291908181526020016000905b828210156122aa5761229b60408302860136819003810190615061565b8152602001906001019061227e565b505050505085612879565b92506122c2868685612d5d565b91506122ce8484612ff8565b905093509350939050565b606061126a8484846040518060600160405280602981526020016152ac602991396130fe565b60606001600160a01b0384163b612398576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401611ade565b600080856001600160a01b0316856040516123b391906150c0565b600060405180830381855af49150503d80600081146123ee576040519150601f19603f3d011682016040523d82523d6000602084013e6123f3565b606091505b5091509150610e8c82828661326b565b600080600061241286866132be565b6040517f582cf84b0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152808316602483015292945090925060009189169063582cf84b90604401602060405180830381865afa158015612482573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a691906150d2565b90507f010000000000000000000000000000000000000000000000000000000000000060005b7fff000000000000000000000000000000000000000000000000000000000000008083169084161080159061252257507fff00000000000000000000000000000000000000000000000000000000000000821615155b1561276957817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168284167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19160361273b576040517f808ba8e00000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015289811660248301527fff000000000000000000000000000000000000000000000000000000000000008416604483015260009182918291908e169063808ba8e090606401608060405180830381865afa15801561260b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262f9190615119565b935093509350506000612641866132ef565b90506000816126508186615171565b61265b9060016151bb565b61266591906151d8565b63ffffffff1690508b6126905761267d600383615171565b61268d9063ffffffff1682615204565b90505b6000857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1611806126db57506000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16115b15612714574281116126f957859a505050505050505050505061126a565b6127034282615217565b9a505050505050505050505061126a565b428111156127355785156127285785612732565b6127324282615217565b95505b50505050505b6001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b91506124cc565b506000199998505050505050505050565b60006127cf826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135b49092919063ffffffff16565b805190915015610b8d57808060200190518101906127ed9190614f43565b610b8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611ade565b606060008083518551600261288e919061522a565b6128989190615204565b67ffffffffffffffff8111156128b0576128b061420c565b6040519080825280602002602001820160405280156128d9578160200160208202803683370190505b50905060005b8551811015612ab35760008060005b858110801561290457508280156129025750815b155b156129d9578215801561295e5750888481518110612924576129246145ba565b6020026020010151600001516001600160a01b031685828151811061294b5761294b6145ba565b60200260200101516001600160a01b0316145b1561296857600192505b811580156129bd5750888481518110612983576129836145ba565b6020026020010151602001516001600160a01b03168582815181106129aa576129aa6145ba565b60200260200101516001600160a01b0316145b156129c757600191505b806129d181614f8f565b9150506128ee565b5081612a3c578783815181106129f1576129f16145ba565b602002602001015160000151848680612a0990614f8f565b975081518110612a1b57612a1b6145ba565b60200260200101906001600160a01b031690816001600160a01b0316815250505b80612a9e57878381518110612a5357612a536145ba565b602002602001015160200151848680612a6b90614f8f565b975081518110612a7d57612a7d6145ba565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50508080612aab90614f8f565b9150506128df565b5060005b8451811015612bb2576000805b8481108015612ad1575081155b15612b3c57868381518110612ae857612ae86145ba565b6020026020010151600001516001600160a01b0316848281518110612b0f57612b0f6145ba565b60200260200101516001600160a01b031603612b2a57600191505b80612b3481614f8f565b915050612ac4565b5080612b9f57858281518110612b5457612b546145ba565b602002602001015160000151838580612b6c90614f8f565b965081518110612b7e57612b7e6145ba565b60200260200101906001600160a01b031690816001600160a01b0316815250505b5080612baa81614f8f565b915050612ab7565b508167ffffffffffffffff811115612bcc57612bcc61420c565b604051908082528060200260200182016040528015612bf5578160200160208202803683370190505b50925060005b82811015612d54576000828281518110612c1757612c176145ba565b6020026020010151905060005b816001600160a01b0316868281518110612c4057612c406145ba565b60200260200101516001600160a01b0316108015612c8a575060006001600160a01b0316868281518110612c7657612c766145ba565b60200260200101516001600160a01b031614155b15612ca15780612c9981614f8f565b915050612c24565b825b81811115612d0b5786612cb7600183615217565b81518110612cc757612cc76145ba565b6020026020010151878281518110612ce157612ce16145ba565b6001600160a01b039092166020928302919091019091015280612d0381615249565b915050612ca3565b5081868281518110612d1f57612d1f6145ba565b60200260200101906001600160a01b031690816001600160a01b03168152505050508080612d4c90614f8f565b915050612bfb565b50505092915050565b60608267ffffffffffffffff811115612d7857612d7861420c565b604051908082528060200260200182016040528015612dbd57816020015b6040805180820190915260008082526020820152815260200190600190039081612d965790505b5090506000805b83518160ff161015612fef576000612ddd826001615260565b90505b84518160ff161015612fdc5760005b86811015612fc957878782818110612e0957612e096145ba565b612e1f9260206040909202019081019150614101565b6001600160a01b0316868460ff1681518110612e3d57612e3d6145ba565b60200260200101516001600160a01b0316148015612eb15750878782818110612e6857612e686145ba565b9050604002016020016020810190612e809190614101565b6001600160a01b0316868360ff1681518110612e9e57612e9e6145ba565b60200260200101516001600160a01b0316145b80612f715750878782818110612ec957612ec96145ba565b9050604002016020016020810190612ee19190614101565b6001600160a01b0316868460ff1681518110612eff57612eff6145ba565b60200260200101516001600160a01b0316148015612f715750878782818110612f2a57612f2a6145ba565b612f409260206040909202019081019150614101565b6001600160a01b0316868360ff1681518110612f5e57612f5e6145ba565b60200260200101516001600160a01b0316145b15612fb7576040805180820190915260ff8085168252831660208201528585612f9981614f8f565b965081518110612fab57612fab6145ba565b60200260200101819052505b80612fc181614f8f565b915050612def565b5080612fd481615279565b915050612de0565b5080612fe781615279565b915050612dc4565b50509392505050565b6060815167ffffffffffffffff8111156130145761301461420c565b60405190808252806020026020018201604052801561303d578160200160208202803683370190505b50905060005b8351811015610a225760005b848281518110613061576130616145ba565b6020026020010151600001516001600160a01b0316848281518110613088576130886145ba565b60200260200101516001600160a01b0316146130b057806130a881614f8f565b91505061304f565b8482815181106130c2576130c26145ba565b6020026020010151602001518382815181106130e0576130e06145ba565b602090810291909101015250806130f681614f8f565b915050613043565b606082471015613190576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401611ade565b6001600160a01b0385163b613201576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611ade565b600080866001600160a01b0316858760405161321d91906150c0565b60006040518083038185875af1925050503d806000811461325a576040519150601f19603f3d011682016040523d82523d6000602084013e61325f565b606091505b50915091506107ef8282865b6060831561327a575081610e91565b82511561328a5782518084602001fd5b816040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ade9190615298565b600080826001600160a01b0316846001600160a01b0316106132e15782846132e4565b83835b909590945092505050565b60007fff0000000000000000000000000000000000000000000000000000000000000082167f0100000000000000000000000000000000000000000000000000000000000000036133425750603c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f020000000000000000000000000000000000000000000000000000000000000003613394575061012c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0400000000000000000000000000000000000000000000000000000000000000036133e65750610384919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0800000000000000000000000000000000000000000000000000000000000000036134385750610708919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f10000000000000000000000000000000000000000000000000000000000000000361348a5750610e10919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f2000000000000000000000000000000000000000000000000000000000000000036134dc5750613840919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f40000000000000000000000000000000000000000000000000000000000000000361352f575062015180919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f800000000000000000000000000000000000000000000000000000000000000003613582575062093a80919050565b6040517fbf3cad0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606061126a84846000856130fe565b6001600160a01b0381168114611ae757600080fd5b63ffffffff81168114611ae757600080fd5b80356135f5816135d8565b919050565b6000806000806080858703121561361057600080fd5b843561361b816135c3565b935060208501359250604085013591506060850135613639816135d8565b939692955090935050565b6000806040838503121561365757600080fd5b8235613662816135c3565b946020939093013593505050565b80356135f5816135c3565b60008060006060848603121561369057600080fd5b833561369b816135c3565b92506020840135915060408401356136b2816135c3565b809150509250925092565b60008083601f8401126136cf57600080fd5b50813567ffffffffffffffff8111156136e757600080fd5b6020830191508360208260051b850101111561370257600080fd5b9250929050565b803560ff811681146135f557600080fd5b60008060008060008060008060e0898b03121561373657600080fd5b8835613741816135c3565b9750602089013567ffffffffffffffff81111561375d57600080fd5b6137698b828c016136bd565b909850965050604089013594506060890135935061378960808a01613709565b925060a0890135915060c089013590509295985092959890939650565b600080602083850312156137b957600080fd5b823567ffffffffffffffff8111156137d057600080fd5b6137dc858286016136bd565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b8281101561383357815180516001600160a01b03168552860151868501529284019290850190600101613805565b5091979650505050505050565b6000806040838503121561385357600080fd5b823561385e816135c3565b9150602083013561386e816135c3565b809150509250929050565b6000806000806060858703121561388f57600080fd5b843561389a816135c3565b9350602085013567ffffffffffffffff8111156138b657600080fd5b6138c2878288016136bd565b9094509250506040850135613639816135c3565b6020808252825182820181905260009190848201906040850190845b8181101561390e578351835292840192918401916001016138f2565b50909695505050505050565b60008060006060848603121561392f57600080fd5b833561393a816135c3565b92506020840135915060408401356136b2816135d8565b60008083601f84011261396357600080fd5b50813567ffffffffffffffff81111561397b57600080fd5b6020830191508360208260061b850101111561370257600080fd5b8015158114611ae757600080fd5b60008083601f8401126139b657600080fd5b50813567ffffffffffffffff8111156139ce57600080fd5b60208301915083602082850101111561370257600080fd5b600080600080600080608087890312156139ff57600080fd5b8635613a0a816135c3565b9550602087013567ffffffffffffffff80821115613a2757600080fd5b613a338a838b01613951565b909750955060408901359150613a4882613996565b90935060608801359080821115613a5e57600080fd5b50613a6b89828a016139a4565b979a9699509497509295939492505050565b600060208083526060808401855160408085880152828251808552608094508489019150868401935060005b81811015613af657613ae68386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613aa9565b505088860151888203601f1901838a0152805180835290870193506000918701905b80831015613b9e57845180516001600160a01b03908116845289820151168984015284810151858401528781015188840152868101518784015260a0808201519084015260c0908101517fff000000000000000000000000000000000000000000000000000000000000001690830152938701936001929092019160e090910190613b18565b509998505050505050505050565b600080600060408486031215613bc157600080fd5b8335613bcc816135c3565b9250602084013567ffffffffffffffff811115613be857600080fd5b613bf486828701613951565b9497909650939450505050565b600060208083526060808401855160408085880152828251808552608094508489019150868401935060005b81811015613c7a57613c6a8386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613c2d565b505088860151888203601f1901838a0152805180835290870193506000918701905b80831015613b9e57845180516001600160a01b039081168452898201511689840152848101518584015287810151888401528601517fff000000000000000000000000000000000000000000000000000000000000001686830152938701936001929092019160a090910190613c9c565b60008060008060808587031215613d2357600080fd5b8435613d2e816135c3565b9350602085013592506040850135613d45816135c3565b91506060850135613639816135c3565b600080600080600080600060c0888a031215613d7057600080fd5b8735613d7b816135c3565b9650602088013567ffffffffffffffff811115613d9757600080fd5b613da38a828b016136bd565b90975095505060408801359350613dbc60608901613709565b92506080880135915060a0880135905092959891949750929550565b600060208284031215613dea57600080fd5b813567ffffffffffffffff811115613e0157600080fd5b820160a08185031215610e9157600080fd5b6000806000806000806000806000806101008b8d031215613e3357600080fd5b8a35613e3e816135c3565b995060208b0135613e4e816135c3565b985060408b0135613e5e816135c3565b975060608b0135613e6e816135d8565b965060808b0135613e7e816135d8565b9550613e8c60a08c01613670565b945060c08b013567ffffffffffffffff80821115613ea957600080fd5b613eb58e838f016136bd565b909650945060e08d0135915080821115613ece57600080fd5b50613edb8d828e016139a4565b915080935050809150509295989b9194979a5092959850565b60005b83811015613f0f578181015183820152602001613ef7565b50506000910152565b60008151808452613f30816020860160208601613ef4565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613fb7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613fa5858351613f18565b94509285019290850190600101613f6b565b5092979650505050505050565b60008060008060608587031215613fda57600080fd5b8435613fe5816135c3565b9350602085013567ffffffffffffffff81111561400157600080fd5b61400d87828801613951565b909450925050604085013561363981613996565b600080600080600080600080610100898b03121561403e57600080fd5b8835614049816135c3565b97506020890135614059816135c3565b96506040890135614069816135c3565b9550606089013594506080890135935061408560a08a01613709565b925060c0890135915060e089013590509295985092959890939650565b600080600080600060a086880312156140ba57600080fd5b85356140c5816135c3565b9450602086013593506040860135925060608601356140e3816135d8565b915060808601356140f3816135c3565b809150509295509295909350565b60006020828403121561411357600080fd5b8135610e91816135c3565b60008060008060008060008060008060006101208c8e03121561414057600080fd5b61414a8c356135c3565b8b359a5061415b60208d01356135c3565b60208c0135995061416e60408d01613670565b985060608c0135975061418360808d016135ea565b965061419160a08d016135ea565b955061419f60c08d01613670565b945067ffffffffffffffff8060e08e013511156141bb57600080fd5b6141cb8e60e08f01358f016136bd565b90955093506101008d01358110156141e257600080fd5b506141f48d6101008e01358e016139a4565b81935080925050509295989b509295989b9093969950565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561425e5761425e61420c565b60405290565b60405160e0810167ffffffffffffffff8111828210171561425e5761425e61420c565b60405160a0810167ffffffffffffffff8111828210171561425e5761425e61420c565b604051601f8201601f1916810167ffffffffffffffff811182821017156142d3576142d361420c565b604052919050565b80516135f5816135c3565b80516135f5816135d8565b80516effffffffffffffffffffffffffffff811681146135f557600080fd5b600061010080838503121561432457600080fd5b6040519081019067ffffffffffffffff821181831017156143475761434761420c565b8160405283519150614358826135c3565b818152614367602085016142db565b6020820152614378604085016142e6565b6040820152614389606085016142e6565b6060820152608084015160808201526143a460a085016142e6565b60a082015260c084015160c08201526143bf60e085016142f1565b60e0820152949350505050565b6000602082840312156143de57600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261441a57600080fd5b830160208101925035905067ffffffffffffffff81111561443a57600080fd5b8060051b360382131561370257600080fd5b60048110614483577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126144bb57600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b878110156138335782840389526144f58288614487565b60408086018235614505816135c3565b6001600160a01b0316875261451c838901846143e5565b888a019390935290829052909150606086019060005b838110156145645781356004811061454957600080fd5b614553848261444c565b509188019190880190600101614532565b505099860199945050908401906001016144de565b60c08152600061458d60c08301898b6144c4565b602083019790975250604081019490945260ff929092166060840152608083015260a09091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261461d57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261465c57600080fd5b83018035915067ffffffffffffffff82111561467757600080fd5b6020019150600581901b360382131561370257600080fd5b60408082528181018490526000906060808401600587811b860183018986805b8b811015614765577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a85030186526146e8838e614487565b80356146f3816135c3565b6001600160a01b03168552602061470c828201836143e5565b92508a82880152828b8801527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115614744578485fd5b91871b918281888c01375096870196940187019392909201916001016146af565b5050506001600160a01b0388166020880152945061126a9350505050565b600067ffffffffffffffff82111561479d5761479d61420c565b5060051b60200190565b600060208083850312156147ba57600080fd5b825167ffffffffffffffff8111156147d157600080fd5b8301601f810185136147e257600080fd5b80516147f56147f082614783565b6142aa565b81815260059190911b8201830190838101908783111561481457600080fd5b928401925b828410156107ef57835182529284019290840190614819565b600081518084526020808501945080840160005b8381101561486b5781516001600160a01b031687529582019590820190600101614846565b509495945050505050565b600081518084526020808501945080840160005b8381101561486b578151805160ff90811689529084015116838801526040909601959082019060010161488a565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6080815260006148f66080830188614832565b82810360208401526149088188614876565b9050851515604084015282810360608401526149258185876148b8565b98975050505050505050565b600061493f6147f084614783565b8381529050602080820190600785901b84018681111561495e57600080fd5b845b818110156149d857608080828a03121561497a5760008081fd5b604080519182019167ffffffffffffffff8311818410171561499e5761499e61420c565b9181528251916149ad836135c3565b9182528285015185830152808301519082015260608083015190820152845292820192608001614960565b505050509392505050565b80517fff00000000000000000000000000000000000000000000000000000000000000811681146135f557600080fd5b60006020808385031215614a2657600080fd5b825167ffffffffffffffff80821115614a3e57600080fd5b81850191506040808388031215614a5457600080fd5b614a5c61423b565b835183811115614a6b57600080fd5b8401601f81018913614a7c57600080fd5b614a8a898251888401614931565b8252508484015183811115614a9e57600080fd5b80850194505087601f850112614ab357600080fd5b83519250614ac36147f084614783565b83815260e0938402850186019386820191908a861115614ae257600080fd5b958701955b85871015614b6b5780878c031215614aff5760008081fd5b614b07614264565b8751614b12816135c3565b815287890151614b21816135c3565b818a01528786015186820152606080890151908201526080808901519082015260a0808901519082015260c0614b58818a016149e3565b9082015283529586019591870191614ae7565b5095820195909552979650505050505050565b604081526000614b916040830185614832565b8281036020840152610b798185614876565b60006020808385031215614bb657600080fd5b825167ffffffffffffffff80821115614bce57600080fd5b81850191506040808388031215614be457600080fd5b614bec61423b565b835183811115614bfb57600080fd5b8401601f81018913614c0c57600080fd5b614c1a898251888401614931565b8252508484015183811115614c2e57600080fd5b80850194505087601f850112614c4357600080fd5b83519250614c536147f084614783565b83815260a0938402850186019386820191908a861115614c7257600080fd5b958701955b85871015614b6b5780878c031215614c8f5760008081fd5b614c97614287565b8751614ca2816135c3565b815287890151614cb1816135c3565b818a01528786015186820152606080890151908201526080614cd4818a016149e3565b9082015283529586019591870191614c77565b60008060408385031215614cfa57600080fd5b505080516020909101519092909150565b60a08082528101869052600060c0600588901b8301810190830189835b8a811015614d9f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40868503018352614d61828d614487565b6040813586526020614d75818401846143e5565b93508282890152614d8983890185836144c4565b9750509485019493909301925050600101614d28565b5050506020830187905260ff861660408401529050606082019390935260800152949350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614dfd57600080fd5b83018035915067ffffffffffffffff821115614e1857600080fd5b60200191503681900382131561370257600080fd5b60006001600160a01b03808b168352808a16602084015288604084015263ffffffff808916606085015280881660808501525080861660a08401525060e060c0830152614e7e60e0830184866144c4565b9a9950505050505050505050565b6001600160a01b038b811682528a81166020830152604082018a905263ffffffff898116606084015288166080830152861660a082015261010060c08201819052600090614edd83820187896144c4565b905082810360e0840152614ef28185876148b8565b9d9c50505050505050505050505050565b600060208284031215614f1557600080fd5b8151610e91816135c3565b8381526001600160a01b03831660208201526060810161126a604083018461444c565b600060208284031215614f5557600080fd5b8151610e9181613996565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006000198203614fa257614fa2614f60565b5060010190565b600060408236031215614fbb57600080fd5b614fc361423b565b8235614fce816135c3565b815260208381013567ffffffffffffffff811115614feb57600080fd5b840136601f820112614ffc57600080fd5b803561500a6147f082614783565b81815260059190911b8201830190838101903683111561502957600080fd5b928401925b82841015615050578335615041816135c3565b8252928401929084019061502e565b938501939093525091949350505050565b60006040828403121561507357600080fd5b6040516040810181811067ffffffffffffffff821117156150965761509661420c565b60405282356150a4816135c3565b815260208301356150b4816135c3565b60208201529392505050565b6000825161461d818460208701613ef4565b6000602082840312156150e457600080fd5b610e91826149e3565b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811681146135f557600080fd5b6000806000806080858703121561512f57600080fd5b845161513a816135d8565b9350615148602086016150ed565b92506040850151615158816135d8565b9150615166606086016150ed565b905092959194509250565b600063ffffffff808416806151af577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b63ffffffff818116838216019080821115610a2257610a22614f60565b600063ffffffff808316818516818304811182151516156151fb576151fb614f60565b02949350505050565b8082018082111561125657611256614f60565b8181038181111561125657611256614f60565b600081600019048311821515161561524457615244614f60565b500290565b60008161525857615258614f60565b506000190190565b60ff818116838216019081111561125657611256614f60565b600060ff821660ff810361528f5761528f614f60565b60010192915050565b602081526000610e916020830184613f1856fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220c76d8001a5c6954cc4414f5a78ee7f4ca6cbae120c7c9e6ae4337c2b67b01cc264736f6c63430008100033

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

000000000000000000000000d6c8fd8100252f0a314407c26e7a47286f7fda24000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4

-----Decoded View---------------
Arg [0] : _swapperRegistry (address): 0xd6C8fd8100252F0a314407C26e7A47286F7Fda24
Arg [1] : _governor (address): 0xEC864BE26084ba3bbF3cAAcF8F6961A9263319C4

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000d6c8fd8100252f0a314407c26e7a47286f7fda24
Arg [1] : 000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4


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

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ 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.