Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 20 from a total of 20 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Create Farm From... | 18263690 | 302 days ago | IN | 0 ETH | 0.05102826 | ||||
Create Farm From... | 18263679 | 302 days ago | IN | 0 ETH | 0.05300115 | ||||
Create Farm From... | 18263658 | 302 days ago | IN | 0 ETH | 0.05546413 | ||||
Create Farm From... | 18263627 | 302 days ago | IN | 0 ETH | 0.0687738 | ||||
Create Farm From... | 18263563 | 302 days ago | IN | 0 ETH | 0.06486849 | ||||
Create Farm From... | 18242135 | 305 days ago | IN | 0 ETH | 0.09074438 | ||||
Create Farm From... | 18222725 | 307 days ago | IN | 0 ETH | 0.04104242 | ||||
Create Farm From... | 18094991 | 325 days ago | IN | 0 ETH | 0.04006874 | ||||
Create Farm From... | 17699929 | 381 days ago | IN | 0 ETH | 0.21947691 | ||||
Create Farm From... | 17669735 | 385 days ago | IN | 0 ETH | 0.0996726 | ||||
Create Farm From... | 17605945 | 394 days ago | IN | 0 ETH | 0.12956672 | ||||
Create Farm From... | 17593581 | 395 days ago | IN | 0 ETH | 0.2954112 | ||||
Create Farm From... | 17572301 | 398 days ago | IN | 0 ETH | 0.08800384 | ||||
Create Farm From... | 17571010 | 399 days ago | IN | 0 ETH | 0.0830426 | ||||
Create Farm From... | 17566172 | 399 days ago | IN | 0 ETH | 0.08177662 | ||||
Create Farm From... | 17473281 | 412 days ago | IN | 0 ETH | 0.05547531 | ||||
Create Farm From... | 17442575 | 417 days ago | IN | 0 ETH | 0.14991491 | ||||
Create Farm From... | 17411676 | 421 days ago | IN | 0 ETH | 0.06767217 | ||||
Create Farm From... | 17411372 | 421 days ago | IN | 0 ETH | 0.10254343 | ||||
0x60806040 | 17411312 | 421 days ago | IN | 0 ETH | 0.14014754 |
Latest 20 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18263690 | 302 days ago | Contract Creation | 0 ETH | |||
18263679 | 302 days ago | Contract Creation | 0 ETH | |||
18263658 | 302 days ago | Contract Creation | 0 ETH | |||
18263627 | 302 days ago | Contract Creation | 0 ETH | |||
18263563 | 302 days ago | Contract Creation | 0 ETH | |||
18242135 | 305 days ago | Contract Creation | 0 ETH | |||
18222725 | 307 days ago | Contract Creation | 0 ETH | |||
18094991 | 325 days ago | Contract Creation | 0 ETH | |||
17699929 | 381 days ago | Contract Creation | 0 ETH | |||
17669735 | 385 days ago | Contract Creation | 0 ETH | |||
17605945 | 394 days ago | Contract Creation | 0 ETH | |||
17593581 | 395 days ago | Contract Creation | 0 ETH | |||
17572301 | 398 days ago | Contract Creation | 0 ETH | |||
17571010 | 399 days ago | Contract Creation | 0 ETH | |||
17566172 | 399 days ago | Contract Creation | 0 ETH | |||
17473281 | 412 days ago | Contract Creation | 0 ETH | |||
17460097 | 414 days ago | Contract Creation | 0 ETH | |||
17442575 | 417 days ago | Contract Creation | 0 ETH | |||
17411676 | 421 days ago | Contract Creation | 0 ETH | |||
17411372 | 421 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
BatonFactory
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2023-06-05 */ // SPDX-License-Identifier: MIT pragma solidity >=0.8.19; /// @notice Simple single owner authorization mixin. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) abstract contract Owned { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OwnershipTransferred(address indexed user, address indexed newOwner); /*////////////////////////////////////////////////////////////// OWNERSHIP STORAGE //////////////////////////////////////////////////////////////*/ address public owner; modifier onlyOwner() virtual { require(msg.sender == owner, "UNAUTHORIZED"); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _owner) { owner = _owner; emit OwnershipTransferred(address(0), _owner); } /*////////////////////////////////////////////////////////////// OWNERSHIP LOGIC //////////////////////////////////////////////////////////////*/ function transferOwnership(address newOwner) public virtual onlyOwner { owner = newOwner; emit OwnershipTransferred(msg.sender, newOwner); } } /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } /// @notice Modern, minimalist, and gas efficient ERC-721 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 indexed id); event Approval(address indexed owner, address indexed spender, uint256 indexed id); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /*////////////////////////////////////////////////////////////// METADATA STORAGE/LOGIC //////////////////////////////////////////////////////////////*/ string public name; string public symbol; function tokenURI(uint256 id) public view virtual returns (string memory); /*////////////////////////////////////////////////////////////// ERC721 BALANCE/OWNER STORAGE //////////////////////////////////////////////////////////////*/ mapping(uint256 => address) internal _ownerOf; mapping(address => uint256) internal _balanceOf; function ownerOf(uint256 id) public view virtual returns (address owner) { require((owner = _ownerOf[id]) != address(0), "NOT_MINTED"); } function balanceOf(address owner) public view virtual returns (uint256) { require(owner != address(0), "ZERO_ADDRESS"); return _balanceOf[owner]; } /*////////////////////////////////////////////////////////////// ERC721 APPROVAL STORAGE //////////////////////////////////////////////////////////////*/ mapping(uint256 => address) public getApproved; mapping(address => mapping(address => bool)) public isApprovedForAll; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol) { name = _name; symbol = _symbol; } /*////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 id) public virtual { address owner = _ownerOf[id]; require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); getApproved[id] = spender; emit Approval(owner, spender, id); } function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function transferFrom( address from, address to, uint256 id ) public virtual { require(from == _ownerOf[id], "WRONG_FROM"); require(to != address(0), "INVALID_RECIPIENT"); require( msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], "NOT_AUTHORIZED" ); // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. unchecked { _balanceOf[from]--; _balanceOf[to]++; } _ownerOf[id] = to; delete getApproved[id]; emit Transfer(from, to, id); } function safeTransferFrom( address from, address to, uint256 id ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function safeTransferFrom( address from, address to, uint256 id, bytes calldata data ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 id) internal virtual { require(to != address(0), "INVALID_RECIPIENT"); require(_ownerOf[id] == address(0), "ALREADY_MINTED"); // Counter overflow is incredibly unrealistic. unchecked { _balanceOf[to]++; } _ownerOf[id] = to; emit Transfer(address(0), to, id); } function _burn(uint256 id) internal virtual { address owner = _ownerOf[id]; require(owner != address(0), "NOT_MINTED"); // Ownership check above ensures no underflow. unchecked { _balanceOf[owner]--; } delete _ownerOf[id]; delete getApproved[id]; emit Transfer(owner, address(0), id); } /*////////////////////////////////////////////////////////////// INTERNAL SAFE MINT LOGIC //////////////////////////////////////////////////////////////*/ function _safeMint(address to, uint256 id) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function _safeMint( address to, uint256 id, bytes memory data ) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } } /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721TokenReceiver { function onERC721Received( address, address, uint256, bytes calldata ) external virtual returns (bytes4) { return ERC721TokenReceiver.onERC721Received.selector; } } /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } } // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } } // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } } /// @notice Gas optimized merkle proof verification library. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) library MerkleProofLib { function verify( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if proof.length { // Left shifting by 5 is like multiplying by 32. let end := add(proof.offset, shl(5, proof.length)) // Initialize offset to the offset of the proof in calldata. let offset := proof.offset // Iterate over proof elements to compute root hash. // prettier-ignore for {} 1 {} { // Slot where the leaf should be put in scratch space. If // leaf > calldataload(offset): slot 32, otherwise: slot 0. let leafSlot := shl(5, gt(leaf, calldataload(offset))) // Store elements to hash contiguously in scratch space. // The xor puts calldataload(offset) in whichever slot leaf // is not occupying, so 0 if leafSlot is 32, and 32 otherwise. mstore(leafSlot, leaf) mstore(xor(leafSlot, 32), calldataload(offset)) // Reuse leaf to store the hash to reduce stack operations. leaf := keccak256(0, 64) // Hash both slots of scratch space. offset := add(offset, 32) // Shift 1 word per cycle. // prettier-ignore if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) // The proof is valid if the roots match. } } } /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } } // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol) /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } } // Inspired by https://github.com/ZeframLou/trustus abstract contract ReservoirOracle { // --- Structs --- struct Message { bytes32 id; bytes payload; // The UNIX timestamp when the message was signed by the oracle uint256 timestamp; // ECDSA signature or EIP-2098 compact signature bytes signature; } // --- Errors --- error InvalidMessage(); // --- Fields --- address public RESERVOIR_ORACLE_ADDRESS; // --- Constructor --- constructor(address reservoirOracleAddress) { RESERVOIR_ORACLE_ADDRESS = reservoirOracleAddress; } // --- Public methods --- function updateReservoirOracleAddress(address newReservoirOracleAddress) public virtual; // --- Internal methods --- function _verifyMessage( bytes32 id, uint256 validFor, Message memory message ) internal view virtual returns (bool success) { // Ensure the message matches the requested id if (id != message.id) { return false; } // Ensure the message timestamp is valid if ( message.timestamp > block.timestamp || message.timestamp + validFor < block.timestamp ) { return false; } bytes32 r; bytes32 s; uint8 v; // Extract the individual signature fields from the signature bytes memory signature = message.signature; if (signature.length == 64) { // EIP-2098 compact signature bytes32 vs; assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) s := and( vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ) v := add(shr(255, vs), 27) } } else if (signature.length == 65) { // ECDSA signature assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } } else { return false; } address signerAddress = ecrecover( keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", // EIP-712 structured-data hash keccak256( abi.encode( keccak256( "Message(bytes32 id,bytes payload,uint256 timestamp)" ), message.id, keccak256(message.payload), message.timestamp ) ) ) ), v, r, s ); // Ensure the signer matches the designated oracle address return signerAddress == RESERVOIR_ORACLE_ADDRESS; } } /// @title LP token /// @author out.eth (@outdoteth) /// @notice LP token which is minted and burned by the Pair contract to represent liquidity in the pool. contract LpToken is Owned, ERC20 { constructor(string memory pairSymbol) Owned(msg.sender) ERC20(string.concat(pairSymbol, " LP token"), string.concat("LP-", pairSymbol), 18) {} /// @notice Mints new LP tokens to the given address. /// @param to The address to mint to. /// @param amount The amount to mint. function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } /// @notice Burns LP tokens from the given address. /// @param from The address to burn from. /// @param amount The amount to burn. function burn(address from, uint256 amount) public onlyOwner { _burn(from, amount); } } // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } } // modified from https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol // produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 // this library will always produce a string symbol to represent the token library SafeERC20Namer { function bytes32ToString(bytes32 x) private pure returns (string memory) { bytes memory bytesString = new bytes(32); uint256 charCount = 0; for (uint256 j = 0; j < 32; j++) { bytes1 char = x[j]; if (char != 0) { bytesString[charCount] = char; charCount++; } } bytes memory bytesStringTrimmed = new bytes(charCount); for (uint256 j = 0; j < charCount; j++) { bytesStringTrimmed[j] = bytesString[j]; } return string(bytesStringTrimmed); } // uses a heuristic to produce a token name from the address // the heuristic returns the full hex of the address string function addressToName(address token) private pure returns (string memory) { return Strings.toHexString(uint160(token)); } // uses a heuristic to produce a token symbol from the address // the heuristic returns the first 4 hex of the address string function addressToSymbol(address token) private pure returns (string memory) { return Strings.toHexString(uint160(token) >> (160 - 4 * 4)); } // calls an external view token contract method that returns a symbol or name, and parses the output into a string function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); // if not implemented, or returns empty data, return empty string if (!success || data.length == 0) { return ""; } // bytes32 data always has length 32 if (data.length == 32) { bytes32 decoded = abi.decode(data, (bytes32)); return bytes32ToString(decoded); } else if (data.length > 64) { return abi.decode(data, (string)); } return ""; } // attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address function tokenSymbol(address token) internal view returns (string memory) { // 0x95d89b41 = bytes4(keccak256("symbol()")) string memory symbol = callAndParseStringReturn(token, 0x95d89b41); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address return addressToSymbol(token); } return symbol; } // attempts to extract the token name. if it does not implement name, returns a name derived from the address function tokenName(address token) internal view returns (string memory) { // 0x06fdde03 = bytes4(keccak256("name()")) string memory name = callAndParseStringReturn(token, 0x06fdde03); if (bytes(name).length == 0) { // fallback to full hex of address return addressToName(token); } return name; } } /// @title caviar.sh /// @author out.eth (@outdoteth) /// @notice An AMM for creating and trading fractionalized NFTs. contract Caviar is Owned { using SafeERC20Namer for address; /// @dev pairs[nft][baseToken][merkleRoot] -> pair mapping(address => mapping(address => mapping(bytes32 => address))) public pairs; /// @dev The stolen nft filter oracle address address public stolenNftFilterOracle; event SetStolenNftFilterOracle(address indexed stolenNftFilterOracle); event Create(address indexed nft, address indexed baseToken, bytes32 indexed merkleRoot); event Destroy(address indexed nft, address indexed baseToken, bytes32 indexed merkleRoot); constructor(address _stolenNftFilterOracle) Owned(msg.sender) { stolenNftFilterOracle = _stolenNftFilterOracle; } /// @notice Sets the stolen nft filter oracle address. /// @param _stolenNftFilterOracle The stolen nft filter oracle address. function setStolenNftFilterOracle(address _stolenNftFilterOracle) public onlyOwner { stolenNftFilterOracle = _stolenNftFilterOracle; emit SetStolenNftFilterOracle(_stolenNftFilterOracle); } /// @notice Creates a new pair. /// @param nft The NFT contract address. /// @param baseToken The base token contract address. /// @param merkleRoot The merkle root for the valid tokenIds. /// @return pair The address of the new pair. function create(address nft, address baseToken, bytes32 merkleRoot) public returns (Pair pair) { // check that the pair doesn't already exist require(pairs[nft][baseToken][merkleRoot] == address(0), "Pair already exists"); require(nft.code.length > 0, "Invalid NFT contract"); require(baseToken.code.length > 0 || baseToken == address(0), "Invalid base token contract"); // deploy the pair string memory baseTokenSymbol = baseToken == address(0) ? "ETH" : baseToken.tokenSymbol(); string memory nftSymbol = nft.tokenSymbol(); string memory nftName = nft.tokenName(); string memory pairSymbol = string.concat(nftSymbol, ":", baseTokenSymbol); pair = new Pair(nft, baseToken, merkleRoot, pairSymbol, nftName, nftSymbol); // save the pair pairs[nft][baseToken][merkleRoot] = address(pair); emit Create(nft, baseToken, merkleRoot); } /// @notice Deletes the pair for the given NFT, base token, and merkle root. /// @param nft The NFT contract address. /// @param baseToken The base token contract address. /// @param merkleRoot The merkle root for the valid tokenIds. function destroy(address nft, address baseToken, bytes32 merkleRoot) public { // check that a pair can only destroy itself require(msg.sender == pairs[nft][baseToken][merkleRoot], "Only pair can destroy itself"); // delete the pair delete pairs[nft][baseToken][merkleRoot]; emit Destroy(nft, baseToken, merkleRoot); } } /// @title StolenNftFilterOracle /// @author out.eth (@outdoteth) /// @notice A contract to check that a set of NFTs are not stolen. contract StolenNftFilterOracle is ReservoirOracle, Owned { bytes32 private constant TOKEN_TYPE_HASH = keccak256("Token(address contract,uint256 tokenId)"); uint256 public cooldownPeriod = 0; uint256 public validFor = 60 minutes; mapping(address => bool) public isDisabled; constructor() Owned(msg.sender) ReservoirOracle(0xAeB1D03929bF87F69888f381e73FBf75753d75AF) {} /// @notice Sets the cooldown period. /// @param _cooldownPeriod The cooldown period. function setCooldownPeriod(uint256 _cooldownPeriod) public onlyOwner { cooldownPeriod = _cooldownPeriod; } /// @notice Sets the valid for period. /// @param _validFor The valid for period. function setValidFor(uint256 _validFor) public onlyOwner { validFor = _validFor; } /// @notice Updates the reservoir oracle address. /// @param newReservoirOracleAddress The new reservoir oracle address. function updateReservoirOracleAddress(address newReservoirOracleAddress) public override onlyOwner { RESERVOIR_ORACLE_ADDRESS = newReservoirOracleAddress; } /// @notice Sets whether a token validation is disabled. /// @param tokenAddress The token address. /// @param _isDisabled Whether the token validation is disabled. function setIsDisabled(address tokenAddress, bool _isDisabled) public onlyOwner { isDisabled[tokenAddress] = _isDisabled; } /// @notice Checks that a set of NFTs are not stolen. /// @param tokenAddress The address of the NFT contract. /// @param tokenIds The ids of the NFTs. /// @param messages The messages signed by the reservoir oracle. function validateTokensAreNotStolen(address tokenAddress, uint256[] calldata tokenIds, Message[] calldata messages) public view { if (isDisabled[tokenAddress]) return; for (uint256 i = 0; i < tokenIds.length; i++) { Message calldata message = messages[i]; // check that the signer is correct and message id matches token id + token address bytes32 expectedMessageId = keccak256(abi.encode(TOKEN_TYPE_HASH, tokenAddress, tokenIds[i])); require(_verifyMessage(expectedMessageId, validFor, message), "Message has invalid signature"); (bool isFlagged, uint256 lastTransferTime) = abi.decode(message.payload, (bool, uint256)); // check that the NFT is not stolen require(!isFlagged, "NFT is flagged as suspicious"); // check that the NFT was not transferred too recently require(lastTransferTime + cooldownPeriod < block.timestamp, "NFT was transferred too recently"); } } } /// @title Pair /// @author out.eth (@outdoteth) /// @notice A pair of an NFT and a base token that can be used to create and trade fractionalized NFTs. contract Pair is ERC20, ERC721TokenReceiver { using SafeTransferLib for address; using SafeTransferLib for ERC20; uint256 public constant CLOSE_GRACE_PERIOD = 7 days; uint256 private constant ONE = 1e18; uint256 private constant MINIMUM_LIQUIDITY = 100_000; address public immutable nft; address public immutable baseToken; // address(0) for ETH bytes32 public immutable merkleRoot; LpToken public immutable lpToken; Caviar public immutable caviar; uint256 public closeTimestamp; event Add(uint256 indexed baseTokenAmount, uint256 indexed fractionalTokenAmount, uint256 indexed lpTokenAmount); event Remove(uint256 indexed baseTokenAmount, uint256 indexed fractionalTokenAmount, uint256 indexed lpTokenAmount); event Buy(uint256 indexed inputAmount, uint256 indexed outputAmount); event Sell(uint256 indexed inputAmount, uint256 indexed outputAmount); event Wrap(uint256[] indexed tokenIds); event Unwrap(uint256[] indexed tokenIds); event Close(uint256 indexed closeTimestamp); event Withdraw(uint256 indexed tokenId); constructor( address _nft, address _baseToken, bytes32 _merkleRoot, string memory pairSymbol, string memory nftName, string memory nftSymbol ) ERC20(string.concat(nftName, " fractional token"), string.concat("f", nftSymbol), 18) { nft = _nft; baseToken = _baseToken; // use address(0) for native ETH merkleRoot = _merkleRoot; lpToken = new LpToken(pairSymbol); caviar = Caviar(msg.sender); } // ************************ // // Core AMM logic // // *********************** // /// @notice Adds liquidity to the pair. /// @param baseTokenAmount The amount of base tokens to add. /// @param fractionalTokenAmount The amount of fractional tokens to add. /// @param minLpTokenAmount The minimum amount of LP tokens to mint. /// @param minPrice The minimum price that the pool should currently be at. /// @param maxPrice The maximum price that the pool should currently be at. /// @param deadline The deadline before the trade expires. /// @return lpTokenAmount The amount of LP tokens minted. function add( uint256 baseTokenAmount, uint256 fractionalTokenAmount, uint256 minLpTokenAmount, uint256 minPrice, uint256 maxPrice, uint256 deadline ) public payable returns (uint256 lpTokenAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // check the token amount inputs are not zero require(baseTokenAmount > 0 && fractionalTokenAmount > 0, "Input token amount is zero"); // check that correct eth input was sent - if the baseToken equals address(0) then native ETH is used require(baseToken == address(0) ? msg.value == baseTokenAmount : msg.value == 0, "Invalid ether input"); uint256 lpTokenSupply = lpToken.totalSupply(); // check that the price is within the bounds if there is liquidity in the pool if (lpTokenSupply != 0) { uint256 _price = price(); require(_price >= minPrice && _price <= maxPrice, "Slippage: price out of bounds"); } // calculate the lp token shares to mint lpTokenAmount = addQuote(baseTokenAmount, fractionalTokenAmount, lpTokenSupply); // check that the amount of lp tokens outputted is greater than the min amount require(lpTokenAmount >= minLpTokenAmount, "Slippage: lp token amount out"); // *** Effects *** // // transfer fractional tokens in _transferFrom(msg.sender, address(this), fractionalTokenAmount); // *** Interactions *** // // mint lp tokens to sender lpToken.mint(msg.sender, lpTokenAmount); // transfer first MINIMUM_LIQUIDITY lp tokens to the owner if (lpTokenSupply == 0) { lpToken.mint(caviar.owner(), MINIMUM_LIQUIDITY); } // transfer base tokens in if the base token is not ETH if (baseToken != address(0)) { // transfer base tokens in ERC20(baseToken).safeTransferFrom(msg.sender, address(this), baseTokenAmount); } emit Add(baseTokenAmount, fractionalTokenAmount, lpTokenAmount); } /// @notice Removes liquidity from the pair. /// @param lpTokenAmount The amount of LP tokens to burn. /// @param minBaseTokenOutputAmount The minimum amount of base tokens to receive. /// @param minFractionalTokenOutputAmount The minimum amount of fractional tokens to receive. /// @param deadline The deadline before the trade expires. /// @return baseTokenOutputAmount The amount of base tokens received. /// @return fractionalTokenOutputAmount The amount of fractional tokens received. function remove( uint256 lpTokenAmount, uint256 minBaseTokenOutputAmount, uint256 minFractionalTokenOutputAmount, uint256 deadline ) public returns (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // calculate the output amounts (baseTokenOutputAmount, fractionalTokenOutputAmount) = removeQuote(lpTokenAmount); // check that the base token output amount is greater than the min amount require(baseTokenOutputAmount >= minBaseTokenOutputAmount, "Slippage: base token amount out"); // check that the fractional token output amount is greater than the min amount require(fractionalTokenOutputAmount >= minFractionalTokenOutputAmount, "Slippage: fractional token out"); // *** Effects *** // // transfer fractional tokens to sender _transferFrom(address(this), msg.sender, fractionalTokenOutputAmount); // *** Interactions *** // // burn lp tokens from sender lpToken.burn(msg.sender, lpTokenAmount); if (baseToken == address(0)) { // if base token is native ETH then send ether to sender msg.sender.safeTransferETH(baseTokenOutputAmount); } else { // transfer base tokens to sender ERC20(baseToken).safeTransfer(msg.sender, baseTokenOutputAmount); } emit Remove(baseTokenOutputAmount, fractionalTokenOutputAmount, lpTokenAmount); } /// @notice Buys fractional tokens from the pair. /// @param outputAmount The amount of fractional tokens to buy. /// @param maxInputAmount The maximum amount of base tokens to spend. /// @param deadline The deadline before the trade expires. /// @return inputAmount The amount of base tokens spent. function buy(uint256 outputAmount, uint256 maxInputAmount, uint256 deadline) public payable returns (uint256 inputAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // check that correct eth input was sent - if the baseToken equals address(0) then native ETH is used require(baseToken == address(0) ? msg.value == maxInputAmount : msg.value == 0, "Invalid ether input"); // calculate required input amount using xyk invariant inputAmount = buyQuote(outputAmount); // check that the required amount of base tokens is less than the max amount require(inputAmount <= maxInputAmount, "Slippage: amount in"); // *** Effects *** // // transfer fractional tokens to sender _transferFrom(address(this), msg.sender, outputAmount); // *** Interactions *** // if (baseToken == address(0)) { // refund surplus eth uint256 refundAmount = maxInputAmount - inputAmount; if (refundAmount > 0) msg.sender.safeTransferETH(refundAmount); } else { // transfer base tokens in ERC20(baseToken).safeTransferFrom(msg.sender, address(this), inputAmount); } emit Buy(inputAmount, outputAmount); } /// @notice Sells fractional tokens to the pair. /// @param inputAmount The amount of fractional tokens to sell. /// @param deadline The deadline before the trade expires. /// @param minOutputAmount The minimum amount of base tokens to receive. /// @return outputAmount The amount of base tokens received. function sell(uint256 inputAmount, uint256 minOutputAmount, uint256 deadline) public returns (uint256 outputAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // calculate output amount using xyk invariant outputAmount = sellQuote(inputAmount); // check that the outputted amount of fractional tokens is greater than the min amount require(outputAmount >= minOutputAmount, "Slippage: amount out"); // *** Effects *** // // transfer fractional tokens from sender _transferFrom(msg.sender, address(this), inputAmount); // *** Interactions *** // if (baseToken == address(0)) { // transfer ether out msg.sender.safeTransferETH(outputAmount); } else { // transfer base tokens out ERC20(baseToken).safeTransfer(msg.sender, outputAmount); } emit Sell(inputAmount, outputAmount); } // ******************** // // Wrap logic // // ******************** // /// @notice Wraps NFTs into fractional tokens. /// @param tokenIds The ids of the NFTs to wrap. /// @param proofs The merkle proofs for the NFTs proving that they can be used in the pair. /// @return fractionalTokenAmount The amount of fractional tokens minted. function wrap(uint256[] calldata tokenIds, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages) public returns (uint256 fractionalTokenAmount) { // *** Checks *** // // check that wrapping is not closed require(closeTimestamp == 0, "Wrap: closed"); // check the tokens exist in the merkle root _validateTokenIds(tokenIds, proofs); // check that the tokens are not stolen with reservoir oracle _validateTokensAreNotStolen(tokenIds, messages); // *** Effects *** // // mint fractional tokens to sender fractionalTokenAmount = tokenIds.length * ONE; _mint(msg.sender, fractionalTokenAmount); // *** Interactions *** // // transfer nfts from sender for (uint256 i = 0; i < tokenIds.length;) { ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]); unchecked { i++; } } emit Wrap(tokenIds); } /// @notice Unwraps fractional tokens into NFTs. /// @param tokenIds The ids of the NFTs to unwrap. /// @param withFee Whether to pay a fee for unwrapping or not. /// @return fractionalTokenAmount The amount of fractional tokens burned. function unwrap(uint256[] calldata tokenIds, bool withFee) public returns (uint256 fractionalTokenAmount) { // *** Effects *** // // burn fractional tokens from sender fractionalTokenAmount = tokenIds.length * ONE; _burn(msg.sender, fractionalTokenAmount); // Take the fee if withFee is true if (withFee) { // calculate fee uint256 fee = fractionalTokenAmount * 3 / 1000; // transfer fee from sender _transferFrom(msg.sender, address(this), fee); fractionalTokenAmount += fee; } // transfer nfts to sender for (uint256 i = 0; i < tokenIds.length;) { ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]); unchecked { i++; } } emit Unwrap(tokenIds); } // *********************** // // NFT AMM logic // // *********************** // /// @notice nftAdd Adds liquidity to the pair using NFTs. /// @param baseTokenAmount The amount of base tokens to add. /// @param tokenIds The ids of the NFTs to add. /// @param minLpTokenAmount The minimum amount of lp tokens to receive. /// @param minPrice The minimum price of the pair. /// @param maxPrice The maximum price of the pair. /// @param deadline The deadline for the transaction. /// @param proofs The merkle proofs for the NFTs. /// @return lpTokenAmount The amount of lp tokens minted. function nftAdd( uint256 baseTokenAmount, uint256[] calldata tokenIds, uint256 minLpTokenAmount, uint256 minPrice, uint256 maxPrice, uint256 deadline, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages ) public payable returns (uint256 lpTokenAmount) { // wrap the incoming NFTs into fractional tokens uint256 fractionalTokenAmount = wrap(tokenIds, proofs, messages); // add liquidity using the fractional tokens and base tokens lpTokenAmount = add(baseTokenAmount, fractionalTokenAmount, minLpTokenAmount, minPrice, maxPrice, deadline); } /// @notice Removes liquidity from the pair using NFTs. /// @param lpTokenAmount The amount of lp tokens to remove. /// @param minBaseTokenOutputAmount The minimum amount of base tokens to receive. /// @param deadline The deadline before the trade expires. /// @param tokenIds The ids of the NFTs to remove. /// @param withFee Whether to pay a fee for unwrapping or not. /// @return baseTokenOutputAmount The amount of base tokens received. /// @return fractionalTokenOutputAmount The amount of fractional tokens received. function nftRemove( uint256 lpTokenAmount, uint256 minBaseTokenOutputAmount, uint256 deadline, uint256[] calldata tokenIds, bool withFee ) public returns (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) { // remove liquidity and send fractional tokens and base tokens to sender (baseTokenOutputAmount, fractionalTokenOutputAmount) = remove(lpTokenAmount, minBaseTokenOutputAmount, tokenIds.length * ONE, deadline); // unwrap the fractional tokens into NFTs and send to sender unwrap(tokenIds, withFee); } /// @notice Buys NFTs from the pair using base tokens. /// @param tokenIds The ids of the NFTs to buy. /// @param maxInputAmount The maximum amount of base tokens to spend. /// @param deadline The deadline before the trade expires. /// @return inputAmount The amount of base tokens spent. function nftBuy(uint256[] calldata tokenIds, uint256 maxInputAmount, uint256 deadline) public payable returns (uint256 inputAmount) { // buy fractional tokens using base tokens inputAmount = buy(tokenIds.length * ONE, maxInputAmount, deadline); // unwrap the fractional tokens into NFTs and send to sender unwrap(tokenIds, false); } /// @notice Sells NFTs to the pair for base tokens. /// @param tokenIds The ids of the NFTs to sell. /// @param minOutputAmount The minimum amount of base tokens to receive. /// @param deadline The deadline before the trade expires. /// @param proofs The merkle proofs for the NFTs. /// @return outputAmount The amount of base tokens received. function nftSell( uint256[] calldata tokenIds, uint256 minOutputAmount, uint256 deadline, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages ) public returns (uint256 outputAmount) { // wrap the incoming NFTs into fractional tokens uint256 inputAmount = wrap(tokenIds, proofs, messages); // sell fractional tokens for base tokens outputAmount = sell(inputAmount, minOutputAmount, deadline); } // ****************************** // // Emergency exit logic // // ****************************** // /// @notice Closes the pair to new wraps. /// @dev Can only be called by the caviar owner. This is used as an emergency exit in case /// the caviar owner suspects that the pair has been compromised. function close() public { // check that the sender is the caviar owner require(caviar.owner() == msg.sender, "Close: not owner"); // set the close timestamp with a grace period closeTimestamp = block.timestamp + CLOSE_GRACE_PERIOD; // remove the pair from the Caviar contract caviar.destroy(nft, baseToken, merkleRoot); emit Close(closeTimestamp); } /// @notice Withdraws a particular NFT from the pair. /// @dev Can only be called by the caviar owner after the close grace period has passed. This /// is used to auction off the NFTs in the pair in case NFTs get stuck due to liquidity /// imbalances. Proceeds from the auction should be distributed pro rata to fractional /// token holders. See documentation for more details. function withdraw(uint256 tokenId) public { // check that the sender is the caviar owner require(caviar.owner() == msg.sender, "Withdraw: not owner"); // check that the close period has been set require(closeTimestamp != 0, "Withdraw not initiated"); // check that the close grace period has passed require(block.timestamp >= closeTimestamp, "Not withdrawable yet"); // transfer the nft to the caviar owner ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenId); emit Withdraw(tokenId); } // ***************** // // Getters // // ***************** // function baseTokenReserves() public view returns (uint256) { return _baseTokenReserves(); } function fractionalTokenReserves() public view returns (uint256) { return balanceOf[address(this)]; } /// @notice The current price of one fractional token in base tokens with 18 decimals of precision. /// @dev Calculated by dividing the base token reserves by the fractional token reserves. /// @return price The price of one fractional token in base tokens * 1e18. function price() public view returns (uint256) { uint256 exponent = baseToken == address(0) ? 18 : (36 - ERC20(baseToken).decimals()); return (_baseTokenReserves() * 10 ** exponent) / fractionalTokenReserves(); } /// @notice The amount of base tokens required to buy a given amount of fractional tokens. /// @dev Calculated using the xyk invariant and a 30bps fee. /// @param outputAmount The amount of fractional tokens to buy. /// @return inputAmount The amount of base tokens required. function buyQuote(uint256 outputAmount) public view returns (uint256) { return FixedPointMathLib.mulDivUp( outputAmount * 1000, baseTokenReserves(), (fractionalTokenReserves() - outputAmount) * 990 ); } /// @notice The amount of base tokens received for selling a given amount of fractional tokens. /// @dev Calculated using the xyk invariant and a 30bps fee. /// @param inputAmount The amount of fractional tokens to sell. /// @return outputAmount The amount of base tokens received. function sellQuote(uint256 inputAmount) public view returns (uint256) { uint256 inputAmountWithFee = inputAmount * 990; return (inputAmountWithFee * baseTokenReserves()) / ((fractionalTokenReserves() * 1000) + inputAmountWithFee); } /// @notice The amount of lp tokens received for adding a given amount of base tokens and fractional tokens. /// @dev Calculated as a share of existing deposits. If there are no existing deposits, then initializes to /// sqrt(baseTokenAmount * fractionalTokenAmount). /// @param baseTokenAmount The amount of base tokens to add. /// @param fractionalTokenAmount The amount of fractional tokens to add. /// @return lpTokenAmount The amount of lp tokens received. function addQuote(uint256 baseTokenAmount, uint256 fractionalTokenAmount, uint256 lpTokenSupply) public view returns (uint256) { if (lpTokenSupply != 0) { // calculate amount of lp tokens as a fraction of existing reserves uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves(); uint256 fractionalTokenShare = (fractionalTokenAmount * lpTokenSupply) / fractionalTokenReserves(); return Math.min(baseTokenShare, fractionalTokenShare); } else { // if there is no liquidity then init return Math.sqrt(baseTokenAmount * fractionalTokenAmount) - MINIMUM_LIQUIDITY; } } /// @notice The amount of base tokens and fractional tokens received for burning a given amount of lp tokens. /// @dev Calculated as a share of existing deposits. /// @param lpTokenAmount The amount of lp tokens to burn. /// @return baseTokenAmount The amount of base tokens received. /// @return fractionalTokenAmount The amount of fractional tokens received. function removeQuote(uint256 lpTokenAmount) public view returns (uint256, uint256) { uint256 lpTokenSupply = lpToken.totalSupply(); uint256 baseTokenOutputAmount = (baseTokenReserves() * lpTokenAmount) / lpTokenSupply; uint256 fractionalTokenOutputAmount = (fractionalTokenReserves() * lpTokenAmount) / lpTokenSupply; uint256 upperFractionalTokenOutputAmount = (fractionalTokenReserves() * (lpTokenAmount + 1)) / lpTokenSupply; if ( fractionalTokenOutputAmount % 1e18 != 0 && upperFractionalTokenOutputAmount - fractionalTokenOutputAmount <= 1000 && lpTokenSupply > 1e15 ) { fractionalTokenOutputAmount = upperFractionalTokenOutputAmount; } return (baseTokenOutputAmount, fractionalTokenOutputAmount); } // ************************ // // Internal utils // // ************************ // function _transferFrom(address from, address to, uint256 amount) internal returns (bool) { balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } function _validateTokensAreNotStolen(uint256[] calldata tokenIds, ReservoirOracle.Message[] calldata messages) internal view { address stolenNftFilterAddress = caviar.stolenNftFilterOracle(); // if filter address is not set then no need to check if nfts are stolen if (stolenNftFilterAddress == address(0)) return; // validate that nfts are not stolen StolenNftFilterOracle(stolenNftFilterAddress).validateTokensAreNotStolen(nft, tokenIds, messages); } /// @dev Validates that the given tokenIds are valid for the contract's merkle root. Reverts /// if any of the tokenId proofs are invalid. function _validateTokenIds(uint256[] calldata tokenIds, bytes32[][] calldata proofs) internal view { // if merkle root is not set then all tokens are valid if (merkleRoot == bytes32(0)) return; // validate merkle proofs against merkle root for (uint256 i = 0; i < tokenIds.length;) { bool isValid = MerkleProofLib.verify( proofs[i], merkleRoot, // double hash to prevent second preimage attacks keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i])))) ); require(isValid, "Invalid merkle proof"); unchecked { i++; } } } /// @dev Returns the current base token reserves. If the base token is ETH then it ignores /// the msg.value that is being sent in the current call context - this is to ensure the /// xyk math is correct in the buy() and add() functions. function _baseTokenReserves() internal view returns (uint256) { return baseToken == address(0) ? address(this).balance - msg.value // subtract the msg.value if the base token is ETH : ERC20(baseToken).balanceOf(address(this)); } } /// @title BatonFarm /// @author Baton team /// @notice Yield farms that allow for users to stake their NFT AMM LP positions into our yield farm /// @dev We note that this implementation is coupled to Caviar's Pair contract. A Pair represents a /// a Uniswap-V2-like CFMM pool consisting of fractions of the NFT (hence a Pair.nft() function). contract BatonFarm is Pausable, Owned, ERC721TokenReceiver { using SafeTransferLib for ERC20; using SafeTransferLib for address; /*////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ // caviar Pair public immutable pair; ERC20 public immutable rewardsToken; // token given as reward ERC20 public immutable stakingToken; // token being staked uint256 public periodFinish; // timestamp in which the farm is shutdown uint256 public rewardRate; // amount of fees given persecond uint256 public rewardsDuration; // duration in seconds that the rewards should be vested over uint256 public lastUpdateTime; // last time the reward was updated uint256 public rewardPerTokenStored; uint256 private _totalSupply; // total amount of staked assets /*////////////////////////////////////////////////////////////// MAPPINGS //////////////////////////////////////////////////////////////*/ mapping(address => uint256) public userRewardPerTokenPaid; mapping(address => uint256) public rewards; // user -> rewards mapping(address => uint256) private _balances; // user -> staked assets /*////////////////////////////////////////////////////////////// MIGRATION VARIABLES //////////////////////////////////////////////////////////////*/ address public migration; // address to migrate to bool public migrationComplete; // has a migration been initilized and completed address public rewardsDistributor; // an address who is capable of updating the pool with new rewards /*////////////////////////////////////////////////////////////// BATON VARIABLES //////////////////////////////////////////////////////////////*/ address public immutable batonMonitor; // this address is the only one who can complete a migration, propose and set // a new // farm fee. BatonFactory public immutable batonFactory; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event RewardAdded(uint256 reward); event Received(address indexed user, uint256 amount); event Staked(address indexed user, uint256 amount); event Withdrawn(address indexed user, uint256 amount); event RewardPaid(address indexed user, uint256 reward); event RewardsDurationUpdated(uint256 newDuration); event MigrationInitiated(address migration); event MigrationComplete(address migration, uint256 amount, uint256 timestamp); event UpdateRewardsDistributor(address _rewardsDistributor); event UpdateRewardsDuration(uint256 newRewardsDuration); event Recovered(address tokenAddress, uint256 tokenAmount); event FoundSurplus(uint256 surplusAmount, address recoveredTo); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( address _owner, address _rewardsDistributor, address _batonMonitor, address _rewardsToken, address _pairAddress, uint256 _rewardsDuration, // in seconds address _batonFactory ) Owned(_owner) { require(_owner != address(0), "_owner shouldnt be address(0)"); require(_rewardsDistributor != address(0), "_rewardsDistributor shouldnt be address(0)"); require(_batonMonitor != address(0), "_batonMonitor shouldnt be address(0)"); require(_rewardsToken != address(0), "_rewardsToken shouldnt be address(0)"); require(_pairAddress != address(0), "_pairAddress shouldnt be address(0)"); require(_batonFactory != address(0), "_batonFactory shouldnt be address(0)"); pair = Pair(_pairAddress); rewardsToken = ERC20(_rewardsToken); stakingToken = ERC20(address(pair.lpToken())); rewardsDistributor = _rewardsDistributor; require(_rewardsDuration > 0, "_rewardsDuration cannot be 0"); require(_rewardsDuration < 5 * (365 days), "_rewardsDuration cannot be more then 5 years"); rewardsDuration = _rewardsDuration; batonMonitor = _batonMonitor; batonFactory = BatonFactory(_batonFactory); ERC721(pair.nft()).setApprovalForAll(address(pair), true); } /*////////////////////////////////////////////////////////////// RECEIVE //////////////////////////////////////////////////////////////*/ receive() external payable { emit Received(msg.sender, msg.value); } /*////////////////////////////////////////////////////////////// VIEWS //////////////////////////////////////////////////////////////*/ function balanceOf(address account) external view returns (uint256) { return _balances[account]; } function totalSupply() external view returns (uint256) { return _totalSupply; } /** * @dev Calculates the percentage of an amount based on basis points. * @param basisPoints The basis points value (100bp == 1%). * @param amount The amount to calculate the percentage of. * @return The calculated percentage. */ function calculatePercentage(uint256 basisPoints, uint256 amount) public pure returns (uint256) { uint256 percentage = basisPoints * amount / 10_000; return percentage; } /** * @dev Returns the last time at which the reward is applicable. * @return The last time reward is applicable (either the current block timestamp or the end of the reward period). */ function lastTimeRewardApplicable() public view returns (uint256) { return block.timestamp < periodFinish ? block.timestamp : periodFinish; } /** * @dev Returns the reward per token earned. * @return The calculated reward per token. */ function rewardPerToken() public view returns (uint256) { if (_totalSupply == 0) { return rewardPerTokenStored; } uint256 lastTimeApplicable = lastTimeRewardApplicable(); uint256 lastUpdateTimeDiff = lastTimeApplicable - lastUpdateTime; uint256 rewardRateMul = rewardRate * 1e18; uint256 rewardPerTokenIncrease = (lastUpdateTimeDiff * rewardRateMul) / _totalSupply; return rewardPerTokenStored + rewardPerTokenIncrease; } /** * @dev Returns the amount of rewards earned by an account. * @param account The address of the account. * @return The amount of rewards earned. */ function earned(address account) public view returns (uint256) { uint256 rpt = rewardPerToken(); uint256 userRewardPerTokenPaidDiff = rpt - userRewardPerTokenPaid[account]; uint256 balanceOfAccount = _balances[account]; uint256 reward = (balanceOfAccount * userRewardPerTokenPaidDiff) / 1e18; return reward + rewards[account]; } /** * @dev Returns the unearned rewards amount. * @return The unearned rewards. */ function _unearnedRewards() internal view returns (uint256) { uint256 currentTime = block.timestamp; uint256 remainingTime = periodFinish - currentTime; uint256 remainingRewards = rewardRate * remainingTime; return remainingRewards; } /*////////////////////////////////////////////////////////////// STAKE FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Allows the user to stake `amount` of the `stakingToken` into this farm * @param amount Amount to stake into the farm */ function stake(uint256 amount) external poolNotMigrated onlyWhenPoolActive whenNotPaused updateReward(msg.sender) { require(amount > 0, "Cannot stake 0"); _totalSupply = _totalSupply + amount; _balances[msg.sender] = _balances[msg.sender] + amount; stakingToken.safeTransferFrom(msg.sender, address(this), amount); emit Staked(msg.sender, amount); } /** * @notice Helper function to 1) deposit equal parts of ETH and NFTs into the related * Caviar NFT AMM pool for the user, 2) auto-staking the LP position into this * contract by letting the contract hold custody of the position * @param tokenIds Given that this farm is present to a Caviar pair, ids of underlying NFT * @param minLpTokenAmount Minimum LP token amount as a means of slippage control * @param minPrice The minimum price of the pair. * @param maxPrice The maximum price of the pair. * @param deadline The deadline for the transaction. */ function nftAddAndStake( uint256[] calldata tokenIds, uint256 minLpTokenAmount, uint256 minPrice, uint256 maxPrice, uint256 deadline, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages ) external payable onlyWhenPoolActive whenNotPaused poolNotMigrated updateReward(msg.sender) { // Retrieve the NFTs from the user for (uint256 i = 0; i < tokenIds.length; i++) { ERC721(pair.nft()).safeTransferFrom(msg.sender, address(this), tokenIds[i]); } // Deposit equal parts of ETH (msg.value) and NFTs into the Caviar NFT AMM pool uint256 lpTokenAmount = pair.nftAdd{ value: msg.value }( msg.value, tokenIds, minLpTokenAmount, minPrice, maxPrice, deadline, proofs, messages ); require(lpTokenAmount > 0, "Cannot stake 0"); // prevent a reentracny attack from `pair.nftAdd`, make sure that stakingToken.balanceOf is acctually updated require( stakingToken.balanceOf(address(this)) == _totalSupply + lpTokenAmount, "stakingToken balance didnt update from lpTokenAmount" ); // Ensure that the LP amount isn't zilch and record in supply / user balance _totalSupply = _totalSupply + lpTokenAmount; _balances[msg.sender] = _balances[msg.sender] + lpTokenAmount; emit Staked(msg.sender, lpTokenAmount); } /*////////////////////////////////////////////////////////////// WITHDRAWING FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Withdraw Pair position from the contract, decomposed as the * baseToken (e.g WETH) and fractionalised NFT tokens. NFTs are returned to the user. * @param amount Amount of the LP position to withdraw * @param minBaseTokenOutputAmount Min amount to get of the base token, accounting for slippage * @param deadline Deadline to retrieve position by * @param tokenIds NFT IDs to reddem from LP * @param withFee An optional unwrapping fee */ function withdrawAndRemoveNftFromPool( uint256 amount, uint256 minBaseTokenOutputAmount, uint256 deadline, uint256[] calldata tokenIds, bool withFee ) external updateReward(msg.sender) { require(amount > 0, "Cannot withdraw 0"); require(amount <= _balances[msg.sender], "Cannot withdraw more then you have staked"); // remove the amount the user is unstaking _totalSupply = _totalSupply - amount; _balances[msg.sender] = _balances[msg.sender] - amount; uint256 batonFeeAmount = calculatePercentage(batonFactory.batonLPFee(), amount); // calculate batons fee // if the fee is more then 0 send the fee to batonMonitor if (batonFeeAmount > 0) { stakingToken.safeTransfer(batonMonitor, batonFeeAmount); } uint256 amountToRemove = amount - batonFeeAmount; (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) = pair.nftRemove(amountToRemove, minBaseTokenOutputAmount, deadline, tokenIds, withFee); // transfer the base / fractional nft tokens to the user SafeTransferLib.safeTransferETH(msg.sender, baseTokenOutputAmount); for (uint256 i = 0; i < tokenIds.length; i++) { ERC721(pair.nft()).safeTransferFrom(address(this), msg.sender, tokenIds[i]); } // send user his rewards harvest(); // emit an event emit Withdrawn(msg.sender, amountToRemove); } /** * @notice Withdraw Pair position from the contract, decomposed as the * baseToken (e.g WETH) and fractionalised NFT tokens * @param amount Amount of the LP position to withdraw * @param minBaseTokenOutputAmount Min amount to get of the base token, accounting for slippage * @param minFractionalTokenOutputAmount Min amount to get of fractional NFT, accounting for * slippage * @param deadline Deadline to retrieve position by */ function withdrawAndRemoveLPFromPool( uint256 amount, uint256 minBaseTokenOutputAmount, uint256 minFractionalTokenOutputAmount, uint256 deadline ) external updateReward(msg.sender) { require(amount > 0, "Cannot withdraw 0"); require(amount <= _balances[msg.sender], "Cannot withdraw more then you have staked"); // remove the amount the user is unstaking _totalSupply = _totalSupply - amount; _balances[msg.sender] = _balances[msg.sender] - amount; // take fee from lptoken uint256 batonFeeAmount = calculatePercentage(batonFactory.batonLPFee(), amount); // calculate batons fee // if the fee is more then 0 send the fee to batonMonitor if (batonFeeAmount > 0) { stakingToken.safeTransfer(batonMonitor, batonFeeAmount); } uint256 amountToRemove = amount - batonFeeAmount; if (amountToRemove > 0) { // calculate the amount of base (eth for example) tokens and fractional nft tokens the user should receive (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) = pair.remove(amountToRemove, minBaseTokenOutputAmount, minFractionalTokenOutputAmount, deadline); // transfer the base / fractional nft tokens to the user SafeTransferLib.safeTransferETH(msg.sender, baseTokenOutputAmount); ERC20(address(pair)).safeTransfer(msg.sender, fractionalTokenOutputAmount); } // send user his rewards harvest(); // emit an event emit Withdrawn(msg.sender, amountToRemove); } /** * @notice Withdraw staked tokens from the farm * @param amount Amount of the Pair position to withdraw */ function withdraw(uint256 amount) public updateReward(msg.sender) { require(amount > 0, "Cannot withdraw 0"); require(amount <= _balances[msg.sender], "Cannot withdraw more then you have staked"); // remove the amount unstaked from the total balance _totalSupply = _totalSupply - amount; _balances[msg.sender] = _balances[msg.sender] - amount; uint256 batonFeeAmount = calculatePercentage(batonFactory.batonLPFee(), amount); // calculate batons fee // if the fee is more then 0 send the fee to batonMonitor if (batonFeeAmount > 0) { stakingToken.safeTransfer(batonMonitor, batonFeeAmount); } uint256 amountToWithdrawal = amount - batonFeeAmount; // transfer the tokens to user if (amountToWithdrawal > 0) { stakingToken.safeTransfer(msg.sender, amountToWithdrawal); } emit Withdrawn(msg.sender, amountToWithdrawal); } /*////////////////////////////////////////////////////////////// GET REWARDS //////////////////////////////////////////////////////////////*/ /** * @notice Allow a user to harvest the rewards accumulated up until this point * @notice If a fee is set then the contract will reduct the fee from the total sent to the fuction caller. */ function harvest() public updateReward(msg.sender) { uint256 reward = rewards[msg.sender]; // get users earned fees uint256 batonFeeAmount = calculatePercentage(batonFactory.batonRewardsFee(), reward); // calculate batons fee rewards[msg.sender] = 0; // clear the reward counter for the user // if the fee is more then 0 send the fee to batonMonitor if (batonFeeAmount > 0) { rewardsToken.safeTransfer(batonMonitor, batonFeeAmount); } uint256 amountToReward = reward - batonFeeAmount; if (amountToReward > 0) { // send the reward rewardsToken.safeTransfer(msg.sender, amountToReward); } // emit an event emit RewardPaid(msg.sender, amountToReward); } /** * @notice Withdraws staked tokens and harvests earned rewards in a single transaction. * @dev Calls both `withdraw` and `harvest` functions for the caller. */ function withdrawAndHarvest() external { withdraw(_balances[msg.sender]); harvest(); } /*////////////////////////////////////////////////////////////// MIGRATE FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Initiates the migration process by setting the migration target. * @dev Only callable by the contract owner when the pool is active. * @param _migration The address of the new contract to migrate to. */ function initiateMigration(address _migration) external onlyOwner poolNotMigrated { require(_migration != address(0), "Please migrate to a valid address"); require(_migration != address(this), "Cannot migrate to self"); migration = _migration; // emit an event emit MigrationInitiated(_migration); } /** * @notice Migrates the unearned rewards to the new contract. * @dev Only callable by the BatonMonitor address when in migration mode and the pool is active. */ function migrate() external onlyBatonMonitor inMigrationMode poolNotMigrated onlyWhenPoolActive { // calculate staking rewards still not rewarded uint256 rewardsToMigrate = _unearnedRewards(); // complete migration migrationComplete = true; // stop farm periodFinish = block.timestamp; // transfer these rewards to the migration address. rewardsToken.safeTransfer(address(migration), rewardsToMigrate); // emit an event emit MigrationComplete(migration, rewardsToMigrate, block.timestamp); } /*////////////////////////////////////////////////////////////// NOTIFY FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Given a reward amount calculate the rewardRate per second * @notice Other fees may be incurred due to token spesific fees (some tokens may take a fee on transferFrom). * @dev Only callable by the rewards distributor or the contract owner. Updates the rewards state. * This function should be called every time new rewards are added to the farm. * @param reward The amount of reward to be distributed. */ function notifyRewardAmount(uint256 reward) external onlyRewardsDistributor poolNotMigrated updateReward(address(0)) { require(reward > 0, "reward cannot be 0"); rewardsToken.transferFrom(msg.sender, address(this), reward); uint256 surplusAmount = 0; if (block.timestamp >= periodFinish) { rewardRate = reward / rewardsDuration; periodFinish = block.timestamp + rewardsDuration; surplusAmount = reward - (rewardRate * rewardsDuration); } else { uint256 remaining = periodFinish - block.timestamp; uint256 leftover = remaining * rewardRate; rewardRate = (reward + leftover) / remaining; surplusAmount = (reward + leftover) - (rewardRate * remaining); } // Ensure the provided reward amount is not more than the balance in the contract. // This keeps the reward rate in the right range, preventing overflows due to // very high values of rewardRate in the earned and rewardsPerToken functions; // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. require(rewardRate > 0, "reward rate = 0"); require(rewardRate * rewardsDuration <= rewardsToken.balanceOf(address(this)), "Provided reward too high"); // check for surplus, if it exist send back to rewards distributor if (surplusAmount != 0) { rewardsToken.safeTransfer(rewardsDistributor, surplusAmount); emit FoundSurplus(surplusAmount, rewardsDistributor); } lastUpdateTime = block.timestamp; emit RewardAdded(reward); } // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner { require(tokenAddress != address(stakingToken), "Cannot withdraw the staking token"); require(tokenAddress != address(rewardsToken), "Cannot withdraw the reward token"); ERC20(tokenAddress).safeTransfer(owner, tokenAmount); emit Recovered(tokenAddress, tokenAmount); } /** * @notice Sets the address of the rewards distributor. * @dev Only callable by the contract owner. * @param _rewardsDistributor The address of the new rewards distributor. */ function setRewardsDistributor(address _rewardsDistributor) external onlyOwner { require(_rewardsDistributor != address(0), "_rewardsDistributor cannot be address(0)"); rewardsDistributor = _rewardsDistributor; emit UpdateRewardsDistributor(_rewardsDistributor); } /** * @notice Update the rewardsDuration. * @dev Only callable by the contract owner. * @param _rewardsDuration The new rewardsDuration */ function setRewardsDuration(uint256 _rewardsDuration) public onlyOwner { require(block.timestamp >= periodFinish, "pool is running, cannot update the duration"); rewardsDuration = _rewardsDuration; emit UpdateRewardsDuration(rewardsDuration); } /** * @notice pause the contract */ function pause() external onlyOwner { _pause(); } /** * @notice unpause the contract */ function unpause() external onlyOwner { _unpause(); } /* ========== MODIFIERS ========== */ /** * @notice Modifier to update the rewards state before executing a function. * @dev Updates rewardPerTokenStored, lastUpdateTime, rewards[account], and userRewardPerTokenPaid[account]. * @param account The address of the account for which to update the rewards state. */ modifier updateReward(address account) { rewardPerTokenStored = rewardPerToken(); lastUpdateTime = lastTimeRewardApplicable(); if (account != address(0)) { rewards[account] = earned(account); userRewardPerTokenPaid[account] = rewardPerTokenStored; } _; } /** * @notice Modifier to ensure that only the rewards distributor or contract owner can call a function. * @dev Requires that the caller is either the rewards distributor or the contract owner. */ modifier onlyRewardsDistributor() { require(msg.sender == rewardsDistributor || msg.sender == owner, "Caller is not RewardsDistributor contract"); _; } /** * @notice Modifier to ensure that only the BatonMonitor contract can call a function. * @dev Requires that the caller is the BatonMonitor contract. */ modifier onlyBatonMonitor() { require(msg.sender == batonMonitor, "Caller is not BatonMonitor contract"); _; } /** * @notice Modifier to ensure that the contract is in migration mode. * @dev Requires that the migration address is not the zero address. */ modifier inMigrationMode() { require(migration != address(0), "Contract owner must first call initiateMigration()"); _; } /** * @notice Modifier to ensure that the pool is still active before calling a function. * @dev Requires that the migration has not been completed. */ modifier poolNotMigrated() { require(!migrationComplete, "This contract has been migrated, you cannot deposit new funds."); _; } /** * @notice Modifier to ensure that the pool is active. * @dev Requires that block.timestamp < periodFinish */ modifier onlyWhenPoolActive() { require(block.timestamp < periodFinish, "This farm is not active"); _; } /** * @notice Modifier to ensure that the pool is still active before calling a function. * @dev Requires that the migration has not been completed. */ modifier onlyWhenPoolOver() { require(block.timestamp >= periodFinish, "This farm is still active"); _; } } // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); } /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); } /** * @title IWETH9 * @author Dapphub * @notice [Wrapped Ether](https://weth.io/) smart contract. Extends **ERC20**. */ interface IWETH9 is IERC20Metadata { /// @notice Emitted when **ETH** is wrapped. event Deposit(address indexed dst, uint256 wad); /// @notice Emitted when **ETH** is unwrapped. event Withdrawal(address indexed src, uint256 wad); /** * @notice Wraps Ether. **WETH** will be minted to the sender at 1 **ETH** : 1 **WETH**. */ receive() external payable; /** * @notice Wraps Ether. **WETH** will be minted to the sender at 1 **ETH** : 1 **WETH**. */ fallback() external payable; /** * @notice Wraps Ether. **WETH** will be minted to the sender at 1 **ETH** : 1 **WETH**. */ function deposit() external payable; /** * @notice Unwraps Ether. **ETH** will be returned to the sender at 1 **ETH** : 1 **WETH**. * @param wad Amount to unwrap. */ function withdraw(uint256 wad) external; } /// @title BatonFactory /// @author Baton team /// @notice Factory contract for creating new BatonFarm contract instances contract BatonFactory { using SafeTransferLib for ERC20; using SafeTransferLib for address; /*////////////////////////////////////////////////////////////// ENUMS //////////////////////////////////////////////////////////////*/ enum Type { ETH, ERC20, NFT } /*////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ IWETH9 public weth; Caviar public caviar; address public batonMonitor; /*////////////////////////////////////////////////////////////// FEE VARIABLES //////////////////////////////////////////////////////////////*/ uint256 public batonRewardsFee; uint256 public proposedRewardsFee; uint256 public rewardsFeeProposalApprovalDate; uint256 public batonLPFee; uint256 public proposedLPFee; uint256 public LPFeeProposalApprovalDate; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event FarmCreated( address farmAddress, address owner, address rewardsDistributor, address rewardsToken, address pairAddress, uint256 rewardsDuration, Type farmType ); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address payable _weth, address _caviar, address _batonMonitor) { weth = IWETH9(_weth); caviar = Caviar(_caviar); batonMonitor = _batonMonitor; } /*////////////////////////////////////////////////////////////// MANAGE FEES FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Propose a new fee * @param _proposedLPFee The proposed fee in basis points (100 bp = 1%) * @dev Only callable by the BatonMonitor. */ function proposeNewLPFee(uint256 _proposedLPFee) external onlyBatonMonitor { require(_proposedLPFee <= 25 * 100, "must: _proposedLPFee <= 2500 bp"); proposedLPFee = _proposedLPFee; LPFeeProposalApprovalDate = block.timestamp + 7 days; } /** * @notice Set the fee to the latest proposed fee if 7 days have passed from proposal * @notice Revert if the date of proposal approval has not arrived * @dev Only callable by the BatonMonitor. */ function setLPFeeRate() external onlyBatonMonitor { require(LPFeeProposalApprovalDate != 0, "no fee proposal"); require(LPFeeProposalApprovalDate < block.timestamp, "must: LPFeeProposalApprovalDate < block.timestamp"); batonLPFee = proposedLPFee; LPFeeProposalApprovalDate = 0; } /** * @notice Propose a new fee * @param _proposedRewardsFee The proposed fee in basis points (100 bp = 1%) * @dev Only callable by the BatonMonitor. */ function proposeNewRewardsFee(uint256 _proposedRewardsFee) external onlyBatonMonitor { require(_proposedRewardsFee <= 25 * 100, "must: _proposedRewardsFee <= 2500 bp"); proposedRewardsFee = _proposedRewardsFee; rewardsFeeProposalApprovalDate = block.timestamp + 7 days; } /** * @notice Set the fee to the latest proposed fee if 7 days have passed from proposal * @notice Revert if the date of proposal approval has not arrived * @dev Only callable by the BatonMonitor. */ function setRewardsFeeRate() external onlyBatonMonitor { require(rewardsFeeProposalApprovalDate != 0, "no fee proposal"); require( rewardsFeeProposalApprovalDate < block.timestamp, "must: rewardsFeeProposalApprovalDate < block.timestamp" ); batonRewardsFee = proposedRewardsFee; rewardsFeeProposalApprovalDate = 0; } /*////////////////////////////////////////////////////////////// CREATE FARMS //////////////////////////////////////////////////////////////*/ /// @notice Creates an instance of BatonFarm, incentivising staking with ERC20 rewards /// @param _owner Owner of the farm /// @param _rewardsToken Address of the rewards token /// @param _rewardAmount Amount of the rewards token to initially bootstrap the pool with /// @param _pairAddress Address of the underlying staking pair /// @param _rewardsDuration Duration that the rewards should be vested over /// @return Address of the newly-created BatonFarm contract instance function createFarmFromExistingPairERC20( address _owner, address _rewardsToken, uint256 _rewardAmount, address _pairAddress, uint256 _rewardsDuration ) external returns (address) { require(_rewardAmount > 0, "expected _rewardAmount > 0"); // create farm BatonFarm farm = new BatonFarm(_owner, address(this), batonMonitor, _rewardsToken, _pairAddress, _rewardsDuration, address(this)); // fund the farm with reward tokens ERC20 rewardsToken = ERC20(_rewardsToken); rewardsToken.safeTransferFrom(msg.sender, address(this), _rewardAmount); rewardsToken.approve(address(farm), _rewardAmount); // update farm with new rewards farm.notifyRewardAmount(_rewardAmount); // Emit event emit FarmCreated( address(farm), _owner, address(this), _rewardsToken, _pairAddress, _rewardsDuration, Type.ERC20 ); // Return address of the farm return address(farm); } /// @notice Creates an instance of BatonFarm, incentivising staking with ETH rewards /// @param _owner Owner of the farm /// @param _pairAddress Address of the underlying staking pair /// @param _rewardsDuration Duration that the rewards should be vested over /// @return Address of the newly-created BatonFarm contract instance /// @dev This is very much `createFarmFromExistingPairERC20` but with ETH function createFarmFromExistingPairETH( address _owner, address _pairAddress, uint256 _rewardsDuration ) external payable returns (address) { require(msg.value > 0, "expected msg.value > 0"); // deposit sent ETH into weth weth.deposit{ value: msg.value }(); // create farm with WETH as reward BatonFarm farm = new BatonFarm(_owner, address(this), batonMonitor, address(weth), _pairAddress, _rewardsDuration, address(this)); // transfer WETH into farm weth.approve(address(farm), msg.value); // update farm with new rewards farm.notifyRewardAmount(msg.value); //emit event emit FarmCreated(address(farm), _owner, address(this), address(weth), _pairAddress, _rewardsDuration, Type.ETH); // return address of the farm return address(farm); } /// @notice Creates an instance of BatonFarm, incentivising staking with fractional NFT rewards /// @param _owner Owner of the farm /// @param _rewardsNFT Address of the NFT to be given as rewards once fractionalised /// @param _rewardsTokenIds IDs of the NFT to retrieve from the msg.sender /// @param _rewardsDuration Duration that the rewards should be vested over /// @param _oracleMessages Messages from Reservoir to send to Caviar for getting fractional amount /// @param _pairAddress Address of the underlying staking pair /// @return Address of the newly-created BatonFarm contract instance /// @dev This is very much `createFarmFromExistingPairERC20` but with fractional NFTs from Caviar function createFarmFromExistingPairNFT( address _owner, address _rewardsNFT, uint256[] calldata _rewardsTokenIds, uint256 _rewardsDuration, address _pairAddress, ReservoirOracle.Message[] calldata _oracleMessages ) external returns (address) { ERC721 rewardsNFT = ERC721(_rewardsNFT); require(_rewardsTokenIds.length > 0, "expected _rewardsTokenIds.length > 0"); // transfer all nfts to this contract for (uint256 i = 0; i < _rewardsTokenIds.length; i++) { rewardsNFT.transferFrom(msg.sender, address(this), _rewardsTokenIds[i]); } // Fractionalise via Caviar Pair pair = Pair(caviar.pairs(_rewardsNFT, address(0), bytes32(0))); rewardsNFT.setApprovalForAll(address(pair), true); // Get fractional token amounts that will be the rewards for staking bytes32[][] memory proof = new bytes32[][](0); uint256 fractionalTokenAmount = pair.wrap(_rewardsTokenIds, proof, _oracleMessages); // Create a new farm BatonFarm farm = new BatonFarm(_owner, address(this), batonMonitor, address(pair), _pairAddress, _rewardsDuration, address(this)); // Deposit the fractionalised-NFT rewards into the farm ERC20(address(pair)).safeTransfer(address(this), fractionalTokenAmount); ERC20(address(pair)).approve(address(farm), fractionalTokenAmount); farm.notifyRewardAmount(fractionalTokenAmount); // Emit event emit FarmCreated(address(farm), _owner, address(this), address(pair), _pairAddress, _rewardsDuration, Type.NFT); // Return address of the farm return address(farm); } /* ========== MODIFIERS ========== */ /** * @notice Modifier to ensure that only the BatonMonitor contract can call a function. * @dev Requires that the caller is the BatonMonitor contract. */ modifier onlyBatonMonitor() { require(msg.sender == batonMonitor, "Caller is not BatonMonitor contract"); _; } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address payable","name":"_weth","type":"address"},{"internalType":"address","name":"_caviar","type":"address"},{"internalType":"address","name":"_batonMonitor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"farmAddress","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"rewardsDistributor","type":"address"},{"indexed":false,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"address","name":"pairAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardsDuration","type":"uint256"},{"indexed":false,"internalType":"enum BatonFactory.Type","name":"farmType","type":"uint8"}],"name":"FarmCreated","type":"event"},{"inputs":[],"name":"LPFeeProposalApprovalDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"batonLPFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"batonMonitor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"batonRewardsFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"caviar","outputs":[{"internalType":"contract Caviar","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"uint256","name":"_rewardAmount","type":"uint256"},{"internalType":"address","name":"_pairAddress","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"createFarmFromExistingPairERC20","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_pairAddress","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"createFarmFromExistingPairETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_rewardsNFT","type":"address"},{"internalType":"uint256[]","name":"_rewardsTokenIds","type":"uint256[]"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"},{"internalType":"address","name":"_pairAddress","type":"address"},{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct ReservoirOracle.Message[]","name":"_oracleMessages","type":"tuple[]"}],"name":"createFarmFromExistingPairNFT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_proposedLPFee","type":"uint256"}],"name":"proposeNewLPFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_proposedRewardsFee","type":"uint256"}],"name":"proposeNewRewardsFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proposedLPFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proposedRewardsFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsFeeProposalApprovalDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"setLPFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setRewardsFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162005da738038062005da7833981016040819052620000349162000090565b600080546001600160a01b039485166001600160a01b031991821617909155600180549385169382169390931790925560028054919093169116179055620000e4565b6001600160a01b03811681146200008d57600080fd5b50565b600080600060608486031215620000a657600080fd5b8351620000b38162000077565b6020850151909350620000c68162000077565b6040850151909250620000d98162000077565b809150509250925092565b615cb380620000f46000396000f3fe608060405260043610620001075760003560e01c80636037a6131162000095578063d904422e1162000060578063d904422e14620002bb578063e4710a4814620002d3578063e7c3d7a31462000302578063f6c6b0a4146200031a57600080fd5b80636037a613146200024e5780637167174114620002665780638368bda4146200027e578063adcc639714620002a357600080fd5b80632f9bd7a211620000d65780632f9bd7a214620001c857806333f2489314620001e05780633fc8cef31462000207578063598f7fcf146200023657600080fd5b806312838957146200010c5780631de3a883146200015b578063217c1d68146200017257806324ee8e7114620001a1575b600080fd5b3480156200011957600080fd5b50620001316200012b366004620014ee565b6200033f565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620001316200016c36600462001550565b620005cd565b3480156200017f57600080fd5b50600154620001319073ffffffffffffffffffffffffffffffffffffffff1681565b348015620001ae57600080fd5b50620001b960065481565b60405190815260200162000152565b348015620001d557600080fd5b50620001b960085481565b348015620001ed57600080fd5b5062000205620001ff36600462001596565b620008d1565b005b3480156200021457600080fd5b50600054620001319073ffffffffffffffffffffffffffffffffffffffff1681565b3480156200024357600080fd5b5062000205620009ce565b3480156200025b57600080fd5b506200020562000b37565b3480156200027357600080fd5b50620001b960055481565b3480156200028b57600080fd5b50620001316200029d366004620015ff565b62000ca0565b348015620002b057600080fd5b50620001b960035481565b348015620002c857600080fd5b50620001b960075481565b348015620002e057600080fd5b50600254620001319073ffffffffffffffffffffffffffffffffffffffff1681565b3480156200030f57600080fd5b50620001b960045481565b3480156200032757600080fd5b50620002056200033936600462001596565b62001243565b6000808411620003965760405162461bcd60e51b815260206004820152601a60248201527f6578706563746564205f726577617264416d6f756e74203e203000000000000060448201526064015b60405180910390fd5b60008630600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1688878730604051620003cf90620014ba565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501528416608084015260a083015290911660c082015260e001604051809103906000f08015801562000435573d6000803e3d6000fd5b509050856200045d73ffffffffffffffffffffffffffffffffffffffff821633308962001365565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820188905282169063095ea7b3906044016020604051808303816000875af1158015620004d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004f99190620016bf565b506040517f3c6b16ab0000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff831690633c6b16ab90602401600060405180830381600087803b1580156200056357600080fd5b505af115801562000578573d6000803e3d6000fd5b505050507f3a6811b476d594278134d846bbe7ef00a8376ce6deaff055fe1716f4051cc6788289308a89896001604051620005ba9796959493929190620016ea565b60405180910390a1509695505050505050565b6000803411620006205760405162461bcd60e51b815260206004820152601660248201527f6578706563746564206d73672e76616c7565203e20300000000000000000000060448201526064016200038d565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156200068957600080fd5b505af11580156200069e573d6000803e3d6000fd5b50506002546000805460405191955089945030935073ffffffffffffffffffffffffffffffffffffffff928316921690889088908590620006df90620014ba565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501528416608084015260a083015290911660c082015260e001604051809103906000f08015801562000745573d6000803e3d6000fd5b506000546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808416600483015234602483015292935091169063095ea7b3906044016020604051808303816000875af1158015620007c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007e79190620016bf565b506040517f3c6b16ab00000000000000000000000000000000000000000000000000000000815234600482015273ffffffffffffffffffffffffffffffffffffffff821690633c6b16ab90602401600060405180830381600087803b1580156200085057600080fd5b505af115801562000865573d6000803e3d6000fd5b5050600080546040517f3a6811b476d594278134d846bbe7ef00a8376ce6deaff055fe1716f4051cc6789450620008c1935085928a92309273ffffffffffffffffffffffffffffffffffffffff909116918b918b9190620016ea565b60405180910390a1949350505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314620009605760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b6109c4811115620009b45760405162461bcd60e51b815260206004820152601f60248201527f6d7573743a205f70726f706f7365644c50466565203c3d20323530302062700060448201526064016200038d565b6007819055620009c84262093a80620017a7565b60085550565b60025473ffffffffffffffffffffffffffffffffffffffff16331462000a5d5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60085460000362000ab15760405162461bcd60e51b815260206004820152600f60248201527f6e6f206665652070726f706f73616c000000000000000000000000000000000060448201526064016200038d565b426008541062000b2a5760405162461bcd60e51b815260206004820152603160248201527f6d7573743a204c5046656550726f706f73616c417070726f76616c446174652060448201527f3c20626c6f636b2e74696d657374616d7000000000000000000000000000000060648201526084016200038d565b6007546006556000600855565b60025473ffffffffffffffffffffffffffffffffffffffff16331462000bc65760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60055460000362000c1a5760405162461bcd60e51b815260206004820152600f60248201527f6e6f206665652070726f706f73616c000000000000000000000000000000000060448201526064016200038d565b426005541062000c935760405162461bcd60e51b815260206004820152603660248201527f6d7573743a207265776172647346656550726f706f73616c417070726f76616c60448201527f44617465203c20626c6f636b2e74696d657374616d700000000000000000000060648201526084016200038d565b6004546003556000600555565b6000878662000d175760405162461bcd60e51b8152602060048201526024808201527f6578706563746564205f72657761726473546f6b656e4964732e6c656e67746860448201527f203e20300000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60005b8781101562000e02578173ffffffffffffffffffffffffffffffffffffffff166323b872dd33308c8c8681811062000d565762000d56620017c3565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801562000dd357600080fd5b505af115801562000de8573d6000803e3d6000fd5b50505050808062000df990620017f2565b91505062000d1a565b506001546040517fb3c3b30400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b811660048301526000602483018190526044830181905292169063b3c3b30490606401602060405180830381865afa15801562000e83573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ea991906200182d565b6040517fa22cb46500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8083166004830152600160248301529192509083169063a22cb46590604401600060405180830381600087803b15801562000f1e57600080fd5b505af115801562000f33573d6000803e3d6000fd5b506000925082915062000f439050565b60405190808252806020026020018201604052801562000f7857816020015b606081526020019060019003908162000f625790505b50905060008273ffffffffffffffffffffffffffffffffffffffff166335ec39d98c8c858b8b6040518663ffffffff1660e01b815260040162000fc0959493929190620019dc565b6020604051808303816000875af115801562000fe0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001006919062001af2565b905060008d30600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16868c8e306040516200104190620014ba565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501528416608084015260a083015290911660c082015260e001604051809103906000f080158015620010a7573d6000803e3d6000fd5b509050620010cd73ffffffffffffffffffffffffffffffffffffffff8516308462001413565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526024820184905285169063095ea7b3906044016020604051808303816000875af115801562001143573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620011699190620016bf565b506040517f3c6b16ab0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff821690633c6b16ab90602401600060405180830381600087803b158015620011d357600080fd5b505af1158015620011e8573d6000803e3d6000fd5b505050507f3a6811b476d594278134d846bbe7ef00a8376ce6deaff055fe1716f4051cc678818f30878d8f60026040516200122a9796959493929190620016ea565b60405180910390a19d9c50505050505050505050505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314620012d25760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b6109c48111156200134b5760405162461bcd60e51b8152602060048201526024808201527f6d7573743a205f70726f706f73656452657761726473466565203c3d2032353060448201527f302062700000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60048190556200135f4262093a80620017a7565b60055550565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806200140c5760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016200038d565b5050505050565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080620014b45760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016200038d565b50505050565b6141718062001b0d83390190565b73ffffffffffffffffffffffffffffffffffffffff81168114620014eb57600080fd5b50565b600080600080600060a086880312156200150757600080fd5b85356200151481620014c8565b945060208601356200152681620014c8565b93506040860135925060608601356200153f81620014c8565b949793965091946080013592915050565b6000806000606084860312156200156657600080fd5b83356200157381620014c8565b925060208401356200158581620014c8565b929592945050506040919091013590565b600060208284031215620015a957600080fd5b5035919050565b60008083601f840112620015c357600080fd5b50813567ffffffffffffffff811115620015dc57600080fd5b6020830191508360208260051b8501011115620015f857600080fd5b9250929050565b60008060008060008060008060c0898b0312156200161c57600080fd5b88356200162981620014c8565b975060208901356200163b81620014c8565b9650604089013567ffffffffffffffff808211156200165957600080fd5b620016678c838d01620015b0565b909850965060608b0135955060808b013591506200168582620014c8565b90935060a08a013590808211156200169c57600080fd5b50620016ab8b828c01620015b0565b999c989b5096995094979396929594505050565b600060208284031215620016d257600080fd5b81518015158114620016e357600080fd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff88811682528781166020830152868116604083015285811660608301528416608082015260a0810183905260e081016003831062001766577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8260c083015298975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115620017bd57620017bd62001778565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362001826576200182662001778565b5060010190565b6000602082840312156200184057600080fd5b8151620016e381620014c8565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126200188357600080fd5b830160208101925035905067ffffffffffffffff811115620018a457600080fd5b803603821315620015f857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b81019150846000805b88811015620019ce578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8189360301811262001958578283fd5b88018035865260806200196e888301836200184d565b828a8a015262001982838a018284620018b4565b9250505060408083013581890152506060620019a1818401846200184d565b9350888303828a0152620019b7838583620018b4565b9d8a019d9850505093870193505060010162001918565b509298975050505050505050565b6060815284606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111562001a1657600080fd5b8560051b8088608085013780830190506080810160206080858403018186015281885180845260a08501915060a08160051b8601019350828a016000805b8381101562001aca578787037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60018552825180518089529087019087890190845b8181101562001ab35783518352928901929189019160010162001a95565b509098505050938501939185019160010162001a54565b505050505050838103604085015262001ae5818688620018fd565b9998505050505050505050565b60006020828403121562001b0557600080fd5b505191905056fe6101206040523480156200001257600080fd5b506040516200417138038062004171833981016040819052620000359162000542565b600080546001600160a81b0319166101006001600160a01b038a169081029190911782556040518992907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b038716620000e15760405162461bcd60e51b815260206004820152601d60248201527f5f6f776e65722073686f756c646e74206265206164647265737328302900000060448201526064015b60405180910390fd5b6001600160a01b0386166200014c5760405162461bcd60e51b815260206004820152602a60248201527f5f726577617264734469737472696275746f722073686f756c646e74206265206044820152696164647265737328302960b01b6064820152608401620000d8565b6001600160a01b038516620001b05760405162461bcd60e51b8152602060048201526024808201527f5f6261746f6e4d6f6e69746f722073686f756c646e74206265206164647265736044820152637328302960e01b6064820152608401620000d8565b6001600160a01b038416620002145760405162461bcd60e51b8152602060048201526024808201527f5f72657761726473546f6b656e2073686f756c646e74206265206164647265736044820152637328302960e01b6064820152608401620000d8565b6001600160a01b038316620002785760405162461bcd60e51b815260206004820152602360248201527f5f70616972416464726573732073686f756c646e74206265206164647265737360448201526228302960e81b6064820152608401620000d8565b6001600160a01b038116620002dc5760405162461bcd60e51b8152602060048201526024808201527f5f6261746f6e466163746f72792073686f756c646e74206265206164647265736044820152637328302960e01b6064820152608401620000d8565b6001600160a01b03808416608081905290851660a05260408051635fcbd28560e01b81529051635fcbd285916004808201926020929091908290030181865afa1580156200032e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003549190620005e1565b6001600160a01b0390811660c052600b80546001600160a01b03191691881691909117905581620003c85760405162461bcd60e51b815260206004820152601c60248201527f5f726577617264734475726174696f6e2063616e6e6f742062652030000000006044820152606401620000d8565b63096601808210620004325760405162461bcd60e51b815260206004820152602c60248201527f5f726577617264734475726174696f6e2063616e6e6f74206265206d6f72652060448201526b7468656e203520796561727360a01b6064820152608401620000d8565b60038290556001600160a01b0380861660e05281811661010052608051604080516323e6650160e11b8152905191909216916347ccca029160048083019260209291908290030181865afa1580156200048f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004b59190620005e1565b60805160405163a22cb46560e01b81526001600160a01b0391821660048201526001602482015291169063a22cb46590604401600060405180830381600087803b1580156200050357600080fd5b505af115801562000518573d6000803e3d6000fd5b505050505050505050505062000608565b6001600160a01b03811681146200053f57600080fd5b50565b600080600080600080600060e0888a0312156200055e57600080fd5b87516200056b8162000529565b60208901519097506200057e8162000529565b6040890151909650620005918162000529565b6060890151909550620005a48162000529565b6080890151909450620005b78162000529565b60a089015160c08a01519194509250620005d18162000529565b8091505092959891949750929550565b600060208284031215620005f457600080fd5b8151620006018162000529565b9392505050565b60805160a05160c05160e05161010051613a5962000718600039600081816106820152818161101f0152818161131101528181611bae0152611ea6015260008181610846015281816110dc015281816113ce01528181611c7b01528181611f63015261267901526000818161060e01528181610d01015281816110ba01528181611122015281816113ac01528181611f41015281816124e30152612b190152600081816107dc015281816117840152818161192801528181611a1001528181611c5901528181611cc10152818161258a01526128cc01526000818161075d01528181610a9001528181610bd101528181611458015281816114df01528181611f9b015261204c0152613a596000f3fe6080604052600436106102be5760003560e01c806372f702f31161016e578063a8aa1b31116100cb578063d1f5c33b1161007f578063e4710a4811610064578063e4710a4814610834578063ebe2b12b14610868578063f2fde38b1461087e57600080fd5b8063d1f5c33b146107fe578063df136d651461081e57600080fd5b8063cc1a378f116100b0578063cc1a378f14610795578063cd3daf9d146107b5578063d1af0c7d146107ca57600080fd5b8063a8aa1b311461074b578063c8f33c911461077f57600080fd5b80638980f11f116101225780638da5cb5b116101075780638da5cb5b146106f15780638fd3ab8014610716578063a694fc3a1461072b57600080fd5b80638980f11f146106a45780638b876347146106c457600080fd5b806380faa57d1161015357806380faa57d146106465780638456cb591461065b57806384a9dd731461067057600080fd5b806372f702f3146105fc5780637b0a47ee1461063057600080fd5b806332a38f6b1161021c5780634641257d116101d057806360a77ec4116101b557806360a77ec41461058657806370a08231146105a657806371726c92146105dc57600080fd5b80634641257d146105595780635c975abb1461056e57600080fd5b80633c6b16ab116102015780633c6b16ab146105045780633f2a5540146105245780633f4ba83a1461054457600080fd5b806332a38f6b146104d9578063386a9525146104ee57600080fd5b80631e69cece116102735780632bff884f116102585780632bff884f146104575780632e1a7d4d146104995780633059bfcb146104b957600080fd5b80631e69cece146104225780632133e3b91461043757600080fd5b8063150b7a02116102a4578063150b7a021461035f5780631705a3bd146103d557806318160ddd1461040d57600080fd5b80628cc262146102ff5780630700037d1461033257600080fd5b366102fa5760405134815233907f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258749060200160405180910390a2005b600080fd5b34801561030b57600080fd5b5061031f61031a366004613280565b61089e565b6040519081526020015b60405180910390f35b34801561033e57600080fd5b5061031f61034d366004613280565b60086020526000908152604090205481565b34801561036b57600080fd5b506103a461037a3660046132a4565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610329565b3480156103e157600080fd5b50600a546103f5906001600160a01b031681565b6040516001600160a01b039091168152602001610329565b34801561041957600080fd5b5060065461031f565b61043561043036600461338f565b610939565b005b34801561044357600080fd5b5061031f610452366004613452565b610e6f565b34801561046357600080fd5b50600a546104899074010000000000000000000000000000000000000000900460ff1681565b6040519015158152602001610329565b3480156104a557600080fd5b506104356104b4366004613474565b610e93565b3480156104c557600080fd5b506104356104d436600461348d565b611185565b3480156104e557600080fd5b5061043561154f565b3480156104fa57600080fd5b5061031f60035481565b34801561051057600080fd5b5061043561051f366004613474565b611572565b34801561053057600080fd5b50600b546103f5906001600160a01b031681565b34801561055057600080fd5b50610435611abb565b34801561056557600080fd5b50610435611b22565b34801561057a57600080fd5b5060005460ff16610489565b34801561059257600080fd5b506104356105a13660046134cd565b611d1a565b3480156105b257600080fd5b5061031f6105c1366004613280565b6001600160a01b031660009081526009602052604090205490565b3480156105e857600080fd5b506104356105f7366004613280565b6121d3565b34801561060857600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561063c57600080fd5b5061031f60025481565b34801561065257600080fd5b5061031f612404565b34801561066757600080fd5b5061043561241b565b34801561067c57600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b3480156106b057600080fd5b506104356106bf366004613540565b612482565b3480156106d057600080fd5b5061031f6106df366004613280565b60076020526000908152604090205481565b3480156106fd57600080fd5b506000546103f59061010090046001600160a01b031681565b34801561072257600080fd5b5061043561266e565b34801561073757600080fd5b50610435610746366004613474565b61293f565b34801561075757600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561078b57600080fd5b5061031f60045481565b3480156107a157600080fd5b506104356107b0366004613474565b612b82565b3480156107c157600080fd5b5061031f612c8e565b3480156107d657600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561080a57600080fd5b50610435610819366004613280565b612d0d565b34801561082a57600080fd5b5061031f60055481565b34801561084057600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561087457600080fd5b5061031f60015481565b34801561088a57600080fd5b50610435610899366004613280565b612e4e565b6000806108a9612c8e565b6001600160a01b038416600090815260076020526040812054919250906108d0908361359b565b6001600160a01b038516600090815260096020526040812054919250670de0b6b3a76400006108ff84846135ae565b61090991906135c5565b6001600160a01b03871660009081526008602052604090205490915061092f9082613600565b9695505050505050565b600154421061098f5760405162461bcd60e51b815260206004820152601760248201527f54686973206661726d206973206e6f742061637469766500000000000000000060448201526064015b60405180910390fd5b610997612f17565b600a5474010000000000000000000000000000000000000000900460ff1615610a285760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b33610a31612c8e565b600555610a3c612404565b6004556001600160a01b03811615610a8357610a578161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60005b8a811015610bcc577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610aec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b109190613613565b6001600160a01b03166342842e0e33308f8f86818110610b3257610b32613630565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b158015610ba157600080fd5b505af1158015610bb5573d6000803e3d6000fd5b505050508080610bc49061365f565b915050610a86565b5060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634cfb998a34348f8f8f8f8f8f8f8f8f8f6040518d63ffffffff1660e01b8152600401610c309b9a99989796959493929190613863565b60206040518083038185885af1158015610c4e573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610c73919061398e565b905060008111610cc55760405162461bcd60e51b815260206004820152600e60248201527f43616e6e6f74207374616b6520300000000000000000000000000000000000006044820152606401610986565b80600654610cd39190613600565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610d50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d74919061398e565b14610de75760405162461bcd60e51b815260206004820152603460248201527f7374616b696e67546f6b656e2062616c616e6365206469646e7420757064617460448201527f652066726f6d206c70546f6b656e416d6f756e740000000000000000000000006064820152608401610986565b80600654610df59190613600565b60065533600090815260096020526040902054610e13908290613600565b33600081815260096020526040908190209290925590517f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d90610e599084815260200190565b60405180910390a2505050505050505050505050565b600080612710610e7f84866135ae565b610e8991906135c5565b9150505b92915050565b33610e9c612c8e565b600555610ea7612404565b6004556001600160a01b03811615610eee57610ec28161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60008211610f3e5760405162461bcd60e51b815260206004820152601160248201527f43616e6e6f7420776974686472617720300000000000000000000000000000006044820152606401610986565b33600090815260096020526040902054821115610fc35760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207769746864726177206d6f7265207468656e20796f7520686160448201527f7665207374616b656400000000000000000000000000000000000000000000006064820152608401610986565b81600654610fd1919061359b565b60065533600090815260096020526040902054610fef90839061359b565b60096000336001600160a01b03166001600160a01b031681526020019081526020016000208190555060006110a57f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324ee8e716040518163ffffffff1660e01b8152600401602060405180830381865afa15801561107b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061109f919061398e565b84610e6f565b90508015611101576111016001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b600061110d828561359b565b90508015611149576111496001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383612f6a565b60405181815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5906020015b60405180910390a250505050565b3361118e612c8e565b600555611199612404565b6004556001600160a01b038116156111e0576111b48161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b600085116112305760405162461bcd60e51b815260206004820152601160248201527f43616e6e6f7420776974686472617720300000000000000000000000000000006044820152606401610986565b336000908152600960205260409020548511156112b55760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207769746864726177206d6f7265207468656e20796f7520686160448201527f7665207374616b656400000000000000000000000000000000000000000000006064820152608401610986565b846006546112c3919061359b565b600655336000908152600960205260409020546112e190869061359b565b60096000336001600160a01b03166001600160a01b031681526020019081526020016000208190555060006113977f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324ee8e716040518163ffffffff1660e01b8152600401602060405180830381865afa15801561136d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611391919061398e565b87610e6f565b905080156113f3576113f36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b60006113ff828861359b565b90508015611509576040517f1ae74cdd0000000000000000000000000000000000000000000000000000000081526004810182905260248101879052604481018690526064810185905260009081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631ae74cdd9060840160408051808303816000875af11580156114a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c491906139a7565b915091506114d2338361300f565b6115066001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383612f6a565b50505b611511611b22565b60405181815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d59060200160405180910390a250505050505050565b3360009081526009602052604090205461156890610e93565b611570611b22565b565b600b546001600160a01b031633148061159a575060005461010090046001600160a01b031633145b61160c5760405162461bcd60e51b815260206004820152602960248201527f43616c6c6572206973206e6f7420526577617264734469737472696275746f7260448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610986565b600a5474010000000000000000000000000000000000000000900460ff161561169d5760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b60006116a7612c8e565b6005556116b2612404565b6004556001600160a01b038116156116f9576116cd8161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b600082116117495760405162461bcd60e51b815260206004820152601260248201527f7265776172642063616e6e6f74206265203000000000000000000000000000006044820152606401610986565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af11580156117d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f991906139cb565b50600060015442106118465760035461181290846135c5565b6002556003546118229042613600565b60015560035460025461183591906135ae565b61183f908461359b565b90506118a8565b600042600154611856919061359b565b905060006002548261186891906135ae565b9050816118758287613600565b61187f91906135c5565b600281905561188f9083906135ae565b6118998287613600565b6118a3919061359b565b925050505b6000600254116118fa5760405162461bcd60e51b815260206004820152600f60248201527f7265776172642072617465203d203000000000000000000000000000000000006044820152606401610986565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611977573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199b919061398e565b6003546002546119ab91906135ae565b11156119f95760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606401610986565b8015611a7f57600b54611a39906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683612f6a565b600b54604080518381526001600160a01b0390921660208301527f3cd094c348efde9110a082dc51109d392ac0874b24682147545ed2a1f8c7e5e5910160405180910390a15b426004556040518381527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d9060200160405180910390a1505050565b60005461010090046001600160a01b03163314611b1a5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b61157061306f565b33611b2b612c8e565b600555611b36612404565b6004556001600160a01b03811615611b7d57611b518161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b600060086000336001600160a01b03166001600160a01b031681526020019081526020016000205490506000611c347f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663adcc63976040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2e919061398e565b83610e6f565b3360009081526008602052604081205590508015611ca057611ca06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b6000611cac828461359b565b90508015611ce857611ce86001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383612f6a565b60405181815233907fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e048690602001611177565b33611d23612c8e565b600555611d2e612404565b6004556001600160a01b03811615611d7557611d498161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60008711611dc55760405162461bcd60e51b815260206004820152601160248201527f43616e6e6f7420776974686472617720300000000000000000000000000000006044820152606401610986565b33600090815260096020526040902054871115611e4a5760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207769746864726177206d6f7265207468656e20796f7520686160448201527f7665207374616b656400000000000000000000000000000000000000000000006064820152608401610986565b86600654611e58919061359b565b60065533600090815260096020526040902054611e7690889061359b565b60096000336001600160a01b03166001600160a01b03168152602001908152602001600020819055506000611f2c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324ee8e716040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f26919061398e565b89610e6f565b90508015611f8857611f886001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b6000611f94828a61359b565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634cba3eca848c8c8c8c8c6040518763ffffffff1660e01b8152600401611fef969594939291906139e8565b60408051808303816000875af115801561200d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203191906139a7565b9150915061203f338361300f565b60005b87811015612188577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120cc9190613613565b6001600160a01b03166342842e0e30338c8c868181106120ee576120ee613630565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561215d57600080fd5b505af1158015612171573d6000803e3d6000fd5b5050505080806121809061365f565b915050612042565b50612191611b22565b60405183815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d59060200160405180910390a25050505050505050505050565b60005461010090046001600160a01b031633146122325760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b600a5474010000000000000000000000000000000000000000900460ff16156122c35760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b6001600160a01b03811661233f5760405162461bcd60e51b815260206004820152602160248201527f506c65617365206d69677261746520746f20612076616c69642061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610986565b306001600160a01b038216036123975760405162461bcd60e51b815260206004820152601660248201527f43616e6e6f74206d69677261746520746f2073656c66000000000000000000006044820152606401610986565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fbf3aee0580cb96d1eb8ffba88fb8dc83e973b7cdf4a18be5a0d3cbb4e6ecb3d3906020015b60405180910390a150565b60006001544210612416575060015490565b504290565b60005461010090046001600160a01b0316331461247a5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b6115706130df565b60005461010090046001600160a01b031633146124e15760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036125885760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207374616b696e6720746f6b6560448201527f6e000000000000000000000000000000000000000000000000000000000000006064820152608401610986565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036126095760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f74207769746864726177207468652072657761726420746f6b656e6044820152606401610986565b600054612628906001600160a01b038481169161010090041683612f6a565b604080516001600160a01b0384168152602081018390527f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa28910160405180910390a15050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461270c5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f61637400000000000000000000000000000000000000000000000000000000006064820152608401610986565b600a546001600160a01b031661278a5760405162461bcd60e51b815260206004820152603260248201527f436f6e7472616374206f776e6572206d7573742066697273742063616c6c206960448201527f6e6974696174654d6967726174696f6e282900000000000000000000000000006064820152608401610986565b600a5474010000000000000000000000000000000000000000900460ff161561281b5760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b600154421061286c5760405162461bcd60e51b815260206004820152601760248201527f54686973206661726d206973206e6f74206163746976650000000000000000006044820152606401610986565b600061287661313a565b600a8054740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff821617909155426001559091506128f5906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683612f6a565b600a54604080516001600160a01b0390921682526020820183905242908201527f0172d08d81df86673f7e65d3d77b5dfa9b07a646ac6aca2f3b25e2b93ffb3430906060016123f9565b600a5474010000000000000000000000000000000000000000900460ff16156129d05760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b6001544210612a215760405162461bcd60e51b815260206004820152601760248201527f54686973206661726d206973206e6f74206163746976650000000000000000006044820152606401610986565b612a29612f17565b33612a32612c8e565b600555612a3d612404565b6004556001600160a01b03811615612a8457612a588161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60008211612ad45760405162461bcd60e51b815260206004820152600e60248201527f43616e6e6f74207374616b6520300000000000000000000000000000000000006044820152606401610986565b81600654612ae29190613600565b60065533600090815260096020526040902054612b00908390613600565b33600081815260096020526040902091909155612b49907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690308561316a565b60405182815233907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d9060200160405180910390a25050565b60005461010090046001600160a01b03163314612be15760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b600154421015612c595760405162461bcd60e51b815260206004820152602b60248201527f706f6f6c2069732072756e6e696e672c2063616e6e6f7420757064617465207460448201527f6865206475726174696f6e0000000000000000000000000000000000000000006064820152608401610986565b60038190556040518181527f93ffc29e2d73e3e6b3262dfdc8cd0e6e838568a0860474ed3087f830a82f4aad906020016123f9565b6000600654600003612ca1575060055490565b6000612cab612404565b9050600060045482612cbd919061359b565b90506000600254670de0b6b3a7640000612cd791906135ae565b905060006006548284612cea91906135ae565b612cf491906135c5565b905080600554612d049190613600565b94505050505090565b60005461010090046001600160a01b03163314612d6c5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b6001600160a01b038116612de85760405162461bcd60e51b815260206004820152602860248201527f5f726577617264734469737472696275746f722063616e6e6f7420626520616460448201527f64726573732830290000000000000000000000000000000000000000000000006064820152608401610986565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f8c1a55060e521f27a67c03004984808ed1060d76b4abbe8c74f6d11484ac5043906020016123f9565b60005461010090046001600160a01b03163314612ead5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff166101006001600160a01b038416908102919091178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b60005460ff16156115705760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610986565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806130095760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610986565b50505050565b600080600080600085875af190508061306a5760405162461bcd60e51b815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610986565b505050565b613077613216565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6130e7612f17565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130c23390565b600080429050600081600154613150919061359b565b905060008160025461316291906135ae565b949350505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061320f5760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610986565b5050505050565b60005460ff166115705760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610986565b6001600160a01b038116811461327d57600080fd5b50565b60006020828403121561329257600080fd5b813561329d81613268565b9392505050565b6000806000806000608086880312156132bc57600080fd5b85356132c781613268565b945060208601356132d781613268565b935060408601359250606086013567ffffffffffffffff808211156132fb57600080fd5b818801915088601f83011261330f57600080fd5b81358181111561331e57600080fd5b89602082850101111561333057600080fd5b9699959850939650602001949392505050565b60008083601f84011261335557600080fd5b50813567ffffffffffffffff81111561336d57600080fd5b6020830191508360208260051b850101111561338857600080fd5b9250929050565b60008060008060008060008060008060e08b8d0312156133ae57600080fd5b8a3567ffffffffffffffff808211156133c657600080fd5b6133d28e838f01613343565b909c509a5060208d0135995060408d0135985060608d0135975060808d0135965060a08d013591508082111561340757600080fd5b6134138e838f01613343565b909650945060c08d013591508082111561342c57600080fd5b506134398d828e01613343565b915080935050809150509295989b9194979a5092959850565b6000806040838503121561346557600080fd5b50508035926020909101359150565b60006020828403121561348657600080fd5b5035919050565b600080600080608085870312156134a357600080fd5b5050823594602084013594506040840135936060013592509050565b801515811461327d57600080fd5b60008060008060008060a087890312156134e657600080fd5b863595506020870135945060408701359350606087013567ffffffffffffffff81111561351257600080fd5b61351e89828a01613343565b9094509250506080870135613532816134bf565b809150509295509295509295565b6000806040838503121561355357600080fd5b823561355e81613268565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610e8d57610e8d61356c565b8082028115828204841417610e8d57610e8d61356c565b6000826135fb577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115610e8d57610e8d61356c565b60006020828403121561362557600080fd5b815161329d81613268565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036136905761369061356c565b5060010190565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156136c957600080fd5b8260051b80836020870137939093016020019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261371757600080fd5b830160208101925035905067ffffffffffffffff81111561373757600080fd5b80360382131561338857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b81019150846000805b88811015613855578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818936030181126137e8578283fd5b88018035865260806137fc888301836136e2565b828a8a015261380e838a018284613746565b925050506040808301358189015250606061382b818401846136e2565b9350888303828a015261383f838583613746565b9d8a019d985050509387019350506001016137aa565b509298975050505050505050565b8b8152600060206101008184015261388061010084018d8f613697565b8b60408501528a60608501528960808501528860a085015283810360c0850152808782528282019050828860051b8301018960005b8a811015613963577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18d360301811261391657600080fd5b8c01868101903567ffffffffffffffff81111561393257600080fd5b8060051b360382131561394457600080fd5b61394f858284613697565b9588019594505050908501906001016138b5565b505085810360e087015261397881888a61378f565b9450505050509c9b505050505050505050505050565b6000602082840312156139a057600080fd5b5051919050565b600080604083850312156139ba57600080fd5b505080516020909101519092909150565b6000602082840312156139dd57600080fd5b815161329d816134bf565b86815285602082015284604082015260a060608201526000613a0e60a083018587613697565b9050821515608083015297965050505050505056fea2646970667358221220afd062ab7f1ce1130225242a24677e8238626f3498607434ff6628cd8af709df64736f6c63430008130033a264697066735822122097876a50fdbcd6bbea0c4a69cc398872370229cf0ec6f0eaf0bd6c83c3ec5dc264736f6c63430008130033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a964d6e8d90e5cd12592a8ef2b1735dae9ba08400000000000000000000000009e902766d3e2e33aa07c4433919cca6a2c6b96d0
Deployed Bytecode
0x608060405260043610620001075760003560e01c80636037a6131162000095578063d904422e1162000060578063d904422e14620002bb578063e4710a4814620002d3578063e7c3d7a31462000302578063f6c6b0a4146200031a57600080fd5b80636037a613146200024e5780637167174114620002665780638368bda4146200027e578063adcc639714620002a357600080fd5b80632f9bd7a211620000d65780632f9bd7a214620001c857806333f2489314620001e05780633fc8cef31462000207578063598f7fcf146200023657600080fd5b806312838957146200010c5780631de3a883146200015b578063217c1d68146200017257806324ee8e7114620001a1575b600080fd5b3480156200011957600080fd5b50620001316200012b366004620014ee565b6200033f565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620001316200016c36600462001550565b620005cd565b3480156200017f57600080fd5b50600154620001319073ffffffffffffffffffffffffffffffffffffffff1681565b348015620001ae57600080fd5b50620001b960065481565b60405190815260200162000152565b348015620001d557600080fd5b50620001b960085481565b348015620001ed57600080fd5b5062000205620001ff36600462001596565b620008d1565b005b3480156200021457600080fd5b50600054620001319073ffffffffffffffffffffffffffffffffffffffff1681565b3480156200024357600080fd5b5062000205620009ce565b3480156200025b57600080fd5b506200020562000b37565b3480156200027357600080fd5b50620001b960055481565b3480156200028b57600080fd5b50620001316200029d366004620015ff565b62000ca0565b348015620002b057600080fd5b50620001b960035481565b348015620002c857600080fd5b50620001b960075481565b348015620002e057600080fd5b50600254620001319073ffffffffffffffffffffffffffffffffffffffff1681565b3480156200030f57600080fd5b50620001b960045481565b3480156200032757600080fd5b50620002056200033936600462001596565b62001243565b6000808411620003965760405162461bcd60e51b815260206004820152601a60248201527f6578706563746564205f726577617264416d6f756e74203e203000000000000060448201526064015b60405180910390fd5b60008630600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1688878730604051620003cf90620014ba565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501528416608084015260a083015290911660c082015260e001604051809103906000f08015801562000435573d6000803e3d6000fd5b509050856200045d73ffffffffffffffffffffffffffffffffffffffff821633308962001365565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820188905282169063095ea7b3906044016020604051808303816000875af1158015620004d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004f99190620016bf565b506040517f3c6b16ab0000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff831690633c6b16ab90602401600060405180830381600087803b1580156200056357600080fd5b505af115801562000578573d6000803e3d6000fd5b505050507f3a6811b476d594278134d846bbe7ef00a8376ce6deaff055fe1716f4051cc6788289308a89896001604051620005ba9796959493929190620016ea565b60405180910390a1509695505050505050565b6000803411620006205760405162461bcd60e51b815260206004820152601660248201527f6578706563746564206d73672e76616c7565203e20300000000000000000000060448201526064016200038d565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156200068957600080fd5b505af11580156200069e573d6000803e3d6000fd5b50506002546000805460405191955089945030935073ffffffffffffffffffffffffffffffffffffffff928316921690889088908590620006df90620014ba565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501528416608084015260a083015290911660c082015260e001604051809103906000f08015801562000745573d6000803e3d6000fd5b506000546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808416600483015234602483015292935091169063095ea7b3906044016020604051808303816000875af1158015620007c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007e79190620016bf565b506040517f3c6b16ab00000000000000000000000000000000000000000000000000000000815234600482015273ffffffffffffffffffffffffffffffffffffffff821690633c6b16ab90602401600060405180830381600087803b1580156200085057600080fd5b505af115801562000865573d6000803e3d6000fd5b5050600080546040517f3a6811b476d594278134d846bbe7ef00a8376ce6deaff055fe1716f4051cc6789450620008c1935085928a92309273ffffffffffffffffffffffffffffffffffffffff909116918b918b9190620016ea565b60405180910390a1949350505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314620009605760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b6109c4811115620009b45760405162461bcd60e51b815260206004820152601f60248201527f6d7573743a205f70726f706f7365644c50466565203c3d20323530302062700060448201526064016200038d565b6007819055620009c84262093a80620017a7565b60085550565b60025473ffffffffffffffffffffffffffffffffffffffff16331462000a5d5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60085460000362000ab15760405162461bcd60e51b815260206004820152600f60248201527f6e6f206665652070726f706f73616c000000000000000000000000000000000060448201526064016200038d565b426008541062000b2a5760405162461bcd60e51b815260206004820152603160248201527f6d7573743a204c5046656550726f706f73616c417070726f76616c446174652060448201527f3c20626c6f636b2e74696d657374616d7000000000000000000000000000000060648201526084016200038d565b6007546006556000600855565b60025473ffffffffffffffffffffffffffffffffffffffff16331462000bc65760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60055460000362000c1a5760405162461bcd60e51b815260206004820152600f60248201527f6e6f206665652070726f706f73616c000000000000000000000000000000000060448201526064016200038d565b426005541062000c935760405162461bcd60e51b815260206004820152603660248201527f6d7573743a207265776172647346656550726f706f73616c417070726f76616c60448201527f44617465203c20626c6f636b2e74696d657374616d700000000000000000000060648201526084016200038d565b6004546003556000600555565b6000878662000d175760405162461bcd60e51b8152602060048201526024808201527f6578706563746564205f72657761726473546f6b656e4964732e6c656e67746860448201527f203e20300000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60005b8781101562000e02578173ffffffffffffffffffffffffffffffffffffffff166323b872dd33308c8c8681811062000d565762000d56620017c3565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801562000dd357600080fd5b505af115801562000de8573d6000803e3d6000fd5b50505050808062000df990620017f2565b91505062000d1a565b506001546040517fb3c3b30400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b811660048301526000602483018190526044830181905292169063b3c3b30490606401602060405180830381865afa15801562000e83573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ea991906200182d565b6040517fa22cb46500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8083166004830152600160248301529192509083169063a22cb46590604401600060405180830381600087803b15801562000f1e57600080fd5b505af115801562000f33573d6000803e3d6000fd5b506000925082915062000f439050565b60405190808252806020026020018201604052801562000f7857816020015b606081526020019060019003908162000f625790505b50905060008273ffffffffffffffffffffffffffffffffffffffff166335ec39d98c8c858b8b6040518663ffffffff1660e01b815260040162000fc0959493929190620019dc565b6020604051808303816000875af115801562000fe0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001006919062001af2565b905060008d30600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16868c8e306040516200104190620014ba565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501528416608084015260a083015290911660c082015260e001604051809103906000f080158015620010a7573d6000803e3d6000fd5b509050620010cd73ffffffffffffffffffffffffffffffffffffffff8516308462001413565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526024820184905285169063095ea7b3906044016020604051808303816000875af115801562001143573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620011699190620016bf565b506040517f3c6b16ab0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff821690633c6b16ab90602401600060405180830381600087803b158015620011d357600080fd5b505af1158015620011e8573d6000803e3d6000fd5b505050507f3a6811b476d594278134d846bbe7ef00a8376ce6deaff055fe1716f4051cc678818f30878d8f60026040516200122a9796959493929190620016ea565b60405180910390a19d9c50505050505050505050505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314620012d25760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f616374000000000000000000000000000000000000000000000000000000000060648201526084016200038d565b6109c48111156200134b5760405162461bcd60e51b8152602060048201526024808201527f6d7573743a205f70726f706f73656452657761726473466565203c3d2032353060448201527f302062700000000000000000000000000000000000000000000000000000000060648201526084016200038d565b60048190556200135f4262093a80620017a7565b60055550565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806200140c5760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016200038d565b5050505050565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080620014b45760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016200038d565b50505050565b6141718062001b0d83390190565b73ffffffffffffffffffffffffffffffffffffffff81168114620014eb57600080fd5b50565b600080600080600060a086880312156200150757600080fd5b85356200151481620014c8565b945060208601356200152681620014c8565b93506040860135925060608601356200153f81620014c8565b949793965091946080013592915050565b6000806000606084860312156200156657600080fd5b83356200157381620014c8565b925060208401356200158581620014c8565b929592945050506040919091013590565b600060208284031215620015a957600080fd5b5035919050565b60008083601f840112620015c357600080fd5b50813567ffffffffffffffff811115620015dc57600080fd5b6020830191508360208260051b8501011115620015f857600080fd5b9250929050565b60008060008060008060008060c0898b0312156200161c57600080fd5b88356200162981620014c8565b975060208901356200163b81620014c8565b9650604089013567ffffffffffffffff808211156200165957600080fd5b620016678c838d01620015b0565b909850965060608b0135955060808b013591506200168582620014c8565b90935060a08a013590808211156200169c57600080fd5b50620016ab8b828c01620015b0565b999c989b5096995094979396929594505050565b600060208284031215620016d257600080fd5b81518015158114620016e357600080fd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff88811682528781166020830152868116604083015285811660608301528416608082015260a0810183905260e081016003831062001766577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8260c083015298975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115620017bd57620017bd62001778565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362001826576200182662001778565b5060010190565b6000602082840312156200184057600080fd5b8151620016e381620014c8565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126200188357600080fd5b830160208101925035905067ffffffffffffffff811115620018a457600080fd5b803603821315620015f857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b81019150846000805b88811015620019ce578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8189360301811262001958578283fd5b88018035865260806200196e888301836200184d565b828a8a015262001982838a018284620018b4565b9250505060408083013581890152506060620019a1818401846200184d565b9350888303828a0152620019b7838583620018b4565b9d8a019d9850505093870193505060010162001918565b509298975050505050505050565b6060815284606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111562001a1657600080fd5b8560051b8088608085013780830190506080810160206080858403018186015281885180845260a08501915060a08160051b8601019350828a016000805b8381101562001aca578787037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60018552825180518089529087019087890190845b8181101562001ab35783518352928901929189019160010162001a95565b509098505050938501939185019160010162001a54565b505050505050838103604085015262001ae5818688620018fd565b9998505050505050505050565b60006020828403121562001b0557600080fd5b505191905056fe6101206040523480156200001257600080fd5b506040516200417138038062004171833981016040819052620000359162000542565b600080546001600160a81b0319166101006001600160a01b038a169081029190911782556040518992907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b038716620000e15760405162461bcd60e51b815260206004820152601d60248201527f5f6f776e65722073686f756c646e74206265206164647265737328302900000060448201526064015b60405180910390fd5b6001600160a01b0386166200014c5760405162461bcd60e51b815260206004820152602a60248201527f5f726577617264734469737472696275746f722073686f756c646e74206265206044820152696164647265737328302960b01b6064820152608401620000d8565b6001600160a01b038516620001b05760405162461bcd60e51b8152602060048201526024808201527f5f6261746f6e4d6f6e69746f722073686f756c646e74206265206164647265736044820152637328302960e01b6064820152608401620000d8565b6001600160a01b038416620002145760405162461bcd60e51b8152602060048201526024808201527f5f72657761726473546f6b656e2073686f756c646e74206265206164647265736044820152637328302960e01b6064820152608401620000d8565b6001600160a01b038316620002785760405162461bcd60e51b815260206004820152602360248201527f5f70616972416464726573732073686f756c646e74206265206164647265737360448201526228302960e81b6064820152608401620000d8565b6001600160a01b038116620002dc5760405162461bcd60e51b8152602060048201526024808201527f5f6261746f6e466163746f72792073686f756c646e74206265206164647265736044820152637328302960e01b6064820152608401620000d8565b6001600160a01b03808416608081905290851660a05260408051635fcbd28560e01b81529051635fcbd285916004808201926020929091908290030181865afa1580156200032e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003549190620005e1565b6001600160a01b0390811660c052600b80546001600160a01b03191691881691909117905581620003c85760405162461bcd60e51b815260206004820152601c60248201527f5f726577617264734475726174696f6e2063616e6e6f742062652030000000006044820152606401620000d8565b63096601808210620004325760405162461bcd60e51b815260206004820152602c60248201527f5f726577617264734475726174696f6e2063616e6e6f74206265206d6f72652060448201526b7468656e203520796561727360a01b6064820152608401620000d8565b60038290556001600160a01b0380861660e05281811661010052608051604080516323e6650160e11b8152905191909216916347ccca029160048083019260209291908290030181865afa1580156200048f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004b59190620005e1565b60805160405163a22cb46560e01b81526001600160a01b0391821660048201526001602482015291169063a22cb46590604401600060405180830381600087803b1580156200050357600080fd5b505af115801562000518573d6000803e3d6000fd5b505050505050505050505062000608565b6001600160a01b03811681146200053f57600080fd5b50565b600080600080600080600060e0888a0312156200055e57600080fd5b87516200056b8162000529565b60208901519097506200057e8162000529565b6040890151909650620005918162000529565b6060890151909550620005a48162000529565b6080890151909450620005b78162000529565b60a089015160c08a01519194509250620005d18162000529565b8091505092959891949750929550565b600060208284031215620005f457600080fd5b8151620006018162000529565b9392505050565b60805160a05160c05160e05161010051613a5962000718600039600081816106820152818161101f0152818161131101528181611bae0152611ea6015260008181610846015281816110dc015281816113ce01528181611c7b01528181611f63015261267901526000818161060e01528181610d01015281816110ba01528181611122015281816113ac01528181611f41015281816124e30152612b190152600081816107dc015281816117840152818161192801528181611a1001528181611c5901528181611cc10152818161258a01526128cc01526000818161075d01528181610a9001528181610bd101528181611458015281816114df01528181611f9b015261204c0152613a596000f3fe6080604052600436106102be5760003560e01c806372f702f31161016e578063a8aa1b31116100cb578063d1f5c33b1161007f578063e4710a4811610064578063e4710a4814610834578063ebe2b12b14610868578063f2fde38b1461087e57600080fd5b8063d1f5c33b146107fe578063df136d651461081e57600080fd5b8063cc1a378f116100b0578063cc1a378f14610795578063cd3daf9d146107b5578063d1af0c7d146107ca57600080fd5b8063a8aa1b311461074b578063c8f33c911461077f57600080fd5b80638980f11f116101225780638da5cb5b116101075780638da5cb5b146106f15780638fd3ab8014610716578063a694fc3a1461072b57600080fd5b80638980f11f146106a45780638b876347146106c457600080fd5b806380faa57d1161015357806380faa57d146106465780638456cb591461065b57806384a9dd731461067057600080fd5b806372f702f3146105fc5780637b0a47ee1461063057600080fd5b806332a38f6b1161021c5780634641257d116101d057806360a77ec4116101b557806360a77ec41461058657806370a08231146105a657806371726c92146105dc57600080fd5b80634641257d146105595780635c975abb1461056e57600080fd5b80633c6b16ab116102015780633c6b16ab146105045780633f2a5540146105245780633f4ba83a1461054457600080fd5b806332a38f6b146104d9578063386a9525146104ee57600080fd5b80631e69cece116102735780632bff884f116102585780632bff884f146104575780632e1a7d4d146104995780633059bfcb146104b957600080fd5b80631e69cece146104225780632133e3b91461043757600080fd5b8063150b7a02116102a4578063150b7a021461035f5780631705a3bd146103d557806318160ddd1461040d57600080fd5b80628cc262146102ff5780630700037d1461033257600080fd5b366102fa5760405134815233907f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258749060200160405180910390a2005b600080fd5b34801561030b57600080fd5b5061031f61031a366004613280565b61089e565b6040519081526020015b60405180910390f35b34801561033e57600080fd5b5061031f61034d366004613280565b60086020526000908152604090205481565b34801561036b57600080fd5b506103a461037a3660046132a4565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610329565b3480156103e157600080fd5b50600a546103f5906001600160a01b031681565b6040516001600160a01b039091168152602001610329565b34801561041957600080fd5b5060065461031f565b61043561043036600461338f565b610939565b005b34801561044357600080fd5b5061031f610452366004613452565b610e6f565b34801561046357600080fd5b50600a546104899074010000000000000000000000000000000000000000900460ff1681565b6040519015158152602001610329565b3480156104a557600080fd5b506104356104b4366004613474565b610e93565b3480156104c557600080fd5b506104356104d436600461348d565b611185565b3480156104e557600080fd5b5061043561154f565b3480156104fa57600080fd5b5061031f60035481565b34801561051057600080fd5b5061043561051f366004613474565b611572565b34801561053057600080fd5b50600b546103f5906001600160a01b031681565b34801561055057600080fd5b50610435611abb565b34801561056557600080fd5b50610435611b22565b34801561057a57600080fd5b5060005460ff16610489565b34801561059257600080fd5b506104356105a13660046134cd565b611d1a565b3480156105b257600080fd5b5061031f6105c1366004613280565b6001600160a01b031660009081526009602052604090205490565b3480156105e857600080fd5b506104356105f7366004613280565b6121d3565b34801561060857600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561063c57600080fd5b5061031f60025481565b34801561065257600080fd5b5061031f612404565b34801561066757600080fd5b5061043561241b565b34801561067c57600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b3480156106b057600080fd5b506104356106bf366004613540565b612482565b3480156106d057600080fd5b5061031f6106df366004613280565b60076020526000908152604090205481565b3480156106fd57600080fd5b506000546103f59061010090046001600160a01b031681565b34801561072257600080fd5b5061043561266e565b34801561073757600080fd5b50610435610746366004613474565b61293f565b34801561075757600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561078b57600080fd5b5061031f60045481565b3480156107a157600080fd5b506104356107b0366004613474565b612b82565b3480156107c157600080fd5b5061031f612c8e565b3480156107d657600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561080a57600080fd5b50610435610819366004613280565b612d0d565b34801561082a57600080fd5b5061031f60055481565b34801561084057600080fd5b506103f57f000000000000000000000000000000000000000000000000000000000000000081565b34801561087457600080fd5b5061031f60015481565b34801561088a57600080fd5b50610435610899366004613280565b612e4e565b6000806108a9612c8e565b6001600160a01b038416600090815260076020526040812054919250906108d0908361359b565b6001600160a01b038516600090815260096020526040812054919250670de0b6b3a76400006108ff84846135ae565b61090991906135c5565b6001600160a01b03871660009081526008602052604090205490915061092f9082613600565b9695505050505050565b600154421061098f5760405162461bcd60e51b815260206004820152601760248201527f54686973206661726d206973206e6f742061637469766500000000000000000060448201526064015b60405180910390fd5b610997612f17565b600a5474010000000000000000000000000000000000000000900460ff1615610a285760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b33610a31612c8e565b600555610a3c612404565b6004556001600160a01b03811615610a8357610a578161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60005b8a811015610bcc577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610aec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b109190613613565b6001600160a01b03166342842e0e33308f8f86818110610b3257610b32613630565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b158015610ba157600080fd5b505af1158015610bb5573d6000803e3d6000fd5b505050508080610bc49061365f565b915050610a86565b5060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634cfb998a34348f8f8f8f8f8f8f8f8f8f6040518d63ffffffff1660e01b8152600401610c309b9a99989796959493929190613863565b60206040518083038185885af1158015610c4e573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610c73919061398e565b905060008111610cc55760405162461bcd60e51b815260206004820152600e60248201527f43616e6e6f74207374616b6520300000000000000000000000000000000000006044820152606401610986565b80600654610cd39190613600565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610d50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d74919061398e565b14610de75760405162461bcd60e51b815260206004820152603460248201527f7374616b696e67546f6b656e2062616c616e6365206469646e7420757064617460448201527f652066726f6d206c70546f6b656e416d6f756e740000000000000000000000006064820152608401610986565b80600654610df59190613600565b60065533600090815260096020526040902054610e13908290613600565b33600081815260096020526040908190209290925590517f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d90610e599084815260200190565b60405180910390a2505050505050505050505050565b600080612710610e7f84866135ae565b610e8991906135c5565b9150505b92915050565b33610e9c612c8e565b600555610ea7612404565b6004556001600160a01b03811615610eee57610ec28161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60008211610f3e5760405162461bcd60e51b815260206004820152601160248201527f43616e6e6f7420776974686472617720300000000000000000000000000000006044820152606401610986565b33600090815260096020526040902054821115610fc35760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207769746864726177206d6f7265207468656e20796f7520686160448201527f7665207374616b656400000000000000000000000000000000000000000000006064820152608401610986565b81600654610fd1919061359b565b60065533600090815260096020526040902054610fef90839061359b565b60096000336001600160a01b03166001600160a01b031681526020019081526020016000208190555060006110a57f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324ee8e716040518163ffffffff1660e01b8152600401602060405180830381865afa15801561107b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061109f919061398e565b84610e6f565b90508015611101576111016001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b600061110d828561359b565b90508015611149576111496001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383612f6a565b60405181815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5906020015b60405180910390a250505050565b3361118e612c8e565b600555611199612404565b6004556001600160a01b038116156111e0576111b48161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b600085116112305760405162461bcd60e51b815260206004820152601160248201527f43616e6e6f7420776974686472617720300000000000000000000000000000006044820152606401610986565b336000908152600960205260409020548511156112b55760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207769746864726177206d6f7265207468656e20796f7520686160448201527f7665207374616b656400000000000000000000000000000000000000000000006064820152608401610986565b846006546112c3919061359b565b600655336000908152600960205260409020546112e190869061359b565b60096000336001600160a01b03166001600160a01b031681526020019081526020016000208190555060006113977f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324ee8e716040518163ffffffff1660e01b8152600401602060405180830381865afa15801561136d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611391919061398e565b87610e6f565b905080156113f3576113f36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b60006113ff828861359b565b90508015611509576040517f1ae74cdd0000000000000000000000000000000000000000000000000000000081526004810182905260248101879052604481018690526064810185905260009081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631ae74cdd9060840160408051808303816000875af11580156114a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c491906139a7565b915091506114d2338361300f565b6115066001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383612f6a565b50505b611511611b22565b60405181815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d59060200160405180910390a250505050505050565b3360009081526009602052604090205461156890610e93565b611570611b22565b565b600b546001600160a01b031633148061159a575060005461010090046001600160a01b031633145b61160c5760405162461bcd60e51b815260206004820152602960248201527f43616c6c6572206973206e6f7420526577617264734469737472696275746f7260448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610986565b600a5474010000000000000000000000000000000000000000900460ff161561169d5760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b60006116a7612c8e565b6005556116b2612404565b6004556001600160a01b038116156116f9576116cd8161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b600082116117495760405162461bcd60e51b815260206004820152601260248201527f7265776172642063616e6e6f74206265203000000000000000000000000000006044820152606401610986565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af11580156117d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f991906139cb565b50600060015442106118465760035461181290846135c5565b6002556003546118229042613600565b60015560035460025461183591906135ae565b61183f908461359b565b90506118a8565b600042600154611856919061359b565b905060006002548261186891906135ae565b9050816118758287613600565b61187f91906135c5565b600281905561188f9083906135ae565b6118998287613600565b6118a3919061359b565b925050505b6000600254116118fa5760405162461bcd60e51b815260206004820152600f60248201527f7265776172642072617465203d203000000000000000000000000000000000006044820152606401610986565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611977573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199b919061398e565b6003546002546119ab91906135ae565b11156119f95760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606401610986565b8015611a7f57600b54611a39906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683612f6a565b600b54604080518381526001600160a01b0390921660208301527f3cd094c348efde9110a082dc51109d392ac0874b24682147545ed2a1f8c7e5e5910160405180910390a15b426004556040518381527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d9060200160405180910390a1505050565b60005461010090046001600160a01b03163314611b1a5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b61157061306f565b33611b2b612c8e565b600555611b36612404565b6004556001600160a01b03811615611b7d57611b518161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b600060086000336001600160a01b03166001600160a01b031681526020019081526020016000205490506000611c347f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663adcc63976040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2e919061398e565b83610e6f565b3360009081526008602052604081205590508015611ca057611ca06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b6000611cac828461359b565b90508015611ce857611ce86001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383612f6a565b60405181815233907fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e048690602001611177565b33611d23612c8e565b600555611d2e612404565b6004556001600160a01b03811615611d7557611d498161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60008711611dc55760405162461bcd60e51b815260206004820152601160248201527f43616e6e6f7420776974686472617720300000000000000000000000000000006044820152606401610986565b33600090815260096020526040902054871115611e4a5760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207769746864726177206d6f7265207468656e20796f7520686160448201527f7665207374616b656400000000000000000000000000000000000000000000006064820152608401610986565b86600654611e58919061359b565b60065533600090815260096020526040902054611e7690889061359b565b60096000336001600160a01b03166001600160a01b03168152602001908152602001600020819055506000611f2c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324ee8e716040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f26919061398e565b89610e6f565b90508015611f8857611f886001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083612f6a565b6000611f94828a61359b565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634cba3eca848c8c8c8c8c6040518763ffffffff1660e01b8152600401611fef969594939291906139e8565b60408051808303816000875af115801561200d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203191906139a7565b9150915061203f338361300f565b60005b87811015612188577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120cc9190613613565b6001600160a01b03166342842e0e30338c8c868181106120ee576120ee613630565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561215d57600080fd5b505af1158015612171573d6000803e3d6000fd5b5050505080806121809061365f565b915050612042565b50612191611b22565b60405183815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d59060200160405180910390a25050505050505050505050565b60005461010090046001600160a01b031633146122325760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b600a5474010000000000000000000000000000000000000000900460ff16156122c35760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b6001600160a01b03811661233f5760405162461bcd60e51b815260206004820152602160248201527f506c65617365206d69677261746520746f20612076616c69642061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610986565b306001600160a01b038216036123975760405162461bcd60e51b815260206004820152601660248201527f43616e6e6f74206d69677261746520746f2073656c66000000000000000000006044820152606401610986565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fbf3aee0580cb96d1eb8ffba88fb8dc83e973b7cdf4a18be5a0d3cbb4e6ecb3d3906020015b60405180910390a150565b60006001544210612416575060015490565b504290565b60005461010090046001600160a01b0316331461247a5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b6115706130df565b60005461010090046001600160a01b031633146124e15760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036125885760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207374616b696e6720746f6b6560448201527f6e000000000000000000000000000000000000000000000000000000000000006064820152608401610986565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036126095760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f74207769746864726177207468652072657761726420746f6b656e6044820152606401610986565b600054612628906001600160a01b038481169161010090041683612f6a565b604080516001600160a01b0384168152602081018390527f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa28910160405180910390a15050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461270c5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f74204261746f6e4d6f6e69746f7220636f6e747260448201527f61637400000000000000000000000000000000000000000000000000000000006064820152608401610986565b600a546001600160a01b031661278a5760405162461bcd60e51b815260206004820152603260248201527f436f6e7472616374206f776e6572206d7573742066697273742063616c6c206960448201527f6e6974696174654d6967726174696f6e282900000000000000000000000000006064820152608401610986565b600a5474010000000000000000000000000000000000000000900460ff161561281b5760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b600154421061286c5760405162461bcd60e51b815260206004820152601760248201527f54686973206661726d206973206e6f74206163746976650000000000000000006044820152606401610986565b600061287661313a565b600a8054740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff821617909155426001559091506128f5906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683612f6a565b600a54604080516001600160a01b0390921682526020820183905242908201527f0172d08d81df86673f7e65d3d77b5dfa9b07a646ac6aca2f3b25e2b93ffb3430906060016123f9565b600a5474010000000000000000000000000000000000000000900460ff16156129d05760405162461bcd60e51b815260206004820152603e60248201527f5468697320636f6e747261637420686173206265656e206d696772617465642c60448201527f20796f752063616e6e6f74206465706f736974206e65772066756e64732e00006064820152608401610986565b6001544210612a215760405162461bcd60e51b815260206004820152601760248201527f54686973206661726d206973206e6f74206163746976650000000000000000006044820152606401610986565b612a29612f17565b33612a32612c8e565b600555612a3d612404565b6004556001600160a01b03811615612a8457612a588161089e565b6001600160a01b0382166000908152600860209081526040808320939093556005546007909152919020555b60008211612ad45760405162461bcd60e51b815260206004820152600e60248201527f43616e6e6f74207374616b6520300000000000000000000000000000000000006044820152606401610986565b81600654612ae29190613600565b60065533600090815260096020526040902054612b00908390613600565b33600081815260096020526040902091909155612b49907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690308561316a565b60405182815233907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d9060200160405180910390a25050565b60005461010090046001600160a01b03163314612be15760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b600154421015612c595760405162461bcd60e51b815260206004820152602b60248201527f706f6f6c2069732072756e6e696e672c2063616e6e6f7420757064617465207460448201527f6865206475726174696f6e0000000000000000000000000000000000000000006064820152608401610986565b60038190556040518181527f93ffc29e2d73e3e6b3262dfdc8cd0e6e838568a0860474ed3087f830a82f4aad906020016123f9565b6000600654600003612ca1575060055490565b6000612cab612404565b9050600060045482612cbd919061359b565b90506000600254670de0b6b3a7640000612cd791906135ae565b905060006006548284612cea91906135ae565b612cf491906135c5565b905080600554612d049190613600565b94505050505090565b60005461010090046001600160a01b03163314612d6c5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b6001600160a01b038116612de85760405162461bcd60e51b815260206004820152602860248201527f5f726577617264734469737472696275746f722063616e6e6f7420626520616460448201527f64726573732830290000000000000000000000000000000000000000000000006064820152608401610986565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f8c1a55060e521f27a67c03004984808ed1060d76b4abbe8c74f6d11484ac5043906020016123f9565b60005461010090046001600160a01b03163314612ead5760405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610986565b600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff166101006001600160a01b038416908102919091178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b60005460ff16156115705760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610986565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806130095760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610986565b50505050565b600080600080600085875af190508061306a5760405162461bcd60e51b815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610986565b505050565b613077613216565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6130e7612f17565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130c23390565b600080429050600081600154613150919061359b565b905060008160025461316291906135ae565b949350505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061320f5760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610986565b5050505050565b60005460ff166115705760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610986565b6001600160a01b038116811461327d57600080fd5b50565b60006020828403121561329257600080fd5b813561329d81613268565b9392505050565b6000806000806000608086880312156132bc57600080fd5b85356132c781613268565b945060208601356132d781613268565b935060408601359250606086013567ffffffffffffffff808211156132fb57600080fd5b818801915088601f83011261330f57600080fd5b81358181111561331e57600080fd5b89602082850101111561333057600080fd5b9699959850939650602001949392505050565b60008083601f84011261335557600080fd5b50813567ffffffffffffffff81111561336d57600080fd5b6020830191508360208260051b850101111561338857600080fd5b9250929050565b60008060008060008060008060008060e08b8d0312156133ae57600080fd5b8a3567ffffffffffffffff808211156133c657600080fd5b6133d28e838f01613343565b909c509a5060208d0135995060408d0135985060608d0135975060808d0135965060a08d013591508082111561340757600080fd5b6134138e838f01613343565b909650945060c08d013591508082111561342c57600080fd5b506134398d828e01613343565b915080935050809150509295989b9194979a5092959850565b6000806040838503121561346557600080fd5b50508035926020909101359150565b60006020828403121561348657600080fd5b5035919050565b600080600080608085870312156134a357600080fd5b5050823594602084013594506040840135936060013592509050565b801515811461327d57600080fd5b60008060008060008060a087890312156134e657600080fd5b863595506020870135945060408701359350606087013567ffffffffffffffff81111561351257600080fd5b61351e89828a01613343565b9094509250506080870135613532816134bf565b809150509295509295509295565b6000806040838503121561355357600080fd5b823561355e81613268565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610e8d57610e8d61356c565b8082028115828204841417610e8d57610e8d61356c565b6000826135fb577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115610e8d57610e8d61356c565b60006020828403121561362557600080fd5b815161329d81613268565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036136905761369061356c565b5060010190565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156136c957600080fd5b8260051b80836020870137939093016020019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261371757600080fd5b830160208101925035905067ffffffffffffffff81111561373757600080fd5b80360382131561338857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b81019150846000805b88811015613855578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818936030181126137e8578283fd5b88018035865260806137fc888301836136e2565b828a8a015261380e838a018284613746565b925050506040808301358189015250606061382b818401846136e2565b9350888303828a015261383f838583613746565b9d8a019d985050509387019350506001016137aa565b509298975050505050505050565b8b8152600060206101008184015261388061010084018d8f613697565b8b60408501528a60608501528960808501528860a085015283810360c0850152808782528282019050828860051b8301018960005b8a811015613963577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18d360301811261391657600080fd5b8c01868101903567ffffffffffffffff81111561393257600080fd5b8060051b360382131561394457600080fd5b61394f858284613697565b9588019594505050908501906001016138b5565b505085810360e087015261397881888a61378f565b9450505050509c9b505050505050505050505050565b6000602082840312156139a057600080fd5b5051919050565b600080604083850312156139ba57600080fd5b505080516020909101519092909150565b6000602082840312156139dd57600080fd5b815161329d816134bf565b86815285602082015284604082015260a060608201526000613a0e60a083018587613697565b9050821515608083015297965050505050505056fea2646970667358221220afd062ab7f1ce1130225242a24677e8238626f3498607434ff6628cd8af709df64736f6c63430008130033a264697066735822122097876a50fdbcd6bbea0c4a69cc398872370229cf0ec6f0eaf0bd6c83c3ec5dc264736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a964d6e8d90e5cd12592a8ef2b1735dae9ba08400000000000000000000000009e902766d3e2e33aa07c4433919cca6a2c6b96d0
-----Decoded View---------------
Arg [0] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : _caviar (address): 0xa964D6E8d90E5Cd12592a8EF2b1735daE9Ba0840
Arg [2] : _batonMonitor (address): 0x9E902766d3e2e33Aa07C4433919CCA6A2C6B96d0
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 000000000000000000000000a964d6e8d90e5cd12592a8ef2b1735dae9ba0840
Arg [2] : 0000000000000000000000009e902766d3e2e33aa07c4433919cca6a2c6b96d0
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.