ETH Price: $3,473.65 (+2.91%)

Token

ERC20 ***
 

Overview

Max Total Supply

0 ERC20 ***

Holders

140

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A

Other Info

Filtered by Token Holder
albertinim.eth
Balance
1 ERC20 ***
0x06b912dc08176500e2a5b2221b9e54ea3fa547f1
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

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

Contract Name:
DCAPair

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 28 : DCAPair.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.6;
pragma abicoder v2;

import './DCAPairParameters.sol';
import './DCAPairPositionHandler.sol';
import './DCAPairSwapHandler.sol';
import './DCAPairLoanHandler.sol';

contract DCAPair is DCAPairParameters, DCAPairSwapHandler, DCAPairPositionHandler, DCAPairLoanHandler, IDCAPair {
  constructor(
    IDCAGlobalParameters _globalParameters,
    IERC20Metadata _tokenA,
    IERC20Metadata _tokenB
  ) DCAPairParameters(_globalParameters, _tokenA, _tokenB) DCAPairPositionHandler(_tokenA, _tokenB) {}
}

File 2 of 28 : DCAPairParameters.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

import '../interfaces/IDCAGlobalParameters.sol';
import '../interfaces/IDCAPair.sol';
import '../libraries/CommonErrors.sol';

import './utils/Math.sol';

abstract contract DCAPairParameters is IDCAPairParameters {
  using EnumerableSet for EnumerableSet.UintSet;

  // Internal constants
  uint112 internal _magnitudeA;
  uint112 internal _magnitudeB;
  uint24 internal _feePrecision;

  // Basic setup
  IDCAGlobalParameters public override globalParameters;
  IERC20Metadata public override tokenA;
  IERC20Metadata public override tokenB;

  // Tracking
  mapping(uint32 => mapping(address => mapping(uint32 => int256))) public override swapAmountDelta; // swap interval => from token => swap number => delta
  mapping(uint32 => uint32) public override performedSwaps; // swap interval => performed swaps
  mapping(uint32 => mapping(address => mapping(uint32 => uint256))) internal _accumRatesPerUnit; // swap interval => from token => swap number => accum
  mapping(address => uint256) internal _balances;
  EnumerableSet.UintSet internal _activeSwapIntervals;

  constructor(
    IDCAGlobalParameters _globalParameters,
    IERC20Metadata _tokenA,
    IERC20Metadata _tokenB
  ) {
    if (address(_globalParameters) == address(0) || address(_tokenA) == address(0) || address(_tokenB) == address(0))
      revert CommonErrors.ZeroAddress();
    globalParameters = _globalParameters;
    _feePrecision = globalParameters.FEE_PRECISION();
    tokenA = _tokenA;
    tokenB = _tokenB;
    _magnitudeA = uint112(10**_tokenA.decimals());
    _magnitudeB = uint112(10**_tokenB.decimals());
  }

  function isSwapIntervalActive(uint32 _activeSwapInterval) external view override returns (bool _isIntervalActive) {
    _isIntervalActive = _activeSwapIntervals.contains(_activeSwapInterval);
  }

  function _getFeeFromAmount(uint32 _feeAmount, uint256 _amount) internal view returns (uint256) {
    return (_amount * _feeAmount) / _feePrecision / 100;
  }
}

File 3 of 28 : DCAPairPositionHandler.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';

import './DCAPairParameters.sol';

abstract contract DCAPairPositionHandler is ReentrancyGuard, DCAPairParameters, IDCAPairPositionHandler, ERC721 {
  struct DCA {
    uint32 lastWithdrawSwap;
    uint32 lastSwap;
    uint32 swapInterval;
    uint160 rate;
    bool fromTokenA;
    uint248 swappedBeforeModified;
  }

  using SafeERC20 for IERC20Metadata;
  using EnumerableSet for EnumerableSet.UintSet;

  mapping(uint256 => DCA) internal _userPositions;
  uint256 internal _idCounter;

  constructor(IERC20Metadata _tokenA, IERC20Metadata _tokenB)
    ERC721(string(abi.encodePacked('DCA: ', _tokenA.symbol(), ' - ', _tokenB.symbol())), 'DCA')
  {}

  function userPosition(uint256 _dcaId) external view override returns (UserPosition memory _userPosition) {
    DCA memory _position = _userPositions[_dcaId];
    _userPosition.from = _position.fromTokenA ? tokenA : tokenB;
    _userPosition.to = _position.fromTokenA ? tokenB : tokenA;
    _userPosition.swapInterval = _position.swapInterval;
    _userPosition.swapsExecuted = _position.swapInterval > 0 ? performedSwaps[_position.swapInterval] - _position.lastWithdrawSwap : 0;
    _userPosition.swapped = _calculateSwapped(_dcaId);
    _userPosition.swapsLeft = _position.lastSwap > performedSwaps[_position.swapInterval]
      ? _position.lastSwap - performedSwaps[_position.swapInterval]
      : 0;
    _userPosition.remaining = _calculateUnswapped(_dcaId);
    _userPosition.rate = _position.rate;
  }

  function deposit(
    address _tokenAddress,
    uint160 _rate,
    uint32 _amountOfSwaps,
    uint32 _swapInterval
  ) external override nonReentrant returns (uint256) {
    if (_tokenAddress != address(tokenA) && _tokenAddress != address(tokenB)) revert InvalidToken();
    if (_amountOfSwaps == 0) revert ZeroSwaps();
    if (!_activeSwapIntervals.contains(_swapInterval) && !globalParameters.isSwapIntervalAllowed(_swapInterval)) revert InvalidInterval();
    IERC20Metadata _from = _tokenAddress == address(tokenA) ? tokenA : tokenB;
    uint256 _amount = _rate * _amountOfSwaps;
    _from.safeTransferFrom(msg.sender, address(this), _amount);
    _balances[_tokenAddress] += _amount;
    _idCounter += 1;
    _safeMint(msg.sender, _idCounter);
    _activeSwapIntervals.add(_swapInterval);
    (uint32 _startingSwap, uint32 _lastSwap) = _addPosition(_idCounter, _tokenAddress, _rate, _amountOfSwaps, 0, _swapInterval);
    emit Deposited(msg.sender, _idCounter, _tokenAddress, _rate, _startingSwap, _swapInterval, _lastSwap);
    return _idCounter;
  }

  function withdrawSwapped(uint256 _dcaId) external override nonReentrant returns (uint256 _swapped) {
    _assertPositionExistsAndCanBeOperatedByCaller(_dcaId);

    _swapped = _calculateSwapped(_dcaId);

    _userPositions[_dcaId].lastWithdrawSwap = performedSwaps[_userPositions[_dcaId].swapInterval];
    _userPositions[_dcaId].swappedBeforeModified = 0;

    IERC20Metadata _to = _getTo(_dcaId);
    _balances[address(_to)] -= _swapped;
    _to.safeTransfer(msg.sender, _swapped);

    emit Withdrew(msg.sender, _dcaId, address(_to), _swapped);
  }

  function withdrawSwappedMany(uint256[] calldata _dcaIds)
    external
    override
    nonReentrant
    returns (uint256 _swappedTokenA, uint256 _swappedTokenB)
  {
    for (uint256 i; i < _dcaIds.length; i++) {
      uint256 _dcaId = _dcaIds[i];
      _assertPositionExistsAndCanBeOperatedByCaller(_dcaId);
      uint256 _swappedDCA = _calculateSwapped(_dcaId);
      if (_userPositions[_dcaId].fromTokenA) {
        _swappedTokenB += _swappedDCA;
      } else {
        _swappedTokenA += _swappedDCA;
      }
      _userPositions[_dcaId].lastWithdrawSwap = performedSwaps[_userPositions[_dcaId].swapInterval];
      _userPositions[_dcaId].swappedBeforeModified = 0;
    }

    if (_swappedTokenA > 0) {
      _balances[address(tokenA)] -= _swappedTokenA;
      tokenA.safeTransfer(msg.sender, _swappedTokenA);
    }

    if (_swappedTokenB > 0) {
      _balances[address(tokenB)] -= _swappedTokenB;
      tokenB.safeTransfer(msg.sender, _swappedTokenB);
    }
    emit WithdrewMany(msg.sender, _dcaIds, _swappedTokenA, _swappedTokenB);
  }

  function terminate(uint256 _dcaId) external override nonReentrant {
    _assertPositionExistsAndCanBeOperatedByCaller(_dcaId);

    uint256 _swapped = _calculateSwapped(_dcaId);
    uint256 _unswapped = _calculateUnswapped(_dcaId);

    IERC20Metadata _from = _getFrom(_dcaId);
    IERC20Metadata _to = _getTo(_dcaId);
    _removePosition(_dcaId);
    _burn(_dcaId);

    if (_swapped > 0) {
      _balances[address(_to)] -= _swapped;
      _to.safeTransfer(msg.sender, _swapped);
    }

    if (_unswapped > 0) {
      _balances[address(_from)] -= _unswapped;
      _from.safeTransfer(msg.sender, _unswapped);
    }

    emit Terminated(msg.sender, _dcaId, _unswapped, _swapped);
  }

  function modifyRate(uint256 _dcaId, uint160 _newRate) external override nonReentrant {
    _assertPositionExistsAndCanBeOperatedByCaller(_dcaId);
    uint32 _swapsLeft = _userPositions[_dcaId].lastSwap - performedSwaps[_userPositions[_dcaId].swapInterval];
    if (_swapsLeft == 0) revert PositionCompleted();

    _modifyRateAndSwaps(_dcaId, _newRate, _swapsLeft);
  }

  function modifySwaps(uint256 _dcaId, uint32 _newSwaps) external override nonReentrant {
    _modifyRateAndSwaps(_dcaId, _userPositions[_dcaId].rate, _newSwaps);
  }

  function modifyRateAndSwaps(
    uint256 _dcaId,
    uint160 _newRate,
    uint32 _newAmountOfSwaps
  ) external override nonReentrant {
    _modifyRateAndSwaps(_dcaId, _newRate, _newAmountOfSwaps);
  }

  function addFundsToPosition(
    uint256 _dcaId,
    uint256 _amount,
    uint32 _newSwaps
  ) external override nonReentrant {
    if (_amount == 0) revert ZeroAmount();
    if (_newSwaps == 0) revert ZeroSwaps();

    uint256 _unswapped = _calculateUnswapped(_dcaId);
    uint256 _total = _unswapped + _amount;

    _modifyPosition(_dcaId, _total, _unswapped, uint160(_total / _newSwaps), _newSwaps);
  }

  function tokenURI(uint256 tokenId) public view override returns (string memory) {
    return globalParameters.nftDescriptor().tokenURI(this, tokenId);
  }

  /** Helper function to modify a position */
  function _modifyRateAndSwaps(
    uint256 _dcaId,
    uint160 _newRate,
    uint32 _newAmountOfSwaps
  ) internal {
    _modifyPosition(_dcaId, _newRate * _newAmountOfSwaps, _calculateUnswapped(_dcaId), _newRate, _newAmountOfSwaps);
  }

  function _modifyPosition(
    uint256 _dcaId,
    uint256 _totalNecessary,
    uint256 _unswapped,
    uint160 _newRate,
    uint32 _newAmountOfSwaps
  ) internal {
    _assertPositionExistsAndCanBeOperatedByCaller(_dcaId);
    IERC20Metadata _from = _getFrom(_dcaId);

    uint256 _swapped = _calculateSwapped(_dcaId);
    if (_swapped > type(uint248).max) revert MandatoryWithdraw(); // You should withdraw before modifying, to avoid losing funds

    uint32 _swapInterval = _userPositions[_dcaId].swapInterval;
    _removePosition(_dcaId);
    (uint32 _startingSwap, uint32 _lastSwap) = _addPosition(
      _dcaId,
      address(_from),
      _newRate,
      _newAmountOfSwaps,
      uint248(_swapped),
      _swapInterval
    );

    if (_totalNecessary > _unswapped) {
      // We need to ask for more funds
      _from.safeTransferFrom(msg.sender, address(this), _totalNecessary - _unswapped);
      _balances[address(_from)] += _totalNecessary - _unswapped;
    } else if (_totalNecessary < _unswapped) {
      // We need to return to the owner the amount that won't be used anymore
      _balances[address(_from)] -= _unswapped - _totalNecessary;
      _from.safeTransfer(msg.sender, _unswapped - _totalNecessary);
    }

    emit Modified(msg.sender, _dcaId, _newRate, _startingSwap, _lastSwap);
  }

  function _assertPositionExistsAndCanBeOperatedByCaller(uint256 _dcaId) internal view {
    if (_userPositions[_dcaId].rate == 0) revert InvalidPosition();
    if (!_isApprovedOrOwner(msg.sender, _dcaId)) revert UnauthorizedCaller();
  }

  function _addPosition(
    uint256 _dcaId,
    address _from,
    uint160 _rate,
    uint32 _amountOfSwaps,
    uint248 _swappedBeforeModified,
    uint32 _swapInterval
  ) internal returns (uint32 _startingSwap, uint32 _lastSwap) {
    if (_rate == 0) revert ZeroRate();
    uint32 _performedSwaps = performedSwaps[_swapInterval];
    _startingSwap = _performedSwaps + 1;
    _lastSwap = _performedSwaps + _amountOfSwaps;
    swapAmountDelta[_swapInterval][_from][_startingSwap] += int160(_rate);
    swapAmountDelta[_swapInterval][_from][_lastSwap + 1] -= int160(_rate);
    _userPositions[_dcaId] = DCA(_performedSwaps, _lastSwap, _swapInterval, _rate, _from == address(tokenA), _swappedBeforeModified);
  }

  function _removePosition(uint256 _dcaId) internal {
    uint32 _swapInterval = _userPositions[_dcaId].swapInterval;
    uint32 _lastSwap = _userPositions[_dcaId].lastSwap;
    uint32 _performedSwaps = performedSwaps[_swapInterval];

    if (_lastSwap > _performedSwaps) {
      int160 _rate = int160(_userPositions[_dcaId].rate);
      address _from = address(_getFrom(_dcaId));
      swapAmountDelta[_swapInterval][_from][_performedSwaps + 1] -= _rate;
      swapAmountDelta[_swapInterval][_from][_lastSwap + 1] += _rate;
    }
    delete _userPositions[_dcaId];
  }

  /** Returns the amount of tokens swapped in TO */
  function _calculateSwapped(uint256 _dcaId) internal view returns (uint256 _swapped) {
    DCA memory _userDCA = _userPositions[_dcaId];
    address _from = _userDCA.fromTokenA ? address(tokenA) : address(tokenB);
    uint256 _accumRatesLastSwap = _accumRatesPerUnit[_userDCA.swapInterval][_from][
      performedSwaps[_userDCA.swapInterval] < _userDCA.lastSwap ? performedSwaps[_userDCA.swapInterval] : _userDCA.lastSwap
    ];

    uint256 _accumPerUnit = _accumRatesLastSwap - _accumRatesPerUnit[_userDCA.swapInterval][_from][_userDCA.lastWithdrawSwap];
    uint256 _magnitude = _userDCA.fromTokenA ? _magnitudeA : _magnitudeB;
    (bool _ok, uint256 _mult) = Math.tryMul(_accumPerUnit, _userDCA.rate);
    uint256 _swappedInCurrentPosition = _ok ? _mult / _magnitude : (_accumPerUnit / _magnitude) * _userDCA.rate;
    _swapped = _swappedInCurrentPosition + _userDCA.swappedBeforeModified;
  }

  /** Returns how many FROM remains unswapped  */
  function _calculateUnswapped(uint256 _dcaId) internal view returns (uint256 _unswapped) {
    uint32 _performedSwaps = performedSwaps[_userPositions[_dcaId].swapInterval];
    uint32 _lastSwap = _userPositions[_dcaId].lastSwap;

    if (_lastSwap <= _performedSwaps) return 0;
    _unswapped = (_lastSwap - _performedSwaps) * _userPositions[_dcaId].rate;
  }

  function _getFrom(uint256 _dcaId) internal view returns (IERC20Metadata _from) {
    _from = _userPositions[_dcaId].fromTokenA ? tokenA : tokenB;
  }

  function _getTo(uint256 _dcaId) internal view returns (IERC20Metadata _to) {
    _to = _userPositions[_dcaId].fromTokenA ? tokenB : tokenA;
  }
}

File 4 of 28 : DCAPairSwapHandler.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.6;
pragma abicoder v2;

import '@openzeppelin/contracts/security/ReentrancyGuard.sol';

import '../interfaces/IDCAPairSwapCallee.sol';
import '../libraries/CommonErrors.sol';

import './DCAPairParameters.sol';

abstract contract DCAPairSwapHandler is ReentrancyGuard, DCAPairParameters, IDCAPairSwapHandler {
  using SafeERC20 for IERC20Metadata;
  using EnumerableSet for EnumerableSet.UintSet;

  mapping(uint32 => mapping(address => uint256)) public override swapAmountAccumulator; // swap interval => from token => swap amount accum

  mapping(uint32 => uint32) public override nextSwapAvailable; // swap interval => timestamp

  function _addNewRatePerUnit(
    uint32 _swapInterval,
    address _address,
    uint32 _performedSwap,
    uint256 _ratePerUnit
  ) internal {
    uint256 _accumRatesPerUnitPreviousSwap = _accumRatesPerUnit[_swapInterval][_address][_performedSwap - 1];
    _accumRatesPerUnit[_swapInterval][_address][_performedSwap] = _accumRatesPerUnitPreviousSwap + _ratePerUnit;
  }

  function _registerSwap(
    uint32 _swapInterval,
    address _token,
    uint256 _internalAmountUsedToSwap,
    uint256 _ratePerUnit,
    uint32 _swapToRegister
  ) internal {
    swapAmountAccumulator[_swapInterval][_token] = _internalAmountUsedToSwap;
    _addNewRatePerUnit(_swapInterval, _token, _swapToRegister, _ratePerUnit);
    delete swapAmountDelta[_swapInterval][_token][_swapToRegister];
  }

  function _getAmountToSwap(
    uint32 _swapInterval,
    address _address,
    uint32 _swapToPerform
  ) internal view returns (uint256 _swapAmountAccumulator) {
    unchecked {
      _swapAmountAccumulator =
        swapAmountAccumulator[_swapInterval][_address] +
        uint256(swapAmountDelta[_swapInterval][_address][_swapToPerform]);
    }
  }

  function _convertTo(
    uint256 _fromTokenMagnitude,
    uint256 _amountFrom,
    uint256 _rateFromTo
  ) internal pure returns (uint256 _amountTo) {
    _amountTo = (_amountFrom * _rateFromTo) / _fromTokenMagnitude;
  }

  function _getNextSwapsToPerform() internal view virtual returns (SwapInformation[] memory _swapsToPerform, uint8 _amountOfSwapsToPerform) {
    uint256 _activeSwapIntervalsLength = _activeSwapIntervals.length();
    _swapsToPerform = new SwapInformation[](_activeSwapIntervalsLength);
    for (uint256 i; i < _activeSwapIntervalsLength; i++) {
      uint32 _swapInterval = uint32(_activeSwapIntervals.at(i));
      if (nextSwapAvailable[_swapInterval] <= _getTimestamp()) {
        uint32 _swapToPerform = performedSwaps[_swapInterval] + 1;
        _swapsToPerform[_amountOfSwapsToPerform++] = SwapInformation({
          interval: _swapInterval,
          swapToPerform: _swapToPerform,
          amountToSwapTokenA: _getAmountToSwap(_swapInterval, address(tokenA), _swapToPerform),
          amountToSwapTokenB: _getAmountToSwap(_swapInterval, address(tokenB), _swapToPerform)
        });
      }
    }
  }

  function secondsUntilNextSwap() external view override returns (uint32 _secondsUntil) {
    _secondsUntil = type(uint32).max;
    uint32 _timestamp = _getTimestamp();
    for (uint256 i; i < _activeSwapIntervals.length(); i++) {
      uint32 _swapInterval = uint32(_activeSwapIntervals.at(i));
      if (nextSwapAvailable[_swapInterval] <= _timestamp) {
        _secondsUntil = 0;
        break;
      } else {
        uint32 _diff = nextSwapAvailable[_swapInterval] - _timestamp;
        if (_diff < _secondsUntil) {
          _secondsUntil = _diff;
        }
      }
    }
  }

  function getNextSwapInfo() external view override returns (NextSwapInformation memory _nextSwapInformation) {
    IDCAGlobalParameters.SwapParameters memory _swapParameters = globalParameters.swapParameters();
    (_nextSwapInformation, , ) = _getNextSwapInfo(_swapParameters.swapFee, _swapParameters.oracle);
  }

  function _getNextSwapInfo(uint32 _swapFee, ITimeWeightedOracle _oracle)
    internal
    view
    virtual
    returns (
      NextSwapInformation memory _nextSwapInformation,
      uint256 _ratePerUnitBToAWithFee,
      uint256 _ratePerUnitAToBWithFee
    )
  {
    uint256 _amountToSwapTokenA;
    uint256 _amountToSwapTokenB;
    {
      (SwapInformation[] memory _swapsToPerform, uint8 _amountOfSwaps) = _getNextSwapsToPerform();
      for (uint256 i; i < _amountOfSwaps; i++) {
        _amountToSwapTokenA += _swapsToPerform[i].amountToSwapTokenA;
        _amountToSwapTokenB += _swapsToPerform[i].amountToSwapTokenB;
      }
      _nextSwapInformation.swapsToPerform = _swapsToPerform;
      _nextSwapInformation.amountOfSwaps = _amountOfSwaps;
    }

    _nextSwapInformation.ratePerUnitBToA = _oracle.quote(address(tokenB), _magnitudeB, address(tokenA));
    _nextSwapInformation.ratePerUnitAToB = (uint256(_magnitudeB) * _magnitudeA) / _nextSwapInformation.ratePerUnitBToA;

    _ratePerUnitBToAWithFee = _nextSwapInformation.ratePerUnitBToA - _getFeeFromAmount(_swapFee, _nextSwapInformation.ratePerUnitBToA);
    _ratePerUnitAToBWithFee = _nextSwapInformation.ratePerUnitAToB - _getFeeFromAmount(_swapFee, _nextSwapInformation.ratePerUnitAToB);

    uint256 _finalNeededTokenA = _convertTo(_magnitudeB, _amountToSwapTokenB, _ratePerUnitBToAWithFee);
    uint256 _finalNeededTokenB = _convertTo(_magnitudeA, _amountToSwapTokenA, _ratePerUnitAToBWithFee);

    uint256 _amountOfTokenAIfTokenBSwapped = _convertTo(_magnitudeB, _amountToSwapTokenB, _nextSwapInformation.ratePerUnitBToA);
    if (_amountOfTokenAIfTokenBSwapped < _amountToSwapTokenA) {
      _nextSwapInformation.tokenToBeProvidedBySwapper = tokenB;
      _nextSwapInformation.tokenToRewardSwapperWith = tokenA;
      _nextSwapInformation.platformFeeTokenA = _getFeeFromAmount(_swapFee, _amountOfTokenAIfTokenBSwapped);
      _nextSwapInformation.platformFeeTokenB = _getFeeFromAmount(_swapFee, _amountToSwapTokenB);
      _nextSwapInformation.amountToBeProvidedBySwapper = _finalNeededTokenB + _nextSwapInformation.platformFeeTokenB - _amountToSwapTokenB;
      _nextSwapInformation.amountToRewardSwapperWith = _amountToSwapTokenA - _finalNeededTokenA - _nextSwapInformation.platformFeeTokenA;
      _nextSwapInformation.availableToBorrowTokenA = _balances[address(tokenA)] - _nextSwapInformation.amountToRewardSwapperWith;
      _nextSwapInformation.availableToBorrowTokenB = _balances[address(tokenB)];
    } else if (_amountOfTokenAIfTokenBSwapped > _amountToSwapTokenA) {
      _nextSwapInformation.tokenToBeProvidedBySwapper = tokenA;
      _nextSwapInformation.tokenToRewardSwapperWith = tokenB;
      _nextSwapInformation.platformFeeTokenA = _getFeeFromAmount(_swapFee, _amountToSwapTokenA);
      _nextSwapInformation.platformFeeTokenB = _getFeeFromAmount(
        _swapFee,
        (_amountToSwapTokenA * _magnitudeB) / _nextSwapInformation.ratePerUnitBToA
      );
      _nextSwapInformation.amountToBeProvidedBySwapper = _finalNeededTokenA + _nextSwapInformation.platformFeeTokenA - _amountToSwapTokenA;
      _nextSwapInformation.amountToRewardSwapperWith = _amountToSwapTokenB - _finalNeededTokenB - _nextSwapInformation.platformFeeTokenB;
      _nextSwapInformation.availableToBorrowTokenA = _balances[address(tokenA)];
      _nextSwapInformation.availableToBorrowTokenB = _balances[address(tokenB)] - _nextSwapInformation.amountToRewardSwapperWith;
    } else {
      _nextSwapInformation.platformFeeTokenA = _getFeeFromAmount(_swapFee, _amountToSwapTokenA);
      _nextSwapInformation.platformFeeTokenB = _getFeeFromAmount(_swapFee, _amountToSwapTokenB);
      _nextSwapInformation.availableToBorrowTokenA = _balances[address(tokenA)];
      _nextSwapInformation.availableToBorrowTokenB = _balances[address(tokenB)];
    }
  }

  function swap() external override {
    swap(0, 0, msg.sender, '');
  }

  function swap(
    uint256 _amountToBorrowTokenA,
    uint256 _amountToBorrowTokenB,
    address _to,
    bytes memory _data
  ) public override nonReentrant {
    IDCAGlobalParameters.SwapParameters memory _swapParameters = globalParameters.swapParameters();
    if (_swapParameters.isPaused) revert CommonErrors.Paused();

    NextSwapInformation memory _nextSwapInformation;

    {
      uint256 _ratePerUnitBToAWithFee;
      uint256 _ratePerUnitAToBWithFee;
      (_nextSwapInformation, _ratePerUnitBToAWithFee, _ratePerUnitAToBWithFee) = _getNextSwapInfo(
        _swapParameters.swapFee,
        _swapParameters.oracle
      );
      if (_nextSwapInformation.amountOfSwaps == 0) revert NoSwapsToExecute();

      uint32 _timestamp = _getTimestamp();
      for (uint256 i; i < _nextSwapInformation.amountOfSwaps; i++) {
        uint32 _swapInterval = _nextSwapInformation.swapsToPerform[i].interval;
        uint32 _swapToPerform = _nextSwapInformation.swapsToPerform[i].swapToPerform;
        if (_nextSwapInformation.swapsToPerform[i].amountToSwapTokenA > 0 || _nextSwapInformation.swapsToPerform[i].amountToSwapTokenB > 0) {
          _registerSwap(
            _swapInterval,
            address(tokenA),
            _nextSwapInformation.swapsToPerform[i].amountToSwapTokenA,
            _ratePerUnitAToBWithFee,
            _swapToPerform
          );
          _registerSwap(
            _swapInterval,
            address(tokenB),
            _nextSwapInformation.swapsToPerform[i].amountToSwapTokenB,
            _ratePerUnitBToAWithFee,
            _swapToPerform
          );
          performedSwaps[_swapInterval] = _swapToPerform;
          nextSwapAvailable[_swapInterval] = ((_timestamp / _swapInterval) + 1) * _swapInterval;
        } else {
          _activeSwapIntervals.remove(_swapInterval);
        }
      }
    }

    if (
      _amountToBorrowTokenA > _nextSwapInformation.availableToBorrowTokenA ||
      _amountToBorrowTokenB > _nextSwapInformation.availableToBorrowTokenB
    ) revert CommonErrors.InsufficientLiquidity();

    uint256 _finalAmountToHaveTokenA = _nextSwapInformation.availableToBorrowTokenA - _nextSwapInformation.platformFeeTokenA;
    uint256 _finalAmountToHaveTokenB = _nextSwapInformation.availableToBorrowTokenB - _nextSwapInformation.platformFeeTokenB;

    {
      // scope for _amountToSendToken{A,B}, avoids stack too deep errors
      uint256 _amountToSendTokenA = _amountToBorrowTokenA;
      uint256 _amountToSendTokenB = _amountToBorrowTokenB;

      if (_nextSwapInformation.tokenToRewardSwapperWith == tokenA) {
        _amountToSendTokenA += _nextSwapInformation.amountToRewardSwapperWith;
        _finalAmountToHaveTokenB += _nextSwapInformation.amountToBeProvidedBySwapper;
      } else {
        _amountToSendTokenB += _nextSwapInformation.amountToRewardSwapperWith;
        _finalAmountToHaveTokenA += _nextSwapInformation.amountToBeProvidedBySwapper;
      }

      // Optimistically transfer tokens
      if (_amountToSendTokenA > 0) tokenA.safeTransfer(_to, _amountToSendTokenA);
      if (_amountToSendTokenB > 0) tokenB.safeTransfer(_to, _amountToSendTokenB);
    }

    if (_data.length > 0) {
      // Make call
      IDCAPairSwapCallee(_to).DCAPairSwapCall(
        msg.sender,
        tokenA,
        tokenB,
        _amountToBorrowTokenA,
        _amountToBorrowTokenB,
        _nextSwapInformation.tokenToRewardSwapperWith == tokenA,
        _nextSwapInformation.amountToRewardSwapperWith,
        _nextSwapInformation.amountToBeProvidedBySwapper,
        _data
      );
    }

    uint256 _balanceTokenA = tokenA.balanceOf(address(this));
    uint256 _balanceTokenB = tokenB.balanceOf(address(this));

    // Make sure that they sent the tokens back
    if (
      _balanceTokenA < (_finalAmountToHaveTokenA + _nextSwapInformation.platformFeeTokenA) ||
      _balanceTokenB < (_finalAmountToHaveTokenB + _nextSwapInformation.platformFeeTokenB)
    ) revert CommonErrors.LiquidityNotReturned();

    // Update balances
    _balances[address(tokenA)] = _finalAmountToHaveTokenA;
    _balances[address(tokenB)] = _finalAmountToHaveTokenB;

    // Send fees and extra
    uint256 _toFeeRecipientTokenA = _balanceTokenA - _finalAmountToHaveTokenA;
    uint256 _toFeeRecipientTokenB = _balanceTokenB - _finalAmountToHaveTokenB;
    if (_toFeeRecipientTokenA > 0) tokenA.safeTransfer(_swapParameters.feeRecipient, _toFeeRecipientTokenA);
    if (_toFeeRecipientTokenB > 0) tokenB.safeTransfer(_swapParameters.feeRecipient, _toFeeRecipientTokenB);

    // Emit event
    emit Swapped(msg.sender, _to, _amountToBorrowTokenA, _amountToBorrowTokenB, _swapParameters.swapFee, _nextSwapInformation);
  }

  function _getTimestamp() internal view virtual returns (uint32 _blockTimestamp) {
    _blockTimestamp = uint32(block.timestamp);
  }
}

File 5 of 28 : DCAPairLoanHandler.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/security/ReentrancyGuard.sol';

import '../interfaces/IDCAPairLoanCallee.sol';
import '../libraries/CommonErrors.sol';

import './DCAPairParameters.sol';

abstract contract DCAPairLoanHandler is ReentrancyGuard, DCAPairParameters, IDCAPairLoanHandler {
  using SafeERC20 for IERC20Metadata;

  function availableToBorrow() external view override returns (uint256 _amountToBorrowTokenA, uint256 _amountToBorrowTokenB) {
    _amountToBorrowTokenA = _balances[address(tokenA)];
    _amountToBorrowTokenB = _balances[address(tokenB)];
  }

  function loan(
    uint256 _amountToBorrowTokenA,
    uint256 _amountToBorrowTokenB,
    address _to,
    bytes calldata _data
  ) external override nonReentrant {
    if (_amountToBorrowTokenA == 0 && _amountToBorrowTokenB == 0) revert ZeroLoan();

    IDCAGlobalParameters.LoanParameters memory _loanParameters = globalParameters.loanParameters();

    if (_loanParameters.isPaused) revert CommonErrors.Paused();

    uint256 _beforeBalanceTokenA = _balances[address(tokenA)];
    uint256 _beforeBalanceTokenB = _balances[address(tokenB)];

    if (_amountToBorrowTokenA > _beforeBalanceTokenA || _amountToBorrowTokenB > _beforeBalanceTokenB)
      revert CommonErrors.InsufficientLiquidity();

    // Calculate fees
    uint256 _feeTokenA = _amountToBorrowTokenA > 0 ? _getFeeFromAmount(_loanParameters.loanFee, _amountToBorrowTokenA) : 0;
    uint256 _feeTokenB = _amountToBorrowTokenB > 0 ? _getFeeFromAmount(_loanParameters.loanFee, _amountToBorrowTokenB) : 0;

    if (_amountToBorrowTokenA > 0) tokenA.safeTransfer(_to, _amountToBorrowTokenA);
    if (_amountToBorrowTokenB > 0) tokenB.safeTransfer(_to, _amountToBorrowTokenB);

    // Make call
    IDCAPairLoanCallee(_to).DCAPairLoanCall(
      msg.sender,
      tokenA,
      tokenB,
      _amountToBorrowTokenA,
      _amountToBorrowTokenB,
      _feeTokenA,
      _feeTokenB,
      _data
    );

    uint256 _afterBalanceTokenA = tokenA.balanceOf(address(this));
    uint256 _afterBalanceTokenB = tokenB.balanceOf(address(this));

    // Make sure that they sent the tokens back
    if (_afterBalanceTokenA < (_beforeBalanceTokenA + _feeTokenA) || _afterBalanceTokenB < (_beforeBalanceTokenB + _feeTokenB))
      revert CommonErrors.LiquidityNotReturned();

    {
      // Send fees and extra (if any)
      uint256 _toFeeRecipientTokenA = _afterBalanceTokenA - _beforeBalanceTokenA;
      uint256 _toFeeRecipientTokenB = _afterBalanceTokenB - _beforeBalanceTokenB;
      if (_toFeeRecipientTokenA > 0) tokenA.safeTransfer(_loanParameters.feeRecipient, _toFeeRecipientTokenA);
      if (_toFeeRecipientTokenB > 0) tokenB.safeTransfer(_loanParameters.feeRecipient, _toFeeRecipientTokenB);
    }

    // Emit event
    emit Loaned(msg.sender, _to, _amountToBorrowTokenA, _amountToBorrowTokenB, _loanParameters.loanFee);
  }
}

File 6 of 28 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.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));
        }
    }

    /**
     * @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 7 of 28 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 8 of 28 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

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 9 of 28 : IDCAGlobalParameters.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.6;

import './ITimeWeightedOracle.sol';
import './IDCATokenDescriptor.sol';

/// @title The interface for handling parameters the affect the whole DCA ecosystem
/// @notice This contract will manage configuration that affects all pairs, swappers, etc
interface IDCAGlobalParameters {
  /// @notice A compilation of all parameters that affect a swap
  struct SwapParameters {
    // The address of the fee recipient
    address feeRecipient;
    // Whether swaps are paused or not
    bool isPaused;
    // The swap fee
    uint32 swapFee;
    // The oracle contract
    ITimeWeightedOracle oracle;
  }

  /// @notice A compilation of all parameters that affect a loan
  struct LoanParameters {
    // The address of the fee recipient
    address feeRecipient;
    // Whether loans are paused or not
    bool isPaused;
    // The loan fee
    uint32 loanFee;
  }

  /// @notice Emitted when a new fee recipient is set
  /// @param _feeRecipient The address of the new fee recipient
  event FeeRecipientSet(address _feeRecipient);

  /// @notice Emitted when a new NFT descriptor is set
  /// @param _descriptor The new NFT descriptor contract
  event NFTDescriptorSet(IDCATokenDescriptor _descriptor);

  /// @notice Emitted when a new oracle is set
  /// @param _oracle The new oracle contract
  event OracleSet(ITimeWeightedOracle _oracle);

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

  /// @notice Emitted when a new loan fee is set
  /// @param _feeSet The new loan fee
  event LoanFeeSet(uint32 _feeSet);

  /// @notice Emitted when new swap intervals are allowed
  /// @param _swapIntervals The new swap intervals
  /// @param _descriptions The descriptions for each swap interval
  event SwapIntervalsAllowed(uint32[] _swapIntervals, string[] _descriptions);

  /// @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 Thrown when trying to set a fee higher than the maximum allowed
  error HighFee();

  /// @notice Thrown when trying to support new swap intervals, but the amount of descriptions doesn't match
  error InvalidParams();

  /// @notice Thrown when trying to support a new swap interval of value zero
  error ZeroInterval();

  /// @notice Thrown when trying a description for a new swap interval is empty
  error EmptyDescription();

  /// @notice Returns the address of the fee recipient
  /// @return _feeRecipient The address of the fee recipient
  function feeRecipient() external view returns (address _feeRecipient);

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

  /// @notice Returns fee charged on loans
  /// @return _loanFee The fee itself
  function loanFee() external view returns (uint32 _loanFee);

  /// @notice Returns the NFT descriptor contract
  /// @return _nftDescriptor The contract itself
  function nftDescriptor() external view returns (IDCATokenDescriptor _nftDescriptor);

  /// @notice Returns the time-weighted oracle contract
  /// @return _oracle The contract itself
  function oracle() external view returns (ITimeWeightedOracle _oracle);

  /// @notice Returns the precision used for fees
  /// @dev Cannot be modified
  /// @return _precision The precision used for fees
  // solhint-disable-next-line func-name-mixedcase
  function FEE_PRECISION() external view returns (uint24 _precision);

  /// @notice Returns the max fee that can be set for either swap or loans
  /// @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 list of all the allowed swap intervals
  /// @return _allowedSwapIntervals An array with all allowed swap intervals
  function allowedSwapIntervals() external view returns (uint32[] memory _allowedSwapIntervals);

  /// @notice Returns the description for a given swap interval
  /// @return _description The swap interval's description
  function intervalDescription(uint32 _swapInterval) external view returns (string memory _description);

  /// @notice Returns whether a swap interval is currently allowed
  /// @return _isAllowed Whether the given swap interval is currently allowed
  function isSwapIntervalAllowed(uint32 _swapInterval) external view returns (bool _isAllowed);

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

  /// @notice Returns a compilation of all parameters that affect a swap
  /// @return _swapParameters All parameters that affect a swap
  function swapParameters() external view returns (SwapParameters memory _swapParameters);

  /// @notice Returns a compilation of all parameters that affect a loan
  /// @return _loanParameters All parameters that affect a loan
  function loanParameters() external view returns (LoanParameters memory _loanParameters);

  /// @notice Sets a new fee recipient address
  /// @dev Will revert with ZeroAddress if the zero address is passed
  /// @param _feeRecipient The new fee recipient address
  function setFeeRecipient(address _feeRecipient) external;

  /// @notice Sets a new swap fee
  /// @dev Will rever with HighFee if the fee is higher than the maximum
  /// @param _fee The new swap fee
  function setSwapFee(uint32 _fee) external;

  /// @notice Sets a new loan fee
  /// @dev Will rever with HighFee if the fee is higher than the maximum
  /// @param _fee The new loan fee
  function setLoanFee(uint32 _fee) external;

  /// @notice Sets a new NFT descriptor
  /// @dev Will revert with ZeroAddress if the zero address is passed
  /// @param _descriptor The new descriptor contract
  function setNFTDescriptor(IDCATokenDescriptor _descriptor) external;

  /// @notice Sets a new time-weighted oracle
  /// @dev Will revert with ZeroAddress if the zero address is passed
  /// @param _oracle The new oracle contract
  function setOracle(ITimeWeightedOracle _oracle) external;

  /// @notice Adds new swap intervals to the allowed list
  /// @dev Will revert with:
  /// InvalidParams if the amount of swap intervals is different from the amount of descriptions passed
  /// ZeroInterval if any of the swap intervals is zero
  /// EmptyDescription if any of the descriptions is empty
  /// @param _swapIntervals The new swap intervals
  /// @param _descriptions Their descriptions
  function addSwapIntervalsToAllowedList(uint32[] calldata _swapIntervals, string[] calldata _descriptions) 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 loans
  function pause() external;

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

File 10 of 28 : IDCAPair.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import './IDCAGlobalParameters.sol';

/// @title The interface for all state related queries
/// @notice These methods allow users to read the pair's current values
interface IDCAPairParameters {
  /// @notice Returns the global parameters contract
  /// @dev Global parameters has information about swaps and pairs, like swap intervals, fees charged, etc.
  /// @return The Global Parameters contract
  function globalParameters() external view returns (IDCAGlobalParameters);

  /// @notice Returns the token A contract
  /// @return The contract for token A
  function tokenA() external view returns (IERC20Metadata);

  /// @notice Returns the token B contract
  /// @return The contract for token B
  function tokenB() external view returns (IERC20Metadata);

  /// @notice Returns how much will the amount to swap differ from the previous swap
  /// @dev f.e. if the returned value is -100, then the amount to swap will be 100 less than the swap just before it
  /// @param _swapInterval The swap interval to check
  /// @param _from The 'from' token of the deposits
  /// @param _swap The swap number to check
  /// @return _delta How much will the amount to swap differ, when compared to the swap just before this one
  function swapAmountDelta(
    uint32 _swapInterval,
    address _from,
    uint32 _swap
  ) external view returns (int256 _delta);

  /// @notice Returns if a certain swap interval is active or not
  /// @dev We consider a swap interval to be active if there is at least one active position on that interval
  /// @param _swapInterval The swap interval to check
  /// @return _isActive Whether the given swap interval is currently active
  function isSwapIntervalActive(uint32 _swapInterval) external view returns (bool _isActive);

  /// @notice Returns the amount of swaps executed for a certain interval
  /// @param _swapInterval The swap interval to check
  /// @return _swaps The amount of swaps performed on the given interval
  function performedSwaps(uint32 _swapInterval) external view returns (uint32 _swaps);
}

/// @title The interface for all position related matters in a DCA pair
/// @notice These methods allow users to create, modify and terminate their positions
interface IDCAPairPositionHandler is IDCAPairParameters {
  /// @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
    uint160 rate;
  }

  /// @notice Emitted when a position is terminated
  /// @param _user The address of the user that terminated the position
  /// @param _dcaId 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, uint256 _dcaId, uint256 _returnedUnswapped, uint256 _returnedSwapped);

  /// @notice Emitted when a position is created
  /// @param _user The address of the user that created the position
  /// @param _dcaId The id of the position that was created
  /// @param _fromToken The address of the "from" token
  /// @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 _swapInterval How frequently the position's swaps should be executed
  /// @param _lastSwap The number of the swap when the position will be executed for the last time
  event Deposited(
    address indexed _user,
    uint256 _dcaId,
    address _fromToken,
    uint160 _rate,
    uint32 _startingSwap,
    uint32 _swapInterval,
    uint32 _lastSwap
  );

  /// @notice Emitted when a user withdraws all swapped tokens from a position
  /// @param _user The address of the user that executed the withdraw
  /// @param _dcaId 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 _user, uint256 _dcaId, address _token, uint256 _amount);

  /// @notice Emitted when a user withdraws all swapped tokens from many positions
  /// @param _user The address of the user that executed the withdraw
  /// @param _dcaIds The ids of the positions that were affected
  /// @param _swappedTokenA The total amount that was withdrawn in token A
  /// @param _swappedTokenB The total amount that was withdrawn in token B
  event WithdrewMany(address indexed _user, uint256[] _dcaIds, uint256 _swappedTokenA, uint256 _swappedTokenB);

  /// @notice Emitted when a position is modified
  /// @param _user The address of the user that modified the position
  /// @param _dcaId 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 _dcaId, uint160 _rate, uint32 _startingSwap, uint32 _lastSwap);

  /// @notice Thrown when a user tries to create a position with a token that is neither token A nor token B
  error InvalidToken();

  /// @notice Thrown when a user tries to create that a position with an unsupported swap interval
  error InvalidInterval();

  /// @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 or modify a position by setting the rate to be zero
  error ZeroRate();

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

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

  /// @notice Thrown when a user tries to modify the rate of a position that has already been completed
  error PositionCompleted();

  /// @notice Thrown when a user tries to modify a position that has too much swapped balance. This error
  /// is thrown so that the user doesn't lose any funds. The error indicates that the user must perform a withdraw
  /// before modifying their position
  error MandatoryWithdraw();

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

  /// @notice Creates a new position
  /// @dev Will revert:
  /// With InvalidToken if _tokenAddress is neither token A nor token B
  /// With ZeroRate if _rate is zero
  /// With ZeroSwaps if _amountOfSwaps is zero
  /// With InvalidInterval if _swapInterval is not a valid swap interval
  /// @param _tokenAddress The address of the token that will be deposited
  /// @param _rate How many "from" tokens need to be traded in each swap
  /// @param _amountOfSwaps How many swaps to execute for this position
  /// @param _swapInterval How frequently the position's swaps should be executed
  /// @return _dcaId The id of the created position
  function deposit(
    address _tokenAddress,
    uint160 _rate,
    uint32 _amountOfSwaps,
    uint32 _swapInterval
  ) external returns (uint256 _dcaId);

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

  /// @notice Withdraws all swapped tokens from many positions
  /// @dev Will revert:
  /// With InvalidPosition if any of the ids in _dcaIds is invalid
  /// With UnauthorizedCaller if the caller doesn't have access to any of the positions in _dcaIds
  /// @param _dcaIds The positions' ids
  /// @return _swappedTokenA How much was withdrawn in token A
  /// @return _swappedTokenB How much was withdrawn in token B
  function withdrawSwappedMany(uint256[] calldata _dcaIds) external returns (uint256 _swappedTokenA, uint256 _swappedTokenB);

  /// @notice Modifies the rate of a position. Could request more funds or return deposited funds
  /// depending on whether the new rate is greater than the previous one.
  /// @dev Will revert:
  /// With InvalidPosition if _dcaId is invalid
  /// With UnauthorizedCaller if the caller doesn't have access to the position
  /// With PositionCompleted if position has already been completed
  /// With ZeroRate if _newRate is zero
  /// With MandatoryWithdraw if the user must execute a withdraw before modifying their position
  /// @param _dcaId The position's id
  /// @param _newRate The new rate to set
  function modifyRate(uint256 _dcaId, uint160 _newRate) external;

  /// @notice Modifies the amount of swaps of a position. Could request more funds or return
  /// deposited funds depending on whether the new amount of swaps is greater than the swaps left.
  /// @dev Will revert:
  /// With InvalidPosition if _dcaId is invalid
  /// With UnauthorizedCaller if the caller doesn't have access to the position
  /// With MandatoryWithdraw if the user must execute a withdraw before modifying their position
  /// @param _dcaId The position's id
  /// @param _newSwaps The new amount of swaps
  function modifySwaps(uint256 _dcaId, uint32 _newSwaps) external;

  /// @notice Modifies both the rate and amount of swaps of a position. Could request more funds or return
  /// deposited funds depending on whether the new parameters require more or less than the the unswapped funds.
  /// @dev Will revert:
  /// With InvalidPosition if _dcaId is invalid
  /// With UnauthorizedCaller if the caller doesn't have access to the position
  /// With ZeroRate if _newRate is zero
  /// With MandatoryWithdraw if the user must execute a withdraw before modifying their position
  /// @param _dcaId The position's id
  /// @param _newRate The new rate to set
  /// @param _newSwaps The new amount of swaps
  function modifyRateAndSwaps(
    uint256 _dcaId,
    uint160 _newRate,
    uint32 _newSwaps
  ) external;

  /// @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 _dcaId is invalid
  /// With UnauthorizedCaller if the caller doesn't have access to the position
  /// With ZeroAmount if _amount is zero
  /// With ZeroSwaps if _newSwaps is zero
  /// With MandatoryWithdraw if the user must execute a withdraw before modifying their position
  /// @param _dcaId The position's id
  /// @param _amount Amounts of funds to add to the position
  /// @param _newSwaps The new amount of swaps
  function addFundsToPosition(
    uint256 _dcaId,
    uint256 _amount,
    uint32 _newSwaps
  ) external;

  /// @notice Terminates the position and sends all unswapped and swapped balance to the caller
  /// @dev Will revert:
  /// With InvalidPosition if _dcaId is invalid
  /// With UnauthorizedCaller if the caller doesn't have access to the position
  /// @param _dcaId The position's id
  function terminate(uint256 _dcaId) external;
}

/// @title The interface for all swap related matters in a DCA pair
/// @notice These methods allow users to get information about the next swap, and how to execute it
interface IDCAPairSwapHandler {
  /// @notice Information about an available swap for a specific swap interval
  struct SwapInformation {
    // The affected swap interval
    uint32 interval;
    // The number of the swap that will be performed
    uint32 swapToPerform;
    // The amount of token A that needs swapping
    uint256 amountToSwapTokenA;
    // The amount of token B that needs swapping
    uint256 amountToSwapTokenB;
  }

  /// @notice All information about the next swap
  struct NextSwapInformation {
    // All swaps that can be executed
    SwapInformation[] swapsToPerform;
    // How many entries of the swapsToPerform array are valid
    uint8 amountOfSwaps;
    // How much can be borrowed in token A during a flash swap
    uint256 availableToBorrowTokenA;
    // How much can be borrowed in token B during a flash swap
    uint256 availableToBorrowTokenB;
    // How much 10**decimals(tokenB) is when converted to token A
    uint256 ratePerUnitBToA;
    // How much 10**decimals(tokenA) is when converted to token B
    uint256 ratePerUnitAToB;
    // How much token A will be sent to the platform in terms of fee
    uint256 platformFeeTokenA;
    // How much token B will be sent to the platform in terms of fee
    uint256 platformFeeTokenB;
    // The amount of tokens that need to be provided by the swapper
    uint256 amountToBeProvidedBySwapper;
    // The amount of tokens that will be sent to the swapper optimistically
    uint256 amountToRewardSwapperWith;
    // The token that needs to be provided by the swapper
    IERC20Metadata tokenToBeProvidedBySwapper;
    // The token that will be sent to the swapper optimistically
    IERC20Metadata tokenToRewardSwapperWith;
  }

  /// @notice Emitted when a swap is executed
  /// @param _sender The address of the user that initiated the swap
  /// @param _to The address that received the reward + loan
  /// @param _amountBorrowedTokenA How much was borrowed in token A
  /// @param _amountBorrowedTokenB How much was borrowed in token B
  /// @param _fee How much was charged as a swap fee to position owners
  /// @param _nextSwapInformation All information related to the swap
  event Swapped(
    address indexed _sender,
    address indexed _to,
    uint256 _amountBorrowedTokenA,
    uint256 _amountBorrowedTokenB,
    uint32 _fee,
    NextSwapInformation _nextSwapInformation
  );

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

  /// @notice Returns when the next swap will be available for a given swap interval
  /// @param _swapInterval The swap interval to check
  /// @return _when The moment when the next swap will be available. Take into account that if the swap is already available, this result could
  /// be in the past
  function nextSwapAvailable(uint32 _swapInterval) external view returns (uint32 _when);

  /// @notice Returns the amount of tokens that needed swapping in the last swap, for all positions in the given swap interval that were deposited in the given token
  /// @param _swapInterval The swap interval to check
  /// @param _from The address of the token that all positions used to deposit
  /// @return _amount The amount that needed swapping in the last swap
  function swapAmountAccumulator(uint32 _swapInterval, address _from) external view returns (uint256);

  /// @notice Returns all information related to the next swap
  /// @return _nextSwapInformation The information about the next swap
  function getNextSwapInfo() external view returns (NextSwapInformation memory _nextSwapInformation);

  /// @notice Executes a swap
  /// @dev This method assumes that the required amount has already been sent. Will revert with:
  /// Paused if swaps are paused by protocol
  /// NoSwapsToExecute if there are no swaps to execute
  /// LiquidityNotReturned if the required tokens were not sent before calling the function
  function swap() external;

  /// @notice Executes a flash swap
  /// @dev Will revert with:
  /// Paused if swaps are paused by protocol
  /// NoSwapsToExecute if there are no swaps to execute
  /// InsufficientLiquidity if asked to borrow more than the actual reserves
  /// LiquidityNotReturned if the required tokens were not back during the callback
  /// @param _amountToBorrowTokenA How much to borrow in token A
  /// @param _amountToBorrowTokenB How much to borrow in token B
  /// @param _to Address to send the reward + the borrowed tokens
  /// @param _data Bytes to send to the caller during the callback. If this parameter is empty, the callback won't be executed
  function swap(
    uint256 _amountToBorrowTokenA,
    uint256 _amountToBorrowTokenB,
    address _to,
    bytes calldata _data
  ) external;

  /// @notice Returns how many seconds left until the next swap is available
  /// @return _secondsUntilNextSwap The amount of seconds until next swap. Returns 0 if a swap can already be executed
  function secondsUntilNextSwap() external view returns (uint32 _secondsUntilNextSwap);
}

/// @title The interface for all loan related matters in a DCA pair
/// @notice These methods allow users to ask how much is available for loans, and also to execute them
interface IDCAPairLoanHandler {
  /// @notice Emitted when a flash loan is executed
  /// @param _sender The address of the user that initiated the loan
  /// @param _to The address that received the loan
  /// @param _amountBorrowedTokenA How much was borrowed in token A
  /// @param _amountBorrowedTokenB How much was borrowed in token B
  /// @param _loanFee How much was charged as a fee
  event Loaned(address indexed _sender, address indexed _to, uint256 _amountBorrowedTokenA, uint256 _amountBorrowedTokenB, uint32 _loanFee);

  // @notice Thrown when trying to execute a flash loan but without actually asking for tokens
  error ZeroLoan();

  /// @notice Returns the amount of tokens that can be asked for during a flash loan
  /// @return _amountToBorrowTokenA The amount of token A that is available for borrowing
  /// @return _amountToBorrowTokenB The amount of token B that is available for borrowing
  function availableToBorrow() external view returns (uint256 _amountToBorrowTokenA, uint256 _amountToBorrowTokenB);

  /// @notice Executes a flash loan, sending the required amounts to the specified loan recipient
  /// @dev Will revert:
  /// With ZeroLoan if both _amountToBorrowTokenA & _amountToBorrowTokenB are 0
  /// With Paused if loans are paused by protocol
  /// With InsufficientLiquidity if asked for more that reserves
  /// @param _amountToBorrowTokenA The amount to borrow in token A
  /// @param _amountToBorrowTokenB The amount to borrow in token B
  /// @param _to Address that will receive the loan. This address should be a contract that implements IDCAPairLoanCallee
  /// @param _data Any data that should be passed through to the callback
  function loan(
    uint256 _amountToBorrowTokenA,
    uint256 _amountToBorrowTokenB,
    address _to,
    bytes calldata _data
  ) external;
}

interface IDCAPair is IDCAPairParameters, IDCAPairSwapHandler, IDCAPairPositionHandler, IDCAPairLoanHandler {}

File 11 of 28 : CommonErrors.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.6;

library CommonErrors {
  error ZeroAddress();
  error Paused();
  error InsufficientLiquidity();
  error LiquidityNotReturned();
}

File 12 of 28 : Math.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.6;

library Math {
  function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
    unchecked {
      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
      // benefit is lost if 'b' is also tested.
      // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
      if (a == 0) return (true, 0);
      uint256 c = a * b;
      if (c / a != b) return (false, 0);
      return (true, c);
    }
  }
}

File 13 of 28 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 14 of 28 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 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);
    }

    function _verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) private 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 15 of 28 : ITimeWeightedOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol';

/// @title The interface for an oracle that provies TWAP quotes
/// @notice These methods allow users to add support for pairs, and then ask for quotes
interface ITimeWeightedOracle {
  /// @notice Emitted when the oracle add supports for a new pair
  /// @param _tokenA One of the pair's tokens
  /// @param _tokenB The other of the pair's tokens
  event AddedSupportForPair(address _tokenA, address _tokenB);

  /// @notice Returns whether this oracle can support this 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 _canSupport Whether the given pair of tokens can be supported by the oracle
  function canSupportPair(address _tokenA, address _tokenB) external view returns (bool _canSupport);

  /// @notice Returns a quote, based on the given tokens and amount
  /// @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
  /// @return _amountOut How much _tokenOut will be returned in exchange for _amountIn amount of _tokenIn
  function quote(
    address _tokenIn,
    uint128 _amountIn,
    address _tokenOut
  ) external view returns (uint256 _amountOut);

  /// @notice Add support for a given pair to the contract. This function will let the oracle take some actions to
  /// configure the pair for future quotes. Could be called more than one 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
  function addSupportForPair(address _tokenA, address _tokenB) external;
}

/// @title An implementation of ITimeWeightedOracle that uses Uniswap V3 pool oracles
/// @notice This oracle will attempt to use all fee tiers of the same pair when calculating quotes
interface IUniswapV3OracleAggregator is ITimeWeightedOracle {
  /// @notice Emitted when a new fee tier is added
  /// @return _feeTier The added fee tier
  event AddedFeeTier(uint24 _feeTier);

  /// @notice Emitted when a new period is set
  /// @return _period The new period
  event PeriodChanged(uint32 _period);

  /// @notice Returns the Uniswap V3 Factory
  /// @return _factory The Uniswap V3 Factory
  function factory() external view returns (IUniswapV3Factory _factory);

  /// @notice Returns a list of all supported Uniswap V3 fee tiers
  /// @return _feeTiers An array of all supported fee tiers
  function supportedFeeTiers() external view returns (uint24[] memory _feeTiers);

  /// @notice Returns a list of all Uniswap V3 pools used for a given pair
  /// @dev _tokenA and _tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
  /// @return _pools An array with all pools used for quoting the given pair
  function poolsUsedForPair(address _tokenA, address _tokenB) external view returns (address[] memory _pools);

  /// @notice Returns the period used for the TWAP calculation
  /// @return _period The period used for the TWAP
  function period() external view returns (uint16 _period);

  /// @notice Returns minimum possible period
  /// @dev Cannot be modified
  /// @return The minimum possible period
  // solhint-disable-next-line func-name-mixedcase
  function MINIMUM_PERIOD() external view returns (uint16);

  /// @notice Returns maximum possible period
  /// @dev Cannot be modified
  /// @return The maximum possible period
  // solhint-disable-next-line func-name-mixedcase
  function MAXIMUM_PERIOD() external view returns (uint16);

  /// @notice Returns the minimum liquidity that a pool needs to have in order to be used for a pair's quote
  /// @dev This check is only performed when adding support for a pair. If the pool's liquidity then
  /// goes below the threshold, then it will still be used for the quote calculation
  /// @return The minimum liquidity threshold
  // solhint-disable-next-line func-name-mixedcase
  function MINIMUM_LIQUIDITY_THRESHOLD() external view returns (uint16);

  /// @notice Adds support for a new Uniswap V3 fee tier
  /// @dev Will revert if the provided fee tier is not supported by Uniswap V3
  /// @param _feeTier The new fee tier
  function addFeeTier(uint24 _feeTier) external;

  /// @notice Sets the period to be used for the TWAP calculation
  /// @dev Will revert it is lower than MINIMUM_PERIOD or greater than MAXIMUM_PERIOD
  /// WARNING: increasing the period could cause big problems, because Uniswap V3 pools might not support a TWAP so old.
  /// @param _period The new period
  function setPeriod(uint16 _period) external;
}

File 16 of 28 : IDCATokenDescriptor.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.6;

import './IDCAPair.sol';

/// @title The interface for generating a token's description
/// @notice Contracts that implement this interface must return a base64 JSON with the entire description
interface IDCATokenDescriptor {
  /// @notice Generates a token's description, both the JSON and the image inside
  /// @param _positionHandler The pair where the position was created
  /// @param _tokenId The token/position id
  /// @return _description The position's description
  function tokenURI(IDCAPairPositionHandler _positionHandler, uint256 _tokenId) external view returns (string memory _description);
}

File 17 of 28 : IUniswapV3Factory.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
    /// @notice Emitted when the owner of the factory is changed
    /// @param oldOwner The owner before the owner was changed
    /// @param newOwner The owner after the owner was changed
    event OwnerChanged(address indexed oldOwner, address indexed newOwner);

    /// @notice Emitted when a pool is created
    /// @param token0 The first token of the pool by address sort order
    /// @param token1 The second token of the pool by address sort order
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks
    /// @param pool The address of the created pool
    event PoolCreated(
        address indexed token0,
        address indexed token1,
        uint24 indexed fee,
        int24 tickSpacing,
        address pool
    );

    /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
    /// @param fee The enabled fee, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
    event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);

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

    /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
    /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
    /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
    /// @return The tick spacing
    function feeAmountTickSpacing(uint24 fee) external view returns (int24);

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

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

    /// @notice Updates the owner of the factory
    /// @dev Must be called by the current owner
    /// @param _owner The new owner of the factory
    function setOwner(address _owner) external;

    /// @notice Enables a fee amount with the given tickSpacing
    /// @dev Fee amounts may never be removed once enabled
    /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
    /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
    function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}

File 18 of 28 : ERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overriden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @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.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

File 19 of 28 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 20 of 28 : IERC721.sol
// SPDX-License-Identifier: MIT

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`, 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 be 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 Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @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 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);

    /**
     * @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;
}

File 21 of 28 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 22 of 28 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 23 of 28 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 24 of 28 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 25 of 28 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 26 of 28 : IERC165.sol
// SPDX-License-Identifier: MIT

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 27 of 28 : IDCAPairSwapCallee.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

/// @title The interface for handling flash swaps
/// @notice Users that want to execute flash swaps must implement this interface
interface IDCAPairSwapCallee {
  /// @notice Handles the flash swap callback
  /// @param _sender The swap originator
  /// @param _tokenA Address for token A
  /// @param _tokenB Address for token B
  /// @param _amountBorrowedTokenA Amount borrowed in token A
  /// @param _amountBorrowedTokenB Amount borrowed in token B
  /// @param _isRewardTokenA Determines which token is the reward and which to provide to the pair
  /// @param _rewardAmount How much was sent to this contract optimistically
  /// @param _amountToProvide How much needs to be sent back to the pair
  /// @param _data Arbitrary bytes sent to the pair when initiating the swap
  // solhint-disable-next-line func-name-mixedcase
  function DCAPairSwapCall(
    address _sender,
    IERC20Metadata _tokenA,
    IERC20Metadata _tokenB,
    uint256 _amountBorrowedTokenA,
    uint256 _amountBorrowedTokenB,
    bool _isRewardTokenA,
    uint256 _rewardAmount,
    uint256 _amountToProvide,
    bytes calldata _data
  ) external;
}

File 28 of 28 : IDCAPairLoanCallee.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

/// @title The interface for handling flash loans
/// @notice Users that want to execute flash loans must implement this interface
interface IDCAPairLoanCallee {
  /// @notice Handles the flash loan callback
  /// @param _sender The loan originator
  /// @param _tokenA Address for token A
  /// @param _tokenB Address for token B
  /// @param _amountBorrowedTokenA Amount borrowed in token A
  /// @param _amountBorrowedTokenB Amount borrowed in token B
  /// @param _feeTokenA How much extra to return in fees in token A
  /// @param _feeTokenB How much extra to return in fees in token B
  /// @param _data Arbitrary bytes sent to the pair when initiating the loan
  // solhint-disable-next-line func-name-mixedcase
  function DCAPairLoanCall(
    address _sender,
    IERC20Metadata _tokenA,
    IERC20Metadata _tokenB,
    uint256 _amountBorrowedTokenA,
    uint256 _amountBorrowedTokenB,
    uint256 _feeTokenA,
    uint256 _feeTokenB,
    bytes calldata _data
  ) external;
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IDCAGlobalParameters","name":"_globalParameters","type":"address"},{"internalType":"contract IERC20Metadata","name":"_tokenA","type":"address"},{"internalType":"contract IERC20Metadata","name":"_tokenB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InvalidInterval","type":"error"},{"inputs":[],"name":"InvalidPosition","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"LiquidityNotReturned","type":"error"},{"inputs":[],"name":"MandatoryWithdraw","type":"error"},{"inputs":[],"name":"NoSwapsToExecute","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"PositionCompleted","type":"error"},{"inputs":[],"name":"UnauthorizedCaller","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"inputs":[],"name":"ZeroLoan","type":"error"},{"inputs":[],"name":"ZeroRate","type":"error"},{"inputs":[],"name":"ZeroSwaps","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_dcaId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_fromToken","type":"address"},{"indexed":false,"internalType":"uint160","name":"_rate","type":"uint160"},{"indexed":false,"internalType":"uint32","name":"_startingSwap","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"_swapInterval","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"_lastSwap","type":"uint32"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountBorrowedTokenA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountBorrowedTokenB","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"_loanFee","type":"uint32"}],"name":"Loaned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_dcaId","type":"uint256"},{"indexed":false,"internalType":"uint160","name":"_rate","type":"uint160"},{"indexed":false,"internalType":"uint32","name":"_startingSwap","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"_lastSwap","type":"uint32"}],"name":"Modified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountBorrowedTokenA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountBorrowedTokenB","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"_fee","type":"uint32"},{"components":[{"components":[{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"swapToPerform","type":"uint32"},{"internalType":"uint256","name":"amountToSwapTokenA","type":"uint256"},{"internalType":"uint256","name":"amountToSwapTokenB","type":"uint256"}],"internalType":"struct IDCAPairSwapHandler.SwapInformation[]","name":"swapsToPerform","type":"tuple[]"},{"internalType":"uint8","name":"amountOfSwaps","type":"uint8"},{"internalType":"uint256","name":"availableToBorrowTokenA","type":"uint256"},{"internalType":"uint256","name":"availableToBorrowTokenB","type":"uint256"},{"internalType":"uint256","name":"ratePerUnitBToA","type":"uint256"},{"internalType":"uint256","name":"ratePerUnitAToB","type":"uint256"},{"internalType":"uint256","name":"platformFeeTokenA","type":"uint256"},{"internalType":"uint256","name":"platformFeeTokenB","type":"uint256"},{"internalType":"uint256","name":"amountToBeProvidedBySwapper","type":"uint256"},{"internalType":"uint256","name":"amountToRewardSwapperWith","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenToBeProvidedBySwapper","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenToRewardSwapperWith","type":"address"}],"indexed":false,"internalType":"struct IDCAPairSwapHandler.NextSwapInformation","name":"_nextSwapInformation","type":"tuple"}],"name":"Swapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_dcaId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_returnedUnswapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_returnedSwapped","type":"uint256"}],"name":"Terminated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_dcaId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Withdrew","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_dcaIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"_swappedTokenA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_swappedTokenB","type":"uint256"}],"name":"WithdrewMany","type":"event"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"}],"name":"addFundsToPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"availableToBorrow","outputs":[{"internalType":"uint256","name":"_amountToBorrowTokenA","type":"uint256"},{"internalType":"uint256","name":"_amountToBorrowTokenB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint160","name":"_rate","type":"uint160"},{"internalType":"uint32","name":"_amountOfSwaps","type":"uint32"},{"internalType":"uint32","name":"_swapInterval","type":"uint32"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextSwapInfo","outputs":[{"components":[{"components":[{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"swapToPerform","type":"uint32"},{"internalType":"uint256","name":"amountToSwapTokenA","type":"uint256"},{"internalType":"uint256","name":"amountToSwapTokenB","type":"uint256"}],"internalType":"struct IDCAPairSwapHandler.SwapInformation[]","name":"swapsToPerform","type":"tuple[]"},{"internalType":"uint8","name":"amountOfSwaps","type":"uint8"},{"internalType":"uint256","name":"availableToBorrowTokenA","type":"uint256"},{"internalType":"uint256","name":"availableToBorrowTokenB","type":"uint256"},{"internalType":"uint256","name":"ratePerUnitBToA","type":"uint256"},{"internalType":"uint256","name":"ratePerUnitAToB","type":"uint256"},{"internalType":"uint256","name":"platformFeeTokenA","type":"uint256"},{"internalType":"uint256","name":"platformFeeTokenB","type":"uint256"},{"internalType":"uint256","name":"amountToBeProvidedBySwapper","type":"uint256"},{"internalType":"uint256","name":"amountToRewardSwapperWith","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenToBeProvidedBySwapper","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenToRewardSwapperWith","type":"address"}],"internalType":"struct IDCAPairSwapHandler.NextSwapInformation","name":"_nextSwapInformation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalParameters","outputs":[{"internalType":"contract IDCAGlobalParameters","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_activeSwapInterval","type":"uint32"}],"name":"isSwapIntervalActive","outputs":[{"internalType":"bool","name":"_isIntervalActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountToBorrowTokenA","type":"uint256"},{"internalType":"uint256","name":"_amountToBorrowTokenB","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"loan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"},{"internalType":"uint160","name":"_newRate","type":"uint160"}],"name":"modifyRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"},{"internalType":"uint160","name":"_newRate","type":"uint160"},{"internalType":"uint32","name":"_newAmountOfSwaps","type":"uint32"}],"name":"modifyRateAndSwaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"}],"name":"modifySwaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"nextSwapAvailable","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"performedSwaps","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"secondsUntilNextSwap","outputs":[{"internalType":"uint32","name":"_secondsUntil","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountToBorrowTokenA","type":"uint256"},{"internalType":"uint256","name":"_amountToBorrowTokenB","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"}],"name":"swapAmountAccumulator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"swapAmountDelta","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"}],"name":"terminate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenA","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenB","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"}],"name":"userPosition","outputs":[{"components":[{"internalType":"contract IERC20Metadata","name":"from","type":"address"},{"internalType":"contract IERC20Metadata","name":"to","type":"address"},{"internalType":"uint32","name":"swapInterval","type":"uint32"},{"internalType":"uint32","name":"swapsExecuted","type":"uint32"},{"internalType":"uint256","name":"swapped","type":"uint256"},{"internalType":"uint32","name":"swapsLeft","type":"uint32"},{"internalType":"uint256","name":"remaining","type":"uint256"},{"internalType":"uint160","name":"rate","type":"uint160"}],"internalType":"struct IDCAPairPositionHandler.UserPosition","name":"_userPosition","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dcaId","type":"uint256"}],"name":"withdrawSwapped","outputs":[{"internalType":"uint256","name":"_swapped","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_dcaIds","type":"uint256[]"}],"name":"withdrawSwappedMany","outputs":[{"internalType":"uint256","name":"_swappedTokenA","type":"uint256"},{"internalType":"uint256","name":"_swappedTokenB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604051620056da380380620056da8339810160408190526200003491620004c9565b8181816001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b1580156200007057600080fd5b505afa15801562000085573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000af91908101906200051d565b816001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015620000e957600080fd5b505afa158015620000fe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200012891908101906200051d565b6040516020016200013b92919062000628565b60408051601f19818403018152828201909152600382526244434160e81b60208301526001600055908686866001600160a01b03831615806200018557506001600160a01b038216155b806200019857506001600160a01b038116155b15620001b75760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0385169081179091556040805163e63a391f60e01b8152905163e63a391f91600480820192602092909190829003018186803b1580156200020c57600080fd5b505afa15801562000221573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002479190620005d5565b6001805462ffffff92909216600160e01b0262ffffff60e01b19909216919091179055600380546001600160a01b038085166001600160a01b0319928316811790935560048054918516919092161781556040805163313ce56760e01b8152905163313ce56792828101926020929190829003018186803b158015620002cc57600080fd5b505afa158015620002e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000307919062000603565b6200031490600a620006c4565b600160006101000a8154816001600160701b0302191690836001600160701b03160217905550806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156200037457600080fd5b505afa15801562000389573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003af919062000603565b620003bc90600a620006c4565b600180546001600160701b0392909216600160701b02600160701b600160e01b031990921691909117905550508251620003ff9150600d90602085019062000423565b5080516200041590600e90602084019062000423565b50505050505050506200083a565b8280546200043190620007b8565b90600052602060002090601f016020900481019282620004555760008555620004a0565b82601f106200047057805160ff1916838001178555620004a0565b82800160010185558215620004a0579182015b82811115620004a057825182559160200191906001019062000483565b50620004ae929150620004b2565b5090565b5b80821115620004ae5760008155600101620004b3565b600080600060608486031215620004df57600080fd5b8351620004ec8162000821565b6020850151909350620004ff8162000821565b6040850151909250620005128162000821565b809150509250925092565b6000602082840312156200053057600080fd5b81516001600160401b03808211156200054857600080fd5b818401915084601f8301126200055d57600080fd5b8151818111156200057257620005726200080b565b604051601f8201601f19908116603f011681019083821181831017156200059d576200059d6200080b565b81604052828152876020848701011115620005b757600080fd5b620005ca83602083016020880162000785565b979650505050505050565b600060208284031215620005e857600080fd5b815162ffffff81168114620005fc57600080fd5b9392505050565b6000602082840312156200061657600080fd5b815160ff81168114620005fc57600080fd5b6402221a09d160dd1b8152600083516200064a81600585016020880162000785565b6201016960ed1b60059184019182015283516200066f81600884016020880162000785565b01600801949350505050565b600181815b80851115620006bc578160001904821115620006a057620006a0620007f5565b80851615620006ae57918102915b93841c939080029062000680565b509250929050565b6000620005fc60ff841683600082620006e0575060016200077f565b81620006ef575060006200077f565b8160018114620007085760028114620007135762000733565b60019150506200077f565b60ff841115620007275762000727620007f5565b50506001821b6200077f565b5060208310610133831016604e8410600b841016171562000758575081810a6200077f565b6200076483836200067b565b80600019048211156200077b576200077b620007f5565b0290505b92915050565b60005b83811015620007a257818101518382015260200162000788565b83811115620007b2576000848401525b50505050565b600181811c90821680620007cd57607f821691505b60208210811415620007ef57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146200083757600080fd5b50565b614e90806200084a6000396000f3fe608060405234801561001057600080fd5b506004361061021c5760003560e01c80635f64b55b11610125578063a57351f9116100ad578063c96ed6391161007c578063c96ed63914610521578063e111e3e714610534578063e985e9c514610547578063eb391aa81461055a578063f8fd31001461056d57600080fd5b8063a57351f9146104c8578063b0d2847c146104d0578063b88d4fde146104fb578063c87b56dd1461050e57600080fd5b80637e29e8a4116100f45780637e29e8a41461046a5780638119c06514610492578063833e46181461049a57806395d89b41146104ad578063a22cb465146104b557600080fd5b80635f64b55b1461041e5780636352211e1461043157806370a08231146104445780637a828b281461045757600080fd5b806323b872dd116101a857806332e8af461161017757806332e8af46146103865780633d783717146103c55780633fcf01e6146103d857806342842e0e146103eb5780635b6fd01d146103fe57600080fd5b806323b872dd1461032557806326742bed146103385780632a52803e1461034b5780632f84cbf51461037157600080fd5b8063095ea7b3116101ef578063095ea7b31461029e5780630ad82b2f146102b15780630b74e852146102c45780630f7d0f2b146102d75780630fc63d101461031257600080fd5b806301ffc9a714610221578063022c0d9f1461024957806306fdde031461025e578063081812fc14610273575b600080fd5b61023461022f366004614294565b61059b565b60405190151581526020015b60405180910390f35b61025c610257366004614588565b6105ed565b005b610266610cff565b604051610240919061495a565b610286610281366004614459565b610d91565b6040516001600160a01b039091168152602001610240565b61025c6102ac3660046141d6565b610e26565b600254610286906001600160a01b031681565b61025c6102d236600461448b565b610f3c565b6102fd6102e5366004614626565b60066020526000908152604090205463ffffffff1681565b60405163ffffffff9091168152602001610240565b600354610286906001600160a01b031681565b61025c61033336600461409f565b610fee565b61025c6103463660046144b0565b61101f565b6102fd610359366004614626565b600c6020526000908152604090205463ffffffff1681565b610379611052565b6040516102409190614a47565b6103b7610394366004614661565b600560209081526000938452604080852082529284528284209052825290205481565b604051908152602001610240565b61025c6103d33660046144f2565b6110f4565b61025c6103e6366004614601565b611545565b61025c6103f936600461409f565b6115a1565b61041161040c366004614459565b6115bc565b6040516102409190614a5a565b600454610286906001600160a01b031681565b61028661043f366004614459565b6117da565b6103b7610452366004614049565b611851565b61025c610465366004614459565b6118d8565b61047d610478366004614202565b611a2e565b60408051928352602083019190915201610240565b61025c611c1f565b6102346104a8366004614626565b611c3d565b610266611c53565b61025c6104c336600461414c565b611c62565b6102fd611d27565b6103b76104de366004614643565b600b60209081526000928352604080842090915290825290205481565b61025c6105093660046140e0565b611dd4565b61026661051c366004614459565b611e0c565b61025c61052f3660046145d3565b611f0d565b6103b7610542366004614459565b611fba565b610234610555366004614066565b6120ea565b6103b761056836600461417a565b612118565b6003546001600160a01b0390811660009081526008602052604080822054600454909316825290205461047d565b60006001600160e01b031982166380ac58cd60e01b14806105cc57506001600160e01b03198216635b5e139f60e01b145b806105e757506301ffc9a760e01b6001600160e01b03198316145b92915050565b600260005414156106195760405162461bcd60e51b815260040161061090614a10565b60405180910390fd5b600260008181559054604080516303956f4760e21b815290516001600160a01b0390921691630e55bd1c91600480820192608092909190829003018186803b15801561066457600080fd5b505afa158015610678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069c91906143d4565b90508060200151156106c1576040516313d0ff5960e31b815260040160405180910390fd5b6106c9613f7d565b6000806106de846040015185606001516123d5565b6020830151929550909350915060ff1661070b576040516346f8d7b160e11b815260040160405180910390fd5b4260005b846020015160ff168110156108d95760008560000151828151811061073657610736614de3565b602002602001015160000151905060008660000151838151811061075c5761075c614de3565b602002602001015160200151905060008760000151848151811061078257610782614de3565b60200260200101516040015111806107bb57506000876000015184815181106107ad576107ad614de3565b602002602001015160600151115b156108ae57600354875180516107fc9285926001600160a01b0390911691879081106107e9576107e9614de3565b602002602001015160400151888561282b565b600454875180516108389285926001600160a01b03909116918790811061082557610825614de3565b602002602001015160600151898561282b565b63ffffffff8281166000908152600660205260409020805463ffffffff19169183169190911790558161086b8186614bf0565b610876906001614bb4565b6108809190614c61565b63ffffffff8381166000908152600c60205260409020805463ffffffff1916929091169190911790556108c4565b6108c2600963ffffffff8085169061289e16565b505b505080806108d190614d66565b91505061070f565b5050505080604001518611806108f25750806060015185115b156109105760405163bb55fd2760e01b815260040160405180910390fd5b60008160c0015182604001516109269190614cc3565b905060008260e00151836060015161093e9190614cc3565b600354610160850151919250899189916001600160a01b039182169116141561098b576101208501516109719083614b9c565b9150846101000151836109849190614b9c565b92506109b1565b61012085015161099b9082614b9c565b9050846101000151846109ae9190614b9c565b93505b81156109ce576003546109ce906001600160a01b031689846128b1565b80156109eb576004546109eb906001600160a01b031689836128b1565b5050845115610ac557856001600160a01b0316633d33803c33600360009054906101000a90046001600160a01b0316600460009054906101000a90046001600160a01b03168c8c600360009054906101000a90046001600160a01b03166001600160a01b03168a61016001516001600160a01b0316148a61012001518b61010001518e6040518a63ffffffff1660e01b8152600401610a9299989796959493929190614836565b600060405180830381600087803b158015610aac57600080fd5b505af1158015610ac0573d6000803e3d6000fd5b505050505b6003546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015610b0957600080fd5b505afa158015610b1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b419190614472565b600480546040516370a0823160e01b815230928101929092529192506000916001600160a01b0316906370a082319060240160206040518083038186803b158015610b8b57600080fd5b505afa158015610b9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc39190614472565b90508460c0015184610bd59190614b9c565b821080610bef575060e0850151610bec9084614b9c565b81105b15610c0d5760405163d562e29f60e01b815260040160405180910390fd5b6003546001600160a01b039081166000908152600860205260408082208790556004549092168152908120849055610c458584614cc3565b90506000610c538584614cc3565b90508115610c76578751600354610c76916001600160a01b0390911690846128b1565b8015610c97578751600454610c97916001600160a01b0390911690836128b1565b896001600160a01b0316336001600160a01b03167f985cca3348e25076ac7404c08e594a15b8529496ecbf6b08af7fa24c5d4e742b8e8e8c604001518c604051610ce49493929190614ad7565b60405180910390a35050600160005550505050505050505050565b6060600d8054610d0e90614d2b565b80601f0160208091040260200160405190810160405280929190818152602001828054610d3a90614d2b565b8015610d875780601f10610d5c57610100808354040283529160200191610d87565b820191906000526020600020905b815481529060010190602001808311610d6a57829003601f168201915b5050505050905090565b6000818152600f60205260408120546001600160a01b0316610e0a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610610565b506000908152601160205260409020546001600160a01b031690565b6000610e31826117da565b9050806001600160a01b0316836001600160a01b03161415610e9f5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610610565b336001600160a01b0382161480610ebb5750610ebb81336120ea565b610f2d5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610610565b610f378383612914565b505050565b60026000541415610f5f5760405162461bcd60e51b815260040161061090614a10565b6002600055610f6d82612982565b60008281526013602081815260408084205463ffffffff600160401b820481168652600684529185205487865293909252610fb392811691640100000000900416614cda565b905063ffffffff8116610fd95760405163d43195c360e01b815260040160405180910390fd5b610fe48383836129e8565b5050600160005550565b610ff83382612a15565b6110145760405162461bcd60e51b8152600401610610906149bf565b610f37838383612aec565b600260005414156110425760405162461bcd60e51b815260040161061090614a10565b6002600055610fe48383836129e8565b61105a613f7d565b600254604080516303956f4760e21b815290516000926001600160a01b031691630e55bd1c916004808301926080929190829003018186803b15801561109f57600080fd5b505afa1580156110b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d791906143d4565b90506110eb816040015182606001516123d5565b50909392505050565b600260005414156111175760405162461bcd60e51b815260040161061090614a10565b600260005584158015611128575083155b156111465760405163125f2a8360e21b815260040160405180910390fd5b6002546040805163b1d0a33760e01b815290516000926001600160a01b03169163b1d0a337916004808301926060929190829003018186803b15801561118b57600080fd5b505afa15801561119f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c39190614362565b90508060200151156111e8576040516313d0ff5960e31b815260040160405180910390fd5b6003546001600160a01b039081166000908152600860205260408082205460045490931682529020548188118061121e57508087115b1561123c5760405163bb55fd2760e01b815260040160405180910390fd5b600080891161124c57600061125a565b61125a84604001518a612c8c565b9050600080891161126c57600061127a565b61127a85604001518a612c8c565b9050891561129957600354611299906001600160a01b0316898c6128b1565b88156112b6576004546112b6906001600160a01b0316898b6128b1565b876001600160a01b031663f62c404133600360009054906101000a90046001600160a01b0316600460009054906101000a90046001600160a01b03168e8e88888f8f6040518a63ffffffff1660e01b815260040161131c9998979695949392919061489b565b600060405180830381600087803b15801561133657600080fd5b505af115801561134a573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152306004820152600093506001600160a01b0390911691506370a082319060240160206040518083038186803b15801561139457600080fd5b505afa1580156113a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cc9190614472565b600480546040516370a0823160e01b815230928101929092529192506000916001600160a01b0316906370a082319060240160206040518083038186803b15801561141657600080fd5b505afa15801561142a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144e9190614472565b905061145a8487614b9c565b82108061146f575061146c8386614b9c565b81105b1561148d5760405163d562e29f60e01b815260040160405180910390fd5b60006114998784614cc3565b905060006114a78784614cc3565b905081156114ca5788516003546114ca916001600160a01b0390911690846128b1565b80156114eb5788516004546114eb916001600160a01b0390911690836128b1565b505060408088015181518e8152602081018e905263ffffffff909116918101919091526001600160a01b038b169033907f891529dc768bd9c3ee9afa282dfdaf4ddffed309e6dae2af016a804e1d05473390606001610ce4565b600260005414156115685760405162461bcd60e51b815260040161061090614a10565b6002600090815582815260136020526040902054611598908390600160601b90046001600160a01b0316836129e8565b50506001600055565b610f3783838360405180602001604052806000815250611dd4565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152600082815260136020908152604091829020825160c081018452815463ffffffff80821683526401000000008204811694830194909452600160401b810490931693810193909352600160601b9091046001600160a01b031660608301526001015460ff81161515608083018190526101009091046001600160f81b031660a0830152611695576004546001600160a01b03166116a2565b6003546001600160a01b03165b6001600160a01b0316825260808101516116c7576003546001600160a01b03166116d4565b6004546001600160a01b03165b6001600160a01b031660208301526040808201805163ffffffff90811692850192909252511661170557600061172f565b805160408083015163ffffffff90811660009081526006602052919091205461172f929116614cda565b63ffffffff16606083015261174383612cc5565b608083015260408082015163ffffffff908116600090815260066020908152929020549183015191811691161161177b5760006117aa565b60408082015163ffffffff90811660009081526006602090815292902054918301516117aa9290911690614cda565b63ffffffff1660a08301526117be83612efd565b60c0830152606001516001600160a01b031660e0820152919050565b6000818152600f60205260408120546001600160a01b0316806105e75760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610610565b60006001600160a01b0382166118bc5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610610565b506001600160a01b031660009081526010602052604090205490565b600260005414156118fb5760405162461bcd60e51b815260040161061090614a10565b600260005561190981612982565b600061191482612cc5565b9050600061192183612efd565b9050600061192e84612f97565b9050600061193b85612fd2565b90506119468561300d565b61194f8561316a565b8315611997576001600160a01b0381166000908152600860205260408120805486929061197d908490614cc3565b9091555061199790506001600160a01b03821633866128b1565b82156119df576001600160a01b038216600090815260086020526040812080548592906119c5908490614cc3565b909155506119df90506001600160a01b03831633856128b1565b604080518681526020810185905290810185905233907f9b863fe095959050c7ed1d85b45c0922e5667da8f82299a3eebf4cbc584f79ec9060600160405180910390a250506001600055505050565b60008060026000541415611a545760405162461bcd60e51b815260040161061090614a10565b600260009081555b83811015611b31576000858583818110611a7857611a78614de3565b905060200201359050611a8a81612982565b6000611a9582612cc5565b60008381526013602052604090206001015490915060ff1615611ac357611abc8185614b9c565b9350611ad0565b611acd8186614b9c565b94505b5060008181526013602081815260408084208054600160401b810463ffffffff9081168752600685529286205496909552929091529290921663ffffffff19909116178155600101805460ff16905580611b2981614d66565b915050611a5c565b508115611b7e576003546001600160a01b031660009081526008602052604081208054849290611b62908490614cc3565b9091555050600354611b7e906001600160a01b031633846128b1565b8015611bca576004546001600160a01b031660009081526008602052604081208054839290611bae908490614cc3565b9091555050600454611bca906001600160a01b031633836128b1565b336001600160a01b03167f4fd01d816751d768469443eeebd391cbef7fae3c437c75730c0e1b9a3c174eee85858585604051611c099493929190614911565b60405180910390a2600160005590939092509050565b611c3b60008033604051806020016040528060008152506105ed565b565b60006105e7600963ffffffff8085169061320516565b6060600e8054610d0e90614d2b565b6001600160a01b038216331415611cbb5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610610565b3360008181526012602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b63ffffffff4260005b611d3a600961321d565b811015611dcf576000611d4e600983613227565b63ffffffff8082166000908152600c6020526040902054919250808516911611611d7c576000935050505090565b63ffffffff8082166000908152600c60205260408120549091611da191869116614cda565b90508463ffffffff168163ffffffff161015611dbb578094505b505080611dc781614d66565b915050611d30565b505090565b611dde3383612a15565b611dfa5760405162461bcd60e51b8152600401610610906149bf565b611e0684848484613233565b50505050565b60025460408051630442675760e41b815290516060926001600160a01b0316916344267570916004808301926020929190829003018186803b158015611e5157600080fd5b505afa158015611e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8991906142ce565b60405163e9dc637560e01b8152306004820152602481018490526001600160a01b03919091169063e9dc63759060440160006040518083038186803b158015611ed157600080fd5b505afa158015611ee5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105e791908101906142eb565b60026000541415611f305760405162461bcd60e51b815260040161061090614a10565b600260005581611f5357604051631f2a200560e01b815260040160405180910390fd5b63ffffffff8116611f7757604051633c92196b60e11b815260040160405180910390fd5b6000611f8284612efd565b90506000611f908483614b9c565b9050611fae858284611fa863ffffffff881683614bdc565b87613266565b50506001600055505050565b600060026000541415611fdf5760405162461bcd60e51b815260040161061090614a10565b6002600055611fed82612982565b611ff682612cc5565b60008381526013602081815260408084208054600160401b810463ffffffff9081168752600685529286205489875294909352921663ffffffff19909116178155600101805460ff16905590915061204d83612fd2565b6001600160a01b03811660009081526008602052604081208054929350849290919061207a908490614cc3565b9091555061209490506001600160a01b03821633846128b1565b604080518481526001600160a01b038316602082015290810183905233907f7d3fe0088fc0b393d0c7629c8d9df3043bd33e8fc1a953dfa251ab201c6ebc389060600160405180910390a2506001600055919050565b6001600160a01b03918216600090815260126020908152604080832093909416825291909152205460ff1690565b60006002600054141561213d5760405162461bcd60e51b815260040161061090614a10565b60026000556003546001600160a01b0386811691161480159061216e57506004546001600160a01b03868116911614155b1561218c5760405163c1ab6dc160e01b815260040160405180910390fd5b63ffffffff83166121b057604051633c92196b60e11b815260040160405180910390fd5b6121c4600963ffffffff8085169061320516565b15801561224f5750600254604051639eceefe160e01b815263ffffffff841660048201526001600160a01b0390911690639eceefe19060240160206040518083038186803b15801561221557600080fd5b505afa158015612229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224d9190614277565b155b1561226d576040516305fabb6160e41b815260040160405180910390fd5b6003546000906001600160a01b03878116911614612296576004546001600160a01b03166122a3565b6003546001600160a01b03165b905060006122b763ffffffff861687614c13565b6001600160a01b0390811691506122d2908316333084613414565b6001600160a01b038716600090815260086020526040812080548392906122fa908490614b9c565b925050819055506001601460008282546123149190614b9c565b925050819055506123273360145461344c565b61233b600963ffffffff8087169061346a16565b506000806123506014548a8a8a60008b613476565b601454604080519182526001600160a01b038d811660208401528c169082015263ffffffff8084166060830152808a166080830152821660a0820152919350915033907f7bd589f2fd6a39318e78fbbbadbcd21ce8c1306fc0ecb4d7a49a7e4be9c67e639060c00160405180910390a250506014546001600055979650505050505050565b6123dd613f7d565b6000806000806000806123ee61367d565b9150915060005b8160ff168110156124685782818151811061241257612412614de3565b602002602001015160400151856124299190614b9c565b945082818151811061243d5761243d614de3565b602002602001015160600151846124549190614b9c565b93508061246081614d66565b9150506123f5565b5090865260ff1660208601526004805460015460035460405163a513355f60e01b81526001600160a01b0393841694810194909452600160701b9091046001600160701b031660248401528116604483015287169063a513355f9060640160206040518083038186803b1580156124de57600080fd5b505afa1580156124f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125169190614472565b6080860181905260015461253d906001600160701b0380821691600160701b900416614c42565b6125479190614bdc565b60a0860152608085015161255c908890612c8c565b856080015161256b9190614cc3565b935061257b878660a00151612c8c565b8560a0015161258a9190614cc3565b6001549093506000906125ae90600160701b90046001600160701b03168387613800565b6001549091506000906125cb906001600160701b03168587613800565b905060006125fc6001600e9054906101000a90046001600160701b03166001600160701b0316858a60800151613800565b9050848110156126d1576004546001600160a01b039081166101408a01526003541661016089015261262e8a82612c8c565b60c089015261263d8a85612c8c565b60e0890181905284906126509084614b9c565b61265a9190614cc3565b61010089015260c088015161266f8487614cc3565b6126799190614cc3565b61012089018190526003546001600160a01b03166000908152600860205260409020546126a69190614cc3565b6040808a01919091526004546001600160a01b0316600090815260086020522054606089015261281f565b848111156127cc576003546001600160a01b039081166101408a0152600454166101608901526127018a86612c8c565b60c0890152608088015160015461273d918c9161272e90600160701b90046001600160701b031689614c42565b6127389190614bdc565b612c8c565b60e089015260c088015185906127539085614b9c565b61275d9190614cc3565b61010089015260e08801516127728386614cc3565b61277c9190614cc3565b61012089019081526003546001600160a01b03908116600090815260086020818152604080842054818f0152945160045490941683525291909120546127c29190614cc3565b606089015261281f565b6127d68a86612c8c565b60c08901526127e58a85612c8c565b60e08901526003546001600160a01b03908116600090815260086020818152604080842054818e0152600454909416835252205460608901525b50505050509250925092565b63ffffffff85166000908152600b602090815260408083206001600160a01b0388168452909152902083905561286385858385613817565b63ffffffff94851660009081526005602090815260408083206001600160a01b03909716835295815285822092909616815294525050812055565b60006128aa83836138ab565b9392505050565b6040516001600160a01b038316602482015260448101829052610f3790849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526139a5565b600081815260116020526040902080546001600160a01b0319166001600160a01b0384169081179091558190612949826117da565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600081815260136020526040902054600160601b90046001600160a01b03166129be5760405163673f032f60e11b815260040160405180910390fd5b6129c83382612a15565b6129e557604051635c427cd960e01b815260040160405180910390fd5b50565b610f37836129fc63ffffffff841685614c13565b6001600160a01b0316612a0e86612efd565b8585613266565b6000818152600f60205260408120546001600160a01b0316612a8e5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610610565b6000612a99836117da565b9050806001600160a01b0316846001600160a01b03161480612ad45750836001600160a01b0316612ac984610d91565b6001600160a01b0316145b80612ae45750612ae481856120ea565b949350505050565b826001600160a01b0316612aff826117da565b6001600160a01b031614612b675760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610610565b6001600160a01b038216612bc95760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610610565b612bd4600082612914565b6001600160a01b0383166000908152601060205260408120805460019290612bfd908490614cc3565b90915550506001600160a01b0382166000908152601060205260408120805460019290612c2b908490614b9c565b90915550506000818152600f602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600154600090606490600160e01b900462ffffff16612cb163ffffffff861685614c42565b612cbb9190614bdc565b6128aa9190614bdc565b6000818152601360209081526040808320815160c081018352815463ffffffff80821683526401000000008204811695830195909552600160401b810490941692810192909252600160601b9092046001600160a01b0316606082015260019091015460ff81161515608083018190526101009091046001600160f81b031660a08301528290612d60576004546001600160a01b0316612d6d565b6003546001600160a01b03165b6040808401805163ffffffff9081166000908152600760209081528482206001600160a01b03871683528152848220818901519451841683526006909152938120549495509384928216911610612dc8578460200151612de7565b60408086015163ffffffff908116600090815260066020529190912054165b63ffffffff908116825260208083019390935260409182016000908120548784015183168252600785528382206001600160a01b03881683528552838220885190931682529190935290822054909250612e419083614cc3565b905060008460800151612e6657600154600160701b90046001600160701b0316612e73565b6001546001600160701b03165b6001600160701b03169050600080612e988488606001516001600160a01b0316613a77565b91509150600082612eca5760608801516001600160a01b0316612ebb8587614bdc565b612ec59190614c42565b612ed4565b612ed48483614bdc565b90508760a001516001600160f81b031681612eef9190614b9c565b9a9950505050505050505050565b60008181526013602081815260408084205463ffffffff600160401b8204811686526006845291852054868652939092529182169164010000000090910416818111612f4d575060009392505050565b600084815260136020526040902054600160601b90046001600160a01b0316612f768383614cda565b63ffffffff16612f869190614c13565b6001600160a01b0316949350505050565b60008181526013602052604081206001015460ff16612fc1576004546001600160a01b03166105e7565b50506003546001600160a01b031690565b60008181526013602052604081206001015460ff16612ffc576003546001600160a01b03166105e7565b50506004546001600160a01b031690565b60008181526013602090815260408083205463ffffffff600160401b820481168086526006909452919093205491926401000000009004811691168082111561315057600084815260136020526040812054600160601b90046001600160a01b03169061307986612f97565b63ffffffff861660009081526005602090815260408083206001600160a01b03851684529091528120919250601384900b91906130b7866001614bb4565b63ffffffff1663ffffffff16815260200190815260200160002060008282546130e09190614c84565b909155505063ffffffff851660009081526005602090815260408083206001600160a01b03851684529091528120601384900b9161311f876001614bb4565b63ffffffff1663ffffffff16815260200190815260200160002060008282546131489190614b5b565b909155505050505b505050600090815260136020526040812081815560010155565b6000613175826117da565b9050613182600083612914565b6001600160a01b03811660009081526010602052604081208054600192906131ab908490614cc3565b90915550506000828152600f602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b600081815260018301602052604081205415156128aa565b60006105e7825490565b60006128aa8383613abf565b61323e848484612aec565b61324a84848484613ae9565b611e065760405162461bcd60e51b81526004016106109061496d565b61326f85612982565b600061327a86612f97565b9050600061328787612cc5565b90506001600160f81b038111156132b157604051630930608960e11b815260040160405180910390fd5b600087815260136020526040902054600160401b900463ffffffff166132d68861300d565b6000806132e78a8689898888613476565b91509150878911156133515761331433306133028b8d614cc3565b6001600160a01b038916929190613414565b61331e888a614cc3565b6001600160a01b03861660009081526008602052604081208054909190613346908490614b9c565b909155506133b09050565b878910156133b0576133638989614cc3565b6001600160a01b0386166000908152600860205260408120805490919061338b908490614cc3565b909155506133b090503361339f8b8b614cc3565b6001600160a01b03881691906128b1565b604080518b81526001600160a01b038916602082015263ffffffff8481168284015283166060820152905133917ffa7c989fea93eb42545a9e7a74f2b8d966813e216a1dd7b59562af10dba259b4919081900360800190a250505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611e069085906323b872dd60e01b906084016128dd565b613466828260405180602001604052806000815250613bf6565b5050565b60006128aa8383613c29565b6000806001600160a01b0386166134a0576040516314cee7cd60e11b815260040160405180910390fd5b63ffffffff808416600090815260066020526040902054166134c3816001614bb4565b92506134cf8682614bb4565b63ffffffff80861660009081526005602090815260408083206001600160a01b038e16845282528083209388168352929052908120805492945060138a900b9290919061351d908490614b5b565b909155505063ffffffff841660009081526005602090815260408083206001600160a01b038c1684529091528120601389900b9161355c856001614bb4565b63ffffffff1663ffffffff16815260200190815260200160002060008282546135859190614c84565b90915550506040805160c08101825263ffffffff928316815283831660208083019182529684168284019081526001600160a01b039a8b16606084019081526003549c8c169c8c169c909c14608084019081526001600160f81b03998a1660a0850190815260009e8f52601390995293909c209151825491519c519b51909a16600160601b026bffffffffffffffffffffffff9b8516600160401b029b909b1667ffffffffffffffff9c85166401000000000267ffffffffffffffff199092169a90941699909917989098179990991617969096178555955190519091166101000290151560ff161760019290920191909155509091565b606060008061368c600961321d565b90508067ffffffffffffffff8111156136a7576136a7614df9565b6040519080825280602002602001820160405280156136f957816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816136c55790505b50925060005b818110156137fa576000613714600983613227565b90504263ffffffff8281166000908152600c60205260409020549181169116116137e75763ffffffff808216600090815260066020526040812054909161375d91166001614bb4565b6040805160808101825263ffffffff80861682528316602082015260035492935091908201906137989085906001600160a01b031685613c78565b81526004546020909101906137b89085906001600160a01b031685613c78565b905286866137c581614d81565b975060ff16815181106137da576137da614de3565b6020026020010181905250505b50806137f281614d66565b9150506136ff565b50509091565b60008361380d8385614c42565b612ae49190614bdc565b63ffffffff841660009081526007602090815260408083206001600160a01b038716845290915281208161384c600186614cda565b63ffffffff168152602081019190915260400160002054905061386f8282614b9c565b63ffffffff95861660009081526007602090815260408083206001600160a01b0390981683529681528682209590971681529390955250502055565b600081815260018301602052604081205480156139945760006138cf600183614cc3565b85549091506000906138e390600190614cc3565b905081811461394857600086600001828154811061390357613903614de3565b906000526020600020015490508087600001848154811061392657613926614de3565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061395957613959614dcd565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105e7565b60009150506105e7565b5092915050565b60006139fa826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613ccb9092919063ffffffff16565b805190915015610f375780806020019051810190613a189190614277565b610f375760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610610565b60008083613a8b5750600190506000613ab8565b83830283858281613a9e57613a9e614db7565b0414613ab1576000809250925050613ab8565b6001925090505b9250929050565b6000826000018281548110613ad657613ad6614de3565b9060005260206000200154905092915050565b60006001600160a01b0384163b15613beb57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290613b2d9033908990889088906004016147f9565b602060405180830381600087803b158015613b4757600080fd5b505af1925050508015613b77575060408051601f3d908101601f19168201909252613b74918101906142b1565b60015b613bd1573d808015613ba5576040519150601f19603f3d011682016040523d82523d6000602084013e613baa565b606091505b508051613bc95760405162461bcd60e51b81526004016106109061496d565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612ae4565b506001949350505050565b613c008383613cda565b613c0d6000848484613ae9565b610f375760405162461bcd60e51b81526004016106109061496d565b6000818152600183016020526040812054613c70575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105e7565b5060006105e7565b63ffffffff92831660008181526005602090815260408083206001600160a01b0390961680845295825280832094909616825292835284812054918152600b835284812093815292909152919020540190565b6060612ae48484600085613e1c565b6001600160a01b038216613d305760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610610565b6000818152600f60205260409020546001600160a01b031615613d955760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610610565b6001600160a01b0382166000908152601060205260408120805460019290613dbe908490614b9c565b90915550506000818152600f602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606082471015613e7d5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610610565b843b613ecb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610610565b600080866001600160a01b03168587604051613ee791906147dd565b60006040518083038185875af1925050503d8060008114613f24576040519150601f19603f3d011682016040523d82523d6000602084013e613f29565b606091505b5091509150613f39828286613f44565b979650505050505050565b60608315613f535750816128aa565b825115613f635782518084602001fd5b8160405162461bcd60e51b8152600401610610919061495a565b60405180610180016040528060608152602001600060ff168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b600082601f83011261400457600080fd5b813561401761401282614b33565b614b02565b81815284602083860101111561402c57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561405b57600080fd5b81356128aa81614e0f565b6000806040838503121561407957600080fd5b823561408481614e0f565b9150602083013561409481614e0f565b809150509250929050565b6000806000606084860312156140b457600080fd5b83356140bf81614e0f565b925060208401356140cf81614e0f565b929592945050506040919091013590565b600080600080608085870312156140f657600080fd5b843561410181614e0f565b9350602085013561411181614e0f565b925060408501359150606085013567ffffffffffffffff81111561413457600080fd5b61414087828801613ff3565b91505092959194509250565b6000806040838503121561415f57600080fd5b823561416a81614e0f565b9150602083013561409481614e24565b6000806000806080858703121561419057600080fd5b843561419b81614e0f565b935060208501356141ab81614e0f565b925060408501356141bb81614e48565b915060608501356141cb81614e48565b939692955090935050565b600080604083850312156141e957600080fd5b82356141f481614e0f565b946020939093013593505050565b6000806020838503121561421557600080fd5b823567ffffffffffffffff8082111561422d57600080fd5b818501915085601f83011261424157600080fd5b81358181111561425057600080fd5b8660208260051b850101111561426557600080fd5b60209290920196919550909350505050565b60006020828403121561428957600080fd5b81516128aa81614e24565b6000602082840312156142a657600080fd5b81356128aa81614e32565b6000602082840312156142c357600080fd5b81516128aa81614e32565b6000602082840312156142e057600080fd5b81516128aa81614e0f565b6000602082840312156142fd57600080fd5b815167ffffffffffffffff81111561431457600080fd5b8201601f8101841361432557600080fd5b805161433361401282614b33565b81815285602083850101111561434857600080fd5b614359826020830160208601614cff565b95945050505050565b60006060828403121561437457600080fd5b6040516060810181811067ffffffffffffffff8211171561439757614397614df9565b60405282516143a581614e0f565b815260208301516143b581614e24565b602082015260408301516143c881614e48565b60408201529392505050565b6000608082840312156143e657600080fd5b6040516080810181811067ffffffffffffffff8211171561440957614409614df9565b604052825161441781614e0f565b8152602083015161442781614e24565b6020820152604083015161443a81614e48565b6040820152606083015161444d81614e0f565b60608201529392505050565b60006020828403121561446b57600080fd5b5035919050565b60006020828403121561448457600080fd5b5051919050565b6000806040838503121561449e57600080fd5b82359150602083013561409481614e0f565b6000806000606084860312156144c557600080fd5b8335925060208401356144d781614e0f565b915060408401356144e781614e48565b809150509250925092565b60008060008060006080868803121561450a57600080fd5b8535945060208601359350604086013561452381614e0f565b9250606086013567ffffffffffffffff8082111561454057600080fd5b818801915088601f83011261455457600080fd5b81358181111561456357600080fd5b89602082850101111561457557600080fd5b9699959850939650602001949392505050565b6000806000806080858703121561459e57600080fd5b843593506020850135925060408501356145b781614e0f565b9150606085013567ffffffffffffffff81111561413457600080fd5b6000806000606084860312156145e857600080fd5b833592506020840135915060408401356144e781614e48565b6000806040838503121561461457600080fd5b82359150602083013561409481614e48565b60006020828403121561463857600080fd5b81356128aa81614e48565b6000806040838503121561465657600080fd5b823561408481614e48565b60008060006060848603121561467657600080fd5b833561468181614e48565b925060208401356144d781614e0f565b600081518084526020808501945080840160005b838110156146ea578151805163ffffffff90811689528482015116848901526040808201519089015260609081015190880152608090960195908201906001016146a5565b509495945050505050565b6000815180845261470d816020860160208601614cff565b601f01601f19169290920160200192915050565b6000610180825181855261473782860182614691565b915050602083015161474e602086018260ff169052565b5060408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e0850152610100808401518186015250610120808401518186015250610140808401516147be828701826001600160a01b03169052565b5050610160928301516001600160a01b03169390920192909252919050565b600082516147ef818460208701614cff565b9190910192915050565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061482c908301846146f5565b9695505050505050565b600061012060018060a01b03808d168452808c166020850152808b1660408501525088606084015287608084015286151560a08401528560c08401528460e08401528061010084015261488b818401856146f5565b9c9b505050505050505050505050565b6001600160a01b038a81168252898116602083015288166040820152606081018790526080810186905260a0810185905260c0810184905261010060e08201819052810182905260006101208385828501376000838501820152601f909301601f19169091019091019998505050505050505050565b6060808252810184905260006001600160fb1b0385111561493157600080fd5b8460051b8087608085013760009083016080019081526020830194909452506040015292915050565b6020815260006128aa60208301846146f5565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020815260006128aa6020830184614721565b60006101008201905060018060a01b0380845116835280602085015116602084015250604083015163ffffffff8082166040850152806060860151166060850152608085015160808501528060a08601511660a0850152505060c083015160c083015260e083015161399e60e08401826001600160a01b03169052565b84815283602082015263ffffffff8316604082015260806060820152600061482c6080830184614721565b604051601f8201601f1916810167ffffffffffffffff81118282101715614b2b57614b2b614df9565b604052919050565b600067ffffffffffffffff821115614b4d57614b4d614df9565b50601f01601f191660200190565b600080821280156001600160ff1b0384900385131615614b7d57614b7d614da1565b600160ff1b8390038412811615614b9657614b96614da1565b50500190565b60008219821115614baf57614baf614da1565b500190565b600063ffffffff808316818516808303821115614bd357614bd3614da1565b01949350505050565b600082614beb57614beb614db7565b500490565b600063ffffffff80841680614c0757614c07614db7565b92169190910492915050565b60006001600160a01b0382811684821681151582840482111615614c3957614c39614da1565b02949350505050565b6000816000190483118215151615614c5c57614c5c614da1565b500290565b600063ffffffff80831681851681830481118215151615614c3957614c39614da1565b60008083128015600160ff1b850184121615614ca257614ca2614da1565b6001600160ff1b0384018313811615614cbd57614cbd614da1565b50500390565b600082821015614cd557614cd5614da1565b500390565b600063ffffffff83811690831681811015614cf757614cf7614da1565b039392505050565b60005b83811015614d1a578181015183820152602001614d02565b83811115611e065750506000910152565b600181811c90821680614d3f57607f821691505b60208210811415614d6057634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415614d7a57614d7a614da1565b5060010190565b600060ff821660ff811415614d9857614d98614da1565b60010192915050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146129e557600080fd5b80151581146129e557600080fd5b6001600160e01b0319811681146129e557600080fd5b63ffffffff811681146129e557600080fdfea2646970667358221220da0ad3258994f7770df48129c19337805ef590f1281c528c08c5b258ec87a40964736f6c634300080600330000000000000000000000000dbcca406b622ed1f455abc9560d51aa0c81560f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061021c5760003560e01c80635f64b55b11610125578063a57351f9116100ad578063c96ed6391161007c578063c96ed63914610521578063e111e3e714610534578063e985e9c514610547578063eb391aa81461055a578063f8fd31001461056d57600080fd5b8063a57351f9146104c8578063b0d2847c146104d0578063b88d4fde146104fb578063c87b56dd1461050e57600080fd5b80637e29e8a4116100f45780637e29e8a41461046a5780638119c06514610492578063833e46181461049a57806395d89b41146104ad578063a22cb465146104b557600080fd5b80635f64b55b1461041e5780636352211e1461043157806370a08231146104445780637a828b281461045757600080fd5b806323b872dd116101a857806332e8af461161017757806332e8af46146103865780633d783717146103c55780633fcf01e6146103d857806342842e0e146103eb5780635b6fd01d146103fe57600080fd5b806323b872dd1461032557806326742bed146103385780632a52803e1461034b5780632f84cbf51461037157600080fd5b8063095ea7b3116101ef578063095ea7b31461029e5780630ad82b2f146102b15780630b74e852146102c45780630f7d0f2b146102d75780630fc63d101461031257600080fd5b806301ffc9a714610221578063022c0d9f1461024957806306fdde031461025e578063081812fc14610273575b600080fd5b61023461022f366004614294565b61059b565b60405190151581526020015b60405180910390f35b61025c610257366004614588565b6105ed565b005b610266610cff565b604051610240919061495a565b610286610281366004614459565b610d91565b6040516001600160a01b039091168152602001610240565b61025c6102ac3660046141d6565b610e26565b600254610286906001600160a01b031681565b61025c6102d236600461448b565b610f3c565b6102fd6102e5366004614626565b60066020526000908152604090205463ffffffff1681565b60405163ffffffff9091168152602001610240565b600354610286906001600160a01b031681565b61025c61033336600461409f565b610fee565b61025c6103463660046144b0565b61101f565b6102fd610359366004614626565b600c6020526000908152604090205463ffffffff1681565b610379611052565b6040516102409190614a47565b6103b7610394366004614661565b600560209081526000938452604080852082529284528284209052825290205481565b604051908152602001610240565b61025c6103d33660046144f2565b6110f4565b61025c6103e6366004614601565b611545565b61025c6103f936600461409f565b6115a1565b61041161040c366004614459565b6115bc565b6040516102409190614a5a565b600454610286906001600160a01b031681565b61028661043f366004614459565b6117da565b6103b7610452366004614049565b611851565b61025c610465366004614459565b6118d8565b61047d610478366004614202565b611a2e565b60408051928352602083019190915201610240565b61025c611c1f565b6102346104a8366004614626565b611c3d565b610266611c53565b61025c6104c336600461414c565b611c62565b6102fd611d27565b6103b76104de366004614643565b600b60209081526000928352604080842090915290825290205481565b61025c6105093660046140e0565b611dd4565b61026661051c366004614459565b611e0c565b61025c61052f3660046145d3565b611f0d565b6103b7610542366004614459565b611fba565b610234610555366004614066565b6120ea565b6103b761056836600461417a565b612118565b6003546001600160a01b0390811660009081526008602052604080822054600454909316825290205461047d565b60006001600160e01b031982166380ac58cd60e01b14806105cc57506001600160e01b03198216635b5e139f60e01b145b806105e757506301ffc9a760e01b6001600160e01b03198316145b92915050565b600260005414156106195760405162461bcd60e51b815260040161061090614a10565b60405180910390fd5b600260008181559054604080516303956f4760e21b815290516001600160a01b0390921691630e55bd1c91600480820192608092909190829003018186803b15801561066457600080fd5b505afa158015610678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069c91906143d4565b90508060200151156106c1576040516313d0ff5960e31b815260040160405180910390fd5b6106c9613f7d565b6000806106de846040015185606001516123d5565b6020830151929550909350915060ff1661070b576040516346f8d7b160e11b815260040160405180910390fd5b4260005b846020015160ff168110156108d95760008560000151828151811061073657610736614de3565b602002602001015160000151905060008660000151838151811061075c5761075c614de3565b602002602001015160200151905060008760000151848151811061078257610782614de3565b60200260200101516040015111806107bb57506000876000015184815181106107ad576107ad614de3565b602002602001015160600151115b156108ae57600354875180516107fc9285926001600160a01b0390911691879081106107e9576107e9614de3565b602002602001015160400151888561282b565b600454875180516108389285926001600160a01b03909116918790811061082557610825614de3565b602002602001015160600151898561282b565b63ffffffff8281166000908152600660205260409020805463ffffffff19169183169190911790558161086b8186614bf0565b610876906001614bb4565b6108809190614c61565b63ffffffff8381166000908152600c60205260409020805463ffffffff1916929091169190911790556108c4565b6108c2600963ffffffff8085169061289e16565b505b505080806108d190614d66565b91505061070f565b5050505080604001518611806108f25750806060015185115b156109105760405163bb55fd2760e01b815260040160405180910390fd5b60008160c0015182604001516109269190614cc3565b905060008260e00151836060015161093e9190614cc3565b600354610160850151919250899189916001600160a01b039182169116141561098b576101208501516109719083614b9c565b9150846101000151836109849190614b9c565b92506109b1565b61012085015161099b9082614b9c565b9050846101000151846109ae9190614b9c565b93505b81156109ce576003546109ce906001600160a01b031689846128b1565b80156109eb576004546109eb906001600160a01b031689836128b1565b5050845115610ac557856001600160a01b0316633d33803c33600360009054906101000a90046001600160a01b0316600460009054906101000a90046001600160a01b03168c8c600360009054906101000a90046001600160a01b03166001600160a01b03168a61016001516001600160a01b0316148a61012001518b61010001518e6040518a63ffffffff1660e01b8152600401610a9299989796959493929190614836565b600060405180830381600087803b158015610aac57600080fd5b505af1158015610ac0573d6000803e3d6000fd5b505050505b6003546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015610b0957600080fd5b505afa158015610b1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b419190614472565b600480546040516370a0823160e01b815230928101929092529192506000916001600160a01b0316906370a082319060240160206040518083038186803b158015610b8b57600080fd5b505afa158015610b9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc39190614472565b90508460c0015184610bd59190614b9c565b821080610bef575060e0850151610bec9084614b9c565b81105b15610c0d5760405163d562e29f60e01b815260040160405180910390fd5b6003546001600160a01b039081166000908152600860205260408082208790556004549092168152908120849055610c458584614cc3565b90506000610c538584614cc3565b90508115610c76578751600354610c76916001600160a01b0390911690846128b1565b8015610c97578751600454610c97916001600160a01b0390911690836128b1565b896001600160a01b0316336001600160a01b03167f985cca3348e25076ac7404c08e594a15b8529496ecbf6b08af7fa24c5d4e742b8e8e8c604001518c604051610ce49493929190614ad7565b60405180910390a35050600160005550505050505050505050565b6060600d8054610d0e90614d2b565b80601f0160208091040260200160405190810160405280929190818152602001828054610d3a90614d2b565b8015610d875780601f10610d5c57610100808354040283529160200191610d87565b820191906000526020600020905b815481529060010190602001808311610d6a57829003601f168201915b5050505050905090565b6000818152600f60205260408120546001600160a01b0316610e0a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610610565b506000908152601160205260409020546001600160a01b031690565b6000610e31826117da565b9050806001600160a01b0316836001600160a01b03161415610e9f5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610610565b336001600160a01b0382161480610ebb5750610ebb81336120ea565b610f2d5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610610565b610f378383612914565b505050565b60026000541415610f5f5760405162461bcd60e51b815260040161061090614a10565b6002600055610f6d82612982565b60008281526013602081815260408084205463ffffffff600160401b820481168652600684529185205487865293909252610fb392811691640100000000900416614cda565b905063ffffffff8116610fd95760405163d43195c360e01b815260040160405180910390fd5b610fe48383836129e8565b5050600160005550565b610ff83382612a15565b6110145760405162461bcd60e51b8152600401610610906149bf565b610f37838383612aec565b600260005414156110425760405162461bcd60e51b815260040161061090614a10565b6002600055610fe48383836129e8565b61105a613f7d565b600254604080516303956f4760e21b815290516000926001600160a01b031691630e55bd1c916004808301926080929190829003018186803b15801561109f57600080fd5b505afa1580156110b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d791906143d4565b90506110eb816040015182606001516123d5565b50909392505050565b600260005414156111175760405162461bcd60e51b815260040161061090614a10565b600260005584158015611128575083155b156111465760405163125f2a8360e21b815260040160405180910390fd5b6002546040805163b1d0a33760e01b815290516000926001600160a01b03169163b1d0a337916004808301926060929190829003018186803b15801561118b57600080fd5b505afa15801561119f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c39190614362565b90508060200151156111e8576040516313d0ff5960e31b815260040160405180910390fd5b6003546001600160a01b039081166000908152600860205260408082205460045490931682529020548188118061121e57508087115b1561123c5760405163bb55fd2760e01b815260040160405180910390fd5b600080891161124c57600061125a565b61125a84604001518a612c8c565b9050600080891161126c57600061127a565b61127a85604001518a612c8c565b9050891561129957600354611299906001600160a01b0316898c6128b1565b88156112b6576004546112b6906001600160a01b0316898b6128b1565b876001600160a01b031663f62c404133600360009054906101000a90046001600160a01b0316600460009054906101000a90046001600160a01b03168e8e88888f8f6040518a63ffffffff1660e01b815260040161131c9998979695949392919061489b565b600060405180830381600087803b15801561133657600080fd5b505af115801561134a573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152306004820152600093506001600160a01b0390911691506370a082319060240160206040518083038186803b15801561139457600080fd5b505afa1580156113a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cc9190614472565b600480546040516370a0823160e01b815230928101929092529192506000916001600160a01b0316906370a082319060240160206040518083038186803b15801561141657600080fd5b505afa15801561142a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144e9190614472565b905061145a8487614b9c565b82108061146f575061146c8386614b9c565b81105b1561148d5760405163d562e29f60e01b815260040160405180910390fd5b60006114998784614cc3565b905060006114a78784614cc3565b905081156114ca5788516003546114ca916001600160a01b0390911690846128b1565b80156114eb5788516004546114eb916001600160a01b0390911690836128b1565b505060408088015181518e8152602081018e905263ffffffff909116918101919091526001600160a01b038b169033907f891529dc768bd9c3ee9afa282dfdaf4ddffed309e6dae2af016a804e1d05473390606001610ce4565b600260005414156115685760405162461bcd60e51b815260040161061090614a10565b6002600090815582815260136020526040902054611598908390600160601b90046001600160a01b0316836129e8565b50506001600055565b610f3783838360405180602001604052806000815250611dd4565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152600082815260136020908152604091829020825160c081018452815463ffffffff80821683526401000000008204811694830194909452600160401b810490931693810193909352600160601b9091046001600160a01b031660608301526001015460ff81161515608083018190526101009091046001600160f81b031660a0830152611695576004546001600160a01b03166116a2565b6003546001600160a01b03165b6001600160a01b0316825260808101516116c7576003546001600160a01b03166116d4565b6004546001600160a01b03165b6001600160a01b031660208301526040808201805163ffffffff90811692850192909252511661170557600061172f565b805160408083015163ffffffff90811660009081526006602052919091205461172f929116614cda565b63ffffffff16606083015261174383612cc5565b608083015260408082015163ffffffff908116600090815260066020908152929020549183015191811691161161177b5760006117aa565b60408082015163ffffffff90811660009081526006602090815292902054918301516117aa9290911690614cda565b63ffffffff1660a08301526117be83612efd565b60c0830152606001516001600160a01b031660e0820152919050565b6000818152600f60205260408120546001600160a01b0316806105e75760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610610565b60006001600160a01b0382166118bc5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610610565b506001600160a01b031660009081526010602052604090205490565b600260005414156118fb5760405162461bcd60e51b815260040161061090614a10565b600260005561190981612982565b600061191482612cc5565b9050600061192183612efd565b9050600061192e84612f97565b9050600061193b85612fd2565b90506119468561300d565b61194f8561316a565b8315611997576001600160a01b0381166000908152600860205260408120805486929061197d908490614cc3565b9091555061199790506001600160a01b03821633866128b1565b82156119df576001600160a01b038216600090815260086020526040812080548592906119c5908490614cc3565b909155506119df90506001600160a01b03831633856128b1565b604080518681526020810185905290810185905233907f9b863fe095959050c7ed1d85b45c0922e5667da8f82299a3eebf4cbc584f79ec9060600160405180910390a250506001600055505050565b60008060026000541415611a545760405162461bcd60e51b815260040161061090614a10565b600260009081555b83811015611b31576000858583818110611a7857611a78614de3565b905060200201359050611a8a81612982565b6000611a9582612cc5565b60008381526013602052604090206001015490915060ff1615611ac357611abc8185614b9c565b9350611ad0565b611acd8186614b9c565b94505b5060008181526013602081815260408084208054600160401b810463ffffffff9081168752600685529286205496909552929091529290921663ffffffff19909116178155600101805460ff16905580611b2981614d66565b915050611a5c565b508115611b7e576003546001600160a01b031660009081526008602052604081208054849290611b62908490614cc3565b9091555050600354611b7e906001600160a01b031633846128b1565b8015611bca576004546001600160a01b031660009081526008602052604081208054839290611bae908490614cc3565b9091555050600454611bca906001600160a01b031633836128b1565b336001600160a01b03167f4fd01d816751d768469443eeebd391cbef7fae3c437c75730c0e1b9a3c174eee85858585604051611c099493929190614911565b60405180910390a2600160005590939092509050565b611c3b60008033604051806020016040528060008152506105ed565b565b60006105e7600963ffffffff8085169061320516565b6060600e8054610d0e90614d2b565b6001600160a01b038216331415611cbb5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610610565b3360008181526012602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b63ffffffff4260005b611d3a600961321d565b811015611dcf576000611d4e600983613227565b63ffffffff8082166000908152600c6020526040902054919250808516911611611d7c576000935050505090565b63ffffffff8082166000908152600c60205260408120549091611da191869116614cda565b90508463ffffffff168163ffffffff161015611dbb578094505b505080611dc781614d66565b915050611d30565b505090565b611dde3383612a15565b611dfa5760405162461bcd60e51b8152600401610610906149bf565b611e0684848484613233565b50505050565b60025460408051630442675760e41b815290516060926001600160a01b0316916344267570916004808301926020929190829003018186803b158015611e5157600080fd5b505afa158015611e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8991906142ce565b60405163e9dc637560e01b8152306004820152602481018490526001600160a01b03919091169063e9dc63759060440160006040518083038186803b158015611ed157600080fd5b505afa158015611ee5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105e791908101906142eb565b60026000541415611f305760405162461bcd60e51b815260040161061090614a10565b600260005581611f5357604051631f2a200560e01b815260040160405180910390fd5b63ffffffff8116611f7757604051633c92196b60e11b815260040160405180910390fd5b6000611f8284612efd565b90506000611f908483614b9c565b9050611fae858284611fa863ffffffff881683614bdc565b87613266565b50506001600055505050565b600060026000541415611fdf5760405162461bcd60e51b815260040161061090614a10565b6002600055611fed82612982565b611ff682612cc5565b60008381526013602081815260408084208054600160401b810463ffffffff9081168752600685529286205489875294909352921663ffffffff19909116178155600101805460ff16905590915061204d83612fd2565b6001600160a01b03811660009081526008602052604081208054929350849290919061207a908490614cc3565b9091555061209490506001600160a01b03821633846128b1565b604080518481526001600160a01b038316602082015290810183905233907f7d3fe0088fc0b393d0c7629c8d9df3043bd33e8fc1a953dfa251ab201c6ebc389060600160405180910390a2506001600055919050565b6001600160a01b03918216600090815260126020908152604080832093909416825291909152205460ff1690565b60006002600054141561213d5760405162461bcd60e51b815260040161061090614a10565b60026000556003546001600160a01b0386811691161480159061216e57506004546001600160a01b03868116911614155b1561218c5760405163c1ab6dc160e01b815260040160405180910390fd5b63ffffffff83166121b057604051633c92196b60e11b815260040160405180910390fd5b6121c4600963ffffffff8085169061320516565b15801561224f5750600254604051639eceefe160e01b815263ffffffff841660048201526001600160a01b0390911690639eceefe19060240160206040518083038186803b15801561221557600080fd5b505afa158015612229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224d9190614277565b155b1561226d576040516305fabb6160e41b815260040160405180910390fd5b6003546000906001600160a01b03878116911614612296576004546001600160a01b03166122a3565b6003546001600160a01b03165b905060006122b763ffffffff861687614c13565b6001600160a01b0390811691506122d2908316333084613414565b6001600160a01b038716600090815260086020526040812080548392906122fa908490614b9c565b925050819055506001601460008282546123149190614b9c565b925050819055506123273360145461344c565b61233b600963ffffffff8087169061346a16565b506000806123506014548a8a8a60008b613476565b601454604080519182526001600160a01b038d811660208401528c169082015263ffffffff8084166060830152808a166080830152821660a0820152919350915033907f7bd589f2fd6a39318e78fbbbadbcd21ce8c1306fc0ecb4d7a49a7e4be9c67e639060c00160405180910390a250506014546001600055979650505050505050565b6123dd613f7d565b6000806000806000806123ee61367d565b9150915060005b8160ff168110156124685782818151811061241257612412614de3565b602002602001015160400151856124299190614b9c565b945082818151811061243d5761243d614de3565b602002602001015160600151846124549190614b9c565b93508061246081614d66565b9150506123f5565b5090865260ff1660208601526004805460015460035460405163a513355f60e01b81526001600160a01b0393841694810194909452600160701b9091046001600160701b031660248401528116604483015287169063a513355f9060640160206040518083038186803b1580156124de57600080fd5b505afa1580156124f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125169190614472565b6080860181905260015461253d906001600160701b0380821691600160701b900416614c42565b6125479190614bdc565b60a0860152608085015161255c908890612c8c565b856080015161256b9190614cc3565b935061257b878660a00151612c8c565b8560a0015161258a9190614cc3565b6001549093506000906125ae90600160701b90046001600160701b03168387613800565b6001549091506000906125cb906001600160701b03168587613800565b905060006125fc6001600e9054906101000a90046001600160701b03166001600160701b0316858a60800151613800565b9050848110156126d1576004546001600160a01b039081166101408a01526003541661016089015261262e8a82612c8c565b60c089015261263d8a85612c8c565b60e0890181905284906126509084614b9c565b61265a9190614cc3565b61010089015260c088015161266f8487614cc3565b6126799190614cc3565b61012089018190526003546001600160a01b03166000908152600860205260409020546126a69190614cc3565b6040808a01919091526004546001600160a01b0316600090815260086020522054606089015261281f565b848111156127cc576003546001600160a01b039081166101408a0152600454166101608901526127018a86612c8c565b60c0890152608088015160015461273d918c9161272e90600160701b90046001600160701b031689614c42565b6127389190614bdc565b612c8c565b60e089015260c088015185906127539085614b9c565b61275d9190614cc3565b61010089015260e08801516127728386614cc3565b61277c9190614cc3565b61012089019081526003546001600160a01b03908116600090815260086020818152604080842054818f0152945160045490941683525291909120546127c29190614cc3565b606089015261281f565b6127d68a86612c8c565b60c08901526127e58a85612c8c565b60e08901526003546001600160a01b03908116600090815260086020818152604080842054818e0152600454909416835252205460608901525b50505050509250925092565b63ffffffff85166000908152600b602090815260408083206001600160a01b0388168452909152902083905561286385858385613817565b63ffffffff94851660009081526005602090815260408083206001600160a01b03909716835295815285822092909616815294525050812055565b60006128aa83836138ab565b9392505050565b6040516001600160a01b038316602482015260448101829052610f3790849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526139a5565b600081815260116020526040902080546001600160a01b0319166001600160a01b0384169081179091558190612949826117da565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600081815260136020526040902054600160601b90046001600160a01b03166129be5760405163673f032f60e11b815260040160405180910390fd5b6129c83382612a15565b6129e557604051635c427cd960e01b815260040160405180910390fd5b50565b610f37836129fc63ffffffff841685614c13565b6001600160a01b0316612a0e86612efd565b8585613266565b6000818152600f60205260408120546001600160a01b0316612a8e5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610610565b6000612a99836117da565b9050806001600160a01b0316846001600160a01b03161480612ad45750836001600160a01b0316612ac984610d91565b6001600160a01b0316145b80612ae45750612ae481856120ea565b949350505050565b826001600160a01b0316612aff826117da565b6001600160a01b031614612b675760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610610565b6001600160a01b038216612bc95760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610610565b612bd4600082612914565b6001600160a01b0383166000908152601060205260408120805460019290612bfd908490614cc3565b90915550506001600160a01b0382166000908152601060205260408120805460019290612c2b908490614b9c565b90915550506000818152600f602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600154600090606490600160e01b900462ffffff16612cb163ffffffff861685614c42565b612cbb9190614bdc565b6128aa9190614bdc565b6000818152601360209081526040808320815160c081018352815463ffffffff80821683526401000000008204811695830195909552600160401b810490941692810192909252600160601b9092046001600160a01b0316606082015260019091015460ff81161515608083018190526101009091046001600160f81b031660a08301528290612d60576004546001600160a01b0316612d6d565b6003546001600160a01b03165b6040808401805163ffffffff9081166000908152600760209081528482206001600160a01b03871683528152848220818901519451841683526006909152938120549495509384928216911610612dc8578460200151612de7565b60408086015163ffffffff908116600090815260066020529190912054165b63ffffffff908116825260208083019390935260409182016000908120548784015183168252600785528382206001600160a01b03881683528552838220885190931682529190935290822054909250612e419083614cc3565b905060008460800151612e6657600154600160701b90046001600160701b0316612e73565b6001546001600160701b03165b6001600160701b03169050600080612e988488606001516001600160a01b0316613a77565b91509150600082612eca5760608801516001600160a01b0316612ebb8587614bdc565b612ec59190614c42565b612ed4565b612ed48483614bdc565b90508760a001516001600160f81b031681612eef9190614b9c565b9a9950505050505050505050565b60008181526013602081815260408084205463ffffffff600160401b8204811686526006845291852054868652939092529182169164010000000090910416818111612f4d575060009392505050565b600084815260136020526040902054600160601b90046001600160a01b0316612f768383614cda565b63ffffffff16612f869190614c13565b6001600160a01b0316949350505050565b60008181526013602052604081206001015460ff16612fc1576004546001600160a01b03166105e7565b50506003546001600160a01b031690565b60008181526013602052604081206001015460ff16612ffc576003546001600160a01b03166105e7565b50506004546001600160a01b031690565b60008181526013602090815260408083205463ffffffff600160401b820481168086526006909452919093205491926401000000009004811691168082111561315057600084815260136020526040812054600160601b90046001600160a01b03169061307986612f97565b63ffffffff861660009081526005602090815260408083206001600160a01b03851684529091528120919250601384900b91906130b7866001614bb4565b63ffffffff1663ffffffff16815260200190815260200160002060008282546130e09190614c84565b909155505063ffffffff851660009081526005602090815260408083206001600160a01b03851684529091528120601384900b9161311f876001614bb4565b63ffffffff1663ffffffff16815260200190815260200160002060008282546131489190614b5b565b909155505050505b505050600090815260136020526040812081815560010155565b6000613175826117da565b9050613182600083612914565b6001600160a01b03811660009081526010602052604081208054600192906131ab908490614cc3565b90915550506000828152600f602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b600081815260018301602052604081205415156128aa565b60006105e7825490565b60006128aa8383613abf565b61323e848484612aec565b61324a84848484613ae9565b611e065760405162461bcd60e51b81526004016106109061496d565b61326f85612982565b600061327a86612f97565b9050600061328787612cc5565b90506001600160f81b038111156132b157604051630930608960e11b815260040160405180910390fd5b600087815260136020526040902054600160401b900463ffffffff166132d68861300d565b6000806132e78a8689898888613476565b91509150878911156133515761331433306133028b8d614cc3565b6001600160a01b038916929190613414565b61331e888a614cc3565b6001600160a01b03861660009081526008602052604081208054909190613346908490614b9c565b909155506133b09050565b878910156133b0576133638989614cc3565b6001600160a01b0386166000908152600860205260408120805490919061338b908490614cc3565b909155506133b090503361339f8b8b614cc3565b6001600160a01b03881691906128b1565b604080518b81526001600160a01b038916602082015263ffffffff8481168284015283166060820152905133917ffa7c989fea93eb42545a9e7a74f2b8d966813e216a1dd7b59562af10dba259b4919081900360800190a250505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611e069085906323b872dd60e01b906084016128dd565b613466828260405180602001604052806000815250613bf6565b5050565b60006128aa8383613c29565b6000806001600160a01b0386166134a0576040516314cee7cd60e11b815260040160405180910390fd5b63ffffffff808416600090815260066020526040902054166134c3816001614bb4565b92506134cf8682614bb4565b63ffffffff80861660009081526005602090815260408083206001600160a01b038e16845282528083209388168352929052908120805492945060138a900b9290919061351d908490614b5b565b909155505063ffffffff841660009081526005602090815260408083206001600160a01b038c1684529091528120601389900b9161355c856001614bb4565b63ffffffff1663ffffffff16815260200190815260200160002060008282546135859190614c84565b90915550506040805160c08101825263ffffffff928316815283831660208083019182529684168284019081526001600160a01b039a8b16606084019081526003549c8c169c8c169c909c14608084019081526001600160f81b03998a1660a0850190815260009e8f52601390995293909c209151825491519c519b51909a16600160601b026bffffffffffffffffffffffff9b8516600160401b029b909b1667ffffffffffffffff9c85166401000000000267ffffffffffffffff199092169a90941699909917989098179990991617969096178555955190519091166101000290151560ff161760019290920191909155509091565b606060008061368c600961321d565b90508067ffffffffffffffff8111156136a7576136a7614df9565b6040519080825280602002602001820160405280156136f957816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816136c55790505b50925060005b818110156137fa576000613714600983613227565b90504263ffffffff8281166000908152600c60205260409020549181169116116137e75763ffffffff808216600090815260066020526040812054909161375d91166001614bb4565b6040805160808101825263ffffffff80861682528316602082015260035492935091908201906137989085906001600160a01b031685613c78565b81526004546020909101906137b89085906001600160a01b031685613c78565b905286866137c581614d81565b975060ff16815181106137da576137da614de3565b6020026020010181905250505b50806137f281614d66565b9150506136ff565b50509091565b60008361380d8385614c42565b612ae49190614bdc565b63ffffffff841660009081526007602090815260408083206001600160a01b038716845290915281208161384c600186614cda565b63ffffffff168152602081019190915260400160002054905061386f8282614b9c565b63ffffffff95861660009081526007602090815260408083206001600160a01b0390981683529681528682209590971681529390955250502055565b600081815260018301602052604081205480156139945760006138cf600183614cc3565b85549091506000906138e390600190614cc3565b905081811461394857600086600001828154811061390357613903614de3565b906000526020600020015490508087600001848154811061392657613926614de3565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061395957613959614dcd565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105e7565b60009150506105e7565b5092915050565b60006139fa826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613ccb9092919063ffffffff16565b805190915015610f375780806020019051810190613a189190614277565b610f375760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610610565b60008083613a8b5750600190506000613ab8565b83830283858281613a9e57613a9e614db7565b0414613ab1576000809250925050613ab8565b6001925090505b9250929050565b6000826000018281548110613ad657613ad6614de3565b9060005260206000200154905092915050565b60006001600160a01b0384163b15613beb57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290613b2d9033908990889088906004016147f9565b602060405180830381600087803b158015613b4757600080fd5b505af1925050508015613b77575060408051601f3d908101601f19168201909252613b74918101906142b1565b60015b613bd1573d808015613ba5576040519150601f19603f3d011682016040523d82523d6000602084013e613baa565b606091505b508051613bc95760405162461bcd60e51b81526004016106109061496d565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612ae4565b506001949350505050565b613c008383613cda565b613c0d6000848484613ae9565b610f375760405162461bcd60e51b81526004016106109061496d565b6000818152600183016020526040812054613c70575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105e7565b5060006105e7565b63ffffffff92831660008181526005602090815260408083206001600160a01b0390961680845295825280832094909616825292835284812054918152600b835284812093815292909152919020540190565b6060612ae48484600085613e1c565b6001600160a01b038216613d305760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610610565b6000818152600f60205260409020546001600160a01b031615613d955760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610610565b6001600160a01b0382166000908152601060205260408120805460019290613dbe908490614b9c565b90915550506000818152600f602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606082471015613e7d5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610610565b843b613ecb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610610565b600080866001600160a01b03168587604051613ee791906147dd565b60006040518083038185875af1925050503d8060008114613f24576040519150601f19603f3d011682016040523d82523d6000602084013e613f29565b606091505b5091509150613f39828286613f44565b979650505050505050565b60608315613f535750816128aa565b825115613f635782518084602001fd5b8160405162461bcd60e51b8152600401610610919061495a565b60405180610180016040528060608152602001600060ff168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b600082601f83011261400457600080fd5b813561401761401282614b33565b614b02565b81815284602083860101111561402c57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561405b57600080fd5b81356128aa81614e0f565b6000806040838503121561407957600080fd5b823561408481614e0f565b9150602083013561409481614e0f565b809150509250929050565b6000806000606084860312156140b457600080fd5b83356140bf81614e0f565b925060208401356140cf81614e0f565b929592945050506040919091013590565b600080600080608085870312156140f657600080fd5b843561410181614e0f565b9350602085013561411181614e0f565b925060408501359150606085013567ffffffffffffffff81111561413457600080fd5b61414087828801613ff3565b91505092959194509250565b6000806040838503121561415f57600080fd5b823561416a81614e0f565b9150602083013561409481614e24565b6000806000806080858703121561419057600080fd5b843561419b81614e0f565b935060208501356141ab81614e0f565b925060408501356141bb81614e48565b915060608501356141cb81614e48565b939692955090935050565b600080604083850312156141e957600080fd5b82356141f481614e0f565b946020939093013593505050565b6000806020838503121561421557600080fd5b823567ffffffffffffffff8082111561422d57600080fd5b818501915085601f83011261424157600080fd5b81358181111561425057600080fd5b8660208260051b850101111561426557600080fd5b60209290920196919550909350505050565b60006020828403121561428957600080fd5b81516128aa81614e24565b6000602082840312156142a657600080fd5b81356128aa81614e32565b6000602082840312156142c357600080fd5b81516128aa81614e32565b6000602082840312156142e057600080fd5b81516128aa81614e0f565b6000602082840312156142fd57600080fd5b815167ffffffffffffffff81111561431457600080fd5b8201601f8101841361432557600080fd5b805161433361401282614b33565b81815285602083850101111561434857600080fd5b614359826020830160208601614cff565b95945050505050565b60006060828403121561437457600080fd5b6040516060810181811067ffffffffffffffff8211171561439757614397614df9565b60405282516143a581614e0f565b815260208301516143b581614e24565b602082015260408301516143c881614e48565b60408201529392505050565b6000608082840312156143e657600080fd5b6040516080810181811067ffffffffffffffff8211171561440957614409614df9565b604052825161441781614e0f565b8152602083015161442781614e24565b6020820152604083015161443a81614e48565b6040820152606083015161444d81614e0f565b60608201529392505050565b60006020828403121561446b57600080fd5b5035919050565b60006020828403121561448457600080fd5b5051919050565b6000806040838503121561449e57600080fd5b82359150602083013561409481614e0f565b6000806000606084860312156144c557600080fd5b8335925060208401356144d781614e0f565b915060408401356144e781614e48565b809150509250925092565b60008060008060006080868803121561450a57600080fd5b8535945060208601359350604086013561452381614e0f565b9250606086013567ffffffffffffffff8082111561454057600080fd5b818801915088601f83011261455457600080fd5b81358181111561456357600080fd5b89602082850101111561457557600080fd5b9699959850939650602001949392505050565b6000806000806080858703121561459e57600080fd5b843593506020850135925060408501356145b781614e0f565b9150606085013567ffffffffffffffff81111561413457600080fd5b6000806000606084860312156145e857600080fd5b833592506020840135915060408401356144e781614e48565b6000806040838503121561461457600080fd5b82359150602083013561409481614e48565b60006020828403121561463857600080fd5b81356128aa81614e48565b6000806040838503121561465657600080fd5b823561408481614e48565b60008060006060848603121561467657600080fd5b833561468181614e48565b925060208401356144d781614e0f565b600081518084526020808501945080840160005b838110156146ea578151805163ffffffff90811689528482015116848901526040808201519089015260609081015190880152608090960195908201906001016146a5565b509495945050505050565b6000815180845261470d816020860160208601614cff565b601f01601f19169290920160200192915050565b6000610180825181855261473782860182614691565b915050602083015161474e602086018260ff169052565b5060408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e0850152610100808401518186015250610120808401518186015250610140808401516147be828701826001600160a01b03169052565b5050610160928301516001600160a01b03169390920192909252919050565b600082516147ef818460208701614cff565b9190910192915050565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061482c908301846146f5565b9695505050505050565b600061012060018060a01b03808d168452808c166020850152808b1660408501525088606084015287608084015286151560a08401528560c08401528460e08401528061010084015261488b818401856146f5565b9c9b505050505050505050505050565b6001600160a01b038a81168252898116602083015288166040820152606081018790526080810186905260a0810185905260c0810184905261010060e08201819052810182905260006101208385828501376000838501820152601f909301601f19169091019091019998505050505050505050565b6060808252810184905260006001600160fb1b0385111561493157600080fd5b8460051b8087608085013760009083016080019081526020830194909452506040015292915050565b6020815260006128aa60208301846146f5565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020815260006128aa6020830184614721565b60006101008201905060018060a01b0380845116835280602085015116602084015250604083015163ffffffff8082166040850152806060860151166060850152608085015160808501528060a08601511660a0850152505060c083015160c083015260e083015161399e60e08401826001600160a01b03169052565b84815283602082015263ffffffff8316604082015260806060820152600061482c6080830184614721565b604051601f8201601f1916810167ffffffffffffffff81118282101715614b2b57614b2b614df9565b604052919050565b600067ffffffffffffffff821115614b4d57614b4d614df9565b50601f01601f191660200190565b600080821280156001600160ff1b0384900385131615614b7d57614b7d614da1565b600160ff1b8390038412811615614b9657614b96614da1565b50500190565b60008219821115614baf57614baf614da1565b500190565b600063ffffffff808316818516808303821115614bd357614bd3614da1565b01949350505050565b600082614beb57614beb614db7565b500490565b600063ffffffff80841680614c0757614c07614db7565b92169190910492915050565b60006001600160a01b0382811684821681151582840482111615614c3957614c39614da1565b02949350505050565b6000816000190483118215151615614c5c57614c5c614da1565b500290565b600063ffffffff80831681851681830481118215151615614c3957614c39614da1565b60008083128015600160ff1b850184121615614ca257614ca2614da1565b6001600160ff1b0384018313811615614cbd57614cbd614da1565b50500390565b600082821015614cd557614cd5614da1565b500390565b600063ffffffff83811690831681811015614cf757614cf7614da1565b039392505050565b60005b83811015614d1a578181015183820152602001614d02565b83811115611e065750506000910152565b600181811c90821680614d3f57607f821691505b60208210811415614d6057634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415614d7a57614d7a614da1565b5060010190565b600060ff821660ff811415614d9857614d98614da1565b60010192915050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146129e557600080fd5b80151581146129e557600080fd5b6001600160e01b0319811681146129e557600080fd5b63ffffffff811681146129e557600080fdfea2646970667358221220da0ad3258994f7770df48129c19337805ef590f1281c528c08c5b258ec87a40964736f6c63430008060033

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.