ETH Price: $3,615.89 (-2.65%)

Token

: Mean Finance - DCA Position (MF-DCA-P)

Overview

Max Total Supply

19,632 MF-DCA-P

Holders

12,757

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Balance
1 MF-DCA-P
0x24d9Ad4603a20512D6Ac47a1404aDb130180062d
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
DCAPermissionsManager

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 9999 runs

Other Settings:
default evmVersion
File 1 of 20 : DCAPermissionsManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import '@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol';
import '../interfaces/IDCAHub.sol';
import '../interfaces/IDCAPermissionManager.sol';
import '../libraries/PermissionMath.sol';
import '../utils/Governable.sol';

// Note: ideally, this would be part of the DCAHub. However, since we've reached the max bytecode size, we needed to make it its own contract
contract DCAPermissionsManager is ERC721, EIP712, Governable, IDCAPermissionManager {
  struct TokenPermission {
    // The actual permissions
    uint8 permissions;
    // The block number when it was last updated
    uint248 lastUpdated;
  }

  using PermissionMath for Permission[];
  using PermissionMath for uint8;

  /// @inheritdoc IDCAPermissionManager
  bytes32 public constant PERMIT_TYPEHASH = keccak256('Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)');
  /// @inheritdoc IDCAPermissionManager
  bytes32 public constant PERMISSION_PERMIT_TYPEHASH =
    keccak256(
      'PermissionPermit(PermissionSet[] permissions,uint256 tokenId,uint256 nonce,uint256 deadline)PermissionSet(address operator,uint8[] permissions)'
    );
  /// @inheritdoc IDCAPermissionManager
  bytes32 public constant MULTI_PERMISSION_PERMIT_TYPEHASH =
    keccak256(
      'MultiPermissionPermit(PositionPermissions[] positions,uint256 nonce,uint256 deadline)PermissionSet(address operator,uint8[] permissions)PositionPermissions(uint256 tokenId,PermissionSet[] permissionSets)'
    );
  /// @inheritdoc IDCAPermissionManager
  bytes32 public constant PERMISSION_SET_TYPEHASH = keccak256('PermissionSet(address operator,uint8[] permissions)');
  /// @inheritdoc IDCAPermissionManager
  bytes32 public constant POSITION_PERMISSIONS_TYPEHASH =
    keccak256('PositionPermissions(uint256 tokenId,PermissionSet[] permissionSets)PermissionSet(address operator,uint8[] permissions)');
  /// @inheritdoc IDCAPermissionManager
  IDCAHubPositionDescriptor public nftDescriptor;
  /// @inheritdoc IDCAPermissionManager
  address public hub;
  /// @inheritdoc IDCAPermissionManager
  mapping(address => uint256) public nonces;
  mapping(uint256 => uint256) public lastOwnershipChange;
  mapping(uint256 => mapping(address => TokenPermission)) public tokenPermissions;
  uint256 internal _burnCounter;

  constructor(address _governor, IDCAHubPositionDescriptor _descriptor)
    ERC721('Mean Finance - DCA Position', 'MF-DCA-P')
    EIP712('Mean Finance - DCA Position', '2')
    Governable(_governor)
  {
    if (address(_descriptor) == address(0)) revert ZeroAddress();
    nftDescriptor = _descriptor;
  }

  /// @inheritdoc IDCAPermissionManager
  function setHub(address _hub) external {
    if (_hub == address(0)) revert ZeroAddress();
    if (hub != address(0)) revert HubAlreadySet();
    hub = _hub;
  }

  /// @inheritdoc IDCAPermissionManager
  function mint(
    uint256 _id,
    address _owner,
    PermissionSet[] calldata _permissions
  ) external {
    if (msg.sender != hub) revert OnlyHubCanExecute();
    _mint(_owner, _id);
    _setPermissions(_id, _permissions);
  }

  /// @inheritdoc IDCAPermissionManager
  function hasPermission(
    uint256 _id,
    address _address,
    Permission _permission
  ) external view returns (bool) {
    if (ownerOf(_id) == _address) {
      return true;
    }
    TokenPermission memory _tokenPermission = tokenPermissions[_id][_address];
    // If there was an ownership change after the permission was last updated, then the address doesn't have the permission
    return _tokenPermission.permissions.hasPermission(_permission) && lastOwnershipChange[_id] < _tokenPermission.lastUpdated;
  }

  /// @inheritdoc IDCAPermissionManager
  function hasPermissions(
    uint256 _id,
    address _address,
    Permission[] calldata _permissions
  ) external view returns (bool[] memory _hasPermissions) {
    _hasPermissions = new bool[](_permissions.length);
    if (ownerOf(_id) == _address) {
      // If the address is the owner, then they have all permissions
      for (uint256 i = 0; i < _permissions.length; i++) {
        _hasPermissions[i] = true;
      }
    } else {
      // If it's not the owner, then check one by one
      TokenPermission memory _tokenPermission = tokenPermissions[_id][_address];
      if (lastOwnershipChange[_id] < _tokenPermission.lastUpdated) {
        for (uint256 i = 0; i < _permissions.length; i++) {
          if (_tokenPermission.permissions.hasPermission(_permissions[i])) {
            _hasPermissions[i] = true;
          }
        }
      }
    }
  }

  /// @inheritdoc IDCAPermissionManager
  function burn(uint256 _id) external {
    if (msg.sender != hub) revert OnlyHubCanExecute();
    _burn(_id);
    ++_burnCounter;
  }

  /// @inheritdoc IDCAPermissionManager
  function modify(uint256 _id, PermissionSet[] calldata _permissions) public virtual {
    if (msg.sender != ownerOf(_id)) revert NotOwner();
    _modify(_id, _permissions);
  }

  /// @inheritdoc IDCAPermissionManager
  function modifyMany(PositionPermissions[] calldata _permissions) external {
    for (uint256 i = 0; i < _permissions.length; ) {
      modify(_permissions[i].tokenId, _permissions[i].permissionSets);
      unchecked {
        i++;
      }
    }
  }

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

  /// @inheritdoc IERC721BasicEnumerable
  function totalSupply() external view returns (uint256) {
    return IDCAHubPositionHandler(hub).totalCreatedPositions() - _burnCounter;
  }

  /// @inheritdoc IDCAPermissionManager
  function permit(
    address _spender,
    uint256 _tokenId,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external {
    if (block.timestamp > _deadline) revert ExpiredDeadline();

    address _owner = ownerOf(_tokenId);
    bytes32 _structHash = keccak256(abi.encode(PERMIT_TYPEHASH, _spender, _tokenId, nonces[_owner]++, _deadline));
    bytes32 _hash = _hashTypedDataV4(_structHash);

    address _signer = ECDSA.recover(_hash, _v, _r, _s);
    if (_signer != _owner) revert InvalidSignature();

    _approve(_spender, _tokenId);
  }

  /// @inheritdoc IDCAPermissionManager
  function permissionPermit(
    PermissionSet[] calldata _permissions,
    uint256 _tokenId,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external {
    if (block.timestamp > _deadline) revert ExpiredDeadline();

    address _owner = ownerOf(_tokenId);
    bytes32 _structHash = keccak256(
      abi.encode(PERMISSION_PERMIT_TYPEHASH, keccak256(_encode(_permissions)), _tokenId, nonces[_owner]++, _deadline)
    );
    bytes32 _hash = _hashTypedDataV4(_structHash);

    address _signer = ECDSA.recover(_hash, _v, _r, _s);
    if (_signer != _owner) revert InvalidSignature();

    _modify(_tokenId, _permissions);
  }

  /// @inheritdoc IDCAPermissionManager
  function multiPermissionPermit(
    PositionPermissions[] calldata _permissions,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external {
    if (block.timestamp > _deadline) revert ExpiredDeadline();

    address _owner = ownerOf(_permissions[0].tokenId);
    bytes32 _structHash = keccak256(abi.encode(MULTI_PERMISSION_PERMIT_TYPEHASH, keccak256(_encode(_permissions)), nonces[_owner]++, _deadline));
    bytes32 _hash = _hashTypedDataV4(_structHash);

    address _signer = ECDSA.recover(_hash, _v, _r, _s);
    if (_signer != _owner) revert InvalidSignature();

    for (uint256 i = 0; i < _permissions.length; ) {
      uint256 _tokenId = _permissions[i].tokenId;
      if (i > 0) {
        address _positionOwner = ownerOf(_tokenId);
        if (_signer != _positionOwner) revert NotOwner();
      }
      _modify(_tokenId, _permissions[i].permissionSets);
      unchecked {
        i++;
      }
    }
  }

  /// @inheritdoc IDCAPermissionManager
  function setNFTDescriptor(IDCAHubPositionDescriptor _descriptor) external onlyGovernor {
    if (address(_descriptor) == address(0)) revert ZeroAddress();
    nftDescriptor = _descriptor;
    emit NFTDescriptorSet(_descriptor);
  }

  /// @inheritdoc ERC721
  function tokenURI(uint256 _tokenId) public view override returns (string memory) {
    return nftDescriptor.tokenURI(hub, _tokenId);
  }

  function _encode(PositionPermissions[] calldata _permissions) internal pure returns (bytes memory _result) {
    for (uint256 i = 0; i < _permissions.length; ) {
      _result = bytes.concat(_result, keccak256(_encode(_permissions[i])));
      unchecked {
        i++;
      }
    }
  }

  function _encode(PositionPermissions calldata _permission) internal pure returns (bytes memory _result) {
    _result = abi.encode(POSITION_PERMISSIONS_TYPEHASH, _permission.tokenId, keccak256(_encode(_permission.permissionSets)));
  }

  function _encode(PermissionSet[] calldata _permissions) internal pure returns (bytes memory _result) {
    for (uint256 i = 0; i < _permissions.length; ) {
      _result = bytes.concat(_result, keccak256(_encode(_permissions[i])));
      unchecked {
        i++;
      }
    }
  }

  function _encode(PermissionSet calldata _permission) internal pure returns (bytes memory _result) {
    _result = abi.encode(PERMISSION_SET_TYPEHASH, _permission.operator, keccak256(_encode(_permission.permissions)));
  }

  function _encode(Permission[] calldata _permissions) internal pure returns (bytes memory _result) {
    _result = new bytes(_permissions.length * 32);
    for (uint256 i = 0; i < _permissions.length; ) {
      _result[(i + 1) * 32 - 1] = bytes1(uint8(_permissions[i]));
      unchecked {
        i++;
      }
    }
  }

  function _modify(uint256 _id, PermissionSet[] calldata _permissions) internal {
    _setPermissions(_id, _permissions);
    emit Modified(_id, _permissions);
  }

  function _setPermissions(uint256 _id, PermissionSet[] calldata _permissions) internal {
    uint248 _blockNumber = uint248(_getBlockNumber());
    for (uint256 i = 0; i < _permissions.length; ) {
      PermissionSet memory _permissionSet = _permissions[i];
      if (_permissionSet.permissions.length == 0) {
        delete tokenPermissions[_id][_permissionSet.operator];
      } else {
        tokenPermissions[_id][_permissionSet.operator] = TokenPermission({
          permissions: _permissionSet.permissions.toUInt8(),
          lastUpdated: _blockNumber
        });
      }
      unchecked {
        i++;
      }
    }
  }

  function _beforeTokenTransfer(
    address _from,
    address _to,
    uint256 _id
  ) internal override {
    if (_to == address(0)) {
      // When token is being burned, we can delete this entry on the mapping
      delete lastOwnershipChange[_id];
    } else if (_from != address(0)) {
      // If the token is being minted, then no need to write this
      lastOwnershipChange[_id] = _getBlockNumber();
    }
  }

  // Note: virtual so that it can be overriden in tests
  function _getBlockNumber() internal view virtual returns (uint256) {
    return block.number;
  }
}

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

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: address zero is not a valid owner");
        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: invalid token ID");
        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) {
        _requireMinted(tokenId);

        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 overridden 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 token owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_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: caller is not token 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: caller is not token 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) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == 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);

        _afterTokenTransfer(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);

        _afterTokenTransfer(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 from incorrect owner");
        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);

        _afterTokenTransfer(from, to, tokenId);
    }

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

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @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.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    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 {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

File 3 of 20 : draft-EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";

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

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

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

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

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import '../interfaces/IDCAPermissionManager.sol';

/// @title Permission Math library
/// @notice Provides functions to easily convert from permissions to an int representation and viceversa
library PermissionMath {
  /// @notice Takes a list of permissions and returns the int representation of the set that contains them all
  /// @param _permissions The list of permissions
  /// @return _representation The uint representation
  function toUInt8(IDCAPermissionManager.Permission[] memory _permissions) internal pure returns (uint8 _representation) {
    for (uint256 i = 0; i < _permissions.length; ) {
      _representation |= uint8(1 << uint8(_permissions[i]));
      unchecked {
        i++;
      }
    }
  }

  /// @notice Takes an int representation of a set of permissions, and returns whether it contains the given permission
  /// @param _representation The int representation
  /// @param _permission The permission to check for
  /// @return _hasPermission Whether the representation contains the given permission
  function hasPermission(uint8 _representation, IDCAPermissionManager.Permission _permission) internal pure returns (bool _hasPermission) {
    uint256 _bitMask = 1 << uint8(_permission);
    _hasPermission = (_representation & _bitMask) != 0;
  }
}

File 7 of 20 : Governable.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.7.0;

interface IGovernable {
  event PendingGovernorSet(address pendingGovernor);
  event PendingGovernorAccepted();

  function setPendingGovernor(address _pendingGovernor) external;

  function acceptPendingGovernor() external;

  function governor() external view returns (address);

  function pendingGovernor() external view returns (address);

  function isGovernor(address _account) external view returns (bool _isGovernor);

  function isPendingGovernor(address _account) external view returns (bool _isPendingGovernor);
}

abstract contract Governable is IGovernable {
  address private _governor;
  address private _pendingGovernor;

  constructor(address __governor) {
    require(__governor != address(0), 'Governable: zero address');
    _governor = __governor;
  }

  function governor() external view override returns (address) {
    return _governor;
  }

  function pendingGovernor() external view override returns (address) {
    return _pendingGovernor;
  }

  function setPendingGovernor(address __pendingGovernor) external virtual override onlyGovernor {
    _setPendingGovernor(__pendingGovernor);
  }

  function _setPendingGovernor(address __pendingGovernor) internal {
    require(__pendingGovernor != address(0), 'Governable: zero address');
    _pendingGovernor = __pendingGovernor;
    emit PendingGovernorSet(__pendingGovernor);
  }

  function acceptPendingGovernor() external virtual override onlyPendingGovernor {
    _acceptPendingGovernor();
  }

  function _acceptPendingGovernor() internal {
    require(_pendingGovernor != address(0), 'Governable: no pending governor');
    _governor = _pendingGovernor;
    _pendingGovernor = address(0);
    emit PendingGovernorAccepted();
  }

  function isGovernor(address _account) public view override returns (bool _isGovernor) {
    return _account == _governor;
  }

  function isPendingGovernor(address _account) public view override returns (bool _isPendingGovernor) {
    return _account == _pendingGovernor;
  }

  modifier onlyGovernor() {
    require(isGovernor(msg.sender), 'Governable: only governor');
    _;
  }

  modifier onlyPendingGovernor() {
    require(isPendingGovernor(msg.sender), 'Governable: only pending governor');
    _;
  }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

File 9 of 20 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

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 `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 10 of 20 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

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 11 of 20 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 12 of 20 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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 13 of 20 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 14 of 20 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

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 15 of 20 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 16 of 20 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.3) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_governor","type":"address"},{"internalType":"contract IDCAHubPositionDescriptor","name":"_descriptor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ExpiredDeadline","type":"error"},{"inputs":[],"name":"HubAlreadySet","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"OnlyHubCanExecute","type":"error"},{"inputs":[],"name":"ZeroAddress","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":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"indexed":false,"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"permissions","type":"tuple[]"}],"name":"Modified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IDCAHubPositionDescriptor","name":"descriptor","type":"address"}],"name":"NFTDescriptorSet","type":"event"},{"anonymous":false,"inputs":[],"name":"PendingGovernorAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingGovernor","type":"address"}],"name":"PendingGovernorSet","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"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MULTI_PERMISSION_PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMISSION_PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMISSION_SET_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POSITION_PERMISSIONS_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptPendingGovernor","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":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"burn","outputs":[],"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":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_address","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission","name":"_permission","type":"uint8"}],"name":"hasPermission","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_address","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"_permissions","type":"uint8[]"}],"name":"hasPermissions","outputs":[{"internalType":"bool[]","name":"_hasPermissions","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hub","outputs":[{"internalType":"address","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":"address","name":"_account","type":"address"}],"name":"isGovernor","outputs":[{"internalType":"bool","name":"_isGovernor","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isPendingGovernor","outputs":[{"internalType":"bool","name":"_isPendingGovernor","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastOwnershipChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"}],"name":"modify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"permissionSets","type":"tuple[]"}],"internalType":"struct IDCAPermissionManager.PositionPermissions[]","name":"_permissions","type":"tuple[]"}],"name":"modifyMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"permissionSets","type":"tuple[]"}],"internalType":"struct IDCAPermissionManager.PositionPermissions[]","name":"_permissions","type":"tuple[]"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"multiPermissionPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftDescriptor","outputs":[{"internalType":"contract IDCAHubPositionDescriptor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"permissionPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_hub","type":"address"}],"name":"setHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IDCAHubPositionDescriptor","name":"_descriptor","type":"address"}],"name":"setNFTDescriptor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"__pendingGovernor","type":"address"}],"name":"setPendingGovernor","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":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"tokenPermissions","outputs":[{"internalType":"uint8","name":"permissions","type":"uint8"},{"internalType":"uint248","name":"lastUpdated","type":"uint248"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101406040523480156200001257600080fd5b5060405162003ddc38038062003ddc833981016040819052620000359162000266565b816040518060400160405280601b81526020017f4d65616e2046696e616e6365202d2044434120506f736974696f6e0000000000815250604051806040016040528060018152602001601960f91b8152506040518060400160405280601b81526020017f4d65616e2046696e616e6365202d2044434120506f736974696f6e00000000008152506040518060400160405280600881526020016704d462d4443412d560c41b8152508160009081620000ee91906200034a565b506001620000fd82826200034a565b5050825160209384012082519284019290922060e08390526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818901819052818301979097526060810194909452608080850193909352308483018190528151808603909301835260c09485019091528151919096012090529290925261012052506001600160a01b038116620001ea5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e61626c653a207a65726f20616464726573730000000000000000604482015260640160405180910390fd5b600680546001600160a01b0319166001600160a01b039283161790558116620002265760405163d92e233d60e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b03929092169190911790555062000416565b6001600160a01b03811681146200026357600080fd5b50565b600080604083850312156200027a57600080fd5b825162000287816200024d565b60208401519092506200029a816200024d565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620002d057607f821691505b602082108103620002f157634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200034557600081815260208120601f850160051c81016020861015620003205750805b601f850160051c820191505b8181101562000341578281556001016200032c565b5050505b505050565b81516001600160401b03811115620003665762000366620002a5565b6200037e81620003778454620002bb565b84620002f7565b602080601f831160018114620003b657600084156200039d5750858301515b600019600386901b1c1916600185901b17855562000341565b600085815260208120601f198616915b82811015620003e757888601518255948401946001909101908401620003c6565b5085821015620004065787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e0516101005161012051613976620004666000396000611e9501526000611ee401526000611ebf01526000611e1801526000611e4201526000611e6c01526139766000f3fe608060405234801561001057600080fd5b50600436106102de5760003560e01c8063551e123b11610186578063a3ca2fb7116100e3578063e1fc815d11610097578063e985e9c511610071578063e985e9c5146106e8578063eef9f66714610724578063f235757f1461074b57600080fd5b8063e1fc815d1461068e578063e3056a34146106b5578063e43581b8146106c657600080fd5b8063b88d4fde116100c8578063b88d4fde14610646578063c87b56dd14610659578063df08aed51461066c57600080fd5b8063a3ca2fb7146105ff578063b3d23ffc1461062657600080fd5b80637ecebe001161013a57806393f03eed1161011f57806393f03eed1461055557806395d89b41146105e4578063a22cb465146105ec57600080fd5b80637ecebe0014610522578063823abfd91461054257600080fd5b806370a082311161016b57806370a08231146104e95780637ac2ff7b146104fc5780637cad6cd11461050f57600080fd5b8063551e123b146104b65780636352211e146104d657600080fd5b806330adf81f1161023f5780633dff0587116101f357806344267570116101cd578063442675701461047d5780634bab5900146104905780634faa3883146104a357600080fd5b80633dff05871461044457806342842e0e1461045757806342966c681461046a57600080fd5b80633644e515116102245780633644e51514610416578063365a86fc1461041e57806336b350981461043157600080fd5b806330adf81f146103dc57806331962cdc1461040357600080fd5b80630c340a241161029657806318160ddd1161027b57806318160ddd1461038c57806323b872dd146103a257806327d70dca146103b557600080fd5b80630c340a241461037357806313f6986d1461038457600080fd5b8063081812fc116102c7578063081812fc14610320578063095ea7b31461034b5780630b7d48201461036057600080fd5b806301ffc9a7146102e357806306fdde031461030b575b600080fd5b6102f66102f1366004612d4d565b61075e565b60405190151581526020015b60405180910390f35b610313610843565b6040516103029190612dba565b61033361032e366004612dcd565b6108d5565b6040516001600160a01b039091168152602001610302565b61035e610359366004612dfb565b6108fc565b005b61035e61036e366004612e73565b610a50565b6006546001600160a01b0316610333565b61035e610aae565b610394610b38565b604051908152602001610302565b61035e6103b0366004612ebf565b610bd2565b6103947fd9daa4fe1723edf458fc13a2d7c6e9147a88485b7932c0ada7e0dd16445f0dda81565b6103947f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad81565b61035e610411366004612f00565b610c59565b610394610d16565b600954610333906001600160a01b031681565b61035e61043f366004612f1d565b610d20565b61035e610452366004612f8f565b610d7f565b61035e610465366004612ebf565b610fcf565b61035e610478366004612dcd565b610fea565b600854610333906001600160a01b031681565b61035e61049e366004612ffe565b61104d565b61035e6104b1366004613040565b6110ba565b6104c96104c4366004612f1d565b611214565b60405161030291906130ba565b6103336104e4366004612dcd565b6113b9565b6103946104f7366004612f00565b61141e565b61035e61050a366004613100565b6114b8565b61035e61051d366004612f00565b611618565b610394610530366004612f00565b600a6020526000908152604090205481565b6102f6610550366004613169565b61171f565b6105ab6105633660046131a7565b600c60209081526000928352604080842090915290825290205460ff81169061010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805160ff90931683527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116602083015201610302565b610313611803565b61035e6105fa3660046131d7565b611812565b6103947fd9f7585827547d4263e0fecc979ffc22c3a65181bb37e3f6662286db9d99709581565b610394610634366004612dcd565b600b6020526000908152604090205481565b61035e6106543660046132bb565b611821565b610313610667366004612dcd565b6118a9565b6102f661067a366004612f00565b6007546001600160a01b0391821691161490565b6103947f5922b2c51ed135e5c892d4fa1b63ad30a4a2c7e7bfc895ee28a38b6e8899962781565b6007546001600160a01b0316610333565b6102f66106d4366004612f00565b6006546001600160a01b0391821691161490565b6102f66106f636600461336a565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6103947f6d0b84f6e69412079eff7b36abc54c5c403692e5d353a7c5546b8d3d9237109b81565b61035e610759366004612f00565b611942565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd0000000000000000000000000000000000000000000000000000000014806107f157507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061083d57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60606000805461085290613398565b80601f016020809104026020016040519081016040528092919081815260200182805461087e90613398565b80156108cb5780601f106108a0576101008083540402835291602001916108cb565b820191906000526020600020905b8154815290600101906020018083116108ae57829003601f168201915b5050505050905090565b60006108e0826119a8565b506000908152600460205260409020546001600160a01b031690565b6000610907826113b9565b9050806001600160a01b0316836001600160a01b0316036109955760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b336001600160a01b03821614806109cf57506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b610a415760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c0000606482015260840161098c565b610a4b8383611a0c565b505050565b610a59836113b9565b6001600160a01b0316336001600160a01b031614610aa3576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a4b838383611a92565b6007546001600160a01b03163314610b2e5760405162461bcd60e51b815260206004820152602160248201527f476f7665726e61626c653a206f6e6c792070656e64696e6720676f7665726e6f60448201527f7200000000000000000000000000000000000000000000000000000000000000606482015260840161098c565b610b36611add565b565b600d54600954604080517f366395800000000000000000000000000000000000000000000000000000000081529051600093926001600160a01b03169163366395809160048083019260209291908290030181865afa158015610b9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc391906133e5565b610bcd919061342d565b905090565b610bdc3382611b9d565b610c4e5760405162461bcd60e51b815260206004820152602e60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206e6f7220617070726f766564000000000000000000000000000000000000606482015260840161098c565b610a4b838383611c1b565b6001600160a01b038116610c99576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6009546001600160a01b031615610cdc576040517f79fb96cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600980547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000610bcd611e0b565b6009546001600160a01b03163314610d64576040517ffc88f3b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d6e8385611f32565b610d79848383612098565b50505050565b83421115610db9576040517ff87d927100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610de987876000818110610dd157610dd1613440565b9050602002810190610de3919061346f565b356113b9565b905060007fd9f7585827547d4263e0fecc979ffc22c3a65181bb37e3f6662286db9d997095610e188989612197565b80516020918201206001600160a01b0385166000908152600a9092526040822080549192610e45836134ad565b9091555060408051602081019490945283019190915260608201526080810187905260a0016040516020818303038152906040528051906020012090506000610e8d82612208565b90506000610e9d82888888612271565b9050836001600160a01b0316816001600160a01b031614610eea576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b89811015610fc25760008b8b83818110610f0957610f09613440565b9050602002810190610f1b919061346f565b3590508115610f7e576000610f2f826113b9565b9050806001600160a01b0316846001600160a01b031614610f7c576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b610fb9818d8d85818110610f9457610f94613440565b9050602002810190610fa6919061346f565b610fb49060208101906134e5565b611a92565b50600101610eed565b5050505050505050505050565b610a4b83838360405180602001604052806000815250611821565b6009546001600160a01b0316331461102e576040517ffc88f3b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61103781612299565b600d60008154611046906134ad565b9091555050565b60005b81811015610a4b576110b283838381811061106d5761106d613440565b905060200281019061107f919061346f565b3584848481811061109257611092613440565b90506020028101906110a4919061346f565b61036e9060208101906134e5565b600101611050565b834211156110f4576040517ff87d927100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006110ff866113b9565b905060007f5922b2c51ed135e5c892d4fa1b63ad30a4a2c7e7bfc895ee28a38b6e8899962761112e8a8a612358565b80516020918201206001600160a01b0385166000908152600a90925260408220805491928b92919061115f836134ad565b909155506040805160208101959095528401929092526060830152608082015260a0810187905260c00160405160208183030381529060405280519060200120905060006111ac82612208565b905060006111bc82888888612271565b9050836001600160a01b0316816001600160a01b031614611209576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc2898c8c611a92565b60608167ffffffffffffffff81111561122f5761122f61320a565b604051908082528060200260200182016040528015611258578160200160208202803683370190505b509050836001600160a01b031661126e866113b9565b6001600160a01b0316036112c35760005b828110156112bd57600182828151811061129b5761129b613440565b91151560209283029190910190910152806112b5816134ad565b91505061127f565b506113b1565b6000858152600c602090815260408083206001600160a01b0388168452825280832081518083018352905460ff8116825261010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16818401819052898552600b90935292205410156113af5760005b838110156113ad5761137185858381811061135157611351613440565b9050602002016020810190611366919061354d565b835160ff16906123c2565b1561139b57600183828151811061138a5761138a613440565b911515602092830291909101909101525b806113a5816134ad565b915050611334565b505b505b949350505050565b6000818152600260205260408120546001600160a01b03168061083d5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e2049440000000000000000604482015260640161098c565b60006001600160a01b03821661149c5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e65720000000000000000000000000000000000000000000000606482015260840161098c565b506001600160a01b031660009081526003602052604090205490565b834211156114f2576040517ff87d927100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006114fd866113b9565b6001600160a01b0381166000908152600a60205260408120805492935090917f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad918a918a918561154c836134ad565b909155506040805160208101959095526001600160a01b03909316928401929092526060830152608082015260a0810187905260c00160405160208183030381529060405280519060200120905060006115a582612208565b905060006115b582888888612271565b9050836001600160a01b0316816001600160a01b031614611602576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61160c8a8a611a0c565b50505050505050505050565b6006546001600160a01b031633146116725760405162461bcd60e51b815260206004820152601960248201527f476f7665726e61626c653a206f6e6c7920676f7665726e6f7200000000000000604482015260640161098c565b6001600160a01b0381166116b2576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f58a2b9171063c6d38177053178bc1844b920884b05d7c9126c480d86b0716bce906020015b60405180910390a150565b6000826001600160a01b0316611734856113b9565b6001600160a01b03160361174a575060016117fc565b6000848152600c602090815260408083206001600160a01b038716845282529182902082518084019093525460ff81168084526101009091047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16918301919091526117b590846123c2565b80156117f8575080602001517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600b600087815260200190815260200160002054105b9150505b9392505050565b60606001805461085290613398565b61181d3383836123ef565b5050565b61182b3383611b9d565b61189d5760405162461bcd60e51b815260206004820152602e60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206e6f7220617070726f766564000000000000000000000000000000000000606482015260840161098c565b610d79848484846124db565b6008546009546040517fe9dc63750000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260248101849052606092919091169063e9dc637590604401600060405180830381865afa15801561191a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261083d9190810190613568565b6006546001600160a01b0316331461199c5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e61626c653a206f6e6c7920676f7665726e6f7200000000000000604482015260640161098c565b6119a581612564565b50565b6000818152600260205260409020546001600160a01b03166119a55760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e2049440000000000000000604482015260640161098c565b600081815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384169081179091558190611a59826113b9565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b611a9d838383612098565b7f5015559e4f379aa0dd2effee572c916b09bb68ed41ad9ab127c8066f052e1643838383604051611ad09392919061360e565b60405180910390a1505050565b6007546001600160a01b0316611b355760405162461bcd60e51b815260206004820152601f60248201527f476f7665726e61626c653a206e6f2070656e64696e6720676f7665726e6f7200604482015260640161098c565b60078054600680547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b038416179091551690556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd7690600090a1565b600080611ba9836113b9565b9050806001600160a01b0316846001600160a01b03161480611bf057506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b806113b15750836001600160a01b0316611c09846108d5565b6001600160a01b031614949350505050565b826001600160a01b0316611c2e826113b9565b6001600160a01b031614611caa5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e6572000000000000000000000000000000000000000000000000000000606482015260840161098c565b6001600160a01b038216611d255760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f7265737300000000000000000000000000000000000000000000000000000000606482015260840161098c565b611d30838383612620565b611d3b600082611a0c565b6001600160a01b0383166000908152600360205260408120805460019290611d6490849061342d565b90915550506001600160a01b0382166000908152600360205260408120805460019290611d929084906137ab565b909155505060008181526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015611e6457507f000000000000000000000000000000000000000000000000000000000000000046145b15611e8e57507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6001600160a01b038216611f885760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161098c565b6000818152600260205260409020546001600160a01b031615611fed5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161098c565b611ff960008383612620565b6001600160a01b03821660009081526003602052604081208054600192906120229084906137ab565b909155505060008181526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b4360005b828110156121905760008484838181106120b8576120b8613440565b90506020028101906120ca919061346f565b6120d3906137be565b905080602001515160000361210b576000868152600c6020908152604080832084516001600160a01b03168452909152812055612187565b60405180604001604052806121238360200151612665565b60ff90811682527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80871660209384015260008a8152600c8452604080822087516001600160a01b031683528552902084519490930151166101000292169190911790555b5060010161209c565b5050505050565b606060005b8281101561220157816121d18585848181106121ba576121ba613440565b90506020028101906121cc919061346f565b6126b9565b80516020918201206040516121e7939201613888565b60408051601f19818403018152919052915060010161219c565b5092915050565b600061083d612215611e0b565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b600080600061228287878787612731565b9150915061228f8161281e565b5095945050505050565b60006122a4826113b9565b90506122b281600084612620565b6122bd600083611a0c565b6001600160a01b03811660009081526003602052604081208054600192906122e690849061342d565b909155505060008281526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b606060005b82811015612201578161239285858481811061237b5761237b613440565b905060200281019061238d919061346f565b612a0a565b80516020918201206040516123a8939201613888565b60408051601f19818403018152919052915060010161235d565b6000808260038111156123d7576123d76135df565b600160ff9182161b9490941690931615159392505050565b816001600160a01b0316836001600160a01b0316036124505760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161098c565b6001600160a01b0383811660008181526005602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6124e6848484611c1b565b6124f284848484612a80565b610d795760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e7465720000000000000000000000000000606482015260840161098c565b6001600160a01b0381166125ba5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e61626c653a207a65726f20616464726573730000000000000000604482015260640161098c565b600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def90602001611714565b6001600160a01b038216612641576000908152600b60205260408120555050565b6001600160a01b03831615610a4b57436000828152600b6020526040902055505050565b6000805b82518110156126b35782818151811061268457612684613440565b6020026020010151600381111561269d5761269d6135df565b600160ff9190911681901b929092179101612669565b50919050565b60607f6d0b84f6e69412079eff7b36abc54c5c403692e5d353a7c5546b8d3d9237109b82356126f36126ee60208601866134e5565b612358565b805160209182012060405161271b949392019283526020830191909152604082015260600190565b6040516020818303038152906040529050919050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156127685750600090506003612815565b8460ff16601b1415801561278057508460ff16601c14155b156127915750600090506004612815565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156127e5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661280e57600060019250925050612815565b9150600090505b94509492505050565b6000816004811115612832576128326135df565b0361283a5750565b600181600481111561284e5761284e6135df565b0361289b5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161098c565b60028160048111156128af576128af6135df565b036128fc5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161098c565b6003816004811115612910576129106135df565b036129835760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015260840161098c565b6004816004811115612997576129976135df565b036119a55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015260840161098c565b60607fd9daa4fe1723edf458fc13a2d7c6e9147a88485b7932c0ada7e0dd16445f0dda612a3a6020840184612f00565b612a4f612a4a60208601866134e5565b612c1e565b805160209182012060405161271b949392019283526001600160a01b03919091166020830152604082015260600190565b60006001600160a01b0384163b15612c16576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063150b7a0290612add9033908990889088906004016138aa565b6020604051808303816000875af1925050508015612b18575060408051601f3d908101601f19168201909252612b15918101906138e6565b60015b612bcb573d808015612b46576040519150601f19603f3d011682016040523d82523d6000602084013e612b4b565b606091505b508051600003612bc35760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e7465720000000000000000000000000000606482015260840161098c565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490506113b1565b5060016113b1565b6060612c2b826020613903565b67ffffffffffffffff811115612c4357612c4361320a565b6040519080825280601f01601f191660200182016040528015612c6d576020820181803683370190505b50905060005b8281101561220157838382818110612c8d57612c8d613440565b9050602002016020810190612ca2919061354d565b6003811115612cb357612cb36135df565b60f81b826001612cc384826137ab565b612cce906020613903565b612cd8919061342d565b81518110612ce857612ce8613440565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101612c73565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146119a557600080fd5b600060208284031215612d5f57600080fd5b81356117fc81612d1f565b60005b83811015612d85578181015183820152602001612d6d565b50506000910152565b60008151808452612da6816020860160208601612d6a565b601f01601f19169290920160200192915050565b6020815260006117fc6020830184612d8e565b600060208284031215612ddf57600080fd5b5035919050565b6001600160a01b03811681146119a557600080fd5b60008060408385031215612e0e57600080fd5b8235612e1981612de6565b946020939093013593505050565b60008083601f840112612e3957600080fd5b50813567ffffffffffffffff811115612e5157600080fd5b6020830191508360208260051b8501011115612e6c57600080fd5b9250929050565b600080600060408486031215612e8857600080fd5b83359250602084013567ffffffffffffffff811115612ea657600080fd5b612eb286828701612e27565b9497909650939450505050565b600080600060608486031215612ed457600080fd5b8335612edf81612de6565b92506020840135612eef81612de6565b929592945050506040919091013590565b600060208284031215612f1257600080fd5b81356117fc81612de6565b60008060008060608587031215612f3357600080fd5b843593506020850135612f4581612de6565b9250604085013567ffffffffffffffff811115612f6157600080fd5b612f6d87828801612e27565b95989497509550505050565b803560ff81168114612f8a57600080fd5b919050565b60008060008060008060a08789031215612fa857600080fd5b863567ffffffffffffffff811115612fbf57600080fd5b612fcb89828a01612e27565b90975095505060208701359350612fe460408801612f79565b925060608701359150608087013590509295509295509295565b6000806020838503121561301157600080fd5b823567ffffffffffffffff81111561302857600080fd5b61303485828601612e27565b90969095509350505050565b600080600080600080600060c0888a03121561305b57600080fd5b873567ffffffffffffffff81111561307257600080fd5b61307e8a828b01612e27565b909850965050602088013594506040880135935061309e60608901612f79565b92506080880135915060a0880135905092959891949750929550565b6020808252825182820181905260009190848201906040850190845b818110156130f45783511515835292840192918401916001016130d6565b50909695505050505050565b60008060008060008060c0878903121561311957600080fd5b863561312481612de6565b9550602087013594506040870135935061314060608801612f79565b92506080870135915060a087013590509295509295509295565b803560048110612f8a57600080fd5b60008060006060848603121561317e57600080fd5b83359250602084013561319081612de6565b915061319e6040850161315a565b90509250925092565b600080604083850312156131ba57600080fd5b8235915060208301356131cc81612de6565b809150509250929050565b600080604083850312156131ea57600080fd5b82356131f581612de6565b9150602083013580151581146131cc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561325c5761325c61320a565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561328b5761328b61320a565b604052919050565b600067ffffffffffffffff8211156132ad576132ad61320a565b50601f01601f191660200190565b600080600080608085870312156132d157600080fd5b84356132dc81612de6565b935060208501356132ec81612de6565b925060408501359150606085013567ffffffffffffffff81111561330f57600080fd5b8501601f8101871361332057600080fd5b803561333361332e82613293565b613262565b81815288602083850101111561334857600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6000806040838503121561337d57600080fd5b823561338881612de6565b915060208301356131cc81612de6565b600181811c908216806133ac57607f821691505b6020821081036126b3577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602082840312156133f757600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561083d5761083d6133fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126134a357600080fd5b9190910192915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036134de576134de6133fe565b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261351a57600080fd5b83018035915067ffffffffffffffff82111561353557600080fd5b6020019150600581901b3603821315612e6c57600080fd5b60006020828403121561355f57600080fd5b6117fc8261315a565b60006020828403121561357a57600080fd5b815167ffffffffffffffff81111561359157600080fd5b8201601f810184136135a257600080fd5b80516135b061332e82613293565b8181528560208385010111156135c557600080fd5b6135d6826020830160208601612d6a565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8381526040602080830182905282820184905260009190606090818501600587811b8701840189875b8a81101561379a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a840301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18d360301811261369857600080fd5b8c0183890181356136a881612de6565b6001600160a01b0316855281880135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126136e857600080fd5b90910187810191903567ffffffffffffffff81111561370657600080fd5b80871b360383131561371757600080fd5b8589018b9052908190526000908986015b81831015613785576137398461315a565b6004808210613770577f4e487b71000000000000000000000000000000000000000000000000000000006000526021815260246000fd5b50815292890192600192909201918901613728565b97890197955050509186019150600101613637565b50909b9a5050505050505050505050565b8082018082111561083d5761083d6133fe565b6000604082360312156137d057600080fd5b6137d8613239565b82356137e381612de6565b815260208381013567ffffffffffffffff8082111561380157600080fd5b9085019036601f83011261381457600080fd5b8135818111156138265761382661320a565b8060051b9150613837848301613262565b818152918301840191848101903684111561385157600080fd5b938501935b83851015613876576138678561315a565b82529385019390850190613856565b94860194909452509295945050505050565b6000835161389a818460208801612d6a565b9190910191825250602001919050565b60006001600160a01b038087168352808616602084015250836040830152608060608301526138dc6080830184612d8e565b9695505050505050565b6000602082840312156138f857600080fd5b81516117fc81612d1f565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561393b5761393b6133fe565b50029056fea264697066735822122070cb35c35fe342156a1eef2c384a2c61ed89450e492418ad80c6aa0f330278bc64736f6c63430008100033000000000000000000000000308810881807189cae91950888b2cb73a1cc59200000000000000000000000004acd4bc402bc8e6ba8abddca639d8011ef0b8a4b

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102de5760003560e01c8063551e123b11610186578063a3ca2fb7116100e3578063e1fc815d11610097578063e985e9c511610071578063e985e9c5146106e8578063eef9f66714610724578063f235757f1461074b57600080fd5b8063e1fc815d1461068e578063e3056a34146106b5578063e43581b8146106c657600080fd5b8063b88d4fde116100c8578063b88d4fde14610646578063c87b56dd14610659578063df08aed51461066c57600080fd5b8063a3ca2fb7146105ff578063b3d23ffc1461062657600080fd5b80637ecebe001161013a57806393f03eed1161011f57806393f03eed1461055557806395d89b41146105e4578063a22cb465146105ec57600080fd5b80637ecebe0014610522578063823abfd91461054257600080fd5b806370a082311161016b57806370a08231146104e95780637ac2ff7b146104fc5780637cad6cd11461050f57600080fd5b8063551e123b146104b65780636352211e146104d657600080fd5b806330adf81f1161023f5780633dff0587116101f357806344267570116101cd578063442675701461047d5780634bab5900146104905780634faa3883146104a357600080fd5b80633dff05871461044457806342842e0e1461045757806342966c681461046a57600080fd5b80633644e515116102245780633644e51514610416578063365a86fc1461041e57806336b350981461043157600080fd5b806330adf81f146103dc57806331962cdc1461040357600080fd5b80630c340a241161029657806318160ddd1161027b57806318160ddd1461038c57806323b872dd146103a257806327d70dca146103b557600080fd5b80630c340a241461037357806313f6986d1461038457600080fd5b8063081812fc116102c7578063081812fc14610320578063095ea7b31461034b5780630b7d48201461036057600080fd5b806301ffc9a7146102e357806306fdde031461030b575b600080fd5b6102f66102f1366004612d4d565b61075e565b60405190151581526020015b60405180910390f35b610313610843565b6040516103029190612dba565b61033361032e366004612dcd565b6108d5565b6040516001600160a01b039091168152602001610302565b61035e610359366004612dfb565b6108fc565b005b61035e61036e366004612e73565b610a50565b6006546001600160a01b0316610333565b61035e610aae565b610394610b38565b604051908152602001610302565b61035e6103b0366004612ebf565b610bd2565b6103947fd9daa4fe1723edf458fc13a2d7c6e9147a88485b7932c0ada7e0dd16445f0dda81565b6103947f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad81565b61035e610411366004612f00565b610c59565b610394610d16565b600954610333906001600160a01b031681565b61035e61043f366004612f1d565b610d20565b61035e610452366004612f8f565b610d7f565b61035e610465366004612ebf565b610fcf565b61035e610478366004612dcd565b610fea565b600854610333906001600160a01b031681565b61035e61049e366004612ffe565b61104d565b61035e6104b1366004613040565b6110ba565b6104c96104c4366004612f1d565b611214565b60405161030291906130ba565b6103336104e4366004612dcd565b6113b9565b6103946104f7366004612f00565b61141e565b61035e61050a366004613100565b6114b8565b61035e61051d366004612f00565b611618565b610394610530366004612f00565b600a6020526000908152604090205481565b6102f6610550366004613169565b61171f565b6105ab6105633660046131a7565b600c60209081526000928352604080842090915290825290205460ff81169061010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805160ff90931683527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116602083015201610302565b610313611803565b61035e6105fa3660046131d7565b611812565b6103947fd9f7585827547d4263e0fecc979ffc22c3a65181bb37e3f6662286db9d99709581565b610394610634366004612dcd565b600b6020526000908152604090205481565b61035e6106543660046132bb565b611821565b610313610667366004612dcd565b6118a9565b6102f661067a366004612f00565b6007546001600160a01b0391821691161490565b6103947f5922b2c51ed135e5c892d4fa1b63ad30a4a2c7e7bfc895ee28a38b6e8899962781565b6007546001600160a01b0316610333565b6102f66106d4366004612f00565b6006546001600160a01b0391821691161490565b6102f66106f636600461336a565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6103947f6d0b84f6e69412079eff7b36abc54c5c403692e5d353a7c5546b8d3d9237109b81565b61035e610759366004612f00565b611942565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd0000000000000000000000000000000000000000000000000000000014806107f157507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061083d57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60606000805461085290613398565b80601f016020809104026020016040519081016040528092919081815260200182805461087e90613398565b80156108cb5780601f106108a0576101008083540402835291602001916108cb565b820191906000526020600020905b8154815290600101906020018083116108ae57829003601f168201915b5050505050905090565b60006108e0826119a8565b506000908152600460205260409020546001600160a01b031690565b6000610907826113b9565b9050806001600160a01b0316836001600160a01b0316036109955760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b336001600160a01b03821614806109cf57506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b610a415760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c0000606482015260840161098c565b610a4b8383611a0c565b505050565b610a59836113b9565b6001600160a01b0316336001600160a01b031614610aa3576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a4b838383611a92565b6007546001600160a01b03163314610b2e5760405162461bcd60e51b815260206004820152602160248201527f476f7665726e61626c653a206f6e6c792070656e64696e6720676f7665726e6f60448201527f7200000000000000000000000000000000000000000000000000000000000000606482015260840161098c565b610b36611add565b565b600d54600954604080517f366395800000000000000000000000000000000000000000000000000000000081529051600093926001600160a01b03169163366395809160048083019260209291908290030181865afa158015610b9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc391906133e5565b610bcd919061342d565b905090565b610bdc3382611b9d565b610c4e5760405162461bcd60e51b815260206004820152602e60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206e6f7220617070726f766564000000000000000000000000000000000000606482015260840161098c565b610a4b838383611c1b565b6001600160a01b038116610c99576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6009546001600160a01b031615610cdc576040517f79fb96cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600980547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000610bcd611e0b565b6009546001600160a01b03163314610d64576040517ffc88f3b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d6e8385611f32565b610d79848383612098565b50505050565b83421115610db9576040517ff87d927100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610de987876000818110610dd157610dd1613440565b9050602002810190610de3919061346f565b356113b9565b905060007fd9f7585827547d4263e0fecc979ffc22c3a65181bb37e3f6662286db9d997095610e188989612197565b80516020918201206001600160a01b0385166000908152600a9092526040822080549192610e45836134ad565b9091555060408051602081019490945283019190915260608201526080810187905260a0016040516020818303038152906040528051906020012090506000610e8d82612208565b90506000610e9d82888888612271565b9050836001600160a01b0316816001600160a01b031614610eea576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b89811015610fc25760008b8b83818110610f0957610f09613440565b9050602002810190610f1b919061346f565b3590508115610f7e576000610f2f826113b9565b9050806001600160a01b0316846001600160a01b031614610f7c576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b610fb9818d8d85818110610f9457610f94613440565b9050602002810190610fa6919061346f565b610fb49060208101906134e5565b611a92565b50600101610eed565b5050505050505050505050565b610a4b83838360405180602001604052806000815250611821565b6009546001600160a01b0316331461102e576040517ffc88f3b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61103781612299565b600d60008154611046906134ad565b9091555050565b60005b81811015610a4b576110b283838381811061106d5761106d613440565b905060200281019061107f919061346f565b3584848481811061109257611092613440565b90506020028101906110a4919061346f565b61036e9060208101906134e5565b600101611050565b834211156110f4576040517ff87d927100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006110ff866113b9565b905060007f5922b2c51ed135e5c892d4fa1b63ad30a4a2c7e7bfc895ee28a38b6e8899962761112e8a8a612358565b80516020918201206001600160a01b0385166000908152600a90925260408220805491928b92919061115f836134ad565b909155506040805160208101959095528401929092526060830152608082015260a0810187905260c00160405160208183030381529060405280519060200120905060006111ac82612208565b905060006111bc82888888612271565b9050836001600160a01b0316816001600160a01b031614611209576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc2898c8c611a92565b60608167ffffffffffffffff81111561122f5761122f61320a565b604051908082528060200260200182016040528015611258578160200160208202803683370190505b509050836001600160a01b031661126e866113b9565b6001600160a01b0316036112c35760005b828110156112bd57600182828151811061129b5761129b613440565b91151560209283029190910190910152806112b5816134ad565b91505061127f565b506113b1565b6000858152600c602090815260408083206001600160a01b0388168452825280832081518083018352905460ff8116825261010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16818401819052898552600b90935292205410156113af5760005b838110156113ad5761137185858381811061135157611351613440565b9050602002016020810190611366919061354d565b835160ff16906123c2565b1561139b57600183828151811061138a5761138a613440565b911515602092830291909101909101525b806113a5816134ad565b915050611334565b505b505b949350505050565b6000818152600260205260408120546001600160a01b03168061083d5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e2049440000000000000000604482015260640161098c565b60006001600160a01b03821661149c5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e65720000000000000000000000000000000000000000000000606482015260840161098c565b506001600160a01b031660009081526003602052604090205490565b834211156114f2576040517ff87d927100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006114fd866113b9565b6001600160a01b0381166000908152600a60205260408120805492935090917f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad918a918a918561154c836134ad565b909155506040805160208101959095526001600160a01b03909316928401929092526060830152608082015260a0810187905260c00160405160208183030381529060405280519060200120905060006115a582612208565b905060006115b582888888612271565b9050836001600160a01b0316816001600160a01b031614611602576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61160c8a8a611a0c565b50505050505050505050565b6006546001600160a01b031633146116725760405162461bcd60e51b815260206004820152601960248201527f476f7665726e61626c653a206f6e6c7920676f7665726e6f7200000000000000604482015260640161098c565b6001600160a01b0381166116b2576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f58a2b9171063c6d38177053178bc1844b920884b05d7c9126c480d86b0716bce906020015b60405180910390a150565b6000826001600160a01b0316611734856113b9565b6001600160a01b03160361174a575060016117fc565b6000848152600c602090815260408083206001600160a01b038716845282529182902082518084019093525460ff81168084526101009091047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16918301919091526117b590846123c2565b80156117f8575080602001517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600b600087815260200190815260200160002054105b9150505b9392505050565b60606001805461085290613398565b61181d3383836123ef565b5050565b61182b3383611b9d565b61189d5760405162461bcd60e51b815260206004820152602e60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206e6f7220617070726f766564000000000000000000000000000000000000606482015260840161098c565b610d79848484846124db565b6008546009546040517fe9dc63750000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260248101849052606092919091169063e9dc637590604401600060405180830381865afa15801561191a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261083d9190810190613568565b6006546001600160a01b0316331461199c5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e61626c653a206f6e6c7920676f7665726e6f7200000000000000604482015260640161098c565b6119a581612564565b50565b6000818152600260205260409020546001600160a01b03166119a55760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e2049440000000000000000604482015260640161098c565b600081815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384169081179091558190611a59826113b9565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b611a9d838383612098565b7f5015559e4f379aa0dd2effee572c916b09bb68ed41ad9ab127c8066f052e1643838383604051611ad09392919061360e565b60405180910390a1505050565b6007546001600160a01b0316611b355760405162461bcd60e51b815260206004820152601f60248201527f476f7665726e61626c653a206e6f2070656e64696e6720676f7665726e6f7200604482015260640161098c565b60078054600680547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b038416179091551690556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd7690600090a1565b600080611ba9836113b9565b9050806001600160a01b0316846001600160a01b03161480611bf057506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b806113b15750836001600160a01b0316611c09846108d5565b6001600160a01b031614949350505050565b826001600160a01b0316611c2e826113b9565b6001600160a01b031614611caa5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e6572000000000000000000000000000000000000000000000000000000606482015260840161098c565b6001600160a01b038216611d255760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f7265737300000000000000000000000000000000000000000000000000000000606482015260840161098c565b611d30838383612620565b611d3b600082611a0c565b6001600160a01b0383166000908152600360205260408120805460019290611d6490849061342d565b90915550506001600160a01b0382166000908152600360205260408120805460019290611d929084906137ab565b909155505060008181526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000306001600160a01b037f00000000000000000000000020bdae1413659f47416f769a4b27044946bc992316148015611e6457507f000000000000000000000000000000000000000000000000000000000000000a46145b15611e8e57507f59dec6c9a90d35c44317b854980edb56318d392d9ad086b3b767166a13aedab590565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fc82c9c6048a2ee26e926f0b57fba8a4c6c4ff4aece582e0fc2ca13ec5aebc670828401527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6001600160a01b038216611f885760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161098c565b6000818152600260205260409020546001600160a01b031615611fed5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161098c565b611ff960008383612620565b6001600160a01b03821660009081526003602052604081208054600192906120229084906137ab565b909155505060008181526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b4360005b828110156121905760008484838181106120b8576120b8613440565b90506020028101906120ca919061346f565b6120d3906137be565b905080602001515160000361210b576000868152600c6020908152604080832084516001600160a01b03168452909152812055612187565b60405180604001604052806121238360200151612665565b60ff90811682527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80871660209384015260008a8152600c8452604080822087516001600160a01b031683528552902084519490930151166101000292169190911790555b5060010161209c565b5050505050565b606060005b8281101561220157816121d18585848181106121ba576121ba613440565b90506020028101906121cc919061346f565b6126b9565b80516020918201206040516121e7939201613888565b60408051601f19818403018152919052915060010161219c565b5092915050565b600061083d612215611e0b565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b600080600061228287878787612731565b9150915061228f8161281e565b5095945050505050565b60006122a4826113b9565b90506122b281600084612620565b6122bd600083611a0c565b6001600160a01b03811660009081526003602052604081208054600192906122e690849061342d565b909155505060008281526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b606060005b82811015612201578161239285858481811061237b5761237b613440565b905060200281019061238d919061346f565b612a0a565b80516020918201206040516123a8939201613888565b60408051601f19818403018152919052915060010161235d565b6000808260038111156123d7576123d76135df565b600160ff9182161b9490941690931615159392505050565b816001600160a01b0316836001600160a01b0316036124505760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161098c565b6001600160a01b0383811660008181526005602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6124e6848484611c1b565b6124f284848484612a80565b610d795760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e7465720000000000000000000000000000606482015260840161098c565b6001600160a01b0381166125ba5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e61626c653a207a65726f20616464726573730000000000000000604482015260640161098c565b600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def90602001611714565b6001600160a01b038216612641576000908152600b60205260408120555050565b6001600160a01b03831615610a4b57436000828152600b6020526040902055505050565b6000805b82518110156126b35782818151811061268457612684613440565b6020026020010151600381111561269d5761269d6135df565b600160ff9190911681901b929092179101612669565b50919050565b60607f6d0b84f6e69412079eff7b36abc54c5c403692e5d353a7c5546b8d3d9237109b82356126f36126ee60208601866134e5565b612358565b805160209182012060405161271b949392019283526020830191909152604082015260600190565b6040516020818303038152906040529050919050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156127685750600090506003612815565b8460ff16601b1415801561278057508460ff16601c14155b156127915750600090506004612815565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156127e5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661280e57600060019250925050612815565b9150600090505b94509492505050565b6000816004811115612832576128326135df565b0361283a5750565b600181600481111561284e5761284e6135df565b0361289b5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161098c565b60028160048111156128af576128af6135df565b036128fc5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161098c565b6003816004811115612910576129106135df565b036129835760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015260840161098c565b6004816004811115612997576129976135df565b036119a55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015260840161098c565b60607fd9daa4fe1723edf458fc13a2d7c6e9147a88485b7932c0ada7e0dd16445f0dda612a3a6020840184612f00565b612a4f612a4a60208601866134e5565b612c1e565b805160209182012060405161271b949392019283526001600160a01b03919091166020830152604082015260600190565b60006001600160a01b0384163b15612c16576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063150b7a0290612add9033908990889088906004016138aa565b6020604051808303816000875af1925050508015612b18575060408051601f3d908101601f19168201909252612b15918101906138e6565b60015b612bcb573d808015612b46576040519150601f19603f3d011682016040523d82523d6000602084013e612b4b565b606091505b508051600003612bc35760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e7465720000000000000000000000000000606482015260840161098c565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490506113b1565b5060016113b1565b6060612c2b826020613903565b67ffffffffffffffff811115612c4357612c4361320a565b6040519080825280601f01601f191660200182016040528015612c6d576020820181803683370190505b50905060005b8281101561220157838382818110612c8d57612c8d613440565b9050602002016020810190612ca2919061354d565b6003811115612cb357612cb36135df565b60f81b826001612cc384826137ab565b612cce906020613903565b612cd8919061342d565b81518110612ce857612ce8613440565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101612c73565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146119a557600080fd5b600060208284031215612d5f57600080fd5b81356117fc81612d1f565b60005b83811015612d85578181015183820152602001612d6d565b50506000910152565b60008151808452612da6816020860160208601612d6a565b601f01601f19169290920160200192915050565b6020815260006117fc6020830184612d8e565b600060208284031215612ddf57600080fd5b5035919050565b6001600160a01b03811681146119a557600080fd5b60008060408385031215612e0e57600080fd5b8235612e1981612de6565b946020939093013593505050565b60008083601f840112612e3957600080fd5b50813567ffffffffffffffff811115612e5157600080fd5b6020830191508360208260051b8501011115612e6c57600080fd5b9250929050565b600080600060408486031215612e8857600080fd5b83359250602084013567ffffffffffffffff811115612ea657600080fd5b612eb286828701612e27565b9497909650939450505050565b600080600060608486031215612ed457600080fd5b8335612edf81612de6565b92506020840135612eef81612de6565b929592945050506040919091013590565b600060208284031215612f1257600080fd5b81356117fc81612de6565b60008060008060608587031215612f3357600080fd5b843593506020850135612f4581612de6565b9250604085013567ffffffffffffffff811115612f6157600080fd5b612f6d87828801612e27565b95989497509550505050565b803560ff81168114612f8a57600080fd5b919050565b60008060008060008060a08789031215612fa857600080fd5b863567ffffffffffffffff811115612fbf57600080fd5b612fcb89828a01612e27565b90975095505060208701359350612fe460408801612f79565b925060608701359150608087013590509295509295509295565b6000806020838503121561301157600080fd5b823567ffffffffffffffff81111561302857600080fd5b61303485828601612e27565b90969095509350505050565b600080600080600080600060c0888a03121561305b57600080fd5b873567ffffffffffffffff81111561307257600080fd5b61307e8a828b01612e27565b909850965050602088013594506040880135935061309e60608901612f79565b92506080880135915060a0880135905092959891949750929550565b6020808252825182820181905260009190848201906040850190845b818110156130f45783511515835292840192918401916001016130d6565b50909695505050505050565b60008060008060008060c0878903121561311957600080fd5b863561312481612de6565b9550602087013594506040870135935061314060608801612f79565b92506080870135915060a087013590509295509295509295565b803560048110612f8a57600080fd5b60008060006060848603121561317e57600080fd5b83359250602084013561319081612de6565b915061319e6040850161315a565b90509250925092565b600080604083850312156131ba57600080fd5b8235915060208301356131cc81612de6565b809150509250929050565b600080604083850312156131ea57600080fd5b82356131f581612de6565b9150602083013580151581146131cc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561325c5761325c61320a565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561328b5761328b61320a565b604052919050565b600067ffffffffffffffff8211156132ad576132ad61320a565b50601f01601f191660200190565b600080600080608085870312156132d157600080fd5b84356132dc81612de6565b935060208501356132ec81612de6565b925060408501359150606085013567ffffffffffffffff81111561330f57600080fd5b8501601f8101871361332057600080fd5b803561333361332e82613293565b613262565b81815288602083850101111561334857600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6000806040838503121561337d57600080fd5b823561338881612de6565b915060208301356131cc81612de6565b600181811c908216806133ac57607f821691505b6020821081036126b3577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602082840312156133f757600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561083d5761083d6133fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126134a357600080fd5b9190910192915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036134de576134de6133fe565b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261351a57600080fd5b83018035915067ffffffffffffffff82111561353557600080fd5b6020019150600581901b3603821315612e6c57600080fd5b60006020828403121561355f57600080fd5b6117fc8261315a565b60006020828403121561357a57600080fd5b815167ffffffffffffffff81111561359157600080fd5b8201601f810184136135a257600080fd5b80516135b061332e82613293565b8181528560208385010111156135c557600080fd5b6135d6826020830160208601612d6a565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8381526040602080830182905282820184905260009190606090818501600587811b8701840189875b8a81101561379a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a840301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18d360301811261369857600080fd5b8c0183890181356136a881612de6565b6001600160a01b0316855281880135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126136e857600080fd5b90910187810191903567ffffffffffffffff81111561370657600080fd5b80871b360383131561371757600080fd5b8589018b9052908190526000908986015b81831015613785576137398461315a565b6004808210613770577f4e487b71000000000000000000000000000000000000000000000000000000006000526021815260246000fd5b50815292890192600192909201918901613728565b97890197955050509186019150600101613637565b50909b9a5050505050505050505050565b8082018082111561083d5761083d6133fe565b6000604082360312156137d057600080fd5b6137d8613239565b82356137e381612de6565b815260208381013567ffffffffffffffff8082111561380157600080fd5b9085019036601f83011261381457600080fd5b8135818111156138265761382661320a565b8060051b9150613837848301613262565b818152918301840191848101903684111561385157600080fd5b938501935b83851015613876576138678561315a565b82529385019390850190613856565b94860194909452509295945050505050565b6000835161389a818460208801612d6a565b9190910191825250602001919050565b60006001600160a01b038087168352808616602084015250836040830152608060608301526138dc6080830184612d8e565b9695505050505050565b6000602082840312156138f857600080fd5b81516117fc81612d1f565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561393b5761393b6133fe565b50029056fea264697066735822122070cb35c35fe342156a1eef2c384a2c61ed89450e492418ad80c6aa0f330278bc64736f6c63430008100033

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

000000000000000000000000308810881807189cae91950888b2cb73a1cc59200000000000000000000000004acd4bc402bc8e6ba8abddca639d8011ef0b8a4b

-----Decoded View---------------
Arg [0] : _governor (address): 0x308810881807189cAe91950888b2cB73A1CC5920
Arg [1] : _descriptor (address): 0x4ACd4BC402bc8e6BA8aBDdcA639d8011ef0b8a4b

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000308810881807189cae91950888b2cb73a1cc5920
Arg [1] : 0000000000000000000000004acd4bc402bc8e6ba8abddca639d8011ef0b8a4b


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