false
true
0

Contract Address Details

0x28E72cF0A1e913fCc733d5d36f7cd99fF3307613

Contract Name
PoolFactory
Creator
0xa38d8d–c7c645 at 0xf42d58–67a922
Balance
0 tPLS
Tokens
Fetching tokens...
Transactions
3 Transactions
Transfers
0 Transfers
Gas Used
9,203,061
Last Balance Update
25351190
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
PoolFactory




Optimization enabled
true
Compiler version
v0.8.28+commit.7893614a




Optimization runs
200
EVM Version
paris




Verified at
2025-05-03T04:05:07.973585Z

Constructor Arguments

0x000000000000000000000000020b90462df25ae8d3c6bde1a9fc728964e44be900000000000000000000000059f373bdffa3aa3a579911c7ccbe55f80ecf8a060000000000000000000000000000000000000000000000000000000000000014

Arg [0] (address) : 0x020b90462df25ae8d3c6bde1a9fc728964e44be9
Arg [1] (address) : 0x59f373bdffa3aa3a579911c7ccbe55f80ecf8a06
Arg [2] (uint256) : 20

              

contracts/Classic/PoolFactory.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { ReentrancyGuard } from '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import { TornadoPool } from './TornadoPool.sol';
import { IPoolFactory } from './interfaces/IPoolFactory.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { PoolCreationFailed, PreviousPoolTreeLimitNotReached, PoolInputNotAllowed } from './Constants.sol';

contract PoolFactory is ReentrancyGuard, IPoolFactory {
    event PoolCreated(address indexed pool, address indexed asset, uint256 denomination);

    address public immutable verifier;
    address public immutable hasher;
    uint256 public immutable merkleHeight;
    uint256 public immutable maxTreeLimit;
    /**
     * a temporary variable to store the asset for the pool creation
     */
    address public asset;
    /**
     * a temporary variable to store the power for the pool creation
     */
    uint256 public power;
    // All existing pools grouped by asset and power (10**x).
    mapping(address => mapping(uint256 => address[])) public poolGroups;

    /// @notice Returns the list of pools under a given asset and power
    /// @param _asset That is being deposited and withdrawn from the pool
    /// @param _power Number x used in the equation 10**x to determine how much of the
    /// asset to deposit. This number is limited to 77
    /// @return pools List with all pool addresses that exist under the same asset and power
    function poolGroupByInput(address _asset, uint256 _power) external view returns (address[] memory pools) {
        pools = poolGroups[_asset][_power];
    }

    /// @notice Returns the length of the group list
    /// @param _asset That is being deposited and withdrawn from the pool
    /// @param _power Number x used in the equation 10**x to determine how much of the
    /// asset to deposit. This number is limited to 77
    /// @return length Of the pool group under the same asset and power
    function poolGroupLength(address _asset, uint256 _power) external view returns (uint256 length) {
        length = _poolGroupLength(_asset, _power);
    }

    function _poolGroupLength(address _asset, uint256 _power) internal view returns (uint256 length) {
        length = poolGroups[_asset][_power].length;
    }

    constructor(address _verifier, address _hasher, uint256 _merkleHeight) {
        verifier = _verifier;
        hasher = _hasher;
        merkleHeight = _merkleHeight;
        maxTreeLimit = 2 ** _merkleHeight;
    }

    /// @notice Creates a new TornadoPool. Anyone is allowed to create a new pool
    /// if particular combination of asset/power does not exist yet or if the previous pool
    /// has reached it's max tree limit - 1048576 insertions.
    /// @param _asset That is being deposited and withdrawn from the pool
    /// @param _power Number x used in the equation 10**x to determine how much of the
    /// asset to deposit. This number is limited to 77
    /// @return pool Address of the created pool
    function createPool(address _asset, uint256 _power) external payable returns (address pool) {
        if (_power > 77) {
            revert PoolInputNotAllowed();
        }
        // non-comprehensive erc20 filter
        if (_asset != address(0) && IERC20(_asset).totalSupply() == 0) {
            revert PoolInputNotAllowed();
        }
        uint256 denomination = 10 ** _power;
        uint256 len = _poolGroupLength(_asset, _power);
        // only allow to create next pool if the previous pool deposit tree limit is reached
        if (len > 0) {
            TornadoPool currentPool = TornadoPool(poolGroups[_asset][_power][len - 1]);
            if (currentPool.currentLeafIndex() > maxTreeLimit) {
                revert PreviousPoolTreeLimitNotReached();
            }
        }
        // this is the first pool
        bytes32 salt = keccak256(abi.encodePacked(_asset, _power));
        power = _power;
        asset = _asset;
        pool = address(_deploy(type(TornadoPool).creationCode, salt));
        delete power;
        delete asset;
        if (pool == address(0)) {
            revert PoolCreationFailed();
        }
        poolGroups[_asset][_power].push(pool);
        emit PoolCreated(pool, _asset, denomination);
    }

    function _deploy(bytes memory _initCode, bytes32 _salt) internal returns (address payable createdContract) {
        assembly {
            createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt)
        }
    }

    /**
     * @notice Deposit funds into the contract. The caller must send (for ETH)
     * or approve (for ERC20) value equal to or `denomination` of this instance.
     * @param _asset That is being deposited and withdrawn from the pool
     * @param _power Number x used in the equation 10**x to determine how much of the
     * asset to deposit. This number is limited to 77
     * @param _commitment the note commitment, which is PedersenHash(nullifier + secret)
     */
    function deposit(address _asset, uint256 _power, bytes32 _commitment) external payable {
        TornadoPool pool = TornadoPool(poolGroups[_asset][_power][_poolGroupLength(_asset, _power) - 1]);
        if (pool.currentLeafIndex() > maxTreeLimit) {
            pool = TornadoPool(this.createPool(_asset, _power));
        }
        if (_asset == address(0)) {
            pool.deposit{ value: 10 ** _power }(_commitment);
        } else {
            pool.deposit(_commitment);
        }
    }
}
        

contracts/Classic/interfaces/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
    function nonces(address owner) external view returns (uint256);
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
          

contracts/Classic/interfaces/IHasher.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IHasher {
    function MiMCSponge(uint256 in_xL, uint256 in_xR) external pure returns (uint256 xL, uint256 xR);
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

@openzeppelin/contracts/utils/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

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

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

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
          

contracts/Classic/Constants.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

error FeeExceedsDenomination();
error InvalidWithdrawalProof();
error InvalidMsgValue();
error InvalidRefundAmount();
error TransferFailed();
error UnknownRoot();
error ZeroAddress();
error RelayerMismatch();
error CallExpired();
error DenominationInvalid();
error NoteAlreadySpent();
error CommitmentAlreadySubmitted();
error PoolCreationFailed();
error PoolInputNotAllowed();
error PreviousPoolTreeLimitNotReached();
          

contracts/Classic/MerkleTreeWithHistory.sol

// https://tornado.ws
/*
 * d888888P                                           dP              a88888b.                   dP
 *    88                                              88             d8'   `88                   88
 *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
 *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
 *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
 *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
 * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
 */

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IHasher} from "./interfaces/IHasher.sol";

contract MerkleTreeWithHistory {
    uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
    uint256 public constant ZERO_VALUE = 21663839004416932945382355908790599225266501822907911457504978515578255421292; // = keccak256("tornado") % FIELD_SIZE

    IHasher public immutable hasher;
    uint32 public immutable levels;

    // the following variables are made public for easier testing and debugging and
    // are not supposed to be accessed in regular code

    // filledSubtrees, zeros, and roots could be bytes32[size], but using mappings makes it cheaper because
    // it removes index range check on every interaction
    mapping(uint256 => bytes32) public filledSubtrees;
    mapping(uint256 => bytes32) public zeros;
    mapping(uint256 => bytes32) public roots;
    uint32 public constant ROOT_HISTORY_SIZE = 30;
    uint32 public currentRootIndex = 0;
    uint32 public currentLeafIndex = 0;

    constructor(uint32 _levels, IHasher _hasher) {
        require(_levels > 0, "_levels should be greater than zero");
        require(_levels < 32, "_levels should be less than 32");
        levels = _levels;
        hasher = _hasher;

        bytes32 currentZero = bytes32(ZERO_VALUE);
        for (uint32 i = 0; i < _levels; i++) {
            zeros[i] = currentZero;
            filledSubtrees[i] = currentZero;
            currentZero = hashLeftRight(_hasher, currentZero, currentZero);
        }

        roots[0] = currentZero;
    }

    /**
     * @dev Hash 2 tree leaves, returns MiMC(_left, _right)
     */
    function hashLeftRight(IHasher _hasher, bytes32 _left, bytes32 _right) public pure returns (bytes32) {
        require(uint256(_left) < FIELD_SIZE, "_left should be inside the field");
        require(uint256(_right) < FIELD_SIZE, "_right should be inside the field");
        uint256 R = uint256(_left);
        uint256 C = 0;
        (R, C) = _hasher.MiMCSponge(R, C);
        R = addmod(R, uint256(_right), FIELD_SIZE);
        (R, C) = _hasher.MiMCSponge(R, C);
        return bytes32(R);
    }

    function _insert(bytes32 _leaf) internal returns (uint32 index) {
        uint32 _currentLeafIndex = currentLeafIndex;
        require(_currentLeafIndex != uint32(2) ** levels, "Merkle tree is full. No more leaves can be added");
        uint32 currentIndex = _currentLeafIndex;
        bytes32 currentLevelHash = _leaf;
        bytes32 left;
        bytes32 right;

        for (uint32 i = 0; i < levels; i++) {
            if (currentIndex % 2 == 0) {
                left = currentLevelHash;
                right = zeros[i];
                filledSubtrees[i] = currentLevelHash;
            } else {
                left = filledSubtrees[i];
                right = currentLevelHash;
            }
            currentLevelHash = hashLeftRight(hasher, left, right);
            currentIndex /= 2;
        }

        uint32 newRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;
        currentRootIndex = newRootIndex;
        roots[newRootIndex] = currentLevelHash;
        currentLeafIndex = _currentLeafIndex + 1;
        return _currentLeafIndex;
    }

    /**
     * @dev Whether the root is present in the root history
     */
    function isKnownRoot(bytes32 _root) public view returns (bool) {
        if (_root == 0) {
            return false;
        }
        uint32 _currentRootIndex = currentRootIndex;
        uint32 i = _currentRootIndex;
        do {
            if (_root == roots[i]) {
                return true;
            }
            if (i == 0) {
                i = ROOT_HISTORY_SIZE;
            }
            i--;
        } while (i != _currentRootIndex);
        return false;
    }

    /**
     * @dev Returns the last root
     */
    function getLastRoot() public view returns (bytes32) {
        return roots[currentRootIndex];
    }
}
          

contracts/Classic/Tornado.sol

// https://tornado.ws
/*
 * d888888P                                           dP              a88888b.                   dP
 *    88                                              88             d8'   `88                   88
 *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
 *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
 *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
 *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
 * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
 */

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IHasher, MerkleTreeWithHistory} from "./MerkleTreeWithHistory.sol";
import {ReentrancyGuard} from "./libraries/ReentrancyGuard.sol";
import {IVerifier} from "./interfaces/IVerifier.sol";
import {IPoolFactory} from "./interfaces/IPoolFactory.sol";
import {
    InvalidWithdrawalProof,
    FeeExceedsDenomination,
    UnknownRoot,
    NoteAlreadySpent,
    CallExpired,
    CommitmentAlreadySubmitted
} from "./Constants.sol";

abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
    IVerifier public immutable verifier;
    uint256 public immutable denomination;

    mapping(bytes32 => bool) public nullifierHashes;
    // we store all commitments just to prevent accidental deposits with the same commitment
    mapping(bytes32 => bool) public commitments;

    event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp);
    event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee);

    /**
     * @dev The constructor
     * @param _verifier the address of SNARK verifier for this contract
     * @param _hasher the address of MiMC hash contract
     * @param _denomination transfer amount for each deposit
     * @param _merkleTreeHeight the height of deposits' Merkle Tree
     */
    constructor(IVerifier _verifier, IHasher _hasher, uint256 _denomination, uint32 _merkleTreeHeight)
        MerkleTreeWithHistory(_merkleTreeHeight, _hasher)
    {
        require(_denomination > 0, "denomination should be greater than 0");
        verifier = _verifier;
        denomination = _denomination;
    }

    /**
     * @dev Deposit funds into the contract. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this instance.
     * @param _commitment the note commitment, which is PedersenHash(nullifier + secret)
     */
    function deposit(bytes32 _commitment) external payable nonReentrant {
        if (commitments[_commitment]) {
            revert CommitmentAlreadySubmitted();
        }

        uint32 insertedIndex = _insert(_commitment);
        commitments[_commitment] = true;
        _processDeposit();

        emit Deposit(_commitment, insertedIndex, block.timestamp);
    }

    /**
     * @dev this function is defined in a child contract
     */
    function _processDeposit() internal virtual;

    struct WithdrawalFromDepositor {
        bytes proof;
        bytes32 root;
        bytes32 nullifierHash;
        address payable recipient;
        address payable relayer;
        uint256 fee;
        uint256 refund;
    }

    struct WithdrawalInputs {
        WithdrawalFromDepositor fromDepositor;
        uint256 deadline;
        address relayer;
    }

    function _verifyWithdrawal(WithdrawalFromDepositor memory withdrawal) internal view {
        if (withdrawal.fee > denomination) {
            revert FeeExceedsDenomination();
        }
        if (!isKnownRoot(withdrawal.root)) {
            revert UnknownRoot();
        }
        if (
            !verifier.verifyProof(
                withdrawal.proof,
                [
                    uint256(withdrawal.root),
                    uint256(withdrawal.nullifierHash),
                    uint256(uint160(address(withdrawal.recipient))),
                    uint256(uint160(address(withdrawal.relayer))),
                    withdrawal.fee,
                    withdrawal.refund
                ]
            )
        ) {
            revert InvalidWithdrawalProof();
        }
    }

    function verifyWithdrawal(WithdrawalFromDepositor calldata withdrawal) external view {
        _verifyWithdrawal({withdrawal: withdrawal});
    }

    /**
     * @dev Withdraw a deposit from the contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
     * `input` array consists of:
     *   - merkle root of all deposits in the contract
     *   - hash of unique deposit nullifier to prevent double spends
     *   - the recipient of funds
     *   - optional fee that goes to the transaction sender (usually a relay)
     */
    function withdraw(WithdrawalInputs memory withdrawal) external payable nonReentrant {
        if (withdrawal.deadline < block.timestamp) {
            revert CallExpired();
        }
        bytes32 nullifierHash = withdrawal.fromDepositor.nullifierHash;
        if (nullifierHashes[nullifierHash]) {
            revert NoteAlreadySpent();
        }
        _verifyWithdrawal({withdrawal: withdrawal.fromDepositor});

        nullifierHashes[nullifierHash] = true;
        address relayer = withdrawal.fromDepositor.relayer;
        if (relayer == address(0)) {
            relayer = withdrawal.relayer;
        }
        _processWithdraw({
            _recipient: withdrawal.fromDepositor.recipient,
            _relayer: payable(relayer),
            _fee: withdrawal.fromDepositor.fee,
            _refund: withdrawal.fromDepositor.refund
        });
        emit Withdrawal({
            to: withdrawal.fromDepositor.recipient,
            nullifierHash: withdrawal.fromDepositor.nullifierHash,
            relayer: relayer,
            fee: withdrawal.fromDepositor.fee
        });
    }

    /**
     * @dev this function is defined in a child contract
     */
    function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund)
        internal
        virtual;
}
          

contracts/Classic/TornadoPool.sol

// https://tornado.ws
/*
 * d888888P                                           dP              a88888b.                   dP
 *    88                                              88             d8'   `88                   88
 *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
 *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
 *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
 *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
 * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
 */

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IVerifier, IHasher, Tornado} from "./Tornado.sol";
import {IERC20, SafeERC20} from "./libraries/SafeERC20.sol";
import {InvalidMsgValue, InvalidRefundAmount, TransferFailed} from "./Constants.sol";
import {IPoolFactory} from "./interfaces/IPoolFactory.sol";

contract TornadoPool is Tornado {
    using SafeERC20 for IERC20;

    address public immutable token;

    constructor()
        Tornado(
            IVerifier(IPoolFactory(msg.sender).verifier()),
            IHasher(IPoolFactory(msg.sender).hasher()),
            10 ** IPoolFactory(msg.sender).power(),
            uint32(IPoolFactory(msg.sender).merkleHeight())
        )
    {
        token = IPoolFactory(msg.sender).asset();
    }

    function _processDeposit() internal override {
        if (token == address(0)) {
            if (msg.value != denomination) {
                revert InvalidMsgValue();
            }
        } else {
            if (msg.value != 0) {
                revert InvalidMsgValue();
            }
            IERC20(token).safeTransferFrom(msg.sender, address(this), denomination);
        }
    }

    function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund)
        internal
        override
    {
        if (token == address(0)) {
            // sanity checks
            if (msg.value != 0) {
                revert InvalidMsgValue();
            }
            if (_refund != 0) {
                revert InvalidRefundAmount();
            }

            (bool success,) = _recipient.call{value: denomination - _fee}("");
            if (!success) {
                revert TransferFailed();
            }
            if (_fee > 0) {
                (success,) = _relayer.call{value: _fee}("");
                if (!success) {
                    revert TransferFailed();
                }
            }
        } else {
            if (msg.value != _refund) {
                revert InvalidRefundAmount();
            }

            IERC20(token).safeTransfer(_recipient, denomination - _fee);
            if (_fee > 0) {
                IERC20(token).safeTransfer(_relayer, _fee);
            }

            if (_refund > 0) {
                (bool success,) = _recipient.call{value: _refund}("");
                if (!success) {
                    // let's return _refund back to the relayer
                    _relayer.transfer(_refund);
                }
            }
        }
    }
}
          

contracts/Classic/interfaces/IPoolFactory.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

interface IPoolFactory {
    /**
     * @notice the verifier of the pool
     * @dev this value is only set during pool creation
     */
    function verifier() external view returns (address);
    /**
     * @notice the hasher of the pool
     * @dev this value is only set during pool creation
     */
    function hasher() external view returns (address);
    /**
     * @notice the height of the merkle tree
     * @dev this value is only set during pool creation
     */
    function merkleHeight() external view returns (uint256);
    /**
     * @notice the asset of the pool
     * @dev this value is only set during pool creation
     */
    function asset() external view returns (address);
    /**
     * @notice the power of the pool
     * @dev the power is the exponent of the denomination
     * @dev the denomination is 10 ** power
     * @dev this value is only set during pool creation
     */
    function power() external view returns (uint256);
}
          

contracts/Classic/interfaces/IVerifier.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IVerifier {
    function verifyProof(bytes memory _proof, uint256[6] memory _input) external view returns (bool);
}
          

contracts/Classic/libraries/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

contracts/Classic/libraries/SafeERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IERC20 } from '../interfaces/IERC20.sol';

library SafeERC20 {
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(token.transfer.selector, to, value)
        );
        // Return native revert data from token
        if (!success) {
            assembly {
                revert(add(32, data), mload(data))
            }
        }
        require(data.length == 0 || abi.decode(data, (bool)), 'SafeERC20: safeTransfer failed');
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
        );
        // Return native revert data from token
        if (!success) {
            assembly {
                revert(add(32, data), mload(data))
            }
        }
        require(data.length == 0 || abi.decode(data, (bool)), 'SafeERC20: safeTransferFrom failed');
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(token.approve.selector, spender, value)
        );
        // Return native revert data from token
        if (!success) {
            assembly {
                revert(add(32, data), mload(data))
            }
        }
        require(data.length == 0 || abi.decode(data, (bool)), 'SafeERC20: safeApprove failed');
    }
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"paris"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_verifier","internalType":"address"},{"type":"address","name":"_hasher","internalType":"address"},{"type":"uint256","name":"_merkleHeight","internalType":"uint256"}]},{"type":"error","name":"PoolCreationFailed","inputs":[]},{"type":"error","name":"PoolInputNotAllowed","inputs":[]},{"type":"error","name":"PreviousPoolTreeLimitNotReached","inputs":[]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"event","name":"PoolCreated","inputs":[{"type":"address","name":"pool","internalType":"address","indexed":true},{"type":"address","name":"asset","internalType":"address","indexed":true},{"type":"uint256","name":"denomination","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"asset","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"address","name":"pool","internalType":"address"}],"name":"createPool","inputs":[{"type":"address","name":"_asset","internalType":"address"},{"type":"uint256","name":"_power","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"deposit","inputs":[{"type":"address","name":"_asset","internalType":"address"},{"type":"uint256","name":"_power","internalType":"uint256"},{"type":"bytes32","name":"_commitment","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"hasher","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxTreeLimit","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"merkleHeight","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"pools","internalType":"address[]"}],"name":"poolGroupByInput","inputs":[{"type":"address","name":"_asset","internalType":"address"},{"type":"uint256","name":"_power","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"length","internalType":"uint256"}],"name":"poolGroupLength","inputs":[{"type":"address","name":"_asset","internalType":"address"},{"type":"uint256","name":"_power","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"poolGroups","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"power","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"verifier","inputs":[]}]
              

Contract Creation Code

0x61010060405234801561001157600080fd5b50604051612f0a380380612f0a83398101604081905261003091610080565b60016000556001600160a01b03808416608052821660a05260c08190526100588160026101bb565b60e052506101ce915050565b80516001600160a01b038116811461007b57600080fd5b919050565b60008060006060848603121561009557600080fd5b61009e84610064565b92506100ac60208501610064565b9150604084015190509250925092565b634e487b7160e01b600052601160045260246000fd5b6001815b600184111561010d578085048111156100f1576100f16100bc565b60018416156100ff57908102905b60019390931c9280026100d6565b935093915050565b600082610124575060016101b5565b81610131575060006101b5565b816001811461014757600281146101515761016d565b60019150506101b5565b60ff841115610162576101626100bc565b50506001821b6101b5565b5060208310610133831016604e8410600b8410161715610190575081810a6101b5565b61019d60001984846100d2565b80600019048211156101b1576101b16100bc565b0290505b92915050565b60006101c78383610115565b9392505050565b60805160a05160c05160e051612cf661021460003960008181610199015281816103a30152610607015260006101f1015260006102450152600060f80152612cf66000f3fe60806040526004361061009c5760003560e01c80633d6dfaf4116100645780633d6dfaf41461016757806341e703f0146101875780634a4d59fa146101c95780636ded7195146101df578063ec19946614610213578063ed33639f1461023357600080fd5b806312d36171146100a157806326b3293f146100d15780632b7ac3f3146100e657806338d52e0f1461011a5780633bae2f0f1461013a575b600080fd5b6100b46100af36600461091d565b610267565b6040516001600160a01b0390911681526020015b60405180910390f35b6100e46100df366004610949565b61059c565b005b3480156100f257600080fd5b506100b47f000000000000000000000000000000000000000000000000000000000000000081565b34801561012657600080fd5b506001546100b4906001600160a01b031681565b34801561014657600080fd5b5061015a61015536600461091d565b6107e7565b6040516100c8919061097e565b34801561017357600080fd5b506100b4610182366004610949565b610866565b34801561019357600080fd5b506101bb7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016100c8565b3480156101d557600080fd5b506101bb60025481565b3480156101eb57600080fd5b506101bb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561021f57600080fd5b506101bb61022e36600461091d565b6108ab565b34801561023f57600080fd5b506100b47f000000000000000000000000000000000000000000000000000000000000000081565b6000604d82111561028b5760405163573c86c160e01b815260040160405180910390fd5b6001600160a01b038316158015906103025750826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061030091906109ca565b155b156103205760405163573c86c160e01b815260040160405180910390fd5b600061032d83600a610ae2565b9050600061033b85856108be565b9050801561044b576001600160a01b03851660009081526003602090815260408083208784529091528120610371600184610aee565b8154811061038157610381610b01565b9060005260206000200160009054906101000a90046001600160a01b031690507f0000000000000000000000000000000000000000000000000000000000000000816001600160a01b03166353e553716040518163ffffffff1660e01b8152600401602060405180830381865afa158015610400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104249190610b17565b63ffffffff16111561044957604051630b630b8360e31b815260040160405180910390fd5b505b6040516bffffffffffffffffffffffff19606087901b1660208201526034810185905260009060540160408051808303601f190181529082905280516020918201206002889055600180546001600160a01b0319166001600160a01b038b1617905592506104d891906104bf9082016108f8565b601f1982820381018352601f90910116604052826108e5565b6000600255600180546001600160a01b031916905593506001600160a01b038416610516576040516337200b1d60e21b815260040160405180910390fd5b6001600160a01b0386811660008181526003602090815260408083208a8452825280832080546001810182559084529282902090920180546001600160a01b031916948916948517905590518681529192917ff8a0462f666b427ea753848be7e91f9ce413975906f6f39950be296ca9a4d524910160405180910390a350505092915050565b6001600160a01b0383166000908152600360209081526040808320858452909152812060016105cb86866108be565b6105d59190610aee565b815481106105e5576105e5610b01565b9060005260206000200160009054906101000a90046001600160a01b031690507f0000000000000000000000000000000000000000000000000000000000000000816001600160a01b03166353e553716040518163ffffffff1660e01b8152600401602060405180830381865afa158015610664573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106889190610b17565b63ffffffff161115610708576040516312d3617160e01b81526001600160a01b03851660048201526024810184905230906312d36171906044016020604051808303816000875af11580156106e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107059190610b3d565b90505b6001600160a01b038416610786576001600160a01b03811663b214faa561073085600a610ae2565b846040518363ffffffff1660e01b815260040161074f91815260200190565b6000604051808303818588803b15801561076857600080fd5b505af115801561077c573d6000803e3d6000fd5b50505050506107e1565b60405163b214faa560e01b8152600481018390526001600160a01b0382169063b214faa590602401600060405180830381600087803b1580156107c857600080fd5b505af11580156107dc573d6000803e3d6000fd5b505050505b50505050565b6001600160a01b038216600090815260036020908152604080832084845282529182902080548351818402810184019094528084526060939283018282801561085957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161083b575b5050505050905092915050565b6003602052826000526040600020602052816000526040600020818154811061088e57600080fd5b6000918252602090912001546001600160a01b0316925083915050565b60006108b783836108be565b9392505050565b6001600160a01b039091166000908152600360209081526040808320938352929052205490565b6000818351602085016000f59392505050565b61216680610b5b83390190565b6001600160a01b038116811461091a57600080fd5b50565b6000806040838503121561093057600080fd5b823561093b81610905565b946020939093013593505050565b60008060006060848603121561095e57600080fd5b833561096981610905565b95602085013595506040909401359392505050565b602080825282518282018190526000918401906040840190835b818110156109bf5783516001600160a01b0316835260209384019390920191600101610998565b509095945050505050565b6000602082840312156109dc57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6001815b6001841115610a3457808504811115610a1857610a186109e3565b6001841615610a2657908102905b60019390931c9280026109fd565b935093915050565b600082610a4b57506001610adc565b81610a5857506000610adc565b8160018114610a6e5760028114610a7857610a94565b6001915050610adc565b60ff841115610a8957610a896109e3565b50506001821b610adc565b5060208310610133831016604e8410600b8410161715610ab7575081810a610adc565b610ac460001984846109f9565b8060001904821115610ad857610ad86109e3565b0290505b92915050565b60006108b78383610a3c565b81810381811115610adc57610adc6109e3565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610b2957600080fd5b815163ffffffff811681146108b757600080fd5b600060208284031215610b4f57600080fd5b81516108b78161090556fe610120604052600380546001600160401b031916905534801561002157600080fd5b50336001600160a01b0316632b7ac3f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610060573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061008491906105dc565b336001600160a01b031663ed33639f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e691906105dc565b336001600160a01b0316634a4d59fa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610124573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610148919061060c565b61015390600a610724565b336001600160a01b0316636ded71956040518163ffffffff1660e01b8152600401602060405180830381865afa158015610191573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101b5919061060c565b808360008263ffffffff161161021e5760405162461bcd60e51b815260206004820152602360248201527f5f6c6576656c732073686f756c642062652067726561746572207468616e207a60448201526265726f60e81b60648201526084015b60405180910390fd5b60208263ffffffff16106102745760405162461bcd60e51b815260206004820152601e60248201527f5f6c6576656c732073686f756c64206265206c657373207468616e20333200006044820152606401610215565b63ffffffff821660a0526001600160a01b0381166080527f2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c60005b8363ffffffff168163ffffffff1610156102fc5763ffffffff811660009081526001602090815260408083208590559082905290208290556102f283838061040f565b91506001016102af565b506000805260026020527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b55505060016004558161038a5760405162461bcd60e51b815260206004820152602560248201527f64656e6f6d696e6174696f6e2073686f756c6420626520677265617465722074604482015264068616e20360dc1b6064820152608401610215565b506001600160a01b0390921660c0525060e052604080516338d52e0f60e01b8152905133916338d52e0f9160048083019260209291908290030181865afa1580156103d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103fd91906105dc565b6001600160a01b031661010052610754565b6000600080516020612146833981519152831061046e5760405162461bcd60e51b815260206004820181905260248201527f5f6c6566742073686f756c6420626520696e7369646520746865206669656c646044820152606401610215565b60008051602061214683398151915282106104d55760405162461bcd60e51b815260206004820152602160248201527f5f72696768742073686f756c6420626520696e7369646520746865206669656c6044820152601960fa1b6064820152608401610215565b60405163f47d33b560e01b81526004810184905260006024820181905284916001600160a01b0387169063f47d33b5906044016040805180830381865afa158015610524573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105489190610730565b909250905060008051602061214683398151915284830860405163f47d33b560e01b815260048101829052602481018390529092506001600160a01b0387169063f47d33b5906044016040805180830381865afa1580156105ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d19190610730565b509695505050505050565b6000602082840312156105ee57600080fd5b81516001600160a01b038116811461060557600080fd5b9392505050565b60006020828403121561061e57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6001815b60018411156106765780850481111561065a5761065a610625565b600184161561066857908102905b60019390931c92800261063f565b935093915050565b60008261068d5750600161071e565b8161069a5750600061071e565b81600181146106b057600281146106ba576106d6565b600191505061071e565b60ff8411156106cb576106cb610625565b50506001821b61071e565b5060208310610133831016604e8410600b84101617156106f9575081810a61071e565b610706600019848461063b565b806000190482111561071a5761071a610625565b0290505b92915050565b6000610605838361067e565b6000806040838503121561074357600080fd5b505080516020909101519092909150565b60805160a05160c05160e0516101005161194c6107fa600039600081816104a701528181610d3c01528181610dd601528181610e2001528181610ff901526110330152600081816102c0015281816109a301528181610d6a01528181610dfa01528181610e9c0152610fcb0152600081816101860152610a1201526000818161020201528181610b350152610bd70152600081816104460152610c6f015261194c6000f3fe60806040526004361061012a5760003560e01c8063a294753d116100ab578063cd87a3b41161006f578063cd87a3b4146103be578063e8295588146103d3578063ec73295914610400578063ed33639f14610434578063f178e47c14610468578063fc0c546a1461049557600080fd5b8063a294753d1461031f578063b214faa514610341578063ba70f75714610354578063c2b40ae41461037e578063c67c84f3146103ab57600080fd5b80636d9833e3116100f25780636d9833e31461025e578063839df9451461027e5780638bca6d16146102ae5780638ea3099e146102e257806390eeb02b1461030257600080fd5b806317cc915c1461012f5780632b7ac3f314610174578063414a37ba146101c05780634ecf518b146101f057806353e5537114610239575b600080fd5b34801561013b57600080fd5b5061015f61014a366004611340565b60056020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b34801561018057600080fd5b506101a87f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161016b565b3480156101cc57600080fd5b506101e26000805160206118f783398151915281565b60405190815260200161016b565b3480156101fc57600080fd5b506102247f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff909116815260200161016b565b34801561024557600080fd5b5060035461022490640100000000900463ffffffff1681565b34801561026a57600080fd5b5061015f610279366004611340565b6104c9565b34801561028a57600080fd5b5061015f610299366004611340565b60066020526000908152604090205460ff1681565b3480156102ba57600080fd5b506101e27f000000000000000000000000000000000000000000000000000000000000000081565b3480156102ee57600080fd5b506101e26102fd36600461136e565b610547565b34801561030e57600080fd5b506003546102249063ffffffff1681565b34801561032b57600080fd5b5061033f61033a3660046113a3565b610719565b005b61033f61034f366004611340565b61072d565b34801561036057600080fd5b5060035463ffffffff166000908152600260205260409020546101e2565b34801561038a57600080fd5b506101e2610399366004611340565b60026020526000908152604090205481565b61033f6103b9366004611557565b61082a565b3480156103ca57600080fd5b50610224601e81565b3480156103df57600080fd5b506101e26103ee366004611340565b60016020526000908152604090205481565b34801561040c57600080fd5b506101e27f2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c81565b34801561044057600080fd5b506101a87f000000000000000000000000000000000000000000000000000000000000000081565b34801561047457600080fd5b506101e2610483366004611340565b60006020819052908152604090205481565b3480156104a157600080fd5b506101a87f000000000000000000000000000000000000000000000000000000000000000081565b60008181036104da57506000919050565b60035463ffffffff16805b63ffffffff8116600090815260026020526040902054840361050b575060019392505050565b8063ffffffff1660000361051d5750601e5b806105278161161d565b9150508163ffffffff168163ffffffff16036104e5575060009392505050565b60006000805160206118f783398151915283106105ab5760405162461bcd60e51b815260206004820181905260248201527f5f6c6566742073686f756c6420626520696e7369646520746865206669656c6460448201526064015b60405180910390fd5b6000805160206118f783398151915282106106125760405162461bcd60e51b815260206004820152602160248201527f5f72696768742073686f756c6420626520696e7369646520746865206669656c6044820152601960fa1b60648201526084016105a2565b60405163f47d33b560e01b81526004810184905260006024820181905284916001600160a01b0387169063f47d33b5906044016040805180830381865afa158015610661573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610685919061163d565b90925090506000805160206118f783398151915284830860405163f47d33b560e01b815260048101829052602481018390529092506001600160a01b0387169063f47d33b5906044016040805180830381865afa1580156106ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070e919061163d565b509695505050505050565b61072a61072582611677565b6109a1565b50565b60026004540361077f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105a2565b600260045560008181526006602052604090205460ff16156107b357604051626a17dd60e61b815260040160405180910390fd5b60006107be82610b1c565b6000838152600660205260409020805460ff1916600117905590506107e1610d3a565b6040805163ffffffff8316815242602082015283917fa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196910160405180910390a250506001600455565b60026004540361087c5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105a2565b600260045560208101514211156108a65760405163c1b6b4bb60e01b815260040160405180910390fd5b805160409081015160008181526005602052919091205460ff16156108de576040516326c9d20d60e01b815260040160405180910390fd5b81516108e9906109a1565b6000818152600560205260409020805460ff191660011790558151608001516001600160a01b03811661091d575060408201515b8251606081015160a082015160c09092015161093a928491610e1e565b825160608082015160408084015160a09094015181516001600160a01b0393841681526020810195909552908401528316917fe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931910160405180910390a25050600160045550565b7f00000000000000000000000000000000000000000000000000000000000000008160a0015111156109e6576040516349609d8560e11b815260040160405180910390fd5b6109f381602001516104c9565b610a1057604051634629008b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663695ef6f982600001516040518060c00160405280856020015160001c8152602001856040015160001c815260200185606001516001600160a01b0316815260200185608001516001600160a01b031681526020018560a0015181526020018560c001518152506040518363ffffffff1660e01b8152600401610abe9291906116ad565b602060405180830381865afa158015610adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aff919061170d565b61072a57604051632e1aaf2760e21b815260040160405180910390fd5b600354600090640100000000900463ffffffff16610b5b7f00000000000000000000000000000000000000000000000000000000000000006002611843565b63ffffffff168163ffffffff1603610bce5760405162461bcd60e51b815260206004820152603060248201527f4d65726b6c6520747265652069732066756c6c2e204e6f206d6f7265206c656160448201526f1d995cc818d85b88189948185919195960821b60648201526084016105a2565b8083600080805b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168163ffffffff161015610cac57610c1460028661185b565b63ffffffff16600003610c4e5763ffffffff8116600090815260016020908152604080832054918390529091208590558493509150610c6a565b63ffffffff811660009081526020819052604090205492508391505b610c957f00000000000000000000000000000000000000000000000000000000000000008484610547565b9350610ca2600286611883565b9450600101610bd5565b50600354600090601e90610cc79063ffffffff1660016118ab565b610cd1919061185b565b6003805463ffffffff191663ffffffff831690811790915560009081526002602052604090208590559050610d078660016118ab565b6003805463ffffffff929092166401000000000267ffffffff000000001990921691909117905550939695505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610daa577f00000000000000000000000000000000000000000000000000000000000000003414610da857604051631841b4e160e01b815260040160405180910390fd5b565b3415610dc957604051631841b4e160e01b815260040160405180910390fd5b610da86001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633307f00000000000000000000000000000000000000000000000000000000000000006110fb565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610fa1573415610e6b57604051631841b4e160e01b815260040160405180910390fd5b8015610e8a576040516316365d5f60e01b815260040160405180910390fd5b60006001600160a01b038516610ec0847f00000000000000000000000000000000000000000000000000000000000000006118c7565b604051600081818185875af1925050503d8060008114610efc576040519150601f19603f3d011682016040523d82523d6000602084013e610f01565b606091505b5050905080610f23576040516312171d8360e31b815260040160405180910390fd5b8215610f9b576040516001600160a01b038516908490600081818185875af1925050503d8060008114610f72576040519150601f19603f3d011682016040523d82523d6000602084013e610f77565b606091505b50508091505080610f9b576040516312171d8360e31b815260040160405180910390fd5b506110f5565b803414610fc1576040516316365d5f60e01b815260040160405180910390fd5b61102084610fef847f00000000000000000000000000000000000000000000000000000000000000006118c7565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190611227565b811561105a5761105a6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168484611227565b80156110f5576000846001600160a01b03168260405160006040518083038185875af1925050503d80600081146110ad576040519150601f19603f3d011682016040523d82523d6000602084013e6110b2565b606091505b50509050806110f3576040516001600160a01b0385169083156108fc029084906000818181858888f193505050501580156110f1573d6000803e3d6000fd5b505b505b50505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161115f91906118da565b6000604051808303816000865af19150503d806000811461119c576040519150601f19603f3d011682016040523d82523d6000602084013e6111a1565b606091505b5091509150816111b357805181602001fd5b805115806111d05750808060200190518101906111d0919061170d565b6110f15760405162461bcd60e51b815260206004820152602260248201527f5361666545524332303a20736166655472616e7366657246726f6d206661696c604482015261195960f21b60648201526084016105a2565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b179052915160009283929087169161128391906118da565b6000604051808303816000865af19150503d80600081146112c0576040519150601f19603f3d011682016040523d82523d6000602084013e6112c5565b606091505b5091509150816112d757805181602001fd5b805115806112f45750808060200190518101906112f4919061170d565b6110f35760405162461bcd60e51b815260206004820152601e60248201527f5361666545524332303a20736166655472616e73666572206661696c6564000060448201526064016105a2565b60006020828403121561135257600080fd5b5035919050565b6001600160a01b038116811461072a57600080fd5b60008060006060848603121561138357600080fd5b833561138e81611359565b95602085013595506040909401359392505050565b6000602082840312156113b557600080fd5b813567ffffffffffffffff8111156113cc57600080fd5b820160e081850312156113de57600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561141e5761141e6113e5565b60405290565b600082601f83011261143557600080fd5b813567ffffffffffffffff81111561144f5761144f6113e5565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561147e5761147e6113e5565b60405281815283820160200185101561149657600080fd5b816020850160208301376000918101602001919091529392505050565b80356114be81611359565b919050565b600060e082840312156114d557600080fd5b6114dd6113fb565b9050813567ffffffffffffffff8111156114f657600080fd5b61150284828501611424565b8252506020828101359082015260408083013590820152611525606083016114b3565b6060820152611536608083016114b3565b608082015260a0828101359082015260c09182013591810191909152919050565b60006020828403121561156957600080fd5b813567ffffffffffffffff81111561158057600080fd5b82016060818503121561159257600080fd5b6040516060810167ffffffffffffffff811182821017156115b5576115b56113e5565b604052813567ffffffffffffffff8111156115cf57600080fd5b6115db868285016114c3565b82525060208281013590820152604090910135906115f882611359565b60408101919091529392505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff82168061163357611633611607565b6000190192915050565b6000806040838503121561165057600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601260045260246000fd5b600061168336836114c3565b92915050565b60005b838110156116a457818101518382015260200161168c565b50506000910152565b60e08152600083518060e08401526116cd81610100850160208801611689565b601f01601f19168201610100019050602082018360005b60068110156117035781518352602092830192909101906001016116e4565b5050509392505050565b60006020828403121561171f57600080fd5b815180151581146113de57600080fd5b6001815b600184111561176a5780850481111561174e5761174e611607565b600184161561175c57908102905b60019390931c928002611733565b935093915050565b60008261178157506001611683565b8161178e57506000611683565b81600181146117a457600281146117ae576117df565b6001915050611683565b60ff8411156117bf576117bf611607565b6001841b915063ffffffff8211156117d9576117d9611607565b50611683565b5060208310610133831016604e8410600b8410161715611816575081810a63ffffffff81111561181157611811611607565b611683565b61182563ffffffff848461172f565b8063ffffffff0482111561183b5761183b611607565b029392505050565b60006113de63ffffffff841663ffffffff8416611772565b600063ffffffff83168061187157611871611661565b8063ffffffff84160691505092915050565b600063ffffffff83168061189957611899611661565b8063ffffffff84160491505092915050565b63ffffffff818116838216019081111561168357611683611607565b8181038181111561168357611683611607565b600082516118ec818460208701611689565b919091019291505056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a2646970667358221220243ebd6330b960bd65385b8e8a53e3cc0705e88eb0269396024a501c27c6f55664736f6c634300081c003330644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a26469706673582212202067c6012c377ee28ceb62976bf750e6a2274ef71b50349cd5409a1d800b50d264736f6c634300081c0033000000000000000000000000020b90462df25ae8d3c6bde1a9fc728964e44be900000000000000000000000059f373bdffa3aa3a579911c7ccbe55f80ecf8a060000000000000000000000000000000000000000000000000000000000000014

Deployed ByteCode

0x60806040526004361061009c5760003560e01c80633d6dfaf4116100645780633d6dfaf41461016757806341e703f0146101875780634a4d59fa146101c95780636ded7195146101df578063ec19946614610213578063ed33639f1461023357600080fd5b806312d36171146100a157806326b3293f146100d15780632b7ac3f3146100e657806338d52e0f1461011a5780633bae2f0f1461013a575b600080fd5b6100b46100af36600461091d565b610267565b6040516001600160a01b0390911681526020015b60405180910390f35b6100e46100df366004610949565b61059c565b005b3480156100f257600080fd5b506100b47f000000000000000000000000020b90462df25ae8d3c6bde1a9fc728964e44be981565b34801561012657600080fd5b506001546100b4906001600160a01b031681565b34801561014657600080fd5b5061015a61015536600461091d565b6107e7565b6040516100c8919061097e565b34801561017357600080fd5b506100b4610182366004610949565b610866565b34801561019357600080fd5b506101bb7f000000000000000000000000000000000000000000000000000000000010000081565b6040519081526020016100c8565b3480156101d557600080fd5b506101bb60025481565b3480156101eb57600080fd5b506101bb7f000000000000000000000000000000000000000000000000000000000000001481565b34801561021f57600080fd5b506101bb61022e36600461091d565b6108ab565b34801561023f57600080fd5b506100b47f00000000000000000000000059f373bdffa3aa3a579911c7ccbe55f80ecf8a0681565b6000604d82111561028b5760405163573c86c160e01b815260040160405180910390fd5b6001600160a01b038316158015906103025750826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061030091906109ca565b155b156103205760405163573c86c160e01b815260040160405180910390fd5b600061032d83600a610ae2565b9050600061033b85856108be565b9050801561044b576001600160a01b03851660009081526003602090815260408083208784529091528120610371600184610aee565b8154811061038157610381610b01565b9060005260206000200160009054906101000a90046001600160a01b031690507f0000000000000000000000000000000000000000000000000000000000100000816001600160a01b03166353e553716040518163ffffffff1660e01b8152600401602060405180830381865afa158015610400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104249190610b17565b63ffffffff16111561044957604051630b630b8360e31b815260040160405180910390fd5b505b6040516bffffffffffffffffffffffff19606087901b1660208201526034810185905260009060540160408051808303601f190181529082905280516020918201206002889055600180546001600160a01b0319166001600160a01b038b1617905592506104d891906104bf9082016108f8565b601f1982820381018352601f90910116604052826108e5565b6000600255600180546001600160a01b031916905593506001600160a01b038416610516576040516337200b1d60e21b815260040160405180910390fd5b6001600160a01b0386811660008181526003602090815260408083208a8452825280832080546001810182559084529282902090920180546001600160a01b031916948916948517905590518681529192917ff8a0462f666b427ea753848be7e91f9ce413975906f6f39950be296ca9a4d524910160405180910390a350505092915050565b6001600160a01b0383166000908152600360209081526040808320858452909152812060016105cb86866108be565b6105d59190610aee565b815481106105e5576105e5610b01565b9060005260206000200160009054906101000a90046001600160a01b031690507f0000000000000000000000000000000000000000000000000000000000100000816001600160a01b03166353e553716040518163ffffffff1660e01b8152600401602060405180830381865afa158015610664573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106889190610b17565b63ffffffff161115610708576040516312d3617160e01b81526001600160a01b03851660048201526024810184905230906312d36171906044016020604051808303816000875af11580156106e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107059190610b3d565b90505b6001600160a01b038416610786576001600160a01b03811663b214faa561073085600a610ae2565b846040518363ffffffff1660e01b815260040161074f91815260200190565b6000604051808303818588803b15801561076857600080fd5b505af115801561077c573d6000803e3d6000fd5b50505050506107e1565b60405163b214faa560e01b8152600481018390526001600160a01b0382169063b214faa590602401600060405180830381600087803b1580156107c857600080fd5b505af11580156107dc573d6000803e3d6000fd5b505050505b50505050565b6001600160a01b038216600090815260036020908152604080832084845282529182902080548351818402810184019094528084526060939283018282801561085957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161083b575b5050505050905092915050565b6003602052826000526040600020602052816000526040600020818154811061088e57600080fd5b6000918252602090912001546001600160a01b0316925083915050565b60006108b783836108be565b9392505050565b6001600160a01b039091166000908152600360209081526040808320938352929052205490565b6000818351602085016000f59392505050565b61216680610b5b83390190565b6001600160a01b038116811461091a57600080fd5b50565b6000806040838503121561093057600080fd5b823561093b81610905565b946020939093013593505050565b60008060006060848603121561095e57600080fd5b833561096981610905565b95602085013595506040909401359392505050565b602080825282518282018190526000918401906040840190835b818110156109bf5783516001600160a01b0316835260209384019390920191600101610998565b509095945050505050565b6000602082840312156109dc57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6001815b6001841115610a3457808504811115610a1857610a186109e3565b6001841615610a2657908102905b60019390931c9280026109fd565b935093915050565b600082610a4b57506001610adc565b81610a5857506000610adc565b8160018114610a6e5760028114610a7857610a94565b6001915050610adc565b60ff841115610a8957610a896109e3565b50506001821b610adc565b5060208310610133831016604e8410600b8410161715610ab7575081810a610adc565b610ac460001984846109f9565b8060001904821115610ad857610ad86109e3565b0290505b92915050565b60006108b78383610a3c565b81810381811115610adc57610adc6109e3565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610b2957600080fd5b815163ffffffff811681146108b757600080fd5b600060208284031215610b4f57600080fd5b81516108b78161090556fe610120604052600380546001600160401b031916905534801561002157600080fd5b50336001600160a01b0316632b7ac3f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610060573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061008491906105dc565b336001600160a01b031663ed33639f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e691906105dc565b336001600160a01b0316634a4d59fa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610124573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610148919061060c565b61015390600a610724565b336001600160a01b0316636ded71956040518163ffffffff1660e01b8152600401602060405180830381865afa158015610191573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101b5919061060c565b808360008263ffffffff161161021e5760405162461bcd60e51b815260206004820152602360248201527f5f6c6576656c732073686f756c642062652067726561746572207468616e207a60448201526265726f60e81b60648201526084015b60405180910390fd5b60208263ffffffff16106102745760405162461bcd60e51b815260206004820152601e60248201527f5f6c6576656c732073686f756c64206265206c657373207468616e20333200006044820152606401610215565b63ffffffff821660a0526001600160a01b0381166080527f2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c60005b8363ffffffff168163ffffffff1610156102fc5763ffffffff811660009081526001602090815260408083208590559082905290208290556102f283838061040f565b91506001016102af565b506000805260026020527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b55505060016004558161038a5760405162461bcd60e51b815260206004820152602560248201527f64656e6f6d696e6174696f6e2073686f756c6420626520677265617465722074604482015264068616e20360dc1b6064820152608401610215565b506001600160a01b0390921660c0525060e052604080516338d52e0f60e01b8152905133916338d52e0f9160048083019260209291908290030181865afa1580156103d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103fd91906105dc565b6001600160a01b031661010052610754565b6000600080516020612146833981519152831061046e5760405162461bcd60e51b815260206004820181905260248201527f5f6c6566742073686f756c6420626520696e7369646520746865206669656c646044820152606401610215565b60008051602061214683398151915282106104d55760405162461bcd60e51b815260206004820152602160248201527f5f72696768742073686f756c6420626520696e7369646520746865206669656c6044820152601960fa1b6064820152608401610215565b60405163f47d33b560e01b81526004810184905260006024820181905284916001600160a01b0387169063f47d33b5906044016040805180830381865afa158015610524573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105489190610730565b909250905060008051602061214683398151915284830860405163f47d33b560e01b815260048101829052602481018390529092506001600160a01b0387169063f47d33b5906044016040805180830381865afa1580156105ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d19190610730565b509695505050505050565b6000602082840312156105ee57600080fd5b81516001600160a01b038116811461060557600080fd5b9392505050565b60006020828403121561061e57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6001815b60018411156106765780850481111561065a5761065a610625565b600184161561066857908102905b60019390931c92800261063f565b935093915050565b60008261068d5750600161071e565b8161069a5750600061071e565b81600181146106b057600281146106ba576106d6565b600191505061071e565b60ff8411156106cb576106cb610625565b50506001821b61071e565b5060208310610133831016604e8410600b84101617156106f9575081810a61071e565b610706600019848461063b565b806000190482111561071a5761071a610625565b0290505b92915050565b6000610605838361067e565b6000806040838503121561074357600080fd5b505080516020909101519092909150565b60805160a05160c05160e0516101005161194c6107fa600039600081816104a701528181610d3c01528181610dd601528181610e2001528181610ff901526110330152600081816102c0015281816109a301528181610d6a01528181610dfa01528181610e9c0152610fcb0152600081816101860152610a1201526000818161020201528181610b350152610bd70152600081816104460152610c6f015261194c6000f3fe60806040526004361061012a5760003560e01c8063a294753d116100ab578063cd87a3b41161006f578063cd87a3b4146103be578063e8295588146103d3578063ec73295914610400578063ed33639f14610434578063f178e47c14610468578063fc0c546a1461049557600080fd5b8063a294753d1461031f578063b214faa514610341578063ba70f75714610354578063c2b40ae41461037e578063c67c84f3146103ab57600080fd5b80636d9833e3116100f25780636d9833e31461025e578063839df9451461027e5780638bca6d16146102ae5780638ea3099e146102e257806390eeb02b1461030257600080fd5b806317cc915c1461012f5780632b7ac3f314610174578063414a37ba146101c05780634ecf518b146101f057806353e5537114610239575b600080fd5b34801561013b57600080fd5b5061015f61014a366004611340565b60056020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b34801561018057600080fd5b506101a87f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161016b565b3480156101cc57600080fd5b506101e26000805160206118f783398151915281565b60405190815260200161016b565b3480156101fc57600080fd5b506102247f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff909116815260200161016b565b34801561024557600080fd5b5060035461022490640100000000900463ffffffff1681565b34801561026a57600080fd5b5061015f610279366004611340565b6104c9565b34801561028a57600080fd5b5061015f610299366004611340565b60066020526000908152604090205460ff1681565b3480156102ba57600080fd5b506101e27f000000000000000000000000000000000000000000000000000000000000000081565b3480156102ee57600080fd5b506101e26102fd36600461136e565b610547565b34801561030e57600080fd5b506003546102249063ffffffff1681565b34801561032b57600080fd5b5061033f61033a3660046113a3565b610719565b005b61033f61034f366004611340565b61072d565b34801561036057600080fd5b5060035463ffffffff166000908152600260205260409020546101e2565b34801561038a57600080fd5b506101e2610399366004611340565b60026020526000908152604090205481565b61033f6103b9366004611557565b61082a565b3480156103ca57600080fd5b50610224601e81565b3480156103df57600080fd5b506101e26103ee366004611340565b60016020526000908152604090205481565b34801561040c57600080fd5b506101e27f2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c81565b34801561044057600080fd5b506101a87f000000000000000000000000000000000000000000000000000000000000000081565b34801561047457600080fd5b506101e2610483366004611340565b60006020819052908152604090205481565b3480156104a157600080fd5b506101a87f000000000000000000000000000000000000000000000000000000000000000081565b60008181036104da57506000919050565b60035463ffffffff16805b63ffffffff8116600090815260026020526040902054840361050b575060019392505050565b8063ffffffff1660000361051d5750601e5b806105278161161d565b9150508163ffffffff168163ffffffff16036104e5575060009392505050565b60006000805160206118f783398151915283106105ab5760405162461bcd60e51b815260206004820181905260248201527f5f6c6566742073686f756c6420626520696e7369646520746865206669656c6460448201526064015b60405180910390fd5b6000805160206118f783398151915282106106125760405162461bcd60e51b815260206004820152602160248201527f5f72696768742073686f756c6420626520696e7369646520746865206669656c6044820152601960fa1b60648201526084016105a2565b60405163f47d33b560e01b81526004810184905260006024820181905284916001600160a01b0387169063f47d33b5906044016040805180830381865afa158015610661573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610685919061163d565b90925090506000805160206118f783398151915284830860405163f47d33b560e01b815260048101829052602481018390529092506001600160a01b0387169063f47d33b5906044016040805180830381865afa1580156106ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070e919061163d565b509695505050505050565b61072a61072582611677565b6109a1565b50565b60026004540361077f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105a2565b600260045560008181526006602052604090205460ff16156107b357604051626a17dd60e61b815260040160405180910390fd5b60006107be82610b1c565b6000838152600660205260409020805460ff1916600117905590506107e1610d3a565b6040805163ffffffff8316815242602082015283917fa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196910160405180910390a250506001600455565b60026004540361087c5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105a2565b600260045560208101514211156108a65760405163c1b6b4bb60e01b815260040160405180910390fd5b805160409081015160008181526005602052919091205460ff16156108de576040516326c9d20d60e01b815260040160405180910390fd5b81516108e9906109a1565b6000818152600560205260409020805460ff191660011790558151608001516001600160a01b03811661091d575060408201515b8251606081015160a082015160c09092015161093a928491610e1e565b825160608082015160408084015160a09094015181516001600160a01b0393841681526020810195909552908401528316917fe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931910160405180910390a25050600160045550565b7f00000000000000000000000000000000000000000000000000000000000000008160a0015111156109e6576040516349609d8560e11b815260040160405180910390fd5b6109f381602001516104c9565b610a1057604051634629008b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663695ef6f982600001516040518060c00160405280856020015160001c8152602001856040015160001c815260200185606001516001600160a01b0316815260200185608001516001600160a01b031681526020018560a0015181526020018560c001518152506040518363ffffffff1660e01b8152600401610abe9291906116ad565b602060405180830381865afa158015610adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aff919061170d565b61072a57604051632e1aaf2760e21b815260040160405180910390fd5b600354600090640100000000900463ffffffff16610b5b7f00000000000000000000000000000000000000000000000000000000000000006002611843565b63ffffffff168163ffffffff1603610bce5760405162461bcd60e51b815260206004820152603060248201527f4d65726b6c6520747265652069732066756c6c2e204e6f206d6f7265206c656160448201526f1d995cc818d85b88189948185919195960821b60648201526084016105a2565b8083600080805b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168163ffffffff161015610cac57610c1460028661185b565b63ffffffff16600003610c4e5763ffffffff8116600090815260016020908152604080832054918390529091208590558493509150610c6a565b63ffffffff811660009081526020819052604090205492508391505b610c957f00000000000000000000000000000000000000000000000000000000000000008484610547565b9350610ca2600286611883565b9450600101610bd5565b50600354600090601e90610cc79063ffffffff1660016118ab565b610cd1919061185b565b6003805463ffffffff191663ffffffff831690811790915560009081526002602052604090208590559050610d078660016118ab565b6003805463ffffffff929092166401000000000267ffffffff000000001990921691909117905550939695505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610daa577f00000000000000000000000000000000000000000000000000000000000000003414610da857604051631841b4e160e01b815260040160405180910390fd5b565b3415610dc957604051631841b4e160e01b815260040160405180910390fd5b610da86001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633307f00000000000000000000000000000000000000000000000000000000000000006110fb565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610fa1573415610e6b57604051631841b4e160e01b815260040160405180910390fd5b8015610e8a576040516316365d5f60e01b815260040160405180910390fd5b60006001600160a01b038516610ec0847f00000000000000000000000000000000000000000000000000000000000000006118c7565b604051600081818185875af1925050503d8060008114610efc576040519150601f19603f3d011682016040523d82523d6000602084013e610f01565b606091505b5050905080610f23576040516312171d8360e31b815260040160405180910390fd5b8215610f9b576040516001600160a01b038516908490600081818185875af1925050503d8060008114610f72576040519150601f19603f3d011682016040523d82523d6000602084013e610f77565b606091505b50508091505080610f9b576040516312171d8360e31b815260040160405180910390fd5b506110f5565b803414610fc1576040516316365d5f60e01b815260040160405180910390fd5b61102084610fef847f00000000000000000000000000000000000000000000000000000000000000006118c7565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190611227565b811561105a5761105a6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168484611227565b80156110f5576000846001600160a01b03168260405160006040518083038185875af1925050503d80600081146110ad576040519150601f19603f3d011682016040523d82523d6000602084013e6110b2565b606091505b50509050806110f3576040516001600160a01b0385169083156108fc029084906000818181858888f193505050501580156110f1573d6000803e3d6000fd5b505b505b50505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161115f91906118da565b6000604051808303816000865af19150503d806000811461119c576040519150601f19603f3d011682016040523d82523d6000602084013e6111a1565b606091505b5091509150816111b357805181602001fd5b805115806111d05750808060200190518101906111d0919061170d565b6110f15760405162461bcd60e51b815260206004820152602260248201527f5361666545524332303a20736166655472616e7366657246726f6d206661696c604482015261195960f21b60648201526084016105a2565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b179052915160009283929087169161128391906118da565b6000604051808303816000865af19150503d80600081146112c0576040519150601f19603f3d011682016040523d82523d6000602084013e6112c5565b606091505b5091509150816112d757805181602001fd5b805115806112f45750808060200190518101906112f4919061170d565b6110f35760405162461bcd60e51b815260206004820152601e60248201527f5361666545524332303a20736166655472616e73666572206661696c6564000060448201526064016105a2565b60006020828403121561135257600080fd5b5035919050565b6001600160a01b038116811461072a57600080fd5b60008060006060848603121561138357600080fd5b833561138e81611359565b95602085013595506040909401359392505050565b6000602082840312156113b557600080fd5b813567ffffffffffffffff8111156113cc57600080fd5b820160e081850312156113de57600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561141e5761141e6113e5565b60405290565b600082601f83011261143557600080fd5b813567ffffffffffffffff81111561144f5761144f6113e5565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561147e5761147e6113e5565b60405281815283820160200185101561149657600080fd5b816020850160208301376000918101602001919091529392505050565b80356114be81611359565b919050565b600060e082840312156114d557600080fd5b6114dd6113fb565b9050813567ffffffffffffffff8111156114f657600080fd5b61150284828501611424565b8252506020828101359082015260408083013590820152611525606083016114b3565b6060820152611536608083016114b3565b608082015260a0828101359082015260c09182013591810191909152919050565b60006020828403121561156957600080fd5b813567ffffffffffffffff81111561158057600080fd5b82016060818503121561159257600080fd5b6040516060810167ffffffffffffffff811182821017156115b5576115b56113e5565b604052813567ffffffffffffffff8111156115cf57600080fd5b6115db868285016114c3565b82525060208281013590820152604090910135906115f882611359565b60408101919091529392505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff82168061163357611633611607565b6000190192915050565b6000806040838503121561165057600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601260045260246000fd5b600061168336836114c3565b92915050565b60005b838110156116a457818101518382015260200161168c565b50506000910152565b60e08152600083518060e08401526116cd81610100850160208801611689565b601f01601f19168201610100019050602082018360005b60068110156117035781518352602092830192909101906001016116e4565b5050509392505050565b60006020828403121561171f57600080fd5b815180151581146113de57600080fd5b6001815b600184111561176a5780850481111561174e5761174e611607565b600184161561175c57908102905b60019390931c928002611733565b935093915050565b60008261178157506001611683565b8161178e57506000611683565b81600181146117a457600281146117ae576117df565b6001915050611683565b60ff8411156117bf576117bf611607565b6001841b915063ffffffff8211156117d9576117d9611607565b50611683565b5060208310610133831016604e8410600b8410161715611816575081810a63ffffffff81111561181157611811611607565b611683565b61182563ffffffff848461172f565b8063ffffffff0482111561183b5761183b611607565b029392505050565b60006113de63ffffffff841663ffffffff8416611772565b600063ffffffff83168061187157611871611661565b8063ffffffff84160691505092915050565b600063ffffffff83168061189957611899611661565b8063ffffffff84160491505092915050565b63ffffffff818116838216019081111561168357611683611607565b8181038181111561168357611683611607565b600082516118ec818460208701611689565b919091019291505056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a2646970667358221220243ebd6330b960bd65385b8e8a53e3cc0705e88eb0269396024a501c27c6f55664736f6c634300081c003330644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a26469706673582212202067c6012c377ee28ceb62976bf750e6a2274ef71b50349cd5409a1d800b50d264736f6c634300081c0033