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