Address Details
contract

0x2A842A5C2BBb45a321Babd2F00D9D3E513d7b642

Contract Name
PoofMintableLendable
Creator
0x845975–fbc533 at 0x88e598–e08059
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
6 Transactions
Transfers
19 Transfers
Gas Used
5,705,492
Last Balance Update
24842790
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
PoofMintableLendable




Optimization enabled
true
Compiler version
v0.8.3+commit.8d00100c




Optimization runs
200
EVM Version
istanbul




Verified at
2022-08-08T23:07:32.035819Z

project:/contracts/erc20/PoofMintableLendable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

import "./PoofLendable.sol";
import "./../PToken.sol";
import "./../interfaces/IVerifier.sol";
import "./../interfaces/IWERC20.sol";

contract PoofMintableLendable is PoofLendable {
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  PToken public immutable pToken;

  constructor(
    IWERC20 _debtToken,
    IVerifier[5] memory _verifiers,
    bytes32 _accountRoot,
    PToken _pToken
  ) PoofLendable(_debtToken, _verifiers, _accountRoot) {
    pToken = _pToken;
  }

  function burn(bytes[3] memory _proofs, DepositArgs memory _args) external {
    burn(_proofs, _args, new bytes(0), TreeUpdateArgs(0, 0, 0, 0));
  }

  function burn(
    bytes[3] memory _proofs,
    DepositArgs memory _args,
    bytes memory _treeUpdateProof,
    TreeUpdateArgs memory _treeUpdateArgs
  ) public {
    beforeDeposit(_proofs, _args, _treeUpdateProof, _treeUpdateArgs);
    require(_args.amount == 0, "Cannot use amount for burning");
    pToken.burn(msg.sender, _args.debt);
  }

  function mint(bytes[3] memory _proofs, WithdrawArgs memory _args) external {
    mint(_proofs, _args, new bytes(0), TreeUpdateArgs(0, 0, 0, 0));
  }

  function mint(
    bytes[3] memory _proofs,
    WithdrawArgs memory _args,
    bytes memory _treeUpdateProof,
    TreeUpdateArgs memory _treeUpdateArgs
  ) public {
    beforeWithdraw(_proofs, _args, _treeUpdateProof, _treeUpdateArgs);
    require(_args.amount == _args.extData.fee, "Amount can only be used for fee");
    if (_args.amount > 0) {
      uint256 underlyingFeeAmount = debtToken.debtToUnderlying(_args.extData.fee);
      debtToken.unwrap(_args.amount);
      if (underlyingFeeAmount > 0) {
        underlyingToken.safeTransfer(_args.extData.relayer, underlyingFeeAmount);
      }
    }
    if (_args.debt > 0) {
      pToken.mint(_args.extData.recipient, _args.debt);
    }
  }

  function underlyingBalanceOf(address owner) external view returns (uint256) {
    uint256 balanceOf = pToken.balanceOf(owner);
    return debtToken.debtToUnderlying(balanceOf);
  }
}

        

/_openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
          

/_openzeppelin/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens 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 amount
    ) 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, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}
          

/_openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

/_openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

/_openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

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

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

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

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

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

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

/_openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/_openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

/_openzeppelin/contracts/utils/math/SafeMath.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}
          

/project_/contracts/PToken.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract PToken is ERC20, Ownable {
    mapping(address => bool) public supplyManagers;

    constructor(string memory symbol, string memory name) ERC20(symbol, name) {}

    event SupplyManagerAdded(address indexed supplyManager);
    event SupplyManagerRenounced(address indexed supplyManager);

    modifier onlySupplyManager() {
        require(supplyManagers[msg.sender], "PToken: caller is not a supply manager");
        _;
    }

    function mint(address _to, uint256 _amount) external onlySupplyManager {
        _mint(_to, _amount);
    }

    function burn(address _from, uint256 _amount) external onlySupplyManager {
        _burn(_from, _amount);
    }

    function addSupplyManager(address _supplyManager) external onlyOwner {
        supplyManagers[_supplyManager] = true;
        emit SupplyManagerAdded(_supplyManager);
    }

    function renounceSupplyManager(address _supplyManager) external onlyOwner {
        supplyManagers[_supplyManager] = false;
        emit SupplyManagerRenounced(_supplyManager);
    }
}
          

/project_/contracts/PoofBase.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "./interfaces/IVerifier.sol";

contract PoofBase is Ownable {
    using SafeMath for uint256;

    IVerifier public depositVerifier;
    IVerifier public withdrawVerifier;
    IVerifier public inputRootVerifier;
    IVerifier public outputRootVerifier;
    IVerifier public treeUpdateVerifier;

    mapping(bytes32 => bool) public accountNullifiers;

    uint256 public accountCount;
    uint256 public constant ACCOUNT_ROOT_HISTORY_SIZE = 100;
    bytes32[ACCOUNT_ROOT_HISTORY_SIZE] public accountRoots;

    event NewAccount(bytes32 commitment, bytes32 nullifier, bytes encryptedAccount, uint256 index);
    event VerifierUpdated(uint8 indexed verifierIdx, address previousVerifier, address nextVerifier);

    struct TreeUpdateArgs {
        bytes32 oldRoot;
        bytes32 newRoot;
        bytes32 leaf;
        uint256 pathIndices;
    }

    struct AccountUpdate {
        bytes32 inputRoot;
        bytes32 inputNullifierHash;
        bytes32 inputAccountHash;
        bytes32 outputRoot;
        uint256 outputPathIndices;
        bytes32 outputCommitment;
        bytes32 outputAccountHash;
    }

    struct DepositExtData {
        bytes encryptedAccount;
    }

    struct DepositArgs {
        uint256 amount;
        uint256 debt;
        uint256 unitPerUnderlying;
        bytes32 extDataHash;
        DepositExtData extData;
        AccountUpdate account;
    }

    struct WithdrawExtData {
        uint256 fee;
        address recipient;
        address relayer;
        bytes encryptedAccount;
    }

    struct WithdrawArgs {
        uint256 amount;
        uint256 debt;
        uint256 unitPerUnderlying;
        bytes32 extDataHash;
        WithdrawExtData extData;
        AccountUpdate account;
    }

    constructor(IVerifier[5] memory _verifiers, bytes32 _accountRoot) {
        accountRoots[0] = _accountRoot;
        _setVerifiers(_verifiers);
    }

    function setVerifiers(IVerifier[5] memory _verifiers) external onlyOwner {
        _setVerifiers(_verifiers);
    }

    function beforeDeposit(
        bytes[3] memory _proofs,
        DepositArgs memory _args,
        bytes memory _treeUpdateProof,
        TreeUpdateArgs memory _treeUpdateArgs
    ) internal {
        validateAccountUpdate(_args.account, _treeUpdateProof, _treeUpdateArgs);
        require(_args.extDataHash == keccak248(abi.encode(_args.extData)), "Incorrect external data hash");
        require(_args.unitPerUnderlying >= unitPerUnderlying(), "Underlying per unit is overstated");
        require(
            depositVerifier.verifyProof(
                _proofs[0],
                [
                    uint256(_args.amount),
                    uint256(_args.debt),
                    uint256(_args.unitPerUnderlying),
                    uint256(_args.extDataHash),
                    uint256(_args.account.inputAccountHash),
                    uint256(_args.account.outputAccountHash)
                ]
            ),
            "Invalid deposit proof"
        );
        require(
            inputRootVerifier.verifyProof(
                _proofs[1],
                [
                    uint256(_args.account.inputRoot),
                    uint256(_args.account.inputNullifierHash),
                    uint256(_args.account.inputAccountHash)
                ]
            ),
            "Invalid input root proof"
        );
        require(
            outputRootVerifier.verifyProof(
                _proofs[2],
                [
                    uint256(_args.account.inputRoot),
                    uint256(_args.account.outputRoot),
                    uint256(_args.account.outputPathIndices),
                    uint256(_args.account.outputCommitment),
                    uint256(_args.account.outputAccountHash)
                ]
            ),
            "Invalid output root proof"
        );

        accountNullifiers[_args.account.inputNullifierHash] = true;
        insertAccountRoot(_args.account.inputRoot == getLastAccountRoot() ? _args.account.outputRoot : _treeUpdateArgs.newRoot);

        emit NewAccount(
            _args.account.outputCommitment,
            _args.account.inputNullifierHash,
            _args.extData.encryptedAccount,
            accountCount - 1
        );
    }

    function beforeWithdraw(
        bytes[3] memory _proofs,
        WithdrawArgs memory _args,
        bytes memory _treeUpdateProof,
        TreeUpdateArgs memory _treeUpdateArgs
    ) internal {
        validateAccountUpdate(_args.account, _treeUpdateProof, _treeUpdateArgs);
        require(_args.extDataHash == keccak248(abi.encode(_args.extData)), "Incorrect external data hash");
        // Input check because zkSNARKs work modulo p
        require(_args.amount < 2**248, "Amount value out of range");
        require(_args.debt < 2**248, "Debt value out of range");
        require(_args.amount >= _args.extData.fee, "Amount should be >= than fee");
        require(_args.unitPerUnderlying >= unitPerUnderlying(), "Underlying per unit is overstated");
        require(
            withdrawVerifier.verifyProof(
                _proofs[0],
                [
                    uint256(_args.amount),
                    uint256(_args.debt),
                    uint256(_args.unitPerUnderlying),
                    uint256(_args.extDataHash),
                    uint256(_args.account.inputAccountHash),
                    uint256(_args.account.outputAccountHash)
                ]
            ),
            "Invalid withdrawal proof"
        );
        require(
            inputRootVerifier.verifyProof(
                _proofs[1],
                [
                    uint256(_args.account.inputRoot),
                    uint256(_args.account.inputNullifierHash),
                    uint256(_args.account.inputAccountHash)
                ]
            ),
            "Invalid input root proof"
        );
        require(
            outputRootVerifier.verifyProof(
                _proofs[2],
                [
                    uint256(_args.account.inputRoot),
                    uint256(_args.account.outputRoot),
                    uint256(_args.account.outputPathIndices),
                    uint256(_args.account.outputCommitment),
                    uint256(_args.account.outputAccountHash)
                ]
            ),
            "Invalid output root proof"
        );

        insertAccountRoot(_args.account.inputRoot == getLastAccountRoot() ? _args.account.outputRoot : _treeUpdateArgs.newRoot);
        accountNullifiers[_args.account.inputNullifierHash] = true;

        emit NewAccount(
            _args.account.outputCommitment,
            _args.account.inputNullifierHash,
            _args.extData.encryptedAccount,
            accountCount - 1
        );
    }

    // ------VIEW-------

    /**
    @dev Whether the root is present in the root history
    */
    function isKnownAccountRoot(bytes32 _root, uint256 _index) public view returns (bool) {
        return _root != 0 && accountRoots[_index % ACCOUNT_ROOT_HISTORY_SIZE] == _root;
    }

    /**
    @dev Returns the last root
    */
    function getLastAccountRoot() public view returns (bytes32) {
        return accountRoots[accountCount % ACCOUNT_ROOT_HISTORY_SIZE];
    }

    function unitPerUnderlying() public view virtual returns (uint256) {
        return 1;
    }

    // -----INTERNAL-------

    function keccak248(bytes memory _data) internal pure returns (bytes32) {
        return keccak256(_data) & 0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    }

    function validateTreeUpdate(
        bytes memory _proof,
        TreeUpdateArgs memory _args,
        bytes32 _commitment
    ) internal view {
        require(_proof.length > 0, "Outdated account merkle root");
        require(_args.oldRoot == getLastAccountRoot(), "Outdated tree update merkle root");
        require(_args.leaf == _commitment, "Incorrect commitment inserted");
        require(_args.pathIndices == accountCount, "Incorrect account insert index");
        require(
            treeUpdateVerifier.verifyProof(
                _proof,
                [uint256(_args.oldRoot), uint256(_args.newRoot), uint256(_args.leaf), uint256(_args.pathIndices)]
            ),
            "Invalid tree update proof"
        );
    }

    function validateAccountUpdate(
        AccountUpdate memory _account,
        bytes memory _treeUpdateProof,
        TreeUpdateArgs memory _treeUpdateArgs
    ) internal view {
        // Has to be a new nullifier hash
        require(!accountNullifiers[_account.inputNullifierHash], "Outdated account state");
        if (_account.inputRoot != getLastAccountRoot()) {
            // _account.outputPathIndices (= last tree leaf index) is always equal to root index in the history mapping
            // because we always generate a new root for each new leaf
            require(isKnownAccountRoot(_account.inputRoot, _account.outputPathIndices), "Invalid account root");
            validateTreeUpdate(_treeUpdateProof, _treeUpdateArgs, _account.outputCommitment);
        } else {
            require(_account.outputPathIndices == accountCount, "Incorrect account insert index");
        }
    }

    function insertAccountRoot(bytes32 _root) internal {
        accountRoots[++accountCount % ACCOUNT_ROOT_HISTORY_SIZE] = _root;
    }

    function _setVerifiers(IVerifier[5] memory _verifiers) internal {
        emit VerifierUpdated(0, address(depositVerifier), address(_verifiers[0]));
        emit VerifierUpdated(1, address(withdrawVerifier), address(_verifiers[1]));
        emit VerifierUpdated(2, address(inputRootVerifier), address(_verifiers[2]));
        emit VerifierUpdated(3, address(outputRootVerifier), address(_verifiers[3]));
        emit VerifierUpdated(4, address(treeUpdateVerifier), address(_verifiers[4]));
        depositVerifier = _verifiers[0];
        withdrawVerifier = _verifiers[1];
        inputRootVerifier = _verifiers[2];
        outputRootVerifier = _verifiers[3];
        treeUpdateVerifier = _verifiers[4];
    }
}
          

/project_/contracts/erc20/Poof.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

import "./../interfaces/IVerifier.sol";
import "./../PoofBase.sol";

contract Poof is PoofBase {
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  IERC20 public token;

  constructor(
    IERC20 _token,
    IVerifier[5] memory _verifiers,
    bytes32 _accountRoot
  ) PoofBase(_verifiers, _accountRoot) {
    token = _token;
  }

  function deposit(bytes[3] memory _proofs, DepositArgs memory _args) external virtual {
    require(_args.debt == 0, "Cannot use debt for depositing");
    deposit(_proofs, _args, new bytes(0), TreeUpdateArgs(0, 0, 0, 0));
  }

  function deposit(
    bytes[3] memory _proofs,
    DepositArgs memory _args,
    bytes memory _treeUpdateProof,
    TreeUpdateArgs memory _treeUpdateArgs
  ) public virtual {
    beforeDeposit(_proofs, _args, _treeUpdateProof, _treeUpdateArgs);
    token.safeTransferFrom(msg.sender, address(this), _args.amount);
  }

  function withdraw(bytes[3] memory _proofs, WithdrawArgs memory _args) external virtual {
    require(_args.debt == 0, "Cannot use debt for withdrawing");
    withdraw(_proofs, _args, new bytes(0), TreeUpdateArgs(0, 0, 0, 0));
  }

  function withdraw(
    bytes[3] memory _proofs,
    WithdrawArgs memory _args,
    bytes memory _treeUpdateProof,
    TreeUpdateArgs memory _treeUpdateArgs
  ) public virtual {
    beforeWithdraw(_proofs, _args, _treeUpdateProof, _treeUpdateArgs);
    uint256 amount = _args.amount.sub(_args.extData.fee, "Amount should be greater than fee");
    if (amount > 0) {
      token.safeTransfer(_args.extData.recipient, amount);
    }
    if (_args.extData.fee > 0) {
      token.safeTransfer(_args.extData.relayer, _args.extData.fee);
    }
  }
}
          

/project_/contracts/erc20/PoofLendable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

import "./Poof.sol";
import "./../interfaces/IVerifier.sol";
import "./../interfaces/IWERC20.sol";

contract PoofLendable is Poof {
  using SafeMath for uint256;
  using SafeERC20 for IERC20;

  IERC20 public underlyingToken;
  IWERC20 public debtToken;

  constructor(
    IWERC20 _debtToken,
    IVerifier[5] memory _verifiers,
    bytes32 _accountRoot
  ) Poof(_debtToken, _verifiers, _accountRoot) {
    underlyingToken = IERC20(_debtToken.underlyingToken());
    debtToken = _debtToken;
  }

  function deposit(bytes[3] memory _proofs, DepositArgs memory _args) external override {
    deposit(_proofs, _args, new bytes(0), TreeUpdateArgs(0, 0, 0, 0));
  }

  function deposit(
    bytes[3] memory _proofs,
    DepositArgs memory _args,
    bytes memory _treeUpdateProof,
    TreeUpdateArgs memory _treeUpdateArgs
  ) public override {
    beforeDeposit(_proofs, _args, _treeUpdateProof, _treeUpdateArgs);
    uint256 underlyingAmount = debtToken.debtToUnderlying(_args.amount);
    underlyingToken.safeTransferFrom(msg.sender, address(this), underlyingAmount);
    require(underlyingToken.approve(address(debtToken), underlyingAmount), "Approve failed");
    debtToken.wrap(underlyingAmount);
  }

  function withdraw(bytes[3] memory _proofs, WithdrawArgs memory _args) external override {
    withdraw(_proofs, _args, new bytes(0), TreeUpdateArgs(0, 0, 0, 0));
  }

  function withdraw(
    bytes[3] memory _proofs,
    WithdrawArgs memory _args,
    bytes memory _treeUpdateProof,
    TreeUpdateArgs memory _treeUpdateArgs
  ) public override {
    beforeWithdraw(_proofs, _args, _treeUpdateProof, _treeUpdateArgs);
    require(_args.amount >= _args.extData.fee, "Fee cannot be greater than amount");
    uint256 underlyingAmount = debtToken.debtToUnderlying(_args.amount.sub(_args.extData.fee));
    uint256 underlyingFeeAmount = debtToken.debtToUnderlying(_args.extData.fee);
    debtToken.unwrap(_args.amount);

    if (underlyingAmount > 0) {
      underlyingToken.safeTransfer(_args.extData.recipient, underlyingAmount);
    }
    if (underlyingFeeAmount > 0) {
      underlyingToken.safeTransfer(_args.extData.relayer, underlyingFeeAmount);
    }
  }

  function unitPerUnderlying() public view override returns (uint256) {
    return debtToken.underlyingToDebt(1);
  }
}

          

/project_/contracts/interfaces/IVerifier.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IVerifier {
  function verifyProof(bytes memory proof, uint[3] memory pubSignals) external view returns (bool);
  function verifyProof(bytes memory proof, uint[4] memory pubSignals) external view returns (bool);
  function verifyProof(bytes memory proof, uint[5] memory pubSignals) external view returns (bool);
  function verifyProof(bytes memory proof, uint[6] memory pubSignals) external view returns (bool);
}
          

/project_/contracts/interfaces/IWERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

interface IWERC20 is IERC20 {
  function wrap(uint256 underlyingAmount) external;

  function unwrap(uint256 debtAmount) external;

  function underlyingToDebt(uint256 underlyingAmount) external view returns (uint256);

  function debtToUnderlying(uint256 debtAmount) external view returns (uint256);

  function underlyingToken() external view returns (address);

  function underlyingBalanceOf(address owner) external view returns (uint256);
}

          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_debtToken","internalType":"contract IWERC20"},{"type":"address[5]","name":"_verifiers","internalType":"contract IVerifier[5]"},{"type":"bytes32","name":"_accountRoot","internalType":"bytes32"},{"type":"address","name":"_pToken","internalType":"contract PToken"}]},{"type":"event","name":"NewAccount","inputs":[{"type":"bytes32","name":"commitment","internalType":"bytes32","indexed":false},{"type":"bytes32","name":"nullifier","internalType":"bytes32","indexed":false},{"type":"bytes","name":"encryptedAccount","internalType":"bytes","indexed":false},{"type":"uint256","name":"index","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"VerifierUpdated","inputs":[{"type":"uint8","name":"verifierIdx","internalType":"uint8","indexed":true},{"type":"address","name":"previousVerifier","internalType":"address","indexed":false},{"type":"address","name":"nextVerifier","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ACCOUNT_ROOT_HISTORY_SIZE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"accountCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"accountNullifiers","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"accountRoots","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burn","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.DepositArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.DepositExtData","components":[{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]},{"type":"bytes","name":"_treeUpdateProof","internalType":"bytes"},{"type":"tuple","name":"_treeUpdateArgs","internalType":"struct PoofBase.TreeUpdateArgs","components":[{"type":"bytes32","name":"oldRoot","internalType":"bytes32"},{"type":"bytes32","name":"newRoot","internalType":"bytes32"},{"type":"bytes32","name":"leaf","internalType":"bytes32"},{"type":"uint256","name":"pathIndices","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burn","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.DepositArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.DepositExtData","components":[{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IWERC20"}],"name":"debtToken","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deposit","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.DepositArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.DepositExtData","components":[{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]},{"type":"bytes","name":"_treeUpdateProof","internalType":"bytes"},{"type":"tuple","name":"_treeUpdateArgs","internalType":"struct PoofBase.TreeUpdateArgs","components":[{"type":"bytes32","name":"oldRoot","internalType":"bytes32"},{"type":"bytes32","name":"newRoot","internalType":"bytes32"},{"type":"bytes32","name":"leaf","internalType":"bytes32"},{"type":"uint256","name":"pathIndices","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deposit","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.DepositArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.DepositExtData","components":[{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVerifier"}],"name":"depositVerifier","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getLastAccountRoot","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVerifier"}],"name":"inputRootVerifier","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isKnownAccountRoot","inputs":[{"type":"bytes32","name":"_root","internalType":"bytes32"},{"type":"uint256","name":"_index","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"mint","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.WithdrawArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.WithdrawExtData","components":[{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]},{"type":"bytes","name":"_treeUpdateProof","internalType":"bytes"},{"type":"tuple","name":"_treeUpdateArgs","internalType":"struct PoofBase.TreeUpdateArgs","components":[{"type":"bytes32","name":"oldRoot","internalType":"bytes32"},{"type":"bytes32","name":"newRoot","internalType":"bytes32"},{"type":"bytes32","name":"leaf","internalType":"bytes32"},{"type":"uint256","name":"pathIndices","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"mint","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.WithdrawArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.WithdrawExtData","components":[{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVerifier"}],"name":"outputRootVerifier","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract PToken"}],"name":"pToken","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setVerifiers","inputs":[{"type":"address[5]","name":"_verifiers","internalType":"contract IVerifier[5]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVerifier"}],"name":"treeUpdateVerifier","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"underlyingBalanceOf","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"underlyingToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"unitPerUnderlying","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.WithdrawArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.WithdrawExtData","components":[{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]},{"type":"bytes","name":"_treeUpdateProof","internalType":"bytes"},{"type":"tuple","name":"_treeUpdateArgs","internalType":"struct PoofBase.TreeUpdateArgs","components":[{"type":"bytes32","name":"oldRoot","internalType":"bytes32"},{"type":"bytes32","name":"newRoot","internalType":"bytes32"},{"type":"bytes32","name":"leaf","internalType":"bytes32"},{"type":"uint256","name":"pathIndices","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"bytes[3]","name":"_proofs","internalType":"bytes[3]"},{"type":"tuple","name":"_args","internalType":"struct PoofBase.WithdrawArgs","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"unitPerUnderlying","internalType":"uint256"},{"type":"bytes32","name":"extDataHash","internalType":"bytes32"},{"type":"tuple","name":"extData","internalType":"struct PoofBase.WithdrawExtData","components":[{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"bytes","name":"encryptedAccount","internalType":"bytes"}]},{"type":"tuple","name":"account","internalType":"struct PoofBase.AccountUpdate","components":[{"type":"bytes32","name":"inputRoot","internalType":"bytes32"},{"type":"bytes32","name":"inputNullifierHash","internalType":"bytes32"},{"type":"bytes32","name":"inputAccountHash","internalType":"bytes32"},{"type":"bytes32","name":"outputRoot","internalType":"bytes32"},{"type":"uint256","name":"outputPathIndices","internalType":"uint256"},{"type":"bytes32","name":"outputCommitment","internalType":"bytes32"},{"type":"bytes32","name":"outputAccountHash","internalType":"bytes32"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVerifier"}],"name":"withdrawVerifier","inputs":[]}]
              

Contract Creation Code

0x60a06040523480156200001157600080fd5b5060405162002f6438038062002f6483398101604081905262000034916200036b565b838383828282818162000047336200013a565b600881905562000057826200018a565b5050606c80546001600160a01b0319166001600160a01b03948516179055505060408051632495a59960e01b8152905191851691632495a59991600480820192602092909190829003018186803b158015620000b257600080fd5b505afa158015620000c7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ed919062000345565b606d80546001600160a01b03199081166001600160a01b0393841617909155606e80549091169490911693909317909255505060601b6001600160601b031916608052506200045d915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001548151604080516001600160a01b03938416815291909216602082015260009160008051602062002f44833981519152910160405180910390a2600254602080830151604080516001600160a01b039485168152919093169181019190915260019160008051602062002f44833981519152910160405180910390a260035460408083015181516001600160a01b0393841681529216602083015260029160008051602062002f44833981519152910160405180910390a26004546060820151604080516001600160a01b03938416815291909216602082015260039160008051602062002f44833981519152910160405180910390a26005546080820151604080516001600160a01b03938416815291909216602082015260049160008051602062002f44833981519152910160405180910390a28051600180546001600160a01b03199081166001600160a01b0393841617909155602083015160028054831691841691909117905560408301516003805483169184169190911790556060830151600480548316918416919091179055608090920151600580549093169116179055565b8051620003408162000444565b919050565b60006020828403121562000357578081fd5b8151620003648162000444565b9392505050565b600080600080610100858703121562000382578283fd5b84516200038f8162000444565b93506020603f86018713620003a2578384fd5b60405160a081016001600160401b0381118282101715620003c757620003c76200042e565b6040528087830160c089018a811115620003df578788fd5b875b60058110156200040957620003f68362000333565b84529285019291850191600101620003e1565b5051929650919450620004239250505060e0860162000333565b905092959194509250565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146200045a57600080fd5b50565b60805160601c612ab362000491600039600081816102e1015281816106f2015281816109400152610c5c0152612ab36000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c8063715018a611610104578063c6a4b053116100a2578063f15d0b3a11610071578063f15d0b3a146103d8578063f2fde38b146103eb578063f8d89898146103fe578063fc0c546a14610411576101cf565b8063c6a4b053146103a1578063cab9ecc2146103b4578063d1a0c300146103c7578063e4af29fc146103cf576101cf565b80638da5cb5b116100de5780638da5cb5b14610357578063935a8b841461036857806397f098d71461037b578063c14a0b621461038e576101cf565b8063715018a61461032957806371f43e3c14610331578063864eb16414610344576101cf565b806342a660b011610171578063538d2f801161014b578063538d2f80146102c957806358a06f07146102dc5780636e37f07714610303578063703c64d014610316576101cf565b806342a660b01461027b5780634a20de58146102ae57806352ef8e96146102c1576101cf565b806320d43b3b116101ad57806320d43b3b146102175780632495a5991461024257806326120c88146102555780632c58ae4e14610268576101cf565b80630af8d240146101d457806311beac24146101e95780631311b6e8146101fc575b600080fd5b6101e76101e23660046125e4565b610424565b005b6101e76101f73660046124f7565b610675565b61020461075d565b6040519081526020015b60405180910390f35b60035461022a906001600160a01b031681565b6040516001600160a01b03909116815260200161020e565b606d5461022a906001600160a01b031681565b60015461022a906001600160a01b031681565b6101e76102763660046125e4565b610795565b61029e6102893660046126c2565b60066020526000908152604090205460ff1681565b604051901515815260200161020e565b60055461022a906001600160a01b031681565b610204606481565b6101e76102d7366004612496565b61098d565b61022a7f000000000000000000000000000000000000000000000000000000000000000081565b6101e761031136600461263d565b6109c6565b6101e76103243660046124f7565b6109fc565b6101e7610bcd565b6101e761033f36600461258d565b610c03565b60025461022a906001600160a01b031681565b6000546001600160a01b031661022a565b61020461037636600461247a565b610c38565b61029e6103893660046126da565b610d5a565b6101e761039c366004612496565b610d9c565b6102046103af3660046126c2565b610dd1565b6101e76103c236600461258d565b610de8565b610204610e1d565b61020460075481565b60045461022a906001600160a01b031681565b6101e76103f936600461247a565b610e9f565b606e5461022a906001600160a01b031681565b606c5461022a906001600160a01b031681565b61043084848484610f37565b608083015151835110156104955760405162461bcd60e51b815260206004820152602160248201527f4665652063616e6e6f742062652067726561746572207468616e20616d6f756e6044820152601d60fa1b60648201526084015b60405180910390fd5b606e5460808401515184516000926001600160a01b0316916304626a64916104bc916114bf565b6040518263ffffffff1660e01b81526004016104da91815260200190565b60206040518083038186803b1580156104f257600080fd5b505afa158015610506573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061052a91906126fb565b606e546080860151516040516301189a9960e21b81529293506000926001600160a01b03909216916304626a64916105689160040190815260200190565b60206040518083038186803b15801561058057600080fd5b505afa158015610594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b891906126fb565b606e548651604051636f074d1f60e11b815260048101919091529192506001600160a01b03169063de0e9a3e90602401600060405180830381600087803b15801561060257600080fd5b505af1158015610616573d6000803e3d6000fd5b50505050600082111561064557608085015160200151606d54610645916001600160a01b0390911690846114cb565b801561066d57608085015160400151606d5461066d916001600160a01b0390911690836114cb565b505050505050565b61068184848484611533565b8251156106d05760405162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742075736520616d6f756e7420666f72206275726e696e67000000604482015260640161048c565b6020830151604051632770a7eb60e21b815233600482015260248101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639dc29fac906044015b600060405180830381600087803b15801561073f57600080fd5b505af1158015610753573d6000803e3d6000fd5b5050505050505050565b60006008606460075461077091906129fc565b6064811061078e57634e487b7160e01b600052603260045260246000fd5b0154905090565b6107a184848484610f37565b6080830151518351146107f65760405162461bcd60e51b815260206004820152601f60248201527f416d6f756e742063616e206f6e6c79206265207573656420666f722066656500604482015260640161048c565b82511561091057606e546080840151516040516301189a9960e21b815260048101919091526000916001600160a01b0316906304626a649060240160206040518083038186803b15801561084957600080fd5b505afa15801561085d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088191906126fb565b606e548551604051636f074d1f60e11b815260048101919091529192506001600160a01b03169063de0e9a3e90602401600060405180830381600087803b1580156108cb57600080fd5b505af11580156108df573d6000803e3d6000fd5b50505050600081111561090e57608084015160400151606d5461090e916001600160a01b0390911690836114cb565b505b602083015115610987576080830151602090810151908401516040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016926340c10f1992610725926004016001600160a01b03929092168252602082015260400190565b50505050565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c29184918491610675565b5050565b6000546001600160a01b031633146109f05760405162461bcd60e51b815260040161048c906128d6565b6109f981611953565b50565b610a0884848484611533565b606e5483516040516301189a9960e21b815260048101919091526000916001600160a01b0316906304626a649060240160206040518083038186803b158015610a5057600080fd5b505afa158015610a64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8891906126fb565b606d54909150610aa3906001600160a01b0316333084611af7565b606d54606e5460405163095ea7b360e01b81526001600160a01b0391821660048201526024810184905291169063095ea7b390604401602060405180830381600087803b158015610af357600080fd5b505af1158015610b07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2b91906126a2565b610b685760405162461bcd60e51b815260206004820152600e60248201526d105c1c1c9bdd994819985a5b195960921b604482015260640161048c565b606e54604051630ea598cb60e41b8152600481018390526001600160a01b039091169063ea598cb090602401600060405180830381600087803b158015610bae57600080fd5b505af1158015610bc2573d6000803e3d6000fd5b505050505050505050565b6000546001600160a01b03163314610bf75760405162461bcd60e51b815260040161048c906128d6565b610c016000611b2f565b565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c29184918491610795565b6040516370a0823160e01b81526001600160a01b03828116600483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906370a082319060240160206040518083038186803b158015610c9e57600080fd5b505afa158015610cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd691906126fb565b606e546040516301189a9960e21b8152600481018390529192506001600160a01b0316906304626a649060240160206040518083038186803b158015610d1b57600080fd5b505afa158015610d2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5391906126fb565b9392505050565b60008215801590610d535750826008610d746064856129fc565b60648110610d9257634e487b7160e01b600052603260045260246000fd5b0154149392505050565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c291849184916109fc565b60088160648110610de157600080fd5b0154905081565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c29184918491610424565b606e546040516307be638960e51b8152600160048201526000916001600160a01b03169063f7cc71209060240160206040518083038186803b158015610e6257600080fd5b505afa158015610e76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9a91906126fb565b905090565b6000546001600160a01b03163314610ec95760405162461bcd60e51b815260040161048c906128d6565b6001600160a01b038116610f2e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161048c565b6109f981611b2f565b610f468360a001518383611b7f565b610f818360800151604051602001610f5e9190612926565b60405160208183030381529060405280516020909101206001600160f81b031690565b836060015114610fd35760405162461bcd60e51b815260206004820152601c60248201527f496e636f72726563742065787465726e616c2064617461206861736800000000604482015260640161048c565b8251600160f81b116110275760405162461bcd60e51b815260206004820152601960248201527f416d6f756e742076616c7565206f7574206f662072616e676500000000000000604482015260640161048c565b600160f81b83602001511061107e5760405162461bcd60e51b815260206004820152601760248201527f446562742076616c7565206f7574206f662072616e6765000000000000000000604482015260640161048c565b608083015151835110156110d45760405162461bcd60e51b815260206004820152601c60248201527f416d6f756e742073686f756c64206265203e3d207468616e2066656500000000604482015260640161048c565b6110dc610e1d565b836040015110156110ff5760405162461bcd60e51b815260040161048c90612895565b60025484516040805160c08082018352875182526020888101519083015287830151828401526060808901519083015260a080890180518501516080850152519091015190820152905163695ef6f960e01b81526001600160a01b039093169263695ef6f992611173929091600401612847565b60206040518083038186803b15801561118b57600080fd5b505afa15801561119f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c391906126a2565b61120f5760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207769746864726177616c2070726f6f660000000000000000604482015260640161048c565b6003546020808601516040805160608101825260a0880180515182528051850151948201949094529251810151838201525163f219ed6f60e01b81526001600160a01b039093169263f219ed6f9261126a929160040161278b565b60206040518083038186803b15801561128257600080fd5b505afa158015611296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ba91906126a2565b6113015760405162461bcd60e51b815260206004820152601860248201527724b73b30b634b21034b7383aba103937b7ba10383937b7b360411b604482015260640161048c565b60048054604080870151815160a08082018452888101805151835280516060908101516020850152815160809081015185880152825190930151908401525160c00151908201529151630af9a23560e21b81526001600160a01b0390931693632be688d49361137193910161280c565b60206040518083038186803b15801561138957600080fd5b505afa15801561139d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c191906126a2565b6114095760405162461bcd60e51b815260206004820152601960248201527824b73b30b634b21037baba383aba103937b7ba10383937b7b360391b604482015260640161048c565b61143861141461075d565b60a08501515114611429578160200151611433565b8360a00151606001515b611caa565b60a0808401805160209081015160009081526006825260409020805460ff191660019081179091559151928301519201516080860151606001516007547fbd4a5ca11c6f082fd6d00dc9d1dced6ab22490039bc36154bfa748bf2bf7435594936114a19161299e565b6040516114b1949392919061275b565b60405180910390a150505050565b6000610d53828461299e565b6040516001600160a01b03831660248201526044810182905261152e90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611cf0565b505050565b6115428360a001518383611b7f565b61155a8360800151604051602001610f5e919061290b565b8360600151146115ac5760405162461bcd60e51b815260206004820152601c60248201527f496e636f72726563742065787465726e616c2064617461206861736800000000604482015260640161048c565b6115b4610e1d565b836040015110156115d75760405162461bcd60e51b815260040161048c90612895565b60015484516040805160c08082018352875182526020888101519083015287830151828401526060808901519083015260a080890180518501516080850152519091015190820152905163695ef6f960e01b81526001600160a01b039093169263695ef6f99261164b929091600401612847565b60206040518083038186803b15801561166357600080fd5b505afa158015611677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169b91906126a2565b6116df5760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103232b837b9b4ba10383937b7b360591b604482015260640161048c565b6003546020808601516040805160608101825260a0880180515182528051850151948201949094529251810151838201525163f219ed6f60e01b81526001600160a01b039093169263f219ed6f9261173a929160040161278b565b60206040518083038186803b15801561175257600080fd5b505afa158015611766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178a91906126a2565b6117d15760405162461bcd60e51b815260206004820152601860248201527724b73b30b634b21034b7383aba103937b7ba10383937b7b360411b604482015260640161048c565b60048054604080870151815160a08082018452888101805151835280516060908101516020850152815160809081015185880152825190930151908401525160c00151908201529151630af9a23560e21b81526001600160a01b0390931693632be688d49361184193910161280c565b60206040518083038186803b15801561185957600080fd5b505afa15801561186d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189191906126a2565b6118d95760405162461bcd60e51b815260206004820152601960248201527824b73b30b634b21037baba383aba103937b7ba10383937b7b360391b604482015260640161048c565b60a08301516020908101516000908152600690915260409020805460ff1916600117905561190861141461075d565b7fbd4a5ca11c6f082fd6d00dc9d1dced6ab22490039bc36154bfa748bf2bf743558360a0015160a001518460a001516020015185608001516000015160016007546114a1919061299e565b6001548151604080516001600160a01b039384168152919092166020820152600091600080516020612a5e833981519152910160405180910390a2600254602080830151604080516001600160a01b0394851681529190931691810191909152600191600080516020612a5e833981519152910160405180910390a260035460408083015181516001600160a01b03938416815292166020830152600291600080516020612a5e833981519152910160405180910390a26004546060820151604080516001600160a01b039384168152919092166020820152600391600080516020612a5e833981519152910160405180910390a26005546080820151604080516001600160a01b039384168152919092166020820152600491600080516020612a5e833981519152910160405180910390a28051600180546001600160a01b03199081166001600160a01b0393841617909155602083015160028054831691841691909117905560408301516003805483169184169190911790556060830151600480548316918416919091179055608090920151600580549093169116179055565b6040516001600160a01b03808516602483015283166044820152606481018290526109879085906323b872dd60e01b906084016114f7565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60208084015160009081526006909152604090205460ff1615611bdd5760405162461bcd60e51b81526020600482015260166024820152754f75746461746564206163636f756e7420737461746560501b604482015260640161048c565b611be561075d565b835114611c5557611bfe83600001518460800151610d5a565b611c415760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a59081858d8dbdd5b9d081c9bdbdd60621b604482015260640161048c565b611c5082828560a00151611dc2565b61152e565b60075483608001511461152e5760405162461bcd60e51b815260206004820152601e60248201527f496e636f7272656374206163636f756e7420696e7365727420696e6465780000604482015260640161048c565b8060086064600760008154611cbe906129e1565b9182905550611ccd91906129fc565b60648110611ceb57634e487b7160e01b600052603260045260246000fd5b015550565b6000611d45826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166120089092919063ffffffff16565b80519091501561152e5780806020019051810190611d6391906126a2565b61152e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161048c565b6000835111611e135760405162461bcd60e51b815260206004820152601c60248201527f4f75746461746564206163636f756e74206d65726b6c6520726f6f7400000000604482015260640161048c565b611e1b61075d565b825114611e6a5760405162461bcd60e51b815260206004820181905260248201527f4f75746461746564207472656520757064617465206d65726b6c6520726f6f74604482015260640161048c565b80826040015114611ebd5760405162461bcd60e51b815260206004820152601d60248201527f496e636f727265637420636f6d6d69746d656e7420696e736572746564000000604482015260640161048c565b600754826060015114611f125760405162461bcd60e51b815260206004820152601e60248201527f496e636f7272656374206163636f756e7420696e7365727420696e6465780000604482015260640161048c565b60055460408051608081018252845181526020808601519082015284820151818301526060808601519082015290516335f8315960e11b81526001600160a01b0390921691636bf062b291611f6c918791906004016127d1565b60206040518083038186803b158015611f8457600080fd5b505afa158015611f98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fbc91906126a2565b61152e5760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642074726565207570646174652070726f6f6600000000000000604482015260640161048c565b6060612017848460008561201f565b949350505050565b6060824710156120805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161048c565b843b6120ce5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161048c565b600080866001600160a01b031685876040516120ea919061273f565b60006040518083038185875af1925050503d8060008114612127576040519150601f19603f3d011682016040523d82523d6000602084013e61212c565b606091505b509150915061213c828286612147565b979650505050505050565b60608315612156575081610d53565b8251156121665782518084602001fd5b8160405162461bcd60e51b815260040161048c9190612882565b600082601f830112612190578081fd5b61219a606061296d565b8083835b60038110156121c9576121b487833588016121d4565b8452602093840193919091019060010161219e565b509095945050505050565b600082601f8301126121e4578081fd5b813567ffffffffffffffff8111156121fe576121fe612a32565b612211601f8201601f191660200161296d565b818152846020838601011115612225578283fd5b816020850160208301379081016020019190915292915050565b600060e08284031215612250578081fd5b61225a60e061296d565b9050813581526020820135602082015260408201356040820152606082013560608201526080820135608082015260a082013560a082015260c082013560c082015292915050565b600061018082840312156122b4578081fd5b6122be60c061296d565b905081358152602082013560208201526040820135604082015260608201356060820152608082013567ffffffffffffffff808211156122fd57600080fd5b908301906020828603121561231157600080fd5b61231b602061296d565b82358281111561232a57600080fd5b612336878286016121d4565b82525060808401525061234e90508360a0840161223f565b60a082015292915050565b60006080828403121561236a578081fd5b612374608061296d565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b600061018082840312156123b0578081fd5b6123ba60c061296d565b905081358152602082013560208201526040820135604082015260608201356060820152608082013567ffffffffffffffff808211156123f957600080fd5b908301906080828603121561240d57600080fd5b612417608061296d565b82358152602083013561242981612a48565b6020820152604083013561243c81612a48565b604082015260608301358281111561245357600080fd5b61245f878286016121d4565b60608301525060808401525061234e90508360a0840161223f565b60006020828403121561248b578081fd5b8135610d5381612a48565b600080604083850312156124a8578081fd5b823567ffffffffffffffff808211156124bf578283fd5b6124cb86838701612180565b935060208501359150808211156124e0578283fd5b506124ed858286016122a2565b9150509250929050565b60008060008060e0858703121561250c578182fd5b843567ffffffffffffffff80821115612523578384fd5b61252f88838901612180565b95506020870135915080821115612544578384fd5b612550888389016122a2565b94506040870135915080821115612565578384fd5b50612572878288016121d4565b9250506125828660608701612359565b905092959194509250565b6000806040838503121561259f578182fd5b823567ffffffffffffffff808211156125b6578384fd5b6125c286838701612180565b935060208501359150808211156125d7578283fd5b506124ed8582860161239e565b60008060008060e085870312156125f9578182fd5b843567ffffffffffffffff80821115612610578384fd5b61261c88838901612180565b95506020870135915080821115612631578384fd5b6125508883890161239e565b600060a0828403121561264e578081fd5b82601f83011261265c578081fd5b61266660a061296d565b80838560a086011115612677578384fd5b835b60058110156121c957813561268d81612a48565b84526020938401939190910190600101612679565b6000602082840312156126b3578081fd5b81518015158114610d53578182fd5b6000602082840312156126d3578081fd5b5035919050565b600080604083850312156126ec578182fd5b50508035926020909101359150565b60006020828403121561270c578081fd5b5051919050565b6000815180845261272b8160208601602086016129b5565b601f01601f19169290920160200192915050565b600082516127518184602087016129b5565b9190910192915050565b60008582528460208301526080604083015261277a6080830185612713565b905082606083015295945050505050565b60006080825261279e6080830185612713565b905060208083018460005b60038110156127c6578151835291830191908301906001016127a9565b505050509392505050565b600060a082526127e460a0830185612713565b905060208083018460005b60048110156127c6578151835291830191908301906001016127ef565b600060c0825261281f60c0830185612713565b905060208083018460005b60058110156127c65781518352918301919083019060010161282a565b600060e0825261285a60e0830185612713565b905060208083018460005b60068110156127c657815183529183019190830190600101612865565b600060208252610d536020830184612713565b60208082526021908201527f556e6465726c79696e672070657220756e6974206973206f76657273746174656040820152601960fa1b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006020825282516020808401526120176040840182612713565b60006020825282516020830152602083015160018060a01b0380821660408501528060408601511660608501525050606083015160808084015261201760a0840182612713565b604051601f8201601f1916810167ffffffffffffffff8111828210171561299657612996612a32565b604052919050565b6000828210156129b0576129b0612a1c565b500390565b60005b838110156129d05781810151838201526020016129b8565b838111156109875750506000910152565b60006000198214156129f5576129f5612a1c565b5060010190565b600082612a1757634e487b7160e01b81526012600452602481fd5b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146109f957600080fdfeed3e33a70ec4417f1f470c4efd7e210336fa1b2ac4182d8aa0b6b034897583d3a264697066735822122058c1df6233b22712e20f88a4f7f4e91c1f3def4c8cb02b41c62cc4d32d07d2e164736f6c63430008030033ed3e33a70ec4417f1f470c4efd7e210336fa1b2ac4182d8aa0b6b034897583d3000000000000000000000000542f6f3ab2a18a7831aac734d280374aa86f359600000000000000000000000094d3e7812abea685db58f6773fb790e8d1085d590000000000000000000000000a4196e058a93d1ad8ab0b7b040f9987b8b803a20000000000000000000000001cbaa874e59399f0b5016c2e00a5996a71901197000000000000000000000000f861f9e140d8111c8f8d473294236ef79ac7ae3c000000000000000000000000696d8336ab09cfb8e5d571e6f3e87046c1534b5616ace1a65b7534142f8cc1aad810b3d6a7a74ca905d9c275cb98ba57e509fc1000000000000000000000000051d1d8f59cfdf12a5a54892aedb1ee1683a6d8b6

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c8063715018a611610104578063c6a4b053116100a2578063f15d0b3a11610071578063f15d0b3a146103d8578063f2fde38b146103eb578063f8d89898146103fe578063fc0c546a14610411576101cf565b8063c6a4b053146103a1578063cab9ecc2146103b4578063d1a0c300146103c7578063e4af29fc146103cf576101cf565b80638da5cb5b116100de5780638da5cb5b14610357578063935a8b841461036857806397f098d71461037b578063c14a0b621461038e576101cf565b8063715018a61461032957806371f43e3c14610331578063864eb16414610344576101cf565b806342a660b011610171578063538d2f801161014b578063538d2f80146102c957806358a06f07146102dc5780636e37f07714610303578063703c64d014610316576101cf565b806342a660b01461027b5780634a20de58146102ae57806352ef8e96146102c1576101cf565b806320d43b3b116101ad57806320d43b3b146102175780632495a5991461024257806326120c88146102555780632c58ae4e14610268576101cf565b80630af8d240146101d457806311beac24146101e95780631311b6e8146101fc575b600080fd5b6101e76101e23660046125e4565b610424565b005b6101e76101f73660046124f7565b610675565b61020461075d565b6040519081526020015b60405180910390f35b60035461022a906001600160a01b031681565b6040516001600160a01b03909116815260200161020e565b606d5461022a906001600160a01b031681565b60015461022a906001600160a01b031681565b6101e76102763660046125e4565b610795565b61029e6102893660046126c2565b60066020526000908152604090205460ff1681565b604051901515815260200161020e565b60055461022a906001600160a01b031681565b610204606481565b6101e76102d7366004612496565b61098d565b61022a7f00000000000000000000000051d1d8f59cfdf12a5a54892aedb1ee1683a6d8b681565b6101e761031136600461263d565b6109c6565b6101e76103243660046124f7565b6109fc565b6101e7610bcd565b6101e761033f36600461258d565b610c03565b60025461022a906001600160a01b031681565b6000546001600160a01b031661022a565b61020461037636600461247a565b610c38565b61029e6103893660046126da565b610d5a565b6101e761039c366004612496565b610d9c565b6102046103af3660046126c2565b610dd1565b6101e76103c236600461258d565b610de8565b610204610e1d565b61020460075481565b60045461022a906001600160a01b031681565b6101e76103f936600461247a565b610e9f565b606e5461022a906001600160a01b031681565b606c5461022a906001600160a01b031681565b61043084848484610f37565b608083015151835110156104955760405162461bcd60e51b815260206004820152602160248201527f4665652063616e6e6f742062652067726561746572207468616e20616d6f756e6044820152601d60fa1b60648201526084015b60405180910390fd5b606e5460808401515184516000926001600160a01b0316916304626a64916104bc916114bf565b6040518263ffffffff1660e01b81526004016104da91815260200190565b60206040518083038186803b1580156104f257600080fd5b505afa158015610506573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061052a91906126fb565b606e546080860151516040516301189a9960e21b81529293506000926001600160a01b03909216916304626a64916105689160040190815260200190565b60206040518083038186803b15801561058057600080fd5b505afa158015610594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b891906126fb565b606e548651604051636f074d1f60e11b815260048101919091529192506001600160a01b03169063de0e9a3e90602401600060405180830381600087803b15801561060257600080fd5b505af1158015610616573d6000803e3d6000fd5b50505050600082111561064557608085015160200151606d54610645916001600160a01b0390911690846114cb565b801561066d57608085015160400151606d5461066d916001600160a01b0390911690836114cb565b505050505050565b61068184848484611533565b8251156106d05760405162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742075736520616d6f756e7420666f72206275726e696e67000000604482015260640161048c565b6020830151604051632770a7eb60e21b815233600482015260248101919091527f00000000000000000000000051d1d8f59cfdf12a5a54892aedb1ee1683a6d8b66001600160a01b031690639dc29fac906044015b600060405180830381600087803b15801561073f57600080fd5b505af1158015610753573d6000803e3d6000fd5b5050505050505050565b60006008606460075461077091906129fc565b6064811061078e57634e487b7160e01b600052603260045260246000fd5b0154905090565b6107a184848484610f37565b6080830151518351146107f65760405162461bcd60e51b815260206004820152601f60248201527f416d6f756e742063616e206f6e6c79206265207573656420666f722066656500604482015260640161048c565b82511561091057606e546080840151516040516301189a9960e21b815260048101919091526000916001600160a01b0316906304626a649060240160206040518083038186803b15801561084957600080fd5b505afa15801561085d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088191906126fb565b606e548551604051636f074d1f60e11b815260048101919091529192506001600160a01b03169063de0e9a3e90602401600060405180830381600087803b1580156108cb57600080fd5b505af11580156108df573d6000803e3d6000fd5b50505050600081111561090e57608084015160400151606d5461090e916001600160a01b0390911690836114cb565b505b602083015115610987576080830151602090810151908401516040516340c10f1960e01b81526001600160a01b037f00000000000000000000000051d1d8f59cfdf12a5a54892aedb1ee1683a6d8b616926340c10f1992610725926004016001600160a01b03929092168252602082015260400190565b50505050565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c29184918491610675565b5050565b6000546001600160a01b031633146109f05760405162461bcd60e51b815260040161048c906128d6565b6109f981611953565b50565b610a0884848484611533565b606e5483516040516301189a9960e21b815260048101919091526000916001600160a01b0316906304626a649060240160206040518083038186803b158015610a5057600080fd5b505afa158015610a64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8891906126fb565b606d54909150610aa3906001600160a01b0316333084611af7565b606d54606e5460405163095ea7b360e01b81526001600160a01b0391821660048201526024810184905291169063095ea7b390604401602060405180830381600087803b158015610af357600080fd5b505af1158015610b07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2b91906126a2565b610b685760405162461bcd60e51b815260206004820152600e60248201526d105c1c1c9bdd994819985a5b195960921b604482015260640161048c565b606e54604051630ea598cb60e41b8152600481018390526001600160a01b039091169063ea598cb090602401600060405180830381600087803b158015610bae57600080fd5b505af1158015610bc2573d6000803e3d6000fd5b505050505050505050565b6000546001600160a01b03163314610bf75760405162461bcd60e51b815260040161048c906128d6565b610c016000611b2f565b565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c29184918491610795565b6040516370a0823160e01b81526001600160a01b03828116600483015260009182917f00000000000000000000000051d1d8f59cfdf12a5a54892aedb1ee1683a6d8b616906370a082319060240160206040518083038186803b158015610c9e57600080fd5b505afa158015610cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd691906126fb565b606e546040516301189a9960e21b8152600481018390529192506001600160a01b0316906304626a649060240160206040518083038186803b158015610d1b57600080fd5b505afa158015610d2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5391906126fb565b9392505050565b60008215801590610d535750826008610d746064856129fc565b60648110610d9257634e487b7160e01b600052603260045260246000fd5b0154149392505050565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c291849184916109fc565b60088160648110610de157600080fd5b0154905081565b60408051600080825260a082018352602082018181529282018190526060820181905260808201526109c29184918491610424565b606e546040516307be638960e51b8152600160048201526000916001600160a01b03169063f7cc71209060240160206040518083038186803b158015610e6257600080fd5b505afa158015610e76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9a91906126fb565b905090565b6000546001600160a01b03163314610ec95760405162461bcd60e51b815260040161048c906128d6565b6001600160a01b038116610f2e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161048c565b6109f981611b2f565b610f468360a001518383611b7f565b610f818360800151604051602001610f5e9190612926565b60405160208183030381529060405280516020909101206001600160f81b031690565b836060015114610fd35760405162461bcd60e51b815260206004820152601c60248201527f496e636f72726563742065787465726e616c2064617461206861736800000000604482015260640161048c565b8251600160f81b116110275760405162461bcd60e51b815260206004820152601960248201527f416d6f756e742076616c7565206f7574206f662072616e676500000000000000604482015260640161048c565b600160f81b83602001511061107e5760405162461bcd60e51b815260206004820152601760248201527f446562742076616c7565206f7574206f662072616e6765000000000000000000604482015260640161048c565b608083015151835110156110d45760405162461bcd60e51b815260206004820152601c60248201527f416d6f756e742073686f756c64206265203e3d207468616e2066656500000000604482015260640161048c565b6110dc610e1d565b836040015110156110ff5760405162461bcd60e51b815260040161048c90612895565b60025484516040805160c08082018352875182526020888101519083015287830151828401526060808901519083015260a080890180518501516080850152519091015190820152905163695ef6f960e01b81526001600160a01b039093169263695ef6f992611173929091600401612847565b60206040518083038186803b15801561118b57600080fd5b505afa15801561119f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c391906126a2565b61120f5760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207769746864726177616c2070726f6f660000000000000000604482015260640161048c565b6003546020808601516040805160608101825260a0880180515182528051850151948201949094529251810151838201525163f219ed6f60e01b81526001600160a01b039093169263f219ed6f9261126a929160040161278b565b60206040518083038186803b15801561128257600080fd5b505afa158015611296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ba91906126a2565b6113015760405162461bcd60e51b815260206004820152601860248201527724b73b30b634b21034b7383aba103937b7ba10383937b7b360411b604482015260640161048c565b60048054604080870151815160a08082018452888101805151835280516060908101516020850152815160809081015185880152825190930151908401525160c00151908201529151630af9a23560e21b81526001600160a01b0390931693632be688d49361137193910161280c565b60206040518083038186803b15801561138957600080fd5b505afa15801561139d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c191906126a2565b6114095760405162461bcd60e51b815260206004820152601960248201527824b73b30b634b21037baba383aba103937b7ba10383937b7b360391b604482015260640161048c565b61143861141461075d565b60a08501515114611429578160200151611433565b8360a00151606001515b611caa565b60a0808401805160209081015160009081526006825260409020805460ff191660019081179091559151928301519201516080860151606001516007547fbd4a5ca11c6f082fd6d00dc9d1dced6ab22490039bc36154bfa748bf2bf7435594936114a19161299e565b6040516114b1949392919061275b565b60405180910390a150505050565b6000610d53828461299e565b6040516001600160a01b03831660248201526044810182905261152e90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611cf0565b505050565b6115428360a001518383611b7f565b61155a8360800151604051602001610f5e919061290b565b8360600151146115ac5760405162461bcd60e51b815260206004820152601c60248201527f496e636f72726563742065787465726e616c2064617461206861736800000000604482015260640161048c565b6115b4610e1d565b836040015110156115d75760405162461bcd60e51b815260040161048c90612895565b60015484516040805160c08082018352875182526020888101519083015287830151828401526060808901519083015260a080890180518501516080850152519091015190820152905163695ef6f960e01b81526001600160a01b039093169263695ef6f99261164b929091600401612847565b60206040518083038186803b15801561166357600080fd5b505afa158015611677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169b91906126a2565b6116df5760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103232b837b9b4ba10383937b7b360591b604482015260640161048c565b6003546020808601516040805160608101825260a0880180515182528051850151948201949094529251810151838201525163f219ed6f60e01b81526001600160a01b039093169263f219ed6f9261173a929160040161278b565b60206040518083038186803b15801561175257600080fd5b505afa158015611766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178a91906126a2565b6117d15760405162461bcd60e51b815260206004820152601860248201527724b73b30b634b21034b7383aba103937b7ba10383937b7b360411b604482015260640161048c565b60048054604080870151815160a08082018452888101805151835280516060908101516020850152815160809081015185880152825190930151908401525160c00151908201529151630af9a23560e21b81526001600160a01b0390931693632be688d49361184193910161280c565b60206040518083038186803b15801561185957600080fd5b505afa15801561186d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189191906126a2565b6118d95760405162461bcd60e51b815260206004820152601960248201527824b73b30b634b21037baba383aba103937b7ba10383937b7b360391b604482015260640161048c565b60a08301516020908101516000908152600690915260409020805460ff1916600117905561190861141461075d565b7fbd4a5ca11c6f082fd6d00dc9d1dced6ab22490039bc36154bfa748bf2bf743558360a0015160a001518460a001516020015185608001516000015160016007546114a1919061299e565b6001548151604080516001600160a01b039384168152919092166020820152600091600080516020612a5e833981519152910160405180910390a2600254602080830151604080516001600160a01b0394851681529190931691810191909152600191600080516020612a5e833981519152910160405180910390a260035460408083015181516001600160a01b03938416815292166020830152600291600080516020612a5e833981519152910160405180910390a26004546060820151604080516001600160a01b039384168152919092166020820152600391600080516020612a5e833981519152910160405180910390a26005546080820151604080516001600160a01b039384168152919092166020820152600491600080516020612a5e833981519152910160405180910390a28051600180546001600160a01b03199081166001600160a01b0393841617909155602083015160028054831691841691909117905560408301516003805483169184169190911790556060830151600480548316918416919091179055608090920151600580549093169116179055565b6040516001600160a01b03808516602483015283166044820152606481018290526109879085906323b872dd60e01b906084016114f7565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60208084015160009081526006909152604090205460ff1615611bdd5760405162461bcd60e51b81526020600482015260166024820152754f75746461746564206163636f756e7420737461746560501b604482015260640161048c565b611be561075d565b835114611c5557611bfe83600001518460800151610d5a565b611c415760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a59081858d8dbdd5b9d081c9bdbdd60621b604482015260640161048c565b611c5082828560a00151611dc2565b61152e565b60075483608001511461152e5760405162461bcd60e51b815260206004820152601e60248201527f496e636f7272656374206163636f756e7420696e7365727420696e6465780000604482015260640161048c565b8060086064600760008154611cbe906129e1565b9182905550611ccd91906129fc565b60648110611ceb57634e487b7160e01b600052603260045260246000fd5b015550565b6000611d45826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166120089092919063ffffffff16565b80519091501561152e5780806020019051810190611d6391906126a2565b61152e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161048c565b6000835111611e135760405162461bcd60e51b815260206004820152601c60248201527f4f75746461746564206163636f756e74206d65726b6c6520726f6f7400000000604482015260640161048c565b611e1b61075d565b825114611e6a5760405162461bcd60e51b815260206004820181905260248201527f4f75746461746564207472656520757064617465206d65726b6c6520726f6f74604482015260640161048c565b80826040015114611ebd5760405162461bcd60e51b815260206004820152601d60248201527f496e636f727265637420636f6d6d69746d656e7420696e736572746564000000604482015260640161048c565b600754826060015114611f125760405162461bcd60e51b815260206004820152601e60248201527f496e636f7272656374206163636f756e7420696e7365727420696e6465780000604482015260640161048c565b60055460408051608081018252845181526020808601519082015284820151818301526060808601519082015290516335f8315960e11b81526001600160a01b0390921691636bf062b291611f6c918791906004016127d1565b60206040518083038186803b158015611f8457600080fd5b505afa158015611f98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fbc91906126a2565b61152e5760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642074726565207570646174652070726f6f6600000000000000604482015260640161048c565b6060612017848460008561201f565b949350505050565b6060824710156120805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161048c565b843b6120ce5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161048c565b600080866001600160a01b031685876040516120ea919061273f565b60006040518083038185875af1925050503d8060008114612127576040519150601f19603f3d011682016040523d82523d6000602084013e61212c565b606091505b509150915061213c828286612147565b979650505050505050565b60608315612156575081610d53565b8251156121665782518084602001fd5b8160405162461bcd60e51b815260040161048c9190612882565b600082601f830112612190578081fd5b61219a606061296d565b8083835b60038110156121c9576121b487833588016121d4565b8452602093840193919091019060010161219e565b509095945050505050565b600082601f8301126121e4578081fd5b813567ffffffffffffffff8111156121fe576121fe612a32565b612211601f8201601f191660200161296d565b818152846020838601011115612225578283fd5b816020850160208301379081016020019190915292915050565b600060e08284031215612250578081fd5b61225a60e061296d565b9050813581526020820135602082015260408201356040820152606082013560608201526080820135608082015260a082013560a082015260c082013560c082015292915050565b600061018082840312156122b4578081fd5b6122be60c061296d565b905081358152602082013560208201526040820135604082015260608201356060820152608082013567ffffffffffffffff808211156122fd57600080fd5b908301906020828603121561231157600080fd5b61231b602061296d565b82358281111561232a57600080fd5b612336878286016121d4565b82525060808401525061234e90508360a0840161223f565b60a082015292915050565b60006080828403121561236a578081fd5b612374608061296d565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b600061018082840312156123b0578081fd5b6123ba60c061296d565b905081358152602082013560208201526040820135604082015260608201356060820152608082013567ffffffffffffffff808211156123f957600080fd5b908301906080828603121561240d57600080fd5b612417608061296d565b82358152602083013561242981612a48565b6020820152604083013561243c81612a48565b604082015260608301358281111561245357600080fd5b61245f878286016121d4565b60608301525060808401525061234e90508360a0840161223f565b60006020828403121561248b578081fd5b8135610d5381612a48565b600080604083850312156124a8578081fd5b823567ffffffffffffffff808211156124bf578283fd5b6124cb86838701612180565b935060208501359150808211156124e0578283fd5b506124ed858286016122a2565b9150509250929050565b60008060008060e0858703121561250c578182fd5b843567ffffffffffffffff80821115612523578384fd5b61252f88838901612180565b95506020870135915080821115612544578384fd5b612550888389016122a2565b94506040870135915080821115612565578384fd5b50612572878288016121d4565b9250506125828660608701612359565b905092959194509250565b6000806040838503121561259f578182fd5b823567ffffffffffffffff808211156125b6578384fd5b6125c286838701612180565b935060208501359150808211156125d7578283fd5b506124ed8582860161239e565b60008060008060e085870312156125f9578182fd5b843567ffffffffffffffff80821115612610578384fd5b61261c88838901612180565b95506020870135915080821115612631578384fd5b6125508883890161239e565b600060a0828403121561264e578081fd5b82601f83011261265c578081fd5b61266660a061296d565b80838560a086011115612677578384fd5b835b60058110156121c957813561268d81612a48565b84526020938401939190910190600101612679565b6000602082840312156126b3578081fd5b81518015158114610d53578182fd5b6000602082840312156126d3578081fd5b5035919050565b600080604083850312156126ec578182fd5b50508035926020909101359150565b60006020828403121561270c578081fd5b5051919050565b6000815180845261272b8160208601602086016129b5565b601f01601f19169290920160200192915050565b600082516127518184602087016129b5565b9190910192915050565b60008582528460208301526080604083015261277a6080830185612713565b905082606083015295945050505050565b60006080825261279e6080830185612713565b905060208083018460005b60038110156127c6578151835291830191908301906001016127a9565b505050509392505050565b600060a082526127e460a0830185612713565b905060208083018460005b60048110156127c6578151835291830191908301906001016127ef565b600060c0825261281f60c0830185612713565b905060208083018460005b60058110156127c65781518352918301919083019060010161282a565b600060e0825261285a60e0830185612713565b905060208083018460005b60068110156127c657815183529183019190830190600101612865565b600060208252610d536020830184612713565b60208082526021908201527f556e6465726c79696e672070657220756e6974206973206f76657273746174656040820152601960fa1b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006020825282516020808401526120176040840182612713565b60006020825282516020830152602083015160018060a01b0380821660408501528060408601511660608501525050606083015160808084015261201760a0840182612713565b604051601f8201601f1916810167ffffffffffffffff8111828210171561299657612996612a32565b604052919050565b6000828210156129b0576129b0612a1c565b500390565b60005b838110156129d05781810151838201526020016129b8565b838111156109875750506000910152565b60006000198214156129f5576129f5612a1c565b5060010190565b600082612a1757634e487b7160e01b81526012600452602481fd5b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146109f957600080fdfeed3e33a70ec4417f1f470c4efd7e210336fa1b2ac4182d8aa0b6b034897583d3a264697066735822122058c1df6233b22712e20f88a4f7f4e91c1f3def4c8cb02b41c62cc4d32d07d2e164736f6c63430008030033