ETH Price: $2,617.96 (-0.27%)

Token

ERC20 ***
 

Overview

Max Total Supply

0 ERC20 ***

Holders

59

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Filtered by Token Holder
vinims.eth
Balance
1 ERC20 ***
0xc8a44e444e7dac7744bf2fa35256f7300e9c04fb
Loading...
Loading
Loading...
Loading
Loading...
Loading

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

Contract Source Code Verified (Exact Match)

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

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

0000000000000000000000000dbcca406b622ed1f455abc9560d51aa0c81560f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : _globalParameters (address): 0x0dBCcA406b622ed1f455abC9560d51Aa0C81560F
Arg [1] : _tokenA (address): 0x6B175474E89094C44Da98b954EedeAC495271d0F
Arg [2] : _tokenB (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000000dbcca406b622ed1f455abc9560d51aa0c81560f
Arg [1] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2


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.