Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
WithdrawalQueueERC721
Compiler Version
v0.8.9+commit.e5eed63a
Optimization Enabled:
Yes with 200 runs
Other Settings:
istanbul EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2023 Lido <[email protected]>, OpenZeppelin // SPDX-License-Identifier: GPL-3.0 /* See contracts/COMPILERS.md */ pragma solidity 0.8.9; import {IERC721} from "@openzeppelin/contracts-v4.4/token/ERC721/IERC721.sol"; import {IERC721Receiver} from "@openzeppelin/contracts-v4.4/token/ERC721/IERC721Receiver.sol"; import {IERC721Metadata} from "@openzeppelin/contracts-v4.4/token/ERC721/extensions/IERC721Metadata.sol"; import {IERC165} from "@openzeppelin/contracts-v4.4/utils/introspection/IERC165.sol"; import {IERC4906} from "./interfaces/IERC4906.sol"; import {EnumerableSet} from "@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol"; import {Address} from "@openzeppelin/contracts-v4.4/utils/Address.sol"; import {Strings} from "@openzeppelin/contracts-v4.4/utils/Strings.sol"; import {IWstETH, WithdrawalQueue} from "./WithdrawalQueue.sol"; import {AccessControlEnumerable} from "./utils/access/AccessControlEnumerable.sol"; import {UnstructuredRefStorage} from "./lib/UnstructuredRefStorage.sol"; import {UnstructuredStorage} from "./lib/UnstructuredStorage.sol"; /// @title Interface defining INFTDescriptor to generate ERC721 tokenURI interface INFTDescriptor { /// @notice Returns ERC721 tokenURI content /// @param _requestId is an id for particular withdrawal request function constructTokenURI(uint256 _requestId) external view returns (string memory); } /// @title NFT implementation on top of {WithdrawalQueue} /// NFT is minted on every request and burned on claim /// /// @author psirex, folkyatina contract WithdrawalQueueERC721 is IERC721Metadata, IERC4906, WithdrawalQueue { using Address for address; using Strings for uint256; using EnumerableSet for EnumerableSet.UintSet; using UnstructuredRefStorage for bytes32; using UnstructuredStorage for bytes32; bytes32 internal constant TOKEN_APPROVALS_POSITION = keccak256("lido.WithdrawalQueueERC721.tokenApprovals"); bytes32 internal constant OPERATOR_APPROVALS_POSITION = keccak256("lido.WithdrawalQueueERC721.operatorApprovals"); bytes32 internal constant BASE_URI_POSITION = keccak256("lido.WithdrawalQueueERC721.baseUri"); bytes32 internal constant NFT_DESCRIPTOR_ADDRESS_POSITION = keccak256("lido.WithdrawalQueueERC721.nftDescriptorAddress"); bytes32 public constant MANAGE_TOKEN_URI_ROLE = keccak256("MANAGE_TOKEN_URI_ROLE"); // @notion simple wrapper for base URI string // Solidity does not allow to store string in UnstructuredStorage struct BaseURI { string value; } event BaseURISet(string baseURI); event NftDescriptorAddressSet(address nftDescriptorAddress); error ApprovalToOwner(); error ApproveToCaller(); error NotOwnerOrApprovedForAll(address sender); error NotOwnerOrApproved(address sender); error TransferFromIncorrectOwner(address from, address realOwner); error TransferToZeroAddress(); error TransferFromZeroAddress(); error TransferToThemselves(); error TransferToNonIERC721Receiver(address); error InvalidOwnerAddress(address); error StringTooLong(string str); error ZeroMetadata(); // short strings for ERC721 name and symbol bytes32 private immutable NAME; bytes32 private immutable SYMBOL; /// @param _wstETH address of WstETH contract /// @param _name IERC721Metadata name string. Should be shorter than 32 bytes /// @param _symbol IERC721Metadata symbol string. Should be shorter than 32 bytes constructor(address _wstETH, string memory _name, string memory _symbol) WithdrawalQueue(IWstETH(_wstETH)) { if (bytes(_name).length == 0 || bytes(_symbol).length == 0) revert ZeroMetadata(); NAME = _toBytes32(_name); SYMBOL = _toBytes32(_symbol); } /// @dev See {IERC165-supportsInterface}. function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AccessControlEnumerable) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId // 0x49064906 is magic number ERC4906 interfaceId as defined in the standard https://eips.ethereum.org/EIPS/eip-4906 || interfaceId == bytes4(0x49064906) || super.supportsInterface(interfaceId); } /// @dev See {IERC721Metadata-name}. function name() external view override returns (string memory) { return _toString(NAME); } /// @dev See {IERC721Metadata-symbol}. function symbol() external view override returns (string memory) { return _toString(SYMBOL); } /// @dev See {IERC721Metadata-tokenURI}. /// @dev If NFTDescriptor address isn't set the `baseURI` would be used for generating erc721 tokenURI. In case /// NFTDescriptor address is set it would be used as a first-priority method. function tokenURI(uint256 _requestId) public view virtual override returns (string memory) { if (!_existsAndNotClaimed(_requestId)) revert InvalidRequestId(_requestId); address nftDescriptorAddress = NFT_DESCRIPTOR_ADDRESS_POSITION.getStorageAddress(); if (nftDescriptorAddress != address(0)) { return INFTDescriptor(nftDescriptorAddress).constructTokenURI(_requestId); } else { return _constructTokenUri(_requestId); } } /// @notice Base URI for computing {tokenURI}. If set, the resulting URI for each /// token will be the concatenation of the `baseURI` and the `_requestId`. function getBaseURI() external view returns (string memory) { return _getBaseURI().value; } /// @notice Sets the Base URI for computing {tokenURI}. It does not expect the ending slash in provided string. /// @dev If NFTDescriptor address isn't set the `baseURI` would be used for generating erc721 tokenURI. In case /// NFTDescriptor address is set it would be used as a first-priority method. function setBaseURI(string calldata _baseURI) external onlyRole(MANAGE_TOKEN_URI_ROLE) { _getBaseURI().value = _baseURI; emit BaseURISet(_baseURI); } /// @notice Address of NFTDescriptor contract that is responsible for tokenURI generation. function getNFTDescriptorAddress() external view returns (address) { return NFT_DESCRIPTOR_ADDRESS_POSITION.getStorageAddress(); } /// @notice Sets the address of NFTDescriptor contract that is responsible for tokenURI generation. /// @dev If NFTDescriptor address isn't set the `baseURI` would be used for generating erc721 tokenURI. In case /// NFTDescriptor address is set it would be used as a first-priority method. function setNFTDescriptorAddress(address _nftDescriptorAddress) external onlyRole(MANAGE_TOKEN_URI_ROLE) { NFT_DESCRIPTOR_ADDRESS_POSITION.setStorageAddress(_nftDescriptorAddress); emit NftDescriptorAddressSet(_nftDescriptorAddress); } /// @notice Finalize requests from last finalized one up to `_lastRequestIdToBeFinalized` /// @dev ether to finalize all the requests should be calculated using `prefinalize()` and sent along function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable { _checkResumed(); _checkRole(FINALIZE_ROLE, msg.sender); uint256 firstFinalizedRequestId = getLastFinalizedRequestId() + 1; _finalize(_lastRequestIdToBeFinalized, msg.value, _maxShareRate); // ERC4906 metadata update event // We are updating all unfinalized to make it look different as they move closer to finalization in the future emit BatchMetadataUpdate(firstFinalizedRequestId, getLastRequestId()); } /// @dev See {IERC721-balanceOf}. function balanceOf(address _owner) external view override returns (uint256) { if (_owner == address(0)) revert InvalidOwnerAddress(_owner); return _getRequestsByOwner()[_owner].length(); } /// @dev See {IERC721-ownerOf}. function ownerOf(uint256 _requestId) public view override returns (address) { if (_requestId == 0 || _requestId > getLastRequestId()) revert InvalidRequestId(_requestId); WithdrawalRequest storage request = _getQueue()[_requestId]; if (request.claimed) revert RequestAlreadyClaimed(_requestId); return request.owner; } /// @dev See {IERC721-approve}. function approve(address _to, uint256 _requestId) external override { address owner = ownerOf(_requestId); if (_to == owner) revert ApprovalToOwner(); if (msg.sender != owner && !isApprovedForAll(owner, msg.sender)) revert NotOwnerOrApprovedForAll(msg.sender); _approve(_to, _requestId); } /// @dev See {IERC721-getApproved}. function getApproved(uint256 _requestId) external view override returns (address) { if (!_existsAndNotClaimed(_requestId)) revert InvalidRequestId(_requestId); return _getTokenApprovals()[_requestId]; } /// @dev See {IERC721-setApprovalForAll}. function setApprovalForAll(address _operator, bool _approved) external override { _setApprovalForAll(msg.sender, _operator, _approved); } /// @dev See {IERC721-isApprovedForAll}. function isApprovedForAll(address _owner, address _operator) public view override returns (bool) { return _getOperatorApprovals()[_owner][_operator]; } /// @dev See {IERC721-safeTransferFrom}. function safeTransferFrom(address _from, address _to, uint256 _requestId) external override { safeTransferFrom(_from, _to, _requestId, ""); } /// @dev See {IERC721-safeTransferFrom}. function safeTransferFrom(address _from, address _to, uint256 _requestId, bytes memory _data) public override { _transfer(_from, _to, _requestId); if (!_checkOnERC721Received(_from, _to, _requestId, _data)) { revert TransferToNonIERC721Receiver(_to); } } /// @dev See {IERC721-transferFrom}. function transferFrom(address _from, address _to, uint256 _requestId) external override { _transfer(_from, _to, _requestId); } /// @dev Transfers `_requestId` from `_from` to `_to`. /// As opposed to {transferFrom}, this imposes no restrictions on msg.sender. /// /// Requirements: /// /// - `_to` cannot be the zero address. /// - `_requestId` request must not be claimed and be owned by `_from`. /// - `msg.sender` should be approved, or approved for all, or owner function _transfer(address _from, address _to, uint256 _requestId) internal { if (_to == address(0)) revert TransferToZeroAddress(); if (_to == _from) revert TransferToThemselves(); if (_requestId == 0 || _requestId > getLastRequestId()) revert InvalidRequestId(_requestId); WithdrawalRequest storage request = _getQueue()[_requestId]; if (request.claimed) revert RequestAlreadyClaimed(_requestId); if (_from != request.owner) revert TransferFromIncorrectOwner(_from, request.owner); // here and below we are sure that `_from` is the owner of the request address msgSender = msg.sender; if ( !(_from == msgSender || isApprovedForAll(_from, msgSender) || _getTokenApprovals()[_requestId] == msgSender) ) { revert NotOwnerOrApproved(msgSender); } delete _getTokenApprovals()[_requestId]; request.owner = _to; assert(_getRequestsByOwner()[_from].remove(_requestId)); assert(_getRequestsByOwner()[_to].add(_requestId)); _emitTransfer(_from, _to, _requestId); } /// @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. /// The call is not executed if the target address is not a contract. /// /// @param _from address representing the previous owner of the given token ID /// @param _to target address that will receive the tokens /// @param _requestId uint256 ID of the token to be transferred /// @param _data bytes optional data to send along with the call /// @return bool whether the call correctly returned the expected magic value function _checkOnERC721Received(address _from, address _to, uint256 _requestId, bytes memory _data) private returns (bool) { if (_to.isContract()) { try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _requestId, _data) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert TransferToNonIERC721Receiver(_to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } // // Internal getters and setters // /// @dev a little crutch to emit { Transfer } on request and on claim like ERC721 states function _emitTransfer(address _from, address _to, uint256 _requestId) internal override { emit Transfer(_from, _to, _requestId); } /// @dev Returns whether `_requestId` exists and not claimed. function _existsAndNotClaimed(uint256 _requestId) internal view returns (bool) { return _requestId > 0 && _requestId <= getLastRequestId() && !_getQueue()[_requestId].claimed; } /// @dev Approve `_to` to operate on `_requestId` /// Emits a { Approval } event. function _approve(address _to, uint256 _requestId) internal { _getTokenApprovals()[_requestId] = _to; emit Approval(ownerOf(_requestId), _to, _requestId); } /// @dev Approve `operator` to operate on all of `owner` tokens /// Emits a { ApprovalForAll } event. function _setApprovalForAll(address _owner, address _operator, bool _approved) internal { if (_owner == _operator) revert ApproveToCaller(); _getOperatorApprovals()[_owner][_operator] = _approved; emit ApprovalForAll(_owner, _operator, _approved); } /// @dev Decode a `bytes32 to string function _toString(bytes32 _sstr) internal pure returns (string memory) { uint256 len = _length(_sstr); // using `new string(len)` would work locally but is not memory safe. string memory str = new string(32); /// @solidity memory-safe-assembly assembly { mstore(str, len) mstore(add(str, 0x20), _sstr) } return str; } /// @dev encodes string `_str` in bytes32. Reverts if the string length > 31 function _toBytes32(string memory _str) internal pure returns (bytes32) { bytes memory bstr = bytes(_str); if (bstr.length > 31) { revert StringTooLong(_str); } return bytes32(uint256(bytes32(bstr)) | bstr.length); } /// @dev Return the length of a string encoded in bytes32 function _length(bytes32 _sstr) internal pure returns (uint256) { return uint256(_sstr) & 0xFF; } function _getTokenApprovals() internal pure returns (mapping(uint256 => address) storage) { return TOKEN_APPROVALS_POSITION.storageMapUint256Address(); } function _getOperatorApprovals() internal pure returns (mapping(address => mapping(address => bool)) storage) { return OPERATOR_APPROVALS_POSITION.storageMapAddressMapAddressBool(); } function _getBaseURI() internal pure returns (BaseURI storage baseURI) { bytes32 position = BASE_URI_POSITION; assembly { baseURI.slot := position } } function _constructTokenUri(uint256 _requestId) internal view returns (string memory) { string memory baseURI = _getBaseURI().value; if (bytes(baseURI).length == 0) return ""; // ${baseUri}/${_requestId}?requested=${amount}&created_at=${timestamp}[&finalized=${claimableAmount}] string memory uri = string( // we have no string.concat in 0.8.9 yet, so we have to do it with bytes.concat bytes.concat( bytes(baseURI), bytes("/"), bytes(_requestId.toString()), bytes("?requested="), bytes( uint256(_getQueue()[_requestId].cumulativeStETH - _getQueue()[_requestId - 1].cumulativeStETH) .toString() ), bytes("&created_at="), bytes(uint256(_getQueue()[_requestId].timestamp).toString()) ) ); bool finalized = _requestId <= getLastFinalizedRequestId(); if (finalized) { uri = string( bytes.concat( bytes(uri), bytes("&finalized="), bytes( _getClaimableEther(_requestId, _findCheckpointHint(_requestId, 1, getLastCheckpointIndex())) .toString() ) ) ); } return uri; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; /** * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. */ interface IAccessControlEnumerable is IAccessControl { /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) external view returns (address); /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol) pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { return _values(set._inner); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; assembly { result := store } return result; } }
// SPDX-FileCopyrightText: 2023 OpenZeppelin, Lido <[email protected]> // SPDX-License-Identifier: MIT // Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/96a2297e15f1a4bbcf470d2d0d6cb9c579c63893/contracts/interfaces/IERC4906.sol pragma solidity 0.8.9; import {IERC165} from "@openzeppelin/contracts-v4.4/utils/introspection/IERC165.sol"; import {IERC721} from "@openzeppelin/contracts-v4.4/token/ERC721/IERC721.sol"; /// @title EIP-721 Metadata Update Extension interface IERC4906 is IERC165, IERC721 { /// @dev This event emits when the metadata of a token is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFT. event MetadataUpdate(uint256 _tokenId); /// @dev This event emits when the metadata of a range of tokens is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFTs. event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.9; library UnstructuredRefStorage { function storageMapUint256Address(bytes32 _position) internal pure returns ( mapping(uint256 => address) storage result ) { assembly { result.slot := _position } } function storageMapAddressMapAddressBool(bytes32 _position) internal pure returns ( mapping(address => mapping(address => bool)) storage result ) { assembly { result.slot := _position } } }
/* * SPDX-License-Identifier: MIT */ pragma solidity 0.8.9; /** * @notice Aragon Unstructured Storage library */ library UnstructuredStorage { function getStorageBool(bytes32 position) internal view returns (bool data) { assembly { data := sload(position) } } function getStorageAddress(bytes32 position) internal view returns (address data) { assembly { data := sload(position) } } function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { assembly { data := sload(position) } } function getStorageUint256(bytes32 position) internal view returns (uint256 data) { assembly { data := sload(position) } } function setStorageBool(bytes32 position, bool data) internal { assembly { sstore(position, data) } } function setStorageAddress(bytes32 position, address data) internal { assembly { sstore(position, data) } } function setStorageBytes32(bytes32 position, bytes32 data) internal { assembly { sstore(position, data) } } function setStorageUint256(bytes32 position, uint256 data) internal { assembly { sstore(position, data) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol) // // A modified AccessControl contract using unstructured storage. Copied from tree: // https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76/contracts/access // /* See contracts/COMPILERS.md */ pragma solidity 0.8.9; import "@openzeppelin/contracts-v4.4/access/IAccessControl.sol"; import "@openzeppelin/contracts-v4.4/utils/Context.sol"; import "@openzeppelin/contracts-v4.4/utils/Strings.sol"; import "@openzeppelin/contracts-v4.4/utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } /// @dev Storage slot: mapping(bytes32 => RoleData) _roles bytes32 private constant ROLES_POSITION = keccak256("openzeppelin.AccessControl._roles"); function _storageRoles() private pure returns (mapping(bytes32 => RoleData) storage _roles) { bytes32 position = ROLES_POSITION; assembly { _roles.slot := position } } bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view override returns (bool) { return _storageRoles()[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view override returns (bytes32) { return _storageRoles()[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _storageRoles()[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _storageRoles()[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _storageRoles()[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol) // // A modified AccessControlEnumerable contract using unstructured storage. Copied from tree: // https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76/contracts/access // /* See contracts/COMPILERS.md */ pragma solidity 0.8.9; import "@openzeppelin/contracts-v4.4/access/IAccessControlEnumerable.sol"; import "@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol"; import "./AccessControl.sol"; /** * @dev Extension of {AccessControl} that allows enumerating the members of each role. */ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { using EnumerableSet for EnumerableSet.AddressSet; /// @dev Storage slot: mapping(bytes32 => EnumerableSet.AddressSet) _roleMembers bytes32 private constant ROLE_MEMBERS_POSITION = keccak256("openzeppelin.AccessControlEnumerable._roleMembers"); function _storageRoleMembers() private pure returns ( mapping(bytes32 => EnumerableSet.AddressSet) storage _roleMembers ) { bytes32 position = ROLE_MEMBERS_POSITION; assembly { _roleMembers.slot := position } } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) public view override returns (address) { return _storageRoleMembers()[role].at(index); } /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) public view override returns (uint256) { return _storageRoleMembers()[role].length(); } /** * @dev Overload {_grantRole} to track enumerable memberships */ function _grantRole(bytes32 role, address account) internal virtual override { super._grantRole(role, account); _storageRoleMembers()[role].add(account); } /** * @dev Overload {_revokeRole} to track enumerable memberships */ function _revokeRole(bytes32 role, address account) internal virtual override { super._revokeRole(role, account); _storageRoleMembers()[role].remove(account); } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.9; import "../lib/UnstructuredStorage.sol"; contract PausableUntil { using UnstructuredStorage for bytes32; /// Contract resume/pause control storage slot bytes32 internal constant RESUME_SINCE_TIMESTAMP_POSITION = keccak256("lido.PausableUntil.resumeSinceTimestamp"); /// Special value for the infinite pause uint256 public constant PAUSE_INFINITELY = type(uint256).max; /// @notice Emitted when paused by the `pauseFor` or `pauseUntil` call event Paused(uint256 duration); /// @notice Emitted when resumed by the `resume` call event Resumed(); error ZeroPauseDuration(); error PausedExpected(); error ResumedExpected(); error PauseUntilMustBeInFuture(); /// @notice Reverts when resumed modifier whenPaused() { _checkPaused(); _; } /// @notice Reverts when paused modifier whenResumed() { _checkResumed(); _; } function _checkPaused() internal view { if (!isPaused()) { revert PausedExpected(); } } function _checkResumed() internal view { if (isPaused()) { revert ResumedExpected(); } } /// @notice Returns whether the contract is paused function isPaused() public view returns (bool) { return block.timestamp < RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } /// @notice Returns one of: /// - PAUSE_INFINITELY if paused infinitely returns /// - first second when get contract get resumed if paused for specific duration /// - some timestamp in past if not paused function getResumeSinceTimestamp() external view returns (uint256) { return RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } function _resume() internal { _checkPaused(); RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(block.timestamp); emit Resumed(); } function _pauseFor(uint256 _duration) internal { _checkResumed(); if (_duration == 0) revert ZeroPauseDuration(); uint256 resumeSince; if (_duration == PAUSE_INFINITELY) { resumeSince = PAUSE_INFINITELY; } else { resumeSince = block.timestamp + _duration; } _setPausedState(resumeSince); } function _pauseUntil(uint256 _pauseUntilInclusive) internal { _checkResumed(); if (_pauseUntilInclusive < block.timestamp) revert PauseUntilMustBeInFuture(); uint256 resumeSince; if (_pauseUntilInclusive != PAUSE_INFINITELY) { resumeSince = _pauseUntilInclusive + 1; } else { resumeSince = PAUSE_INFINITELY; } _setPausedState(resumeSince); } function _setPausedState(uint256 _resumeSince) internal { RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(_resumeSince); if (_resumeSince == PAUSE_INFINITELY) { emit Paused(PAUSE_INFINITELY); } else { emit Paused(_resumeSince - block.timestamp); } } }
// SPDX-FileCopyrightText: 2022 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.9; import "../lib/UnstructuredStorage.sol"; contract Versioned { using UnstructuredStorage for bytes32; event ContractVersionSet(uint256 version); error NonZeroContractVersionOnInit(); error InvalidContractVersionIncrement(); error UnexpectedContractVersion(uint256 expected, uint256 received); /// @dev Storage slot: uint256 version /// Version of the initialized contract storage. /// The version stored in CONTRACT_VERSION_POSITION equals to: /// - 0 right after the deployment, before an initializer is invoked (and only at that moment); /// - N after calling initialize(), where N is the initially deployed contract version; /// - N after upgrading contract by calling finalizeUpgrade_vN(). bytes32 internal constant CONTRACT_VERSION_POSITION = keccak256("lido.Versioned.contractVersion"); uint256 internal constant PETRIFIED_VERSION_MARK = type(uint256).max; constructor() { // lock version in the implementation's storage to prevent initialization CONTRACT_VERSION_POSITION.setStorageUint256(PETRIFIED_VERSION_MARK); } /// @notice Returns the current contract version. function getContractVersion() public view returns (uint256) { return CONTRACT_VERSION_POSITION.getStorageUint256(); } function _checkContractVersion(uint256 version) internal view { uint256 expectedVersion = getContractVersion(); if (version != expectedVersion) { revert UnexpectedContractVersion(expectedVersion, version); } } /// @dev Sets the contract version to N. Should be called from the initialize() function. function _initializeContractVersionTo(uint256 version) internal { if (getContractVersion() != 0) revert NonZeroContractVersionOnInit(); _setContractVersion(version); } /// @dev Updates the contract version. Should be called from a finalizeUpgrade_vN() function. function _updateContractVersion(uint256 newVersion) internal { if (newVersion != getContractVersion() + 1) revert InvalidContractVersionIncrement(); _setContractVersion(newVersion); } function _setContractVersion(uint256 version) private { CONTRACT_VERSION_POSITION.setStorageUint256(version); emit ContractVersionSet(version); } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 /* See contracts/COMPILERS.md */ pragma solidity 0.8.9; import {WithdrawalQueueBase} from "./WithdrawalQueueBase.sol"; import {IERC20} from "@openzeppelin/contracts-v4.4/token/ERC20/IERC20.sol"; import {IERC20Permit} from "@openzeppelin/contracts-v4.4/token/ERC20/extensions/draft-IERC20Permit.sol"; import {EnumerableSet} from "@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol"; import {AccessControlEnumerable} from "./utils/access/AccessControlEnumerable.sol"; import {UnstructuredStorage} from "./lib/UnstructuredStorage.sol"; import {PausableUntil} from "./utils/PausableUntil.sol"; import {Versioned} from "./utils/Versioned.sol"; /// @notice Interface defining a Lido liquid staking pool /// @dev see also [Lido liquid staking pool core contract](https://docs.lido.fi/contracts/lido) interface IStETH is IERC20, IERC20Permit { function getSharesByPooledEth(uint256 _pooledEthAmount) external view returns (uint256); } /// @notice Interface defining a Lido liquid staking pool wrapper /// @dev see WstETH.sol for full docs interface IWstETH is IERC20, IERC20Permit { function unwrap(uint256 _wstETHAmount) external returns (uint256); function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256); function stETH() external view returns (IStETH); } /// @title A contract for handling stETH withdrawal request queue within the Lido protocol /// @author folkyatina abstract contract WithdrawalQueue is AccessControlEnumerable, PausableUntil, WithdrawalQueueBase, Versioned { using UnstructuredStorage for bytes32; using EnumerableSet for EnumerableSet.UintSet; /// Bunker mode activation timestamp bytes32 internal constant BUNKER_MODE_SINCE_TIMESTAMP_POSITION = keccak256("lido.WithdrawalQueue.bunkerModeSinceTimestamp"); /// Special value for timestamp when bunker mode is inactive (i.e., protocol in turbo mode) uint256 public constant BUNKER_MODE_DISABLED_TIMESTAMP = type(uint256).max; // ACL bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); bytes32 public constant FINALIZE_ROLE = keccak256("FINALIZE_ROLE"); bytes32 public constant ORACLE_ROLE = keccak256("ORACLE_ROLE"); /// @notice minimal amount of stETH that is possible to withdraw uint256 public constant MIN_STETH_WITHDRAWAL_AMOUNT = 100; /// @notice maximum amount of stETH that is possible to withdraw by a single request /// Prevents accumulating too much funds per single request fulfillment in the future. /// @dev To withdraw larger amounts, it's recommended to split it to several requests uint256 public constant MAX_STETH_WITHDRAWAL_AMOUNT = 1000 * 1e18; /// @notice Lido stETH token address IStETH public immutable STETH; /// @notice Lido wstETH token address IWstETH public immutable WSTETH; event InitializedV1(address _admin); event BunkerModeEnabled(uint256 _sinceTimestamp); event BunkerModeDisabled(); error AdminZeroAddress(); error RequestAmountTooSmall(uint256 _amountOfStETH); error RequestAmountTooLarge(uint256 _amountOfStETH); error InvalidReportTimestamp(); error RequestIdsNotSorted(); error ZeroRecipient(); error ArraysLengthMismatch(uint256 _firstArrayLength, uint256 _secondArrayLength); /// @param _wstETH address of WstETH contract constructor(IWstETH _wstETH) { // init immutables WSTETH = _wstETH; STETH = WSTETH.stETH(); } /// @notice Initialize the contract storage explicitly. /// @param _admin admin address that can change every role. /// @dev Reverts if `_admin` equals to `address(0)` /// @dev NB! It's initialized in paused state by default and should be resumed explicitly to start /// @dev NB! Bunker mode is disabled by default function initialize(address _admin) external { if (_admin == address(0)) revert AdminZeroAddress(); _initialize(_admin); } /// @notice Resume withdrawal requests placement and finalization /// Contract is deployed in paused state and should be resumed explicitly function resume() external { _checkRole(RESUME_ROLE, msg.sender); _resume(); } /// @notice Pause withdrawal requests placement and finalization. Claiming finalized requests will still be available /// @param _duration pause duration in seconds (use `PAUSE_INFINITELY` for unlimited) /// @dev Reverts if contract is already paused /// @dev Reverts reason if sender has no `PAUSE_ROLE` /// @dev Reverts if zero duration is passed function pauseFor(uint256 _duration) external onlyRole(PAUSE_ROLE) { _pauseFor(_duration); } /// @notice Pause withdrawal requests placement and finalization. Claiming finalized requests will still be available /// @param _pauseUntilInclusive the last second to pause until inclusive /// @dev Reverts if the timestamp is in the past /// @dev Reverts if sender has no `PAUSE_ROLE` /// @dev Reverts if contract is already paused function pauseUntil(uint256 _pauseUntilInclusive) external onlyRole(PAUSE_ROLE) { _pauseUntil(_pauseUntilInclusive); } /// @notice Request the batch of stETH for withdrawal. Approvals for the passed amounts should be done before. /// @param _amounts an array of stETH amount values. /// The standalone withdrawal request will be created for each item in the passed list. /// @param _owner address that will be able to manage the created requests. /// If `address(0)` is passed, `msg.sender` will be used as owner. /// @return requestIds an array of the created withdrawal request ids function requestWithdrawals(uint256[] calldata _amounts, address _owner) public returns (uint256[] memory requestIds) { _checkResumed(); if (_owner == address(0)) _owner = msg.sender; requestIds = new uint256[](_amounts.length); for (uint256 i = 0; i < _amounts.length; ++i) { _checkWithdrawalRequestAmount(_amounts[i]); requestIds[i] = _requestWithdrawal(_amounts[i], _owner); } } /// @notice Request the batch of wstETH for withdrawal. Approvals for the passed amounts should be done before. /// @param _amounts an array of wstETH amount values. /// The standalone withdrawal request will be created for each item in the passed list. /// @param _owner address that will be able to manage the created requests. /// If `address(0)` is passed, `msg.sender` will be used as an owner. /// @return requestIds an array of the created withdrawal request ids function requestWithdrawalsWstETH(uint256[] calldata _amounts, address _owner) public returns (uint256[] memory requestIds) { _checkResumed(); if (_owner == address(0)) _owner = msg.sender; requestIds = new uint256[](_amounts.length); for (uint256 i = 0; i < _amounts.length; ++i) { requestIds[i] = _requestWithdrawalWstETH(_amounts[i], _owner); } } struct PermitInput { uint256 value; uint256 deadline; uint8 v; bytes32 r; bytes32 s; } /// @notice Request the batch of stETH for withdrawal using EIP-2612 Permit /// @param _amounts an array of stETH amount values /// The standalone withdrawal request will be created for each item in the passed list. /// @param _owner address that will be able to manage the created requests. /// If `address(0)` is passed, `msg.sender` will be used as an owner. /// @param _permit data required for the stETH.permit() method to set the allowance /// @return requestIds an array of the created withdrawal request ids function requestWithdrawalsWithPermit(uint256[] calldata _amounts, address _owner, PermitInput calldata _permit) external returns (uint256[] memory requestIds) { STETH.permit(msg.sender, address(this), _permit.value, _permit.deadline, _permit.v, _permit.r, _permit.s); return requestWithdrawals(_amounts, _owner); } /// @notice Request the batch of wstETH for withdrawal using EIP-2612 Permit /// @param _amounts an array of wstETH amount values /// The standalone withdrawal request will be created for each item in the passed list. /// @param _owner address that will be able to manage the created requests. /// If `address(0)` is passed, `msg.sender` will be used as an owner. /// @param _permit data required for the wtETH.permit() method to set the allowance /// @return requestIds an array of the created withdrawal request ids function requestWithdrawalsWstETHWithPermit( uint256[] calldata _amounts, address _owner, PermitInput calldata _permit ) external returns (uint256[] memory requestIds) { WSTETH.permit(msg.sender, address(this), _permit.value, _permit.deadline, _permit.v, _permit.r, _permit.s); return requestWithdrawalsWstETH(_amounts, _owner); } /// @notice Returns all withdrawal requests that belongs to the `_owner` address /// /// WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed /// to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that /// this function has an unbounded cost, and using it as part of a state-changing function may render the function /// uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. function getWithdrawalRequests(address _owner) external view returns (uint256[] memory requestsIds) { return _getRequestsByOwner()[_owner].values(); } /// @notice Returns status for requests with provided ids /// @param _requestIds array of withdrawal request ids function getWithdrawalStatus(uint256[] calldata _requestIds) external view returns (WithdrawalRequestStatus[] memory statuses) { statuses = new WithdrawalRequestStatus[](_requestIds.length); for (uint256 i = 0; i < _requestIds.length; ++i) { statuses[i] = _getStatus(_requestIds[i]); } } /// @notice Returns amount of ether available for claim for each provided request id /// @param _requestIds array of request ids /// @param _hints checkpoint hints. can be found with `findCheckpointHints(_requestIds, 1, getLastCheckpointIndex())` /// @return claimableEthValues amount of claimable ether for each request, amount is equal to 0 if request /// is not finalized or already claimed function getClaimableEther(uint256[] calldata _requestIds, uint256[] calldata _hints) external view returns (uint256[] memory claimableEthValues) { claimableEthValues = new uint256[](_requestIds.length); for (uint256 i = 0; i < _requestIds.length; ++i) { claimableEthValues[i] = _getClaimableEther(_requestIds[i], _hints[i]); } } /// @notice Claim a batch of withdrawal requests if they are finalized sending ether to `_recipient` /// @param _requestIds array of request ids to claim /// @param _hints checkpoint hint for each id. Can be obtained with `findCheckpointHints()` /// @param _recipient address where claimed ether will be sent to /// @dev /// Reverts if recipient is equal to zero /// Reverts if requestIds and hints arrays length differs /// Reverts if any requestId or hint in arguments are not valid /// Reverts if any request is not finalized or already claimed /// Reverts if msg sender is not an owner of the requests function claimWithdrawalsTo(uint256[] calldata _requestIds, uint256[] calldata _hints, address _recipient) external { if (_recipient == address(0)) revert ZeroRecipient(); if (_requestIds.length != _hints.length) { revert ArraysLengthMismatch(_requestIds.length, _hints.length); } for (uint256 i = 0; i < _requestIds.length; ++i) { _claim(_requestIds[i], _hints[i], _recipient); _emitTransfer(msg.sender, address(0), _requestIds[i]); } } /// @notice Claim a batch of withdrawal requests if they are finalized sending locked ether to the owner /// @param _requestIds array of request ids to claim /// @param _hints checkpoint hint for each id. Can be obtained with `findCheckpointHints()` /// @dev /// Reverts if requestIds and hints arrays length differs /// Reverts if any requestId or hint in arguments are not valid /// Reverts if any request is not finalized or already claimed /// Reverts if msg sender is not an owner of the requests function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external { if (_requestIds.length != _hints.length) { revert ArraysLengthMismatch(_requestIds.length, _hints.length); } for (uint256 i = 0; i < _requestIds.length; ++i) { _claim(_requestIds[i], _hints[i], msg.sender); _emitTransfer(msg.sender, address(0), _requestIds[i]); } } /// @notice Claim one`_requestId` request once finalized sending locked ether to the owner /// @param _requestId request id to claim /// @dev use unbounded loop to find a hint, which can lead to OOG /// @dev /// Reverts if requestId or hint are not valid /// Reverts if request is not finalized or already claimed /// Reverts if msg sender is not an owner of request function claimWithdrawal(uint256 _requestId) external { _claim(_requestId, _findCheckpointHint(_requestId, 1, getLastCheckpointIndex()), msg.sender); _emitTransfer(msg.sender, address(0), _requestId); } /// @notice Finds the list of hints for the given `_requestIds` searching among the checkpoints with indices /// in the range `[_firstIndex, _lastIndex]`. /// NB! Array of request ids should be sorted /// NB! `_firstIndex` should be greater than 0, because checkpoint list is 1-based array /// Usage: findCheckpointHints(_requestIds, 1, getLastCheckpointIndex()) /// @param _requestIds ids of the requests sorted in the ascending order to get hints for /// @param _firstIndex left boundary of the search range. Should be greater than 0 /// @param _lastIndex right boundary of the search range. Should be less than or equal to getLastCheckpointIndex() /// @return hintIds array of hints used to find required checkpoint for the request function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex) external view returns (uint256[] memory hintIds) { hintIds = new uint256[](_requestIds.length); uint256 prevRequestId = 0; for (uint256 i = 0; i < _requestIds.length; ++i) { if (_requestIds[i] < prevRequestId) revert RequestIdsNotSorted(); hintIds[i] = _findCheckpointHint(_requestIds[i], _firstIndex, _lastIndex); _firstIndex = hintIds[i]; prevRequestId = _requestIds[i]; } } /// @notice Update bunker mode state and last report timestamp on oracle report /// @dev should be called by oracle /// /// @param _isBunkerModeNow is bunker mode reported by oracle /// @param _bunkerStartTimestamp timestamp of start of the bunker mode /// @param _currentReportTimestamp timestamp of the current report ref slot function onOracleReport(bool _isBunkerModeNow, uint256 _bunkerStartTimestamp, uint256 _currentReportTimestamp) external { _checkRole(ORACLE_ROLE, msg.sender); if (_bunkerStartTimestamp >= block.timestamp) revert InvalidReportTimestamp(); if (_currentReportTimestamp >= block.timestamp) revert InvalidReportTimestamp(); _setLastReportTimestamp(_currentReportTimestamp); bool isBunkerModeWasSetBefore = isBunkerModeActive(); // on bunker mode state change if (_isBunkerModeNow != isBunkerModeWasSetBefore) { // write previous timestamp to enable bunker or max uint to disable if (_isBunkerModeNow) { BUNKER_MODE_SINCE_TIMESTAMP_POSITION.setStorageUint256(_bunkerStartTimestamp); emit BunkerModeEnabled(_bunkerStartTimestamp); } else { BUNKER_MODE_SINCE_TIMESTAMP_POSITION.setStorageUint256(BUNKER_MODE_DISABLED_TIMESTAMP); emit BunkerModeDisabled(); } } } /// @notice Check if bunker mode is active function isBunkerModeActive() public view returns (bool) { return bunkerModeSinceTimestamp() < BUNKER_MODE_DISABLED_TIMESTAMP; } /// @notice Get bunker mode activation timestamp /// @dev returns `BUNKER_MODE_DISABLED_TIMESTAMP` if bunker mode is disable (i.e., protocol in turbo mode) function bunkerModeSinceTimestamp() public view returns (uint256) { return BUNKER_MODE_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } /// @notice Should emit ERC721 Transfer event in the inheriting contract function _emitTransfer(address from, address to, uint256 _requestId) internal virtual; /// @dev internal initialization helper. Doesn't check provided addresses intentionally function _initialize(address _admin) internal { _initializeQueue(); _pauseFor(PAUSE_INFINITELY); _initializeContractVersionTo(1); _grantRole(DEFAULT_ADMIN_ROLE, _admin); BUNKER_MODE_SINCE_TIMESTAMP_POSITION.setStorageUint256(BUNKER_MODE_DISABLED_TIMESTAMP); emit InitializedV1(_admin); } function _requestWithdrawal(uint256 _amountOfStETH, address _owner) internal returns (uint256 requestId) { STETH.transferFrom(msg.sender, address(this), _amountOfStETH); uint256 amountOfShares = STETH.getSharesByPooledEth(_amountOfStETH); requestId = _enqueue(uint128(_amountOfStETH), uint128(amountOfShares), _owner); _emitTransfer(address(0), _owner, requestId); } function _requestWithdrawalWstETH(uint256 _amountOfWstETH, address _owner) internal returns (uint256 requestId) { WSTETH.transferFrom(msg.sender, address(this), _amountOfWstETH); uint256 amountOfStETH = WSTETH.unwrap(_amountOfWstETH); _checkWithdrawalRequestAmount(amountOfStETH); uint256 amountOfShares = STETH.getSharesByPooledEth(amountOfStETH); requestId = _enqueue(uint128(amountOfStETH), uint128(amountOfShares), _owner); _emitTransfer(address(0), _owner, requestId); } function _checkWithdrawalRequestAmount(uint256 _amountOfStETH) internal pure { if (_amountOfStETH < MIN_STETH_WITHDRAWAL_AMOUNT) { revert RequestAmountTooSmall(_amountOfStETH); } if (_amountOfStETH > MAX_STETH_WITHDRAWAL_AMOUNT) { revert RequestAmountTooLarge(_amountOfStETH); } } /// @notice returns claimable ether under the request. Returns 0 if request is not finalized or claimed function _getClaimableEther(uint256 _requestId, uint256 _hint) internal view returns (uint256) { if (_requestId == 0 || _requestId > getLastRequestId()) revert InvalidRequestId(_requestId); if (_requestId > getLastFinalizedRequestId()) return 0; WithdrawalRequest storage request = _getQueue()[_requestId]; if (request.claimed) return 0; return _calculateClaimableEther(request, _requestId, _hint); } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 /* See contracts/COMPILERS.md */ pragma solidity 0.8.9; import "@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol"; import {UnstructuredStorage} from "./lib/UnstructuredStorage.sol"; /// @title Queue to store and manage WithdrawalRequests. /// @dev Use an optimizations to store max share rates for finalized requests heavily inspired /// by Aragon MiniMe token https://github.com/aragon/aragon-minime/blob/master/contracts/MiniMeToken.sol /// /// @author folkyatina abstract contract WithdrawalQueueBase { using EnumerableSet for EnumerableSet.UintSet; using UnstructuredStorage for bytes32; /// @dev maximal length of the batch array provided for prefinalization. See `prefinalize()` uint256 public constant MAX_BATCHES_LENGTH = 36; /// @notice precision base for share rate uint256 internal constant E27_PRECISION_BASE = 1e27; /// @dev return value for the `find...` methods in case of no result uint256 internal constant NOT_FOUND = 0; /// @dev queue for withdrawal requests, indexes (requestId) start from 1 bytes32 internal constant QUEUE_POSITION = keccak256("lido.WithdrawalQueue.queue"); /// @dev last index in request queue bytes32 internal constant LAST_REQUEST_ID_POSITION = keccak256("lido.WithdrawalQueue.lastRequestId"); /// @dev last index of finalized request in the queue bytes32 internal constant LAST_FINALIZED_REQUEST_ID_POSITION = keccak256("lido.WithdrawalQueue.lastFinalizedRequestId"); /// @dev finalization rate history, indexes start from 1 bytes32 internal constant CHECKPOINTS_POSITION = keccak256("lido.WithdrawalQueue.checkpoints"); /// @dev last index in checkpoints array bytes32 internal constant LAST_CHECKPOINT_INDEX_POSITION = keccak256("lido.WithdrawalQueue.lastCheckpointIndex"); /// @dev amount of eth locked on contract for further claiming bytes32 internal constant LOCKED_ETHER_AMOUNT_POSITION = keccak256("lido.WithdrawalQueue.lockedEtherAmount"); /// @dev withdrawal requests mapped to the owners bytes32 internal constant REQUEST_BY_OWNER_POSITION = keccak256("lido.WithdrawalQueue.requestsByOwner"); /// @dev timestamp of the last oracle report bytes32 internal constant LAST_REPORT_TIMESTAMP_POSITION = keccak256("lido.WithdrawalQueue.lastReportTimestamp"); /// @notice structure representing a request for withdrawal struct WithdrawalRequest { /// @notice sum of the all stETH submitted for withdrawals including this request uint128 cumulativeStETH; /// @notice sum of the all shares locked for withdrawal including this request uint128 cumulativeShares; /// @notice address that can claim or transfer the request address owner; /// @notice block.timestamp when the request was created uint40 timestamp; /// @notice flag if the request was claimed bool claimed; /// @notice timestamp of last oracle report for this request uint40 reportTimestamp; } /// @notice structure to store discounts for requests that are affected by negative rebase struct Checkpoint { uint256 fromRequestId; uint256 maxShareRate; } /// @notice output format struct for `_getWithdrawalStatus()` method struct WithdrawalRequestStatus { /// @notice stETH token amount that was locked on withdrawal queue for this request uint256 amountOfStETH; /// @notice amount of stETH shares locked on withdrawal queue for this request uint256 amountOfShares; /// @notice address that can claim or transfer this request address owner; /// @notice timestamp of when the request was created, in seconds uint256 timestamp; /// @notice true, if request is finalized bool isFinalized; /// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed) bool isClaimed; } /// @dev Contains both stETH token amount and its corresponding shares amount event WithdrawalRequested( uint256 indexed requestId, address indexed requestor, address indexed owner, uint256 amountOfStETH, uint256 amountOfShares ); event WithdrawalsFinalized( uint256 indexed from, uint256 indexed to, uint256 amountOfETHLocked, uint256 sharesToBurn, uint256 timestamp ); event WithdrawalClaimed( uint256 indexed requestId, address indexed owner, address indexed receiver, uint256 amountOfETH ); error ZeroAmountOfETH(); error ZeroShareRate(); error ZeroTimestamp(); error TooMuchEtherToFinalize(uint256 sent, uint256 maxExpected); error NotOwner(address _sender, address _owner); error InvalidRequestId(uint256 _requestId); error InvalidRequestIdRange(uint256 startId, uint256 endId); error InvalidState(); error BatchesAreNotSorted(); error EmptyBatches(); error RequestNotFoundOrNotFinalized(uint256 _requestId); error NotEnoughEther(); error RequestAlreadyClaimed(uint256 _requestId); error InvalidHint(uint256 _hint); error CantSendValueRecipientMayHaveReverted(); /// @notice id of the last request /// NB! requests are indexed from 1, so it returns 0 if there is no requests in the queue function getLastRequestId() public view returns (uint256) { return LAST_REQUEST_ID_POSITION.getStorageUint256(); } /// @notice id of the last finalized request /// NB! requests are indexed from 1, so it returns 0 if there is no finalized requests in the queue function getLastFinalizedRequestId() public view returns (uint256) { return LAST_FINALIZED_REQUEST_ID_POSITION.getStorageUint256(); } /// @notice amount of ETH on this contract balance that is locked for withdrawal and available to claim function getLockedEtherAmount() public view returns (uint256) { return LOCKED_ETHER_AMOUNT_POSITION.getStorageUint256(); } /// @notice length of the checkpoint array. Last possible value for the hint. /// NB! checkpoints are indexed from 1, so it returns 0 if there is no checkpoints function getLastCheckpointIndex() public view returns (uint256) { return LAST_CHECKPOINT_INDEX_POSITION.getStorageUint256(); } /// @notice return the number of unfinalized requests in the queue function unfinalizedRequestNumber() external view returns (uint256) { return getLastRequestId() - getLastFinalizedRequestId(); } /// @notice Returns the amount of stETH in the queue yet to be finalized function unfinalizedStETH() external view returns (uint256) { return _getQueue()[getLastRequestId()].cumulativeStETH - _getQueue()[getLastFinalizedRequestId()].cumulativeStETH; } // // FINALIZATION FLOW // // Process when protocol is fixing the withdrawal request value and lock the required amount of ETH. // The value of a request after finalization can be: // - nominal (when the amount of eth locked for this request are equal to the request's stETH) // - discounted (when the amount of eth will be lower, because the protocol share rate dropped // before request is finalized, so it will be equal to `request's shares` * `protocol share rate`) // The parameters that are required for finalization are: // - current share rate of the protocol // - id of the last request that can be finalized // - the amount of eth that must be locked for these requests // To calculate the eth amount we'll need to know which requests in the queue will be finalized as nominal // and which as discounted and the exact value of the discount. It's impossible to calculate without the unbounded // loop over the unfinalized part of the queue. So, we need to extract a part of the algorithm off-chain, bring the // result with oracle report and check it later and check the result later. // So, we came to this solution: // Off-chain // 1. Oracle iterates over the queue off-chain and calculate the id of the latest finalizable request // in the queue. Then it splits all the requests that will be finalized into batches the way, // that requests in a batch are all nominal or all discounted. // And passes them in the report as the array of the ending ids of these batches. So it can be reconstructed like // `[lastFinalizedRequestId+1, batches[0]], [batches[0]+1, batches[1]] ... [batches[n-2], batches[n-1]]` // 2. Contract checks the validity of the batches on-chain and calculate the amount of eth required to // finalize them. It can be done without unbounded loop using partial sums that are calculated on request enqueueing. // 3. Contract marks the request's as finalized and locks the eth for claiming. It also, // set's the discount checkpoint for these request's if required that will be applied on claim for each request's // individually depending on request's share rate. /// @notice transient state that is used to pass intermediate results between several `calculateFinalizationBatches` // invocations struct BatchesCalculationState { /// @notice amount of ether available in the protocol that can be used to finalize withdrawal requests /// Will decrease on each call and will be equal to the remainder when calculation is finished /// Should be set before the first call uint256 remainingEthBudget; /// @notice flag that is set to `true` if returned state is final and `false` if more calls are required bool finished; /// @notice static array to store last request id in each batch uint256[MAX_BATCHES_LENGTH] batches; /// @notice length of the filled part of `batches` array uint256 batchesLength; } /// @notice Offchain view for the oracle daemon that calculates how many requests can be finalized within /// the given budget, time period and share rate limits. Returned requests are split into batches. /// Each batch consist of the requests that all have the share rate below the `_maxShareRate` or above it. /// Below you can see an example how 14 requests with different share rates will be split into 5 batches by /// this method /// /// ^ share rate /// | /// | • • /// | • • • • • /// |----------------------•------ _maxShareRate /// | • • • • • /// | • /// +-------------------------------> requestId /// | 1st| 2nd |3| 4th | 5th | /// /// @param _maxShareRate current share rate of the protocol (1e27 precision) /// @param _maxTimestamp max timestamp of the request that can be finalized /// @param _maxRequestsPerCall max request number that can be processed per call. /// @param _state structure that accumulates the state across multiple invocations to overcome gas limits. /// To start calculation you should pass `state.remainingEthBudget` and `state.finished == false` and then invoke /// the function with returned `state` until it returns a state with `finished` flag set /// @return state that is changing on each call and should be passed to the next call until `state.finished` is true function calculateFinalizationBatches( uint256 _maxShareRate, uint256 _maxTimestamp, uint256 _maxRequestsPerCall, BatchesCalculationState memory _state ) external view returns (BatchesCalculationState memory) { if (_state.finished || _state.remainingEthBudget == 0) revert InvalidState(); uint256 currentId; WithdrawalRequest memory prevRequest; uint256 prevRequestShareRate; if (_state.batchesLength == 0) { currentId = getLastFinalizedRequestId() + 1; prevRequest = _getQueue()[currentId - 1]; } else { uint256 lastHandledRequestId = _state.batches[_state.batchesLength - 1]; currentId = lastHandledRequestId + 1; prevRequest = _getQueue()[lastHandledRequestId]; (prevRequestShareRate,,) = _calcBatch(_getQueue()[lastHandledRequestId - 1], prevRequest); } uint256 nextCallRequestId = currentId + _maxRequestsPerCall; uint256 queueLength = getLastRequestId() + 1; while (currentId < queueLength && currentId < nextCallRequestId) { WithdrawalRequest memory request = _getQueue()[currentId]; if (request.timestamp > _maxTimestamp) break; // max timestamp break (uint256 requestShareRate, uint256 ethToFinalize, uint256 shares) = _calcBatch(prevRequest, request); if (requestShareRate > _maxShareRate) { // discounted ethToFinalize = (shares * _maxShareRate) / E27_PRECISION_BASE; } if (ethToFinalize > _state.remainingEthBudget) break; // budget break _state.remainingEthBudget -= ethToFinalize; if (_state.batchesLength != 0 && ( // share rate of requests in the same batch can differ by 1-2 wei because of the rounding error // (issue: https://github.com/lidofinance/lido-dao/issues/442 ) // so we're taking requests that are placed during the same report // as equal even if their actual share rate are different prevRequest.reportTimestamp == request.reportTimestamp || // both requests are below the line prevRequestShareRate <= _maxShareRate && requestShareRate <= _maxShareRate || // both requests are above the line prevRequestShareRate > _maxShareRate && requestShareRate > _maxShareRate )) { _state.batches[_state.batchesLength - 1] = currentId; // extend the last batch } else { // to be able to check batches on-chain we need array to have limited length if (_state.batchesLength == MAX_BATCHES_LENGTH) break; // create a new batch _state.batches[_state.batchesLength] = currentId; ++_state.batchesLength; } prevRequestShareRate = requestShareRate; prevRequest = request; unchecked{ ++currentId; } } _state.finished = currentId == queueLength || currentId < nextCallRequestId; return _state; } /// @notice Checks finalization batches, calculates required ether and the amount of shares to burn /// @param _batches finalization batches calculated offchain using `calculateFinalizationBatches()` /// @param _maxShareRate max share rate that will be used for request finalization (1e27 precision) /// @return ethToLock amount of ether that should be sent with `finalize()` method /// @return sharesToBurn amount of shares that belongs to requests that will be finalized function prefinalize(uint256[] calldata _batches, uint256 _maxShareRate) external view returns (uint256 ethToLock, uint256 sharesToBurn) { if (_maxShareRate == 0) revert ZeroShareRate(); if (_batches.length == 0) revert EmptyBatches(); if (_batches[0] <= getLastFinalizedRequestId()) revert InvalidRequestId(_batches[0]); if (_batches[_batches.length - 1] > getLastRequestId()) revert InvalidRequestId(_batches[_batches.length - 1]); uint256 currentBatchIndex; uint256 prevBatchEndRequestId = getLastFinalizedRequestId(); WithdrawalRequest memory prevBatchEnd = _getQueue()[prevBatchEndRequestId]; while (currentBatchIndex < _batches.length) { uint256 batchEndRequestId = _batches[currentBatchIndex]; if (batchEndRequestId <= prevBatchEndRequestId) revert BatchesAreNotSorted(); WithdrawalRequest memory batchEnd = _getQueue()[batchEndRequestId]; (uint256 batchShareRate, uint256 stETH, uint256 shares) = _calcBatch(prevBatchEnd, batchEnd); if (batchShareRate > _maxShareRate) { // discounted ethToLock += shares * _maxShareRate / E27_PRECISION_BASE; } else { // nominal ethToLock += stETH; } sharesToBurn += shares; prevBatchEndRequestId = batchEndRequestId; prevBatchEnd = batchEnd; unchecked{ ++currentBatchIndex; } } } /// @dev Finalize requests in the queue /// Emits WithdrawalsFinalized event. function _finalize(uint256 _lastRequestIdToBeFinalized, uint256 _amountOfETH, uint256 _maxShareRate) internal { if (_lastRequestIdToBeFinalized > getLastRequestId()) revert InvalidRequestId(_lastRequestIdToBeFinalized); uint256 lastFinalizedRequestId = getLastFinalizedRequestId(); if (_lastRequestIdToBeFinalized <= lastFinalizedRequestId) revert InvalidRequestId(_lastRequestIdToBeFinalized); WithdrawalRequest memory lastFinalizedRequest = _getQueue()[lastFinalizedRequestId]; WithdrawalRequest memory requestToFinalize = _getQueue()[_lastRequestIdToBeFinalized]; uint128 stETHToFinalize = requestToFinalize.cumulativeStETH - lastFinalizedRequest.cumulativeStETH; if (_amountOfETH > stETHToFinalize) revert TooMuchEtherToFinalize(_amountOfETH, stETHToFinalize); uint256 firstRequestIdToFinalize = lastFinalizedRequestId + 1; uint256 lastCheckpointIndex = getLastCheckpointIndex(); // add a new checkpoint with current finalization max share rate _getCheckpoints()[lastCheckpointIndex + 1] = Checkpoint(firstRequestIdToFinalize, _maxShareRate); _setLastCheckpointIndex(lastCheckpointIndex + 1); _setLockedEtherAmount(getLockedEtherAmount() + _amountOfETH); _setLastFinalizedRequestId(_lastRequestIdToBeFinalized); emit WithdrawalsFinalized( firstRequestIdToFinalize, _lastRequestIdToBeFinalized, _amountOfETH, requestToFinalize.cumulativeShares - lastFinalizedRequest.cumulativeShares, block.timestamp ); } /// @dev creates a new `WithdrawalRequest` in the queue /// Emits WithdrawalRequested event function _enqueue(uint128 _amountOfStETH, uint128 _amountOfShares, address _owner) internal returns (uint256 requestId) { uint256 lastRequestId = getLastRequestId(); WithdrawalRequest memory lastRequest = _getQueue()[lastRequestId]; uint128 cumulativeShares = lastRequest.cumulativeShares + _amountOfShares; uint128 cumulativeStETH = lastRequest.cumulativeStETH + _amountOfStETH; requestId = lastRequestId + 1; _setLastRequestId(requestId); WithdrawalRequest memory newRequest = WithdrawalRequest( cumulativeStETH, cumulativeShares, _owner, uint40(block.timestamp), false, uint40(_getLastReportTimestamp()) ); _getQueue()[requestId] = newRequest; assert(_getRequestsByOwner()[_owner].add(requestId)); emit WithdrawalRequested(requestId, msg.sender, _owner, _amountOfStETH, _amountOfShares); } /// @dev Returns the status of the withdrawal request with `_requestId` id function _getStatus(uint256 _requestId) internal view returns (WithdrawalRequestStatus memory status) { if (_requestId == 0 || _requestId > getLastRequestId()) revert InvalidRequestId(_requestId); WithdrawalRequest memory request = _getQueue()[_requestId]; WithdrawalRequest memory previousRequest = _getQueue()[_requestId - 1]; status = WithdrawalRequestStatus( request.cumulativeStETH - previousRequest.cumulativeStETH, request.cumulativeShares - previousRequest.cumulativeShares, request.owner, request.timestamp, _requestId <= getLastFinalizedRequestId(), request.claimed ); } /// @dev View function to find a checkpoint hint to use in `claimWithdrawal()` and `getClaimableEther()` /// Search will be performed in the range of `[_firstIndex, _lastIndex]` /// /// @param _requestId request id to search the checkpoint for /// @param _start index of the left boundary of the search range, should be greater than 0 /// @param _end index of the right boundary of the search range, should be less than or equal /// to `getLastCheckpointIndex()` /// /// @return hint for later use in other methods or 0 if hint not found in the range function _findCheckpointHint(uint256 _requestId, uint256 _start, uint256 _end) internal view returns (uint256) { if (_requestId == 0 || _requestId > getLastRequestId()) revert InvalidRequestId(_requestId); uint256 lastCheckpointIndex = getLastCheckpointIndex(); if (_start == 0 || _end > lastCheckpointIndex) revert InvalidRequestIdRange(_start, _end); if (lastCheckpointIndex == 0 || _requestId > getLastFinalizedRequestId() || _start > _end) return NOT_FOUND; // Right boundary if (_requestId >= _getCheckpoints()[_end].fromRequestId) { // it's the last checkpoint, so it's valid if (_end == lastCheckpointIndex) return _end; // it fits right before the next checkpoint if (_requestId < _getCheckpoints()[_end + 1].fromRequestId) return _end; return NOT_FOUND; } // Left boundary if (_requestId < _getCheckpoints()[_start].fromRequestId) { return NOT_FOUND; } // Binary search uint256 min = _start; uint256 max = _end - 1; while (max > min) { uint256 mid = (max + min + 1) / 2; if (_getCheckpoints()[mid].fromRequestId <= _requestId) { min = mid; } else { max = mid - 1; } } return min; } /// @dev Claim the request and transfer locked ether to `_recipient`. /// Emits WithdrawalClaimed event /// @param _requestId id of the request to claim /// @param _hint hint the checkpoint to use. Can be obtained by calling `findCheckpointHint()` /// @param _recipient address to send ether to function _claim(uint256 _requestId, uint256 _hint, address _recipient) internal { if (_requestId == 0) revert InvalidRequestId(_requestId); if (_requestId > getLastFinalizedRequestId()) revert RequestNotFoundOrNotFinalized(_requestId); WithdrawalRequest storage request = _getQueue()[_requestId]; if (request.claimed) revert RequestAlreadyClaimed(_requestId); if (request.owner != msg.sender) revert NotOwner(msg.sender, request.owner); request.claimed = true; assert(_getRequestsByOwner()[request.owner].remove(_requestId)); uint256 ethWithDiscount = _calculateClaimableEther(request, _requestId, _hint); // because of the stETH rounding issue // (issue: https://github.com/lidofinance/lido-dao/issues/442 ) // some dust (1-2 wei per request) will be accumulated upon claiming _setLockedEtherAmount(getLockedEtherAmount() - ethWithDiscount); _sendValue(_recipient, ethWithDiscount); emit WithdrawalClaimed(_requestId, msg.sender, _recipient, ethWithDiscount); } /// @dev Calculates ether value for the request using the provided hint. Checks if hint is valid /// @return claimableEther discounted eth for `_requestId` function _calculateClaimableEther(WithdrawalRequest storage _request, uint256 _requestId, uint256 _hint) internal view returns (uint256 claimableEther) { if (_hint == 0) revert InvalidHint(_hint); uint256 lastCheckpointIndex = getLastCheckpointIndex(); if (_hint > lastCheckpointIndex) revert InvalidHint(_hint); Checkpoint memory checkpoint = _getCheckpoints()[_hint]; // Reverts if requestId is not in range [checkpoint[hint], checkpoint[hint+1]) // ______(>______ // ^ hint if (_requestId < checkpoint.fromRequestId) revert InvalidHint(_hint); if (_hint < lastCheckpointIndex) { // ______(>______(>________ // hint hint+1 ^ Checkpoint memory nextCheckpoint = _getCheckpoints()[_hint + 1]; if (nextCheckpoint.fromRequestId <= _requestId) revert InvalidHint(_hint); } WithdrawalRequest memory prevRequest = _getQueue()[_requestId - 1]; (uint256 batchShareRate, uint256 eth, uint256 shares) = _calcBatch(prevRequest, _request); if (batchShareRate > checkpoint.maxShareRate) { eth = shares * checkpoint.maxShareRate / E27_PRECISION_BASE; } return eth; } /// @dev quazi-constructor function _initializeQueue() internal { // setting dummy zero structs in checkpoints and queue beginning // to avoid uint underflows and related if-branches // 0-index is reserved as 'not_found' response in the interface everywhere _getQueue()[0] = WithdrawalRequest(0, 0, address(0), uint40(block.timestamp), true, 0); _getCheckpoints()[getLastCheckpointIndex()] = Checkpoint(0, 0); } function _sendValue(address _recipient, uint256 _amount) internal { if (address(this).balance < _amount) revert NotEnoughEther(); // solhint-disable-next-line (bool success,) = _recipient.call{value: _amount}(""); if (!success) revert CantSendValueRecipientMayHaveReverted(); } /// @dev calculate batch stats (shareRate, stETH and shares) for the range of `(_preStartRequest, _endRequest]` function _calcBatch(WithdrawalRequest memory _preStartRequest, WithdrawalRequest memory _endRequest) internal pure returns (uint256 shareRate, uint256 stETH, uint256 shares) { stETH = _endRequest.cumulativeStETH - _preStartRequest.cumulativeStETH; shares = _endRequest.cumulativeShares - _preStartRequest.cumulativeShares; shareRate = stETH * E27_PRECISION_BASE / shares; } // // Internal getters and setters for unstructured storage // function _getQueue() internal pure returns (mapping(uint256 => WithdrawalRequest) storage queue) { bytes32 position = QUEUE_POSITION; assembly { queue.slot := position } } function _getCheckpoints() internal pure returns (mapping(uint256 => Checkpoint) storage checkpoints) { bytes32 position = CHECKPOINTS_POSITION; assembly { checkpoints.slot := position } } function _getRequestsByOwner() internal pure returns (mapping(address => EnumerableSet.UintSet) storage requestsByOwner) { bytes32 position = REQUEST_BY_OWNER_POSITION; assembly { requestsByOwner.slot := position } } function _getLastReportTimestamp() internal view returns (uint256) { return LAST_REPORT_TIMESTAMP_POSITION.getStorageUint256(); } function _setLastRequestId(uint256 _lastRequestId) internal { LAST_REQUEST_ID_POSITION.setStorageUint256(_lastRequestId); } function _setLastFinalizedRequestId(uint256 _lastFinalizedRequestId) internal { LAST_FINALIZED_REQUEST_ID_POSITION.setStorageUint256(_lastFinalizedRequestId); } function _setLastCheckpointIndex(uint256 _lastCheckpointIndex) internal { LAST_CHECKPOINT_INDEX_POSITION.setStorageUint256(_lastCheckpointIndex); } function _setLockedEtherAmount(uint256 _lockedEtherAmount) internal { LOCKED_ETHER_AMOUNT_POSITION.setStorageUint256(_lockedEtherAmount); } function _setLastReportTimestamp(uint256 _lastReportTimestamp) internal { LAST_REPORT_TIMESTAMP_POSITION.setStorageUint256(_lastReportTimestamp); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "istanbul", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"_wstETH","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AdminZeroAddress","type":"error"},{"inputs":[],"name":"ApprovalToOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[{"internalType":"uint256","name":"_firstArrayLength","type":"uint256"},{"internalType":"uint256","name":"_secondArrayLength","type":"uint256"}],"name":"ArraysLengthMismatch","type":"error"},{"inputs":[],"name":"BatchesAreNotSorted","type":"error"},{"inputs":[],"name":"CantSendValueRecipientMayHaveReverted","type":"error"},{"inputs":[],"name":"EmptyBatches","type":"error"},{"inputs":[],"name":"InvalidContractVersionIncrement","type":"error"},{"inputs":[{"internalType":"uint256","name":"_hint","type":"uint256"}],"name":"InvalidHint","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"InvalidOwnerAddress","type":"error"},{"inputs":[],"name":"InvalidReportTimestamp","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"InvalidRequestId","type":"error"},{"inputs":[{"internalType":"uint256","name":"startId","type":"uint256"},{"internalType":"uint256","name":"endId","type":"uint256"}],"name":"InvalidRequestIdRange","type":"error"},{"inputs":[],"name":"InvalidState","type":"error"},{"inputs":[],"name":"NonZeroContractVersionOnInit","type":"error"},{"inputs":[],"name":"NotEnoughEther","type":"error"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"NotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"NotOwnerOrApproved","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"NotOwnerOrApprovedForAll","type":"error"},{"inputs":[],"name":"PauseUntilMustBeInFuture","type":"error"},{"inputs":[],"name":"PausedExpected","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"RequestAlreadyClaimed","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amountOfStETH","type":"uint256"}],"name":"RequestAmountTooLarge","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amountOfStETH","type":"uint256"}],"name":"RequestAmountTooSmall","type":"error"},{"inputs":[],"name":"RequestIdsNotSorted","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"RequestNotFoundOrNotFinalized","type":"error"},{"inputs":[],"name":"ResumedExpected","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[{"internalType":"uint256","name":"sent","type":"uint256"},{"internalType":"uint256","name":"maxExpected","type":"uint256"}],"name":"TooMuchEtherToFinalize","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"realOwner","type":"address"}],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferFromZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"TransferToNonIERC721Receiver","type":"error"},{"inputs":[],"name":"TransferToThemselves","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"received","type":"uint256"}],"name":"UnexpectedContractVersion","type":"error"},{"inputs":[],"name":"ZeroAmountOfETH","type":"error"},{"inputs":[],"name":"ZeroMetadata","type":"error"},{"inputs":[],"name":"ZeroPauseDuration","type":"error"},{"inputs":[],"name":"ZeroRecipient","type":"error"},{"inputs":[],"name":"ZeroShareRate","type":"error"},{"inputs":[],"name":"ZeroTimestamp","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"baseURI","type":"string"}],"name":"BaseURISet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[],"name":"BunkerModeDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_sinceTimestamp","type":"uint256"}],"name":"BunkerModeEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"}],"name":"ContractVersionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_admin","type":"address"}],"name":"InitializedV1","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"nftDescriptorAddress","type":"address"}],"name":"NftDescriptorAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[],"name":"Resumed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOfETH","type":"uint256"}],"name":"WithdrawalClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"address","name":"requestor","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOfStETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOfShares","type":"uint256"}],"name":"WithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"from","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"to","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOfETHLocked","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesToBurn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalsFinalized","type":"event"},{"inputs":[],"name":"BUNKER_MODE_DISABLED_TIMESTAMP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FINALIZE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGE_TOKEN_URI_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BATCHES_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STETH_WITHDRAWAL_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_STETH_WITHDRAWAL_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ORACLE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_INFINITELY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESUME_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STETH","outputs":[{"internalType":"contract IStETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WSTETH","outputs":[{"internalType":"contract IWstETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bunkerModeSinceTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxShareRate","type":"uint256"},{"internalType":"uint256","name":"_maxTimestamp","type":"uint256"},{"internalType":"uint256","name":"_maxRequestsPerCall","type":"uint256"},{"components":[{"internalType":"uint256","name":"remainingEthBudget","type":"uint256"},{"internalType":"bool","name":"finished","type":"bool"},{"internalType":"uint256[36]","name":"batches","type":"uint256[36]"},{"internalType":"uint256","name":"batchesLength","type":"uint256"}],"internalType":"struct WithdrawalQueueBase.BatchesCalculationState","name":"_state","type":"tuple"}],"name":"calculateFinalizationBatches","outputs":[{"components":[{"internalType":"uint256","name":"remainingEthBudget","type":"uint256"},{"internalType":"bool","name":"finished","type":"bool"},{"internalType":"uint256[36]","name":"batches","type":"uint256[36]"},{"internalType":"uint256","name":"batchesLength","type":"uint256"}],"internalType":"struct WithdrawalQueueBase.BatchesCalculationState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"claimWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_hints","type":"uint256[]"}],"name":"claimWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_hints","type":"uint256[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimWithdrawalsTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lastRequestIdToBeFinalized","type":"uint256"},{"internalType":"uint256","name":"_maxShareRate","type":"uint256"}],"name":"finalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256","name":"_firstIndex","type":"uint256"},{"internalType":"uint256","name":"_lastIndex","type":"uint256"}],"name":"findCheckpointHints","outputs":[{"internalType":"uint256[]","name":"hintIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_hints","type":"uint256[]"}],"name":"getClaimableEther","outputs":[{"internalType":"uint256[]","name":"claimableEthValues","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContractVersion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastCheckpointIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastFinalizedRequestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastRequestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockedEtherAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNFTDescriptorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getResumeSinceTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getWithdrawalRequests","outputs":[{"internalType":"uint256[]","name":"requestsIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"}],"name":"getWithdrawalStatus","outputs":[{"components":[{"internalType":"uint256","name":"amountOfStETH","type":"uint256"},{"internalType":"uint256","name":"amountOfShares","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"isFinalized","type":"bool"},{"internalType":"bool","name":"isClaimed","type":"bool"}],"internalType":"struct WithdrawalQueueBase.WithdrawalRequestStatus[]","name":"statuses","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isBunkerModeActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_isBunkerModeNow","type":"bool"},{"internalType":"uint256","name":"_bunkerStartTimestamp","type":"uint256"},{"internalType":"uint256","name":"_currentReportTimestamp","type":"uint256"}],"name":"onOracleReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"pauseFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pauseUntilInclusive","type":"uint256"}],"name":"pauseUntil","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_batches","type":"uint256[]"},{"internalType":"uint256","name":"_maxShareRate","type":"uint256"}],"name":"prefinalize","outputs":[{"internalType":"uint256","name":"ethToLock","type":"uint256"},{"internalType":"uint256","name":"sharesToBurn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"}],"name":"requestWithdrawals","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct WithdrawalQueue.PermitInput","name":"_permit","type":"tuple"}],"name":"requestWithdrawalsWithPermit","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"}],"name":"requestWithdrawalsWstETH","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct WithdrawalQueue.PermitInput","name":"_permit","type":"tuple"}],"name":"requestWithdrawalsWstETHWithPermit","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_nftDescriptorAddress","type":"address"}],"name":"setNFTDescriptorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unfinalizedRequestNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unfinalizedStETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101006040523480156200001257600080fd5b506040516200628238038062006282833981016040819052620000359162000296565b82620000726000197f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a66200015360201b6200275b1790919060201c565b6001600160a01b03811660a08190526040805163183fc7c960e31b8152905163c1fe3e4891600480820192602092909190829003018186803b158015620000b857600080fd5b505afa158015620000cd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000f3919062000315565b6001600160a01b031660805250815115806200010e57508051155b156200012d5760405163388bc6b360e11b815260040160405180910390fd5b620001388262000157565b60c052620001468162000157565b60e0525062000399915050565b9055565b600080829050601f815111156200018e578260405163305a27a960e01b81526004016200018591906200033c565b60405180910390fd5b80516200019b8262000371565b179392505050565b6001600160a01b0381168114620001b957600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620001ef578181015183820152602001620001d5565b83811115620001ff576000848401525b50505050565b600082601f8301126200021757600080fd5b81516001600160401b0380821115620002345762000234620001bc565b604051601f8301601f19908116603f011681019082821181831017156200025f576200025f620001bc565b816040528381528660208588010111156200027957600080fd5b6200028c846020830160208901620001d2565b9695505050505050565b600080600060608486031215620002ac57600080fd5b8351620002b981620001a3565b60208501519093506001600160401b0380821115620002d757600080fd5b620002e58783880162000205565b93506040860151915080821115620002fc57600080fd5b506200030b8682870162000205565b9150509250925092565b6000602082840312156200032857600080fd5b81516200033581620001a3565b9392505050565b60208152600082518060208401526200035d816040850160208701620001d2565b601f01601f19169190910160400192915050565b8051602080830151919081101562000393576000198160200360031b1b821691505b50919050565b60805160a05160c05160e051615e7e6200040460003960006116b901526000610cec015260008181610b1b015281816114ae0152818161296d0152612a0b015260008181610b6c01528181611b3801528181612ab301528181613dab0152613e490152615e7e6000f3fe6080604052600436106103e45760003560e01c806391d1485411610208578063c4d66de811610118578063db2296cd116100ab578063e985e9c51161007a578063e985e9c514610bae578063eed53bf514610bce578063f3f449c714610bfb578063f6fa8a4714610c1b578063f844443614610c3057600080fd5b8063db2296cd14610b3d578063e00bfe5014610b5a578063e3afe0a314610b8e578063e7c0835d146108eb57600080fd5b8063d0fb84e8116100e7578063d0fb84e814610ab4578063d547741f14610ac9578063d668104214610ae9578063d9fb643a14610b0957600080fd5b8063c4d66de814610a34578063c87b56dd14610a54578063c97912d814610a74578063ca15c87314610a9457600080fd5b8063a52e9c9f1161019b578063b6013cef1161016a578063b6013cef1461098b578063b7bdf7481461099e578063b88d4fde146109d2578063b8c4b85a146109f2578063c2fc7aff14610a1f57600080fd5b8063a52e9c9f14610901578063abe9cfc814610936578063acf41e4d14610956578063b187bd261461097657600080fd5b80639b36be58116101d75780639b36be58146108a1578063a217fddf146108b6578063a22cb465146108cb578063a302ee38146108eb57600080fd5b806391d148541461082c57806392b18a471461084c57806395d89b411461086c57806396992fed1461088157600080fd5b806336568abe116103035780635e7eead911610296578063714c539811610265578063714c5398146107a25780637951b76f146107b75780637d031b65146107d75780638aa10435146107f75780639010d07c1461080c57600080fd5b80635e7eead91461072257806362abe3fa146107425780636352211e1461076257806370a082311461078257600080fd5b80634f069a13116102d25780634f069a13146106c3578063526eae3e146106d857806355f804b3146106ed578063589ff76c1461070d57600080fd5b806336568abe1461063a578063389ed2671461065a57806342842e0e1461068e57806346a086b4146106ae57600080fd5b806319c2b4c31161037b57806329fd065d1161034a57806329fd065d146105bc5780632b95b781146105d15780632de03aa1146105e65780632f2ff15d1461061a57600080fd5b806319c2b4c314610533578063220ca2f41461054857806323b872dd1461057c578063248a9ca31461059c57600080fd5b8063081812fc116103b7578063081812fc14610499578063095ea7b3146104d15780630d25a957146104f157806319aa62571461050657600080fd5b806301ffc9a7146103e9578063046f7da21461041e57806306fdde031461043557806307e2cea514610457575b600080fd5b3480156103f557600080fd5b5061040961040436600461502f565b610c50565b60405190151581526020015b60405180910390f35b34801561042a57600080fd5b50610433610cb1565b005b34801561044157600080fd5b5061044a610ce5565b60405161041591906150a4565b34801561046357600080fd5b5061048b7f68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef181565b604051908152602001610415565b3480156104a557600080fd5b506104b96104b43660046150b7565b610d15565b6040516001600160a01b039091168152602001610415565b3480156104dd57600080fd5b506104336104ec3660046150ec565b610d6e565b3480156104fd57600080fd5b5061048b606481565b34801561051257600080fd5b50610526610521366004615161565b610dfd565b60405161041591906151b4565b34801561053f57600080fd5b5061048b610ebf565b34801561055457600080fd5b5061048b7f485191a2ef18512555bd4426d18a716ce8e98c80ec2de16394dcf86d7d91bc8081565b34801561058857600080fd5b506104336105973660046151f8565b610ee9565b3480156105a857600080fd5b5061048b6105b73660046150b7565b610ef4565b3480156105c857600080fd5b5061048b602481565b3480156105dd57600080fd5b50610409610f16565b3480156105f257600080fd5b5061048b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b34801561062657600080fd5b50610433610635366004615234565b610f29565b34801561064657600080fd5b50610433610655366004615234565b610f46565b34801561066657600080fd5b5061048b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b34801561069a57600080fd5b506104336106a93660046151f8565b610fc4565b3480156106ba57600080fd5b506104b9610fdf565b3480156106cf57600080fd5b5061048b611009565b3480156106e457600080fd5b5061048b611033565b3480156106f957600080fd5b50610433610708366004615260565b61105d565b34801561071957600080fd5b5061048b6110f2565b34801561072e57600080fd5b5061043361073d3660046152d1565b61110a565b34801561074e57600080fd5b5061052661075d366004615351565b6111dd565b34801561076e57600080fd5b506104b961077d3660046150b7565b6112fb565b34801561078e57600080fd5b5061048b61079d3660046153a1565b611390565b3480156107ae57600080fd5b5061044a6113f1565b3480156107c357600080fd5b506105266107d23660046153bc565b6114a2565b3480156107e357600080fd5b506105266107f23660046153a1565b61155f565b34801561080357600080fd5b5061048b611590565b34801561081857600080fd5b506104b961082736600461542a565b6115ba565b34801561083857600080fd5b50610409610847366004615234565b6115e6565b34801561085857600080fd5b506104336108673660046153a1565b61161e565b34801561087857600080fd5b5061044a6116b2565b34801561088d57600080fd5b5061043361089c36600461545a565b6116dd565b3480156108ad57600080fd5b5061048b6117ff565b3480156108c257600080fd5b5061048b600081565b3480156108d757600080fd5b506104336108e636600461548f565b611817565b3480156108f757600080fd5b5061048b60001981565b34801561090d57600080fd5b5061092161091c3660046154c6565b611822565b60408051928352602083019190915201610415565b34801561094257600080fd5b506104336109513660046150b7565b611af8565b34801561096257600080fd5b506105266109713660046153bc565b611b2c565b34801561098257600080fd5b50610409611bde565b61043361099936600461542a565b611bfd565b3480156109aa57600080fd5b5061048b7fbe882725f03f148e7c5a5e63ec45f182f7dcdb6bb8b92311ade5a6d138e0ee0f81565b3480156109de57600080fd5b506104336109ed3660046155c9565b611c90565b3480156109fe57600080fd5b50610a12610a0d366004615673565b611ccf565b60405161041591906156b4565b348015610a2b57600080fd5b5061048b611d86565b348015610a4057600080fd5b50610433610a4f3660046153a1565b611da2565b348015610a6057600080fd5b5061044a610a6f3660046150b7565b611dd5565b348015610a8057600080fd5b50610526610a8f366004615739565b611ec6565b348015610aa057600080fd5b5061048b610aaf3660046150b7565b611f87565b348015610ac057600080fd5b5061048b611fab565b348015610ad557600080fd5b50610433610ae4366004615234565b61201d565b348015610af557600080fd5b50610526610b04366004615161565b61203a565b348015610b1557600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610b4957600080fd5b5061048b683635c9adc5dea0000081565b348015610b6657600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610b9a57600080fd5b50610433610ba9366004615739565b612115565b348015610bba57600080fd5b50610409610bc93660046157a4565b6121b4565b348015610bda57600080fd5b50610bee610be93660046157ce565b612201565b6040516104159190615893565b348015610c0757600080fd5b50610433610c163660046150b7565b6126d4565b348015610c2757600080fd5b5061048b612708565b348015610c3c57600080fd5b50610433610c4b3660046150b7565b612732565b60006001600160e01b031982166380ac58cd60e01b1480610c8157506001600160e01b03198216635b5e139f60e01b145b80610c9c57506001600160e01b03198216632483248360e11b145b80610cab5750610cab8261275f565b92915050565b610cdb7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c733612784565b610ce36127e8565b565b6060610d107f000000000000000000000000000000000000000000000000000000000000000061282c565b905090565b6000610d2082612861565b610d45576040516364b4f07960e11b8152600481018390526024015b60405180910390fd5b600080516020615e0983398151915260009283526020525060409020546001600160a01b031690565b6000610d79826112fb565b9050806001600160a01b0316836001600160a01b03161415610dae576040516349fa8bc360e11b815260040160405180910390fd5b336001600160a01b03821614801590610dce5750610dcc81336121b4565b155b15610dee5760405163106abbeb60e21b8152336004820152602401610d3c565b610df883836128a7565b505050565b6060610e07612922565b6001600160a01b038216610e19573391505b826001600160401b03811115610e3157610e31615511565b604051908082528060200260200182016040528015610e5a578160200160208202803683370190505b50905060005b83811015610eb757610e8a858583818110610e7d57610e7d6158e7565b9050602002013584612948565b828281518110610e9c57610e9c6158e7565b6020908102919091010152610eb081615913565b9050610e60565b509392505050565b6000610d107f8ee26abbbdf533de3953ccf2204279e845eecb5ab51f8398522746e4ea0680415490565b610df8838383612b58565b6000908152600080516020615da9833981519152602052604090206001015490565b6000600019610f236117ff565b10905090565b610f3282610ef4565b610f3c8133612784565b610df88383612dab565b6001600160a01b0381163314610fb65760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610d3c565b610fc08282612dda565b5050565b610df883838360405180602001604052806000815250611c90565b6000610d107f2a81836f39b1062c2144ef0b520c964f50c3af430524cc6f585ff0aa7dd48c725490565b6000610d107f992f2e0c24ce59a21f2dab8bba13b25c2f872129df7f4d45372155e717db0c485490565b6000610d107f9d8be19d6a54e40bd767aa61b0f462241f5562ef6967d7045485bccac825b2405490565b7fbe882725f03f148e7c5a5e63ec45f182f7dcdb6bb8b92311ade5a6d138e0ee0f6110888133612784565b6110b37f5ed9590a548c88ba856b4395a6a115927662fde6fc45814f1c0fa2b0786939438484614ef2565b507ff9c7803e94e0d3c02900d8a90893a6d5e90dd04d32a4cfe825520f82bf9f32f683836040516110e592919061592e565b60405180910390a1505050565b6000610d10600080516020615de98339815191525490565b6001600160a01b0381166111315760405163d27b444360e01b815260040160405180910390fd5b83821461115b5760405163098b37e560e31b81526004810185905260248101839052604401610d3c565b60005b848110156111d5576111a186868381811061117b5761117b6158e7565b90506020020135858584818110611194576111946158e7565b9050602002013584612e09565b6111c53360008888858181106111b9576111b96158e7565b90506020020135612fcb565b6111ce81615913565b905061115e565b505050505050565b6060836001600160401b038111156111f7576111f7615511565b604051908082528060200260200182016040528015611220578160200160208202803683370190505b5090506000805b858110156112f15781878783818110611242576112426158e7565b9050602002013510156112685760405163374e8bd160e01b815260040160405180910390fd5b61128b87878381811061127d5761127d6158e7565b905060200201358686613011565b83828151811061129d5761129d6158e7565b6020026020010181815250508281815181106112bb576112bb6158e7565b602002602001015194508686828181106112d7576112d76158e7565b905060200201359150806112ea90615913565b9050611227565b5050949350505050565b6000811580611310575061130d610ebf565b82115b15611331576040516364b4f07960e11b815260048101839052602401610d3c565b600061133b6131e2565b6000848152602091909152604090206001810154909150600160c81b900460ff161561137d5760405163f0e0cc2d60e01b815260048101849052602401610d3c565b600101546001600160a01b031692915050565b60006001600160a01b0382166113c35760405162793e5560e21b81526001600160a01b0383166004820152602401610d3c565b6001600160a01b0382166000908152600080516020615d6983398151915260205260409020610cab90613206565b60607f5ed9590a548c88ba856b4395a6a115927662fde6fc45814f1c0fa2b078693943805461141f9061595d565b80601f016020809104026020016040519081016040528092919081815260200182805461144b9061595d565b80156114985780601f1061146d57610100808354040283529160200191611498565b820191906000526020600020905b81548152906001019060200180831161147b57829003601f168201915b5050505050905090565b60606001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d505accf3330853560208701356114eb88880160408a01615992565b886060013589608001356040518863ffffffff1660e01b815260040161151797969594939291906159b5565b600060405180830381600087803b15801561153157600080fd5b505af1158015611545573d6000803e3d6000fd5b50505050611554858585610dfd565b90505b949350505050565b6001600160a01b0381166000908152600080516020615d6983398151915260205260409020606090610cab90613210565b6000610d107f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a65490565b6000828152600080516020615d89833981519152602052604081206115df908361321d565b9392505050565b6000918252600080516020615da9833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b7fbe882725f03f148e7c5a5e63ec45f182f7dcdb6bb8b92311ade5a6d138e0ee0f6116498133612784565b6116727f2a81836f39b1062c2144ef0b520c964f50c3af430524cc6f585ff0aa7dd48c72839055565b6040516001600160a01b03831681527f4ec04ac71c49eea0a94dc5967b493412a8cdb2934b367713019d3b110e9f0ba89060200160405180910390a15050565b6060610d107f000000000000000000000000000000000000000000000000000000000000000061282c565b6117077f68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef133612784565b428210611727576040516334819c0360e01b815260040160405180910390fd5b428110611747576040516334819c0360e01b815260040160405180910390fd5b61175081613229565b600061175a610f16565b9050801515841515146117f95783156117bc57611784600080516020615dc9833981519152849055565b6040518381527f47f03b07e5b5377f871539bb2942f5ecb72733be9fc9d55a17b6d6a05d4183459060200160405180910390a16117f9565b600019600080516020615dc9833981519152556040517fd1f8a2998c0caf73e09434aa93d273a599060d789407c6f70ccd4c9c9f32c8f490600090a15b50505050565b6000610d10600080516020615dc98339815191525490565b610fc0338383613252565b600080826118435760405163e4e9735760e01b815260040160405180910390fd5b83611861576040516312de1df360e01b815260040160405180910390fd5b611869611009565b8585600081811061187c5761187c6158e7565b90506020020135116118be578484600081811061189b5761189b6158e7565b905060200201356040516364b4f07960e11b8152600401610d3c91815260200190565b6118c6610ebf565b85856118d36001826159f6565b8181106118e2576118e26158e7565b90506020020135111561190b5784846118fc6001826159f6565b81811061189b5761189b6158e7565b600080611916611009565b905060006119226131e2565b60008381526020918252604090819020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a082015290505b86831015611aed5760008888858181106119c0576119c06158e7565b9050602002013590508281116119e95760405163337c4a7160e21b815260040160405180910390fd5b60006119f36131e2565b600083815260209182526040808220815160c08101835281546001600160801b038082168352600160801b9091041694810194909452600101546001600160a01b0381169184019190915264ffffffffff600160a01b82048116606085015260ff600160c81b83041615156080850152600160d01b9091041660a08301529091508080611a808685613312565b9250925092508a831115611ac0576b033b2e3c9fd0803ce8000000611aa58c83615a0d565b611aaf9190615a42565b611ab9908b615a56565b9950611acd565b611aca828b615a56565b99505b611ad7818a615a56565b9850506001909601955091935091506119a49050565b505050935093915050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d611b238133612784565b610fc08261337a565b60606001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d505accf333085356020870135611b7588880160408a01615992565b886060013589608001356040518863ffffffff1660e01b8152600401611ba197969594939291906159b5565b600060405180830381600087803b158015611bbb57600080fd5b505af1158015611bcf573d6000803e3d6000fd5b5050505061155485858561203a565b6000611bf6600080516020615de98339815191525490565b4210905090565b611c05612922565b611c2f7f485191a2ef18512555bd4426d18a716ce8e98c80ec2de16394dcf86d7d91bc8033612784565b6000611c39611009565b611c44906001615a56565b9050611c518334846133ce565b7f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c81611c7b610ebf565b604080519283526020830191909152016110e5565b611c9b848484612b58565b611ca78484848461369a565b6117f9576040516309f844e360e01b81526001600160a01b0384166004820152602401610d3c565b6060816001600160401b03811115611ce957611ce9615511565b604051908082528060200260200182016040528015611d2257816020015b611d0f614f76565b815260200190600190039081611d075790505b50905060005b82811015611d7f57611d51848483818110611d4557611d456158e7565b905060200201356137b0565b828281518110611d6357611d636158e7565b602002602001018190525080611d7890615913565b9050611d28565b5092915050565b6000611d90611009565b611d98610ebf565b610d1091906159f6565b6001600160a01b038116611dc95760405163016b8ae160e11b815260040160405180910390fd5b611dd2816139a2565b50565b6060611de082612861565b611e00576040516364b4f07960e11b815260048101839052602401610d3c565b6000611e2a7f2a81836f39b1062c2144ef0b520c964f50c3af430524cc6f585ff0aa7dd48c725490565b90506001600160a01b03811615611eb7576040516344a5a61760e11b8152600481018490526001600160a01b0382169063894b4c2e9060240160006040518083038186803b158015611e7b57600080fd5b505afa158015611e8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115df9190810190615a6e565b6115df83613a1d565b50919050565b6060836001600160401b03811115611ee057611ee0615511565b604051908082528060200260200182016040528015611f09578160200160208202803683370190505b50905060005b84811015611f7e57611f51868683818110611f2c57611f2c6158e7565b90506020020135858584818110611f4557611f456158e7565b90506020020135613ca2565b828281518110611f6357611f636158e7565b6020908102919091010152611f7781615913565b9050611f0f565b50949350505050565b6000818152600080516020615d8983398151915260205260408120610cab90613206565b6000611fb56131e2565b6000611fbf611009565b81526020810191909152604001600020546001600160801b0316611fe16131e2565b6000611feb610ebf565b815260208101919091526040016000205461200f91906001600160801b0316615ae4565b6001600160801b0316905090565b61202682610ef4565b6120308133612784565b610df88383612dda565b6060612044612922565b6001600160a01b038216612056573391505b826001600160401b0381111561206e5761206e615511565b604051908082528060200260200182016040528015612097578160200160208202803683370190505b50905060005b83811015610eb7576120c68585838181106120ba576120ba6158e7565b90506020020135613d34565b6120e88585838181106120db576120db6158e7565b9050602002013584613d86565b8282815181106120fa576120fa6158e7565b602090810291909101015261210e81615913565b905061209d565b82811461213f5760405163098b37e560e31b81526004810184905260248101829052604401610d3c565b60005b838110156121ad5761218585858381811061215f5761215f6158e7565b90506020020135848484818110612178576121786158e7565b9050602002013533612e09565b61219d3360008787858181106111b9576111b96158e7565b6121a681615913565b9050612142565b5050505050565b6001600160a01b0391821660009081527fe6a0e71d546599dab4b90490502c456cf7c806a5710690dde406c1a77d7f25e76020908152604080832093909416825291909152205460ff1690565b612209614fb9565b81602001518061221857508151155b156122365760405163baf3f0f760e01b815260040160405180910390fd5b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260008460600151600014156123295761227e611009565b612289906001615a56565b92506122936131e2565b60006122a06001866159f6565b81526020808201929092526040908101600020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a08201529150612495565b600085604001516001876060015161234191906159f6565b60248110612351576123516158e7565b60200201519050612363816001615a56565b935061236d6131e2565b60008281526020918252604090819020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a0820152925061248e6123f96131e2565b60006124066001856159f6565b81526020808201929092526040908101600020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a082015284613312565b5090925050505b60006124a18785615a56565b905060006124ad610ebf565b6124b8906001615a56565b90505b80851080156124c957508185105b156126b25760006124d86131e2565b60008781526020918252604090819020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b820481166060840181905260ff600160c81b84041615156080850152600160d01b9092041660a08301529091508a101561256957506126b2565b60008060006125788885613312565b9250925092508d8311156125aa576b033b2e3c9fd0803ce800000061259d8f83615a0d565b6125a79190615a42565b91505b8a518211156125bc57505050506126b2565b818b6000018181516125ce91906159f6565b90525060608b01511580159061262257508360a0015164ffffffffff168860a0015164ffffffffff16148061260e57508d871115801561260e57508d8311155b8061262257508d8711801561262257508d83115b1561265857888b6040015160018d6060015161263e91906159f6565b6024811061264e5761264e6158e7565b60200201526126a0565b60248b60600151141561266e57505050506126b2565b888b604001518c6060015160248110612689576126896158e7565b602002015260608b01805161269d90615913565b90525b505060019096019590945092506124bb565b808514806126bf57508185105b15156020880152509498975050505050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d6126ff8133612784565b610fc082613ee6565b6000610d107f0e27eaa2e71c8572ab988fef0b54cd45bbd1740de1e22343fb6cda7536edc12f5490565b61274f81612749836001612744611033565b613011565b33612e09565b611dd233600083612fcb565b9055565b60006001600160e01b03198216635a05180f60e01b1480610cab5750610cab82613f36565b61278e82826115e6565b610fc0576127a6816001600160a01b03166014613f6b565b6127b1836020613f6b565b6040516020016127c2929190615b0c565b60408051601f198184030181529082905262461bcd60e51b8252610d3c916004016150a4565b6127f0614106565b42600080516020615de9833981519152556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f990600090a1565b60408051602080825281830190925260609160ff84169160009180820181803683375050509182525060208101929092525090565b600080821180156128795750612875610ebf565b8211155b8015610cab57506128886131e2565b6000928352602052506040902060010154600160c81b900460ff161590565b6000818152600080516020615e098339815191526020526040902080546001600160a01b0319166001600160a01b03841690811790915581906128e9826112fb565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b61292a611bde565b15610ce357604051630286f07360e31b815260040160405180910390fd5b6040516323b872dd60e01b8152336004820152306024820152604481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd90606401602060405180830381600087803b1580156129b957600080fd5b505af11580156129cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f19190615b81565b50604051636f074d1f60e11b8152600481018490526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063de0e9a3e90602401602060405180830381600087803b158015612a5757600080fd5b505af1158015612a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8f9190615b9e565b9050612a9a81613d34565b604051631920845160e01b8152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063192084519060240160206040518083038186803b158015612afd57600080fd5b505afa158015612b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b359190615b9e565b9050612b4282828661412b565b9250612b5060008585612fcb565b505092915050565b6001600160a01b038216612b7f57604051633a954ecd60e21b815260040160405180910390fd5b826001600160a01b0316826001600160a01b03161415612bb2576040516352ce6f2160e01b815260040160405180910390fd5b801580612bc55750612bc2610ebf565b81115b15612be6576040516364b4f07960e11b815260048101829052602401610d3c565b6000612bf06131e2565b6000838152602091909152604090206001810154909150600160c81b900460ff1615612c325760405163f0e0cc2d60e01b815260048101839052602401610d3c565b60018101546001600160a01b03858116911614612c7b57600181015460405163c0eeaa6160e01b81526001600160a01b0380871660048301529091166024820152604401610d3c565b336001600160a01b038516811480612c985750612c9885826121b4565b80612cc957506000838152600080516020615e0983398151915260205260409020546001600160a01b038281169116145b612cf15760405163aee697e760e01b81526001600160a01b0382166004820152602401610d3c565b600080516020615e0983398151915260008481526020918252604080822080546001600160a01b03199081169091556001860180546001600160a01b038a81169190931617905588168252600080516020615d6983398151915290925220612d59908461439e565b612d6557612d65615bb7565b6001600160a01b0384166000908152600080516020615d6983398151915260205260409020612d9490846143aa565b612da057612da0615bb7565b6121ad858585612fcb565b612db582826143b6565b6000828152600080516020615d8983398151915260205260409020610df8908261442c565b612de48282614441565b6000828152600080516020615d8983398151915260205260409020610df890826144b5565b82612e2a576040516364b4f07960e11b815260048101849052602401610d3c565b612e32611009565b831115612e555760405163095ca04560e31b815260048101849052602401610d3c565b6000612e5f6131e2565b6000858152602091909152604090206001810154909150600160c81b900460ff1615612ea15760405163f0e0cc2d60e01b815260048101859052602401610d3c565b60018101546001600160a01b03163314612ee5576001810154604051631194af8760e11b81523360048201526001600160a01b039091166024820152604401610d3c565b60018101805460ff60c81b1916600160c81b179055612f3684612f13600080516020615d6983398151915290565b60018401546001600160a01b03166000908152602091909152604090209061439e565b612f4257612f42615bb7565b6000612f4f8286866144ca565b9050612f6c81612f5d612708565b612f6791906159f6565b614880565b612f7683826148a9565b826001600160a01b0316336001600160a01b0316867f6ad26c5e238e7d002799f9a5db07e81ef14e37386ae03496d7a7ef04713e145b84604051612fbc91815260200190565b60405180910390a45050505050565b80826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b60008315806130265750613023610ebf565b84115b15613047576040516364b4f07960e11b815260048101859052602401610d3c565b6000613051611033565b905083158061305f57508083115b1561308757604051637189425760e01b81526004810185905260248101849052604401610d3c565b80158061309a5750613097611009565b85115b806130a457508284115b156130b35760009150506115df565b6000838152600080516020615e298339815191526020526040902054851061312e57808314156130e657829150506115df565b600080516020615e298339815191526000613102856001615a56565b81526020019081526020016000206000015485101561312457829150506115df565b60009150506115df565b6000848152600080516020615e29833981519152602052604090205485101561315b5760009150506115df565b8360006131696001866159f6565b90505b818111156131d857600060026131828484615a56565b61318d906001615a56565b6131979190615a42565b6000818152600080516020615e29833981519152602052604090205490915088106131c4578092506131d2565b6131cf6001826159f6565b91505b5061316c565b5095945050505050565b7fe21b95c4eb1b99fd548b219e3b5c175a8efb31f910cb76456b20e14eba8cfe4390565b6000610cab825490565b606060006115df8361493e565b60006115df838361499a565b611dd27f6825d6bead7081b4d1ac062bbb771f0e4ade13182688453e79955a721d58c4dd829055565b816001600160a01b0316836001600160a01b031614156132855760405163b06307db60e01b815260040160405180910390fd5b6001600160a01b0383811660008181527fe6a0e71d546599dab4b90490502c456cf7c806a5710690dde406c1a77d7f25e76020908152604080832094871680845294825291829020805460ff1916861515908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050565b815181516000918291829161332691615ae4565b6001600160801b03169150846020015184602001516133459190615ae4565b6001600160801b03169050806133676b033b2e3c9fd0803ce800000084615a0d565b6133719190615a42565b92509250925092565b613382612922565b428110156133a3576040516339e2ec5360e11b815260040160405180910390fd5b600060001982146133c0576133b9826001615a56565b90506133c5565b506000195b610fc0816149c4565b6133d6610ebf565b8311156133f9576040516364b4f07960e11b815260048101849052602401610d3c565b6000613403611009565b9050808411613428576040516364b4f07960e11b815260048101859052602401610d3c565b60006134326131e2565b600083815260209182526040808220815160c08101835281546001600160801b038082168352600160801b9091041694810194909452600101546001600160a01b0381169184019190915264ffffffffff600160a01b82048116606085015260ff600160c81b83041615156080850152600160d01b9091041660a08301529091506134bb6131e2565b600087815260209182526040808220815160c08101835281546001600160801b03808216808452600160801b90920416958201959095526001909101546001600160a01b0381169282019290925264ffffffffff600160a01b83048116606083015260ff600160c81b84041615156080830152600160d01b90920490911660a08201528451909350909161354f9190615ae4565b9050806001600160801b031686111561358d5760405163252dfe8160e01b8152600481018790526001600160801b0382166024820152604401610d3c565b600061359a856001615a56565b905060006135a6611033565b90506040518060400160405280838152602001888152506135d2600080516020615e2983398151915290565b60006135df846001615a56565b8152602080820192909252604001600020825181559101516001918201556136119061360c908390615a56565b614a50565b6136278861361d612708565b612f679190615a56565b61363089614a79565b88827f197874c72af6a06fb0aa4fab45fd39c7cb61ac0992159872dc3295207da7e9eb8a886020015188602001516136689190615ae4565b604080519283526001600160801b039091166020830152429082015260600160405180910390a3505050505050505050565b60006001600160a01b0384163b156137a857604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906136de903390899088908890600401615bcd565b602060405180830381600087803b1580156136f857600080fd5b505af1925050508015613728575060408051601f3d908101601f1916820190925261372591810190615c0a565b60015b61378e573d808015613756576040519150601f19603f3d011682016040523d82523d6000602084013e61375b565b606091505b508051613786576040516309f844e360e01b81526001600160a01b0386166004820152602401610d3c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611557565b506001611557565b6137b8614f76565b8115806137cb57506137c8610ebf565b82115b156137ec576040516364b4f07960e11b815260048101839052602401610d3c565b60006137f66131e2565b600084815260209182526040808220815160c08101835281546001600160801b038082168352600160801b9091041694810194909452600101546001600160a01b0381169184019190915264ffffffffff600160a01b82048116606085015260ff600160c81b83041615156080850152600160d01b9091041660a083015290915061387f6131e2565b600061388c6001876159f6565b81526020808201929092526040908101600020815160c0808201845282546001600160801b038082168452600160801b90910416948201949094526001909101546001600160a01b0381168284015264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a082015281519283019091528051845191935082916139299190615ae4565b6001600160801b031681526020018260200151846020015161394b9190615ae4565b6001600160801b0316815260200183604001516001600160a01b03168152602001836060015164ffffffffff168152602001613985611009565b861115151581526020018360800151151581525092505050919050565b6139aa614aa2565b6139b5600019613ee6565b6139bf6001614bd8565b6139ca600082612dab565b600019600080516020615dc9833981519152556040516001600160a01b03821681527f20b34d2aaaf6acb4fbbc9c4846858bb824053ab11ff44a59dfba1e22ceb8a509906020015b60405180910390a150565b606060007f5ed9590a548c88ba856b4395a6a115927662fde6fc45814f1c0fa2b0786939438054613a4d9061595d565b80601f0160208091040260200160405190810160405280929190818152602001828054613a799061595d565b8015613ac65780601f10613a9b57610100808354040283529160200191613ac6565b820191906000526020600020905b815481529060010190602001808311613aa957829003601f168201915b50505050509050805160001415613aed575050604080516020810190915260008152919050565b600081604051806040016040528060018152602001602f60f81b815250613b1386614c07565b6040518060400160405280600b81526020016a3f7265717565737465643d60a81b815250613ba2613b426131e2565b6000613b4f60018c6159f6565b81526020810191909152604001600020546001600160801b0316613b716131e2565b60008b81526020919091526040902054613b9491906001600160801b0316615ae4565b6001600160801b0316614c07565b6040518060400160405280600c81526020016b26637265617465645f61743d60a01b815250613bf8613bd26131e2565b60008c81526020919091526040902060010154600160a01b900464ffffffffff16614c07565b604051602001613c0e9796959493929190615c27565b60405160208183030381529060405290506000613c29611009565b851180159150610eb757816040518060400160405280600b81526020016a2666696e616c697a65643d60a81b815250613c77613c7288613c6d8a6001612744611033565b613ca2565b614c07565b604051602001613c8993929190615cb9565b6040516020818303038152906040529150509392505050565b6000821580613cb75750613cb4610ebf565b83115b15613cd8576040516364b4f07960e11b815260048101849052602401610d3c565b613ce0611009565b831115613cef57506000610cab565b6000613cf96131e2565b6000858152602091909152604090206001810154909150600160c81b900460ff1615613d29576000915050610cab565b6115578185856144ca565b6064811015613d595760405163171370f960e31b815260048101829052602401610d3c565b683635c9adc5dea00000811115611dd257604051638ebfb78d60e01b815260048101829052602401610d3c565b6040516323b872dd60e01b8152336004820152306024820152604481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd90606401602060405180830381600087803b158015613df757600080fd5b505af1158015613e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e2f9190615b81565b50604051631920845160e01b8152600481018490526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063192084519060240160206040518083038186803b158015613e9357600080fd5b505afa158015613ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ecb9190615b9e565b9050613ed884828561412b565b9150611d7f60008484612fcb565b613eee612922565b80613f0c5760405163ad58bfc760e01b815260040160405180910390fd5b6000600019821415613f2157506000196133c5565b613f2b8242615a56565b9050610fc0816149c4565b60006001600160e01b03198216637965db0b60e01b1480610cab57506301ffc9a760e01b6001600160e01b0319831614610cab565b60606000613f7a836002615a0d565b613f85906002615a56565b6001600160401b03811115613f9c57613f9c615511565b6040519080825280601f01601f191660200182016040528015613fc6576020820181803683370190505b509050600360fc1b81600081518110613fe157613fe16158e7565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614010576140106158e7565b60200101906001600160f81b031916908160001a9053506000614034846002615a0d565b61403f906001615a56565b90505b60018111156140b7576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614073576140736158e7565b1a60f81b828281518110614089576140896158e7565b60200101906001600160f81b031916908160001a90535060049490941c936140b081615cfc565b9050614042565b5083156115df5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d3c565b61410e611bde565b610ce35760405163b047186b60e01b815260040160405180910390fd5b600080614136610ebf565b905060006141426131e2565b600083815260209182526040808220815160c08101835281546001600160801b038082168352600160801b909104169481018590526001909101546001600160a01b0381169282019290925264ffffffffff600160a01b83048116606083015260ff600160c81b84041615156080830152600160d01b90920490911660a08201529250906141d1908790615d13565b905060008783600001516141e59190615d13565b90506141f2846001615a56565b94506141fd85614d04565b60006040518060c00160405280836001600160801b03168152602001846001600160801b03168152602001886001600160a01b031681526020014264ffffffffff168152602001600015158152602001614255614d2d565b64ffffffffff1690529050806142696131e2565b6000888152602091825260408082208451858501516001600160801b03908116600160801b02911617815584820151600190910180546060870151608088015160a09098015164ffffffffff908116600160d01b0264ffffffffff60d01b19991515600160c81b029990991665ffffffffffff60c81b1991909216600160a01b026001600160c81b03199093166001600160a01b039586161792909217919091161795909517909455928a168152600080516020615d698339815191529091522061433490876143aa565b61434057614340615bb7565b604080516001600160801b03808c1682528a1660208201526001600160a01b03891691339189917ff0cb471f23fb74ea44b8252eb1881a2dca546288d9f6e90d1a0e82fe0ed342ab910160405180910390a450505050509392505050565b60006115df8383614d57565b60006115df8383614e4a565b6143c082826115e6565b610fc0576000828152600080516020615da9833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60006115df836001600160a01b038416614e4a565b61444b82826115e6565b15610fc0576000828152600080516020615da9833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006115df836001600160a01b038416614d57565b6000816144ed57604051636773bc7160e11b815260048101839052602401610d3c565b60006144f7611033565b90508083111561451d57604051636773bc7160e11b815260048101849052602401610d3c565b6000838152600080516020615e298339815191526020908152604091829020825180840190935280548084526001909101549183019190915285101561457957604051636773bc7160e11b815260048101859052602401610d3c565b818410156145f7576000600080516020615e29833981519152600061459f876001615a56565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050858160000151116145f557604051636773bc7160e11b815260048101869052602401610d3c565b505b60006146016131e2565b600061460e6001896159f6565b81526020019081526020016000206040518060c00160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160149054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681526020016001820160199054906101000a900460ff1615151515815260200160018201601a9054906101000a900464ffffffffff1664ffffffffff1664ffffffffff168152505090506000806000614837848b6040518060c00160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160149054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681526020016001820160199054906101000a900460ff1615151515815260200160018201601a9054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681525050613312565b9250925092508460200151831115614873576b033b2e3c9fd0803ce80000008560200151826148669190615a0d565b6148709190615a42565b91505b5098975050505050505050565b611dd27f0e27eaa2e71c8572ab988fef0b54cd45bbd1740de1e22343fb6cda7536edc12f829055565b804710156148ca57604051638a0d377960e01b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614917576040519150601f19603f3d011682016040523d82523d6000602084013e61491c565b606091505b5050905080610df857604051630f0b498d60e01b815260040160405180910390fd5b60608160000180548060200260200160405190810160405280929190818152602001828054801561498e57602002820191906000526020600020905b81548152602001906001019080831161497a575b50505050509050919050565b60008260000182815481106149b1576149b16158e7565b9060005260206000200154905092915050565b6149db600080516020615de9833981519152829055565b600019811415614a175760405160001981527f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e90602001613a12565b7f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e614a4242836159f6565b604051908152602001613a12565b611dd27f9d8be19d6a54e40bd767aa61b0f462241f5562ef6967d7045485bccac825b240829055565b611dd27f992f2e0c24ce59a21f2dab8bba13b25c2f872129df7f4d45372155e717db0c48829055565b6040805160c08101825260008082526020820181905291810182905264ffffffffff421660608201526001608082015260a0810191909152614ae26131e2565b6000808052602091825260408082208451858501516001600160801b03918216600160801b929091169190910217815584820151600190910180546060870151608088015160a0909801516001600160a01b039094166001600160c81b031990921691909117600160a01b64ffffffffff928316021765ffffffffffff60c81b1916600160c81b9715159790970264ffffffffff60d01b191696909617600160d01b969092169590950217909355825180840190935280835290820152600080516020615e298339815191526000614bb8611033565b815260208082019290925260400160002082518155910151600190910155565b614be0611590565b15614bfe5760405163184e52a160e21b815260040160405180910390fd5b611dd281614e99565b606081614c2b5750506040805180820190915260018152600360fc1b602082015290565b8160005b8115614c555780614c3f81615913565b9150614c4e9050600a83615a42565b9150614c2f565b6000816001600160401b03811115614c6f57614c6f615511565b6040519080825280601f01601f191660200182016040528015614c99576020820181803683370190505b5090505b841561155757614cae6001836159f6565b9150614cbb600a86615d3e565b614cc6906030615a56565b60f81b818381518110614cdb57614cdb6158e7565b60200101906001600160f81b031916908160001a905350614cfd600a86615a42565b9450614c9d565b611dd27f8ee26abbbdf533de3953ccf2204279e845eecb5ab51f8398522746e4ea068041829055565b6000610d107f6825d6bead7081b4d1ac062bbb771f0e4ade13182688453e79955a721d58c4dd5490565b60008181526001830160205260408120548015614e40576000614d7b6001836159f6565b8554909150600090614d8f906001906159f6565b9050818114614df4576000866000018281548110614daf57614daf6158e7565b9060005260206000200154905080876000018481548110614dd257614dd26158e7565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614e0557614e05615d52565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610cab565b6000915050610cab565b6000818152600183016020526040812054614e9157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610cab565b506000610cab565b614ec27f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6829055565b6040518181527ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb90602001613a12565b828054614efe9061595d565b90600052602060002090601f016020900481019282614f205760008555614f66565b82601f10614f395782800160ff19823516178555614f66565b82800160010185558215614f66579182015b82811115614f66578235825591602001919060010190614f4b565b50614f72929150614fe5565b5090565b6040518060c00160405280600081526020016000815260200160006001600160a01b03168152602001600081526020016000151581526020016000151581525090565b6040805160808101825260008082526020820152908101614fd8614ffa565b8152602001600081525090565b5b80821115614f725760008155600101614fe6565b6040518061048001604052806024906020820280368337509192915050565b6001600160e01b031981168114611dd257600080fd5b60006020828403121561504157600080fd5b81356115df81615019565b60005b8381101561506757818101518382015260200161504f565b838111156117f95750506000910152565b6000815180845261509081602086016020860161504c565b601f01601f19169290920160200192915050565b6020815260006115df6020830184615078565b6000602082840312156150c957600080fd5b5035919050565b80356001600160a01b03811681146150e757600080fd5b919050565b600080604083850312156150ff57600080fd5b615108836150d0565b946020939093013593505050565b60008083601f84011261512857600080fd5b5081356001600160401b0381111561513f57600080fd5b6020830191508360208260051b850101111561515a57600080fd5b9250929050565b60008060006040848603121561517657600080fd5b83356001600160401b0381111561518c57600080fd5b61519886828701615116565b90945092506151ab9050602085016150d0565b90509250925092565b6020808252825182820181905260009190848201906040850190845b818110156151ec578351835292840192918401916001016151d0565b50909695505050505050565b60008060006060848603121561520d57600080fd5b615216846150d0565b9250615224602085016150d0565b9150604084013590509250925092565b6000806040838503121561524757600080fd5b82359150615257602084016150d0565b90509250929050565b6000806020838503121561527357600080fd5b82356001600160401b038082111561528a57600080fd5b818501915085601f83011261529e57600080fd5b8135818111156152ad57600080fd5b8660208285010111156152bf57600080fd5b60209290920196919550909350505050565b6000806000806000606086880312156152e957600080fd5b85356001600160401b038082111561530057600080fd5b61530c89838a01615116565b9097509550602088013591508082111561532557600080fd5b5061533288828901615116565b90945092506153459050604087016150d0565b90509295509295909350565b6000806000806060858703121561536757600080fd5b84356001600160401b0381111561537d57600080fd5b61538987828801615116565b90989097506020870135966040013595509350505050565b6000602082840312156153b357600080fd5b6115df826150d0565b60008060008084860360e08112156153d357600080fd5b85356001600160401b038111156153e957600080fd5b6153f588828901615116565b90965094506154089050602087016150d0565b925060a0603f198201121561541c57600080fd5b509295919450926040019150565b6000806040838503121561543d57600080fd5b50508035926020909101359150565b8015158114611dd257600080fd5b60008060006060848603121561546f57600080fd5b833561547a8161544c565b95602085013595506040909401359392505050565b600080604083850312156154a257600080fd5b6154ab836150d0565b915060208301356154bb8161544c565b809150509250929050565b6000806000604084860312156154db57600080fd5b83356001600160401b038111156154f157600080fd5b6154fd86828701615116565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b038111828210171561554957615549615511565b60405290565b60405161048081016001600160401b038111828210171561554957615549615511565b604051601f8201601f191681016001600160401b038111828210171561559a5761559a615511565b604052919050565b60006001600160401b038211156155bb576155bb615511565b50601f01601f191660200190565b600080600080608085870312156155df57600080fd5b6155e8856150d0565b93506155f6602086016150d0565b92506040850135915060608501356001600160401b0381111561561857600080fd5b8501601f8101871361562957600080fd5b803561563c615637826155a2565b615572565b81815288602083850101111561565157600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6000806020838503121561568657600080fd5b82356001600160401b0381111561569c57600080fd5b6156a885828601615116565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b8281101561572c578151805185528681015187860152858101516001600160a01b0316868601526060808201519086015260808082015115159086015260a09081015115159085015260c090930192908501906001016156d1565b5091979650505050505050565b6000806000806040858703121561574f57600080fd5b84356001600160401b038082111561576657600080fd5b61577288838901615116565b9096509450602087013591508082111561578b57600080fd5b5061579887828801615116565b95989497509550505050565b600080604083850312156157b757600080fd5b6157c0836150d0565b9150615257602084016150d0565b6000806000808486036105408112156157e657600080fd5b853594506020808701359450604087013593506104e0605f198301121561580c57600080fd5b615814615527565b915060608701358252608087013561582b8161544c565b8282015260bf8701881361583e57600080fd5b61584661554f565b8061052089018a81111561585957600080fd5b60a08a015b81811015615875578035845292840192840161585e565b50604085019190915235606084015250949793965091945090925050565b815181526020808301511515818301526040808401516104e084019291840160005b60248110156158d2578251825291830191908301906001016158b5565b5050505060608301516104c083015292915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415615927576159276158fd565b5060010190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600181811c9082168061597157607f821691505b60208210811415611ec057634e487b7160e01b600052602260045260246000fd5b6000602082840312156159a457600080fd5b813560ff811681146115df57600080fd5b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600082821015615a0857615a086158fd565b500390565b6000816000190483118215151615615a2757615a276158fd565b500290565b634e487b7160e01b600052601260045260246000fd5b600082615a5157615a51615a2c565b500490565b60008219821115615a6957615a696158fd565b500190565b600060208284031215615a8057600080fd5b81516001600160401b03811115615a9657600080fd5b8201601f81018413615aa757600080fd5b8051615ab5615637826155a2565b818152856020838501011115615aca57600080fd5b615adb82602083016020860161504c565b95945050505050565b60006001600160801b0383811690831681811015615b0457615b046158fd565b039392505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615b4481601785016020880161504c565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615b7581602884016020880161504c565b01602801949350505050565b600060208284031215615b9357600080fd5b81516115df8161544c565b600060208284031215615bb057600080fd5b5051919050565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615c0090830184615078565b9695505050505050565b600060208284031215615c1c57600080fd5b81516115df81615019565b600088516020615c3a8285838e0161504c565b895191840191615c4d8184848e0161504c565b8951920191615c5f8184848d0161504c565b8851920191615c718184848c0161504c565b8751920191615c838184848b0161504c565b8651920191615c958184848a0161504c565b8551920191615ca7818484890161504c565b919091019a9950505050505050505050565b60008451615ccb81846020890161504c565b845190830190615cdf81836020890161504c565b8451910190615cf281836020880161504c565b0195945050505050565b600081615d0b57615d0b6158fd565b506000190190565b60006001600160801b03808316818516808303821115615d3557615d356158fd565b01949350505050565b600082615d4d57615d4d615a2c565b500690565b634e487b7160e01b600052603160045260246000fdfe4b9bfe0774f05ab288bd50bd23f74ae80a797f1d0c82d419d43ebda4fdc2fe1f8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe9a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d31450eb8d0693284079f6627b2c1c6bb2e076066e44df1b18ba6ea7cc507e9bcbe8b012900cb200ee5dfc3b895a32791b67d12891b09f117814f167a237783a02528f2b9d45274be04589d1a9e644321ee1435a867d38a1359d022af391cf7d75445f3cbbc114a35d080f2a1953516d74e74d5106860bc2317840ba265f03b51aa2646970667358221220d3998012b0b3706c6f122ee93e35f4fe6c7801cea03916532cbed1bc2a38b7bc64736f6c634300080900330000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000147374455448205769746864726177616c204e46540000000000000000000000000000000000000000000000000000000000000000000000000000000000000007756e737445544800000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106103e45760003560e01c806391d1485411610208578063c4d66de811610118578063db2296cd116100ab578063e985e9c51161007a578063e985e9c514610bae578063eed53bf514610bce578063f3f449c714610bfb578063f6fa8a4714610c1b578063f844443614610c3057600080fd5b8063db2296cd14610b3d578063e00bfe5014610b5a578063e3afe0a314610b8e578063e7c0835d146108eb57600080fd5b8063d0fb84e8116100e7578063d0fb84e814610ab4578063d547741f14610ac9578063d668104214610ae9578063d9fb643a14610b0957600080fd5b8063c4d66de814610a34578063c87b56dd14610a54578063c97912d814610a74578063ca15c87314610a9457600080fd5b8063a52e9c9f1161019b578063b6013cef1161016a578063b6013cef1461098b578063b7bdf7481461099e578063b88d4fde146109d2578063b8c4b85a146109f2578063c2fc7aff14610a1f57600080fd5b8063a52e9c9f14610901578063abe9cfc814610936578063acf41e4d14610956578063b187bd261461097657600080fd5b80639b36be58116101d75780639b36be58146108a1578063a217fddf146108b6578063a22cb465146108cb578063a302ee38146108eb57600080fd5b806391d148541461082c57806392b18a471461084c57806395d89b411461086c57806396992fed1461088157600080fd5b806336568abe116103035780635e7eead911610296578063714c539811610265578063714c5398146107a25780637951b76f146107b75780637d031b65146107d75780638aa10435146107f75780639010d07c1461080c57600080fd5b80635e7eead91461072257806362abe3fa146107425780636352211e1461076257806370a082311461078257600080fd5b80634f069a13116102d25780634f069a13146106c3578063526eae3e146106d857806355f804b3146106ed578063589ff76c1461070d57600080fd5b806336568abe1461063a578063389ed2671461065a57806342842e0e1461068e57806346a086b4146106ae57600080fd5b806319c2b4c31161037b57806329fd065d1161034a57806329fd065d146105bc5780632b95b781146105d15780632de03aa1146105e65780632f2ff15d1461061a57600080fd5b806319c2b4c314610533578063220ca2f41461054857806323b872dd1461057c578063248a9ca31461059c57600080fd5b8063081812fc116103b7578063081812fc14610499578063095ea7b3146104d15780630d25a957146104f157806319aa62571461050657600080fd5b806301ffc9a7146103e9578063046f7da21461041e57806306fdde031461043557806307e2cea514610457575b600080fd5b3480156103f557600080fd5b5061040961040436600461502f565b610c50565b60405190151581526020015b60405180910390f35b34801561042a57600080fd5b50610433610cb1565b005b34801561044157600080fd5b5061044a610ce5565b60405161041591906150a4565b34801561046357600080fd5b5061048b7f68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef181565b604051908152602001610415565b3480156104a557600080fd5b506104b96104b43660046150b7565b610d15565b6040516001600160a01b039091168152602001610415565b3480156104dd57600080fd5b506104336104ec3660046150ec565b610d6e565b3480156104fd57600080fd5b5061048b606481565b34801561051257600080fd5b50610526610521366004615161565b610dfd565b60405161041591906151b4565b34801561053f57600080fd5b5061048b610ebf565b34801561055457600080fd5b5061048b7f485191a2ef18512555bd4426d18a716ce8e98c80ec2de16394dcf86d7d91bc8081565b34801561058857600080fd5b506104336105973660046151f8565b610ee9565b3480156105a857600080fd5b5061048b6105b73660046150b7565b610ef4565b3480156105c857600080fd5b5061048b602481565b3480156105dd57600080fd5b50610409610f16565b3480156105f257600080fd5b5061048b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b34801561062657600080fd5b50610433610635366004615234565b610f29565b34801561064657600080fd5b50610433610655366004615234565b610f46565b34801561066657600080fd5b5061048b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b34801561069a57600080fd5b506104336106a93660046151f8565b610fc4565b3480156106ba57600080fd5b506104b9610fdf565b3480156106cf57600080fd5b5061048b611009565b3480156106e457600080fd5b5061048b611033565b3480156106f957600080fd5b50610433610708366004615260565b61105d565b34801561071957600080fd5b5061048b6110f2565b34801561072e57600080fd5b5061043361073d3660046152d1565b61110a565b34801561074e57600080fd5b5061052661075d366004615351565b6111dd565b34801561076e57600080fd5b506104b961077d3660046150b7565b6112fb565b34801561078e57600080fd5b5061048b61079d3660046153a1565b611390565b3480156107ae57600080fd5b5061044a6113f1565b3480156107c357600080fd5b506105266107d23660046153bc565b6114a2565b3480156107e357600080fd5b506105266107f23660046153a1565b61155f565b34801561080357600080fd5b5061048b611590565b34801561081857600080fd5b506104b961082736600461542a565b6115ba565b34801561083857600080fd5b50610409610847366004615234565b6115e6565b34801561085857600080fd5b506104336108673660046153a1565b61161e565b34801561087857600080fd5b5061044a6116b2565b34801561088d57600080fd5b5061043361089c36600461545a565b6116dd565b3480156108ad57600080fd5b5061048b6117ff565b3480156108c257600080fd5b5061048b600081565b3480156108d757600080fd5b506104336108e636600461548f565b611817565b3480156108f757600080fd5b5061048b60001981565b34801561090d57600080fd5b5061092161091c3660046154c6565b611822565b60408051928352602083019190915201610415565b34801561094257600080fd5b506104336109513660046150b7565b611af8565b34801561096257600080fd5b506105266109713660046153bc565b611b2c565b34801561098257600080fd5b50610409611bde565b61043361099936600461542a565b611bfd565b3480156109aa57600080fd5b5061048b7fbe882725f03f148e7c5a5e63ec45f182f7dcdb6bb8b92311ade5a6d138e0ee0f81565b3480156109de57600080fd5b506104336109ed3660046155c9565b611c90565b3480156109fe57600080fd5b50610a12610a0d366004615673565b611ccf565b60405161041591906156b4565b348015610a2b57600080fd5b5061048b611d86565b348015610a4057600080fd5b50610433610a4f3660046153a1565b611da2565b348015610a6057600080fd5b5061044a610a6f3660046150b7565b611dd5565b348015610a8057600080fd5b50610526610a8f366004615739565b611ec6565b348015610aa057600080fd5b5061048b610aaf3660046150b7565b611f87565b348015610ac057600080fd5b5061048b611fab565b348015610ad557600080fd5b50610433610ae4366004615234565b61201d565b348015610af557600080fd5b50610526610b04366004615161565b61203a565b348015610b1557600080fd5b506104b97f0000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d81565b348015610b4957600080fd5b5061048b683635c9adc5dea0000081565b348015610b6657600080fd5b506104b97f0000000000000000000000003f1c547b21f65e10480de3ad8e19faac46c9503481565b348015610b9a57600080fd5b50610433610ba9366004615739565b612115565b348015610bba57600080fd5b50610409610bc93660046157a4565b6121b4565b348015610bda57600080fd5b50610bee610be93660046157ce565b612201565b6040516104159190615893565b348015610c0757600080fd5b50610433610c163660046150b7565b6126d4565b348015610c2757600080fd5b5061048b612708565b348015610c3c57600080fd5b50610433610c4b3660046150b7565b612732565b60006001600160e01b031982166380ac58cd60e01b1480610c8157506001600160e01b03198216635b5e139f60e01b145b80610c9c57506001600160e01b03198216632483248360e11b145b80610cab5750610cab8261275f565b92915050565b610cdb7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c733612784565b610ce36127e8565b565b6060610d107f7374455448205769746864726177616c204e465400000000000000000000001461282c565b905090565b6000610d2082612861565b610d45576040516364b4f07960e11b8152600481018390526024015b60405180910390fd5b600080516020615e0983398151915260009283526020525060409020546001600160a01b031690565b6000610d79826112fb565b9050806001600160a01b0316836001600160a01b03161415610dae576040516349fa8bc360e11b815260040160405180910390fd5b336001600160a01b03821614801590610dce5750610dcc81336121b4565b155b15610dee5760405163106abbeb60e21b8152336004820152602401610d3c565b610df883836128a7565b505050565b6060610e07612922565b6001600160a01b038216610e19573391505b826001600160401b03811115610e3157610e31615511565b604051908082528060200260200182016040528015610e5a578160200160208202803683370190505b50905060005b83811015610eb757610e8a858583818110610e7d57610e7d6158e7565b9050602002013584612948565b828281518110610e9c57610e9c6158e7565b6020908102919091010152610eb081615913565b9050610e60565b509392505050565b6000610d107f8ee26abbbdf533de3953ccf2204279e845eecb5ab51f8398522746e4ea0680415490565b610df8838383612b58565b6000908152600080516020615da9833981519152602052604090206001015490565b6000600019610f236117ff565b10905090565b610f3282610ef4565b610f3c8133612784565b610df88383612dab565b6001600160a01b0381163314610fb65760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610d3c565b610fc08282612dda565b5050565b610df883838360405180602001604052806000815250611c90565b6000610d107f2a81836f39b1062c2144ef0b520c964f50c3af430524cc6f585ff0aa7dd48c725490565b6000610d107f992f2e0c24ce59a21f2dab8bba13b25c2f872129df7f4d45372155e717db0c485490565b6000610d107f9d8be19d6a54e40bd767aa61b0f462241f5562ef6967d7045485bccac825b2405490565b7fbe882725f03f148e7c5a5e63ec45f182f7dcdb6bb8b92311ade5a6d138e0ee0f6110888133612784565b6110b37f5ed9590a548c88ba856b4395a6a115927662fde6fc45814f1c0fa2b0786939438484614ef2565b507ff9c7803e94e0d3c02900d8a90893a6d5e90dd04d32a4cfe825520f82bf9f32f683836040516110e592919061592e565b60405180910390a1505050565b6000610d10600080516020615de98339815191525490565b6001600160a01b0381166111315760405163d27b444360e01b815260040160405180910390fd5b83821461115b5760405163098b37e560e31b81526004810185905260248101839052604401610d3c565b60005b848110156111d5576111a186868381811061117b5761117b6158e7565b90506020020135858584818110611194576111946158e7565b9050602002013584612e09565b6111c53360008888858181106111b9576111b96158e7565b90506020020135612fcb565b6111ce81615913565b905061115e565b505050505050565b6060836001600160401b038111156111f7576111f7615511565b604051908082528060200260200182016040528015611220578160200160208202803683370190505b5090506000805b858110156112f15781878783818110611242576112426158e7565b9050602002013510156112685760405163374e8bd160e01b815260040160405180910390fd5b61128b87878381811061127d5761127d6158e7565b905060200201358686613011565b83828151811061129d5761129d6158e7565b6020026020010181815250508281815181106112bb576112bb6158e7565b602002602001015194508686828181106112d7576112d76158e7565b905060200201359150806112ea90615913565b9050611227565b5050949350505050565b6000811580611310575061130d610ebf565b82115b15611331576040516364b4f07960e11b815260048101839052602401610d3c565b600061133b6131e2565b6000848152602091909152604090206001810154909150600160c81b900460ff161561137d5760405163f0e0cc2d60e01b815260048101849052602401610d3c565b600101546001600160a01b031692915050565b60006001600160a01b0382166113c35760405162793e5560e21b81526001600160a01b0383166004820152602401610d3c565b6001600160a01b0382166000908152600080516020615d6983398151915260205260409020610cab90613206565b60607f5ed9590a548c88ba856b4395a6a115927662fde6fc45814f1c0fa2b078693943805461141f9061595d565b80601f016020809104026020016040519081016040528092919081815260200182805461144b9061595d565b80156114985780601f1061146d57610100808354040283529160200191611498565b820191906000526020600020905b81548152906001019060200180831161147b57829003601f168201915b5050505050905090565b60606001600160a01b037f0000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d1663d505accf3330853560208701356114eb88880160408a01615992565b886060013589608001356040518863ffffffff1660e01b815260040161151797969594939291906159b5565b600060405180830381600087803b15801561153157600080fd5b505af1158015611545573d6000803e3d6000fd5b50505050611554858585610dfd565b90505b949350505050565b6001600160a01b0381166000908152600080516020615d6983398151915260205260409020606090610cab90613210565b6000610d107f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a65490565b6000828152600080516020615d89833981519152602052604081206115df908361321d565b9392505050565b6000918252600080516020615da9833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b7fbe882725f03f148e7c5a5e63ec45f182f7dcdb6bb8b92311ade5a6d138e0ee0f6116498133612784565b6116727f2a81836f39b1062c2144ef0b520c964f50c3af430524cc6f585ff0aa7dd48c72839055565b6040516001600160a01b03831681527f4ec04ac71c49eea0a94dc5967b493412a8cdb2934b367713019d3b110e9f0ba89060200160405180910390a15050565b6060610d107f756e73744554480000000000000000000000000000000000000000000000000761282c565b6117077f68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef133612784565b428210611727576040516334819c0360e01b815260040160405180910390fd5b428110611747576040516334819c0360e01b815260040160405180910390fd5b61175081613229565b600061175a610f16565b9050801515841515146117f95783156117bc57611784600080516020615dc9833981519152849055565b6040518381527f47f03b07e5b5377f871539bb2942f5ecb72733be9fc9d55a17b6d6a05d4183459060200160405180910390a16117f9565b600019600080516020615dc9833981519152556040517fd1f8a2998c0caf73e09434aa93d273a599060d789407c6f70ccd4c9c9f32c8f490600090a15b50505050565b6000610d10600080516020615dc98339815191525490565b610fc0338383613252565b600080826118435760405163e4e9735760e01b815260040160405180910390fd5b83611861576040516312de1df360e01b815260040160405180910390fd5b611869611009565b8585600081811061187c5761187c6158e7565b90506020020135116118be578484600081811061189b5761189b6158e7565b905060200201356040516364b4f07960e11b8152600401610d3c91815260200190565b6118c6610ebf565b85856118d36001826159f6565b8181106118e2576118e26158e7565b90506020020135111561190b5784846118fc6001826159f6565b81811061189b5761189b6158e7565b600080611916611009565b905060006119226131e2565b60008381526020918252604090819020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a082015290505b86831015611aed5760008888858181106119c0576119c06158e7565b9050602002013590508281116119e95760405163337c4a7160e21b815260040160405180910390fd5b60006119f36131e2565b600083815260209182526040808220815160c08101835281546001600160801b038082168352600160801b9091041694810194909452600101546001600160a01b0381169184019190915264ffffffffff600160a01b82048116606085015260ff600160c81b83041615156080850152600160d01b9091041660a08301529091508080611a808685613312565b9250925092508a831115611ac0576b033b2e3c9fd0803ce8000000611aa58c83615a0d565b611aaf9190615a42565b611ab9908b615a56565b9950611acd565b611aca828b615a56565b99505b611ad7818a615a56565b9850506001909601955091935091506119a49050565b505050935093915050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d611b238133612784565b610fc08261337a565b60606001600160a01b037f0000000000000000000000003f1c547b21f65e10480de3ad8e19faac46c950341663d505accf333085356020870135611b7588880160408a01615992565b886060013589608001356040518863ffffffff1660e01b8152600401611ba197969594939291906159b5565b600060405180830381600087803b158015611bbb57600080fd5b505af1158015611bcf573d6000803e3d6000fd5b5050505061155485858561203a565b6000611bf6600080516020615de98339815191525490565b4210905090565b611c05612922565b611c2f7f485191a2ef18512555bd4426d18a716ce8e98c80ec2de16394dcf86d7d91bc8033612784565b6000611c39611009565b611c44906001615a56565b9050611c518334846133ce565b7f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c81611c7b610ebf565b604080519283526020830191909152016110e5565b611c9b848484612b58565b611ca78484848461369a565b6117f9576040516309f844e360e01b81526001600160a01b0384166004820152602401610d3c565b6060816001600160401b03811115611ce957611ce9615511565b604051908082528060200260200182016040528015611d2257816020015b611d0f614f76565b815260200190600190039081611d075790505b50905060005b82811015611d7f57611d51848483818110611d4557611d456158e7565b905060200201356137b0565b828281518110611d6357611d636158e7565b602002602001018190525080611d7890615913565b9050611d28565b5092915050565b6000611d90611009565b611d98610ebf565b610d1091906159f6565b6001600160a01b038116611dc95760405163016b8ae160e11b815260040160405180910390fd5b611dd2816139a2565b50565b6060611de082612861565b611e00576040516364b4f07960e11b815260048101839052602401610d3c565b6000611e2a7f2a81836f39b1062c2144ef0b520c964f50c3af430524cc6f585ff0aa7dd48c725490565b90506001600160a01b03811615611eb7576040516344a5a61760e11b8152600481018490526001600160a01b0382169063894b4c2e9060240160006040518083038186803b158015611e7b57600080fd5b505afa158015611e8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115df9190810190615a6e565b6115df83613a1d565b50919050565b6060836001600160401b03811115611ee057611ee0615511565b604051908082528060200260200182016040528015611f09578160200160208202803683370190505b50905060005b84811015611f7e57611f51868683818110611f2c57611f2c6158e7565b90506020020135858584818110611f4557611f456158e7565b90506020020135613ca2565b828281518110611f6357611f636158e7565b6020908102919091010152611f7781615913565b9050611f0f565b50949350505050565b6000818152600080516020615d8983398151915260205260408120610cab90613206565b6000611fb56131e2565b6000611fbf611009565b81526020810191909152604001600020546001600160801b0316611fe16131e2565b6000611feb610ebf565b815260208101919091526040016000205461200f91906001600160801b0316615ae4565b6001600160801b0316905090565b61202682610ef4565b6120308133612784565b610df88383612dda565b6060612044612922565b6001600160a01b038216612056573391505b826001600160401b0381111561206e5761206e615511565b604051908082528060200260200182016040528015612097578160200160208202803683370190505b50905060005b83811015610eb7576120c68585838181106120ba576120ba6158e7565b90506020020135613d34565b6120e88585838181106120db576120db6158e7565b9050602002013584613d86565b8282815181106120fa576120fa6158e7565b602090810291909101015261210e81615913565b905061209d565b82811461213f5760405163098b37e560e31b81526004810184905260248101829052604401610d3c565b60005b838110156121ad5761218585858381811061215f5761215f6158e7565b90506020020135848484818110612178576121786158e7565b9050602002013533612e09565b61219d3360008787858181106111b9576111b96158e7565b6121a681615913565b9050612142565b5050505050565b6001600160a01b0391821660009081527fe6a0e71d546599dab4b90490502c456cf7c806a5710690dde406c1a77d7f25e76020908152604080832093909416825291909152205460ff1690565b612209614fb9565b81602001518061221857508151155b156122365760405163baf3f0f760e01b815260040160405180910390fd5b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260008460600151600014156123295761227e611009565b612289906001615a56565b92506122936131e2565b60006122a06001866159f6565b81526020808201929092526040908101600020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a08201529150612495565b600085604001516001876060015161234191906159f6565b60248110612351576123516158e7565b60200201519050612363816001615a56565b935061236d6131e2565b60008281526020918252604090819020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a0820152925061248e6123f96131e2565b60006124066001856159f6565b81526020808201929092526040908101600020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a082015284613312565b5090925050505b60006124a18785615a56565b905060006124ad610ebf565b6124b8906001615a56565b90505b80851080156124c957508185105b156126b25760006124d86131e2565b60008781526020918252604090819020815160c08101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160a01b0381169183019190915264ffffffffff600160a01b820481166060840181905260ff600160c81b84041615156080850152600160d01b9092041660a08301529091508a101561256957506126b2565b60008060006125788885613312565b9250925092508d8311156125aa576b033b2e3c9fd0803ce800000061259d8f83615a0d565b6125a79190615a42565b91505b8a518211156125bc57505050506126b2565b818b6000018181516125ce91906159f6565b90525060608b01511580159061262257508360a0015164ffffffffff168860a0015164ffffffffff16148061260e57508d871115801561260e57508d8311155b8061262257508d8711801561262257508d83115b1561265857888b6040015160018d6060015161263e91906159f6565b6024811061264e5761264e6158e7565b60200201526126a0565b60248b60600151141561266e57505050506126b2565b888b604001518c6060015160248110612689576126896158e7565b602002015260608b01805161269d90615913565b90525b505060019096019590945092506124bb565b808514806126bf57508185105b15156020880152509498975050505050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d6126ff8133612784565b610fc082613ee6565b6000610d107f0e27eaa2e71c8572ab988fef0b54cd45bbd1740de1e22343fb6cda7536edc12f5490565b61274f81612749836001612744611033565b613011565b33612e09565b611dd233600083612fcb565b9055565b60006001600160e01b03198216635a05180f60e01b1480610cab5750610cab82613f36565b61278e82826115e6565b610fc0576127a6816001600160a01b03166014613f6b565b6127b1836020613f6b565b6040516020016127c2929190615b0c565b60408051601f198184030181529082905262461bcd60e51b8252610d3c916004016150a4565b6127f0614106565b42600080516020615de9833981519152556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f990600090a1565b60408051602080825281830190925260609160ff84169160009180820181803683375050509182525060208101929092525090565b600080821180156128795750612875610ebf565b8211155b8015610cab57506128886131e2565b6000928352602052506040902060010154600160c81b900460ff161590565b6000818152600080516020615e098339815191526020526040902080546001600160a01b0319166001600160a01b03841690811790915581906128e9826112fb565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b61292a611bde565b15610ce357604051630286f07360e31b815260040160405180910390fd5b6040516323b872dd60e01b8152336004820152306024820152604481018390526000907f0000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d6001600160a01b0316906323b872dd90606401602060405180830381600087803b1580156129b957600080fd5b505af11580156129cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f19190615b81565b50604051636f074d1f60e11b8152600481018490526000907f0000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d6001600160a01b03169063de0e9a3e90602401602060405180830381600087803b158015612a5757600080fd5b505af1158015612a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8f9190615b9e565b9050612a9a81613d34565b604051631920845160e01b8152600481018290526000907f0000000000000000000000003f1c547b21f65e10480de3ad8e19faac46c950346001600160a01b03169063192084519060240160206040518083038186803b158015612afd57600080fd5b505afa158015612b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b359190615b9e565b9050612b4282828661412b565b9250612b5060008585612fcb565b505092915050565b6001600160a01b038216612b7f57604051633a954ecd60e21b815260040160405180910390fd5b826001600160a01b0316826001600160a01b03161415612bb2576040516352ce6f2160e01b815260040160405180910390fd5b801580612bc55750612bc2610ebf565b81115b15612be6576040516364b4f07960e11b815260048101829052602401610d3c565b6000612bf06131e2565b6000838152602091909152604090206001810154909150600160c81b900460ff1615612c325760405163f0e0cc2d60e01b815260048101839052602401610d3c565b60018101546001600160a01b03858116911614612c7b57600181015460405163c0eeaa6160e01b81526001600160a01b0380871660048301529091166024820152604401610d3c565b336001600160a01b038516811480612c985750612c9885826121b4565b80612cc957506000838152600080516020615e0983398151915260205260409020546001600160a01b038281169116145b612cf15760405163aee697e760e01b81526001600160a01b0382166004820152602401610d3c565b600080516020615e0983398151915260008481526020918252604080822080546001600160a01b03199081169091556001860180546001600160a01b038a81169190931617905588168252600080516020615d6983398151915290925220612d59908461439e565b612d6557612d65615bb7565b6001600160a01b0384166000908152600080516020615d6983398151915260205260409020612d9490846143aa565b612da057612da0615bb7565b6121ad858585612fcb565b612db582826143b6565b6000828152600080516020615d8983398151915260205260409020610df8908261442c565b612de48282614441565b6000828152600080516020615d8983398151915260205260409020610df890826144b5565b82612e2a576040516364b4f07960e11b815260048101849052602401610d3c565b612e32611009565b831115612e555760405163095ca04560e31b815260048101849052602401610d3c565b6000612e5f6131e2565b6000858152602091909152604090206001810154909150600160c81b900460ff1615612ea15760405163f0e0cc2d60e01b815260048101859052602401610d3c565b60018101546001600160a01b03163314612ee5576001810154604051631194af8760e11b81523360048201526001600160a01b039091166024820152604401610d3c565b60018101805460ff60c81b1916600160c81b179055612f3684612f13600080516020615d6983398151915290565b60018401546001600160a01b03166000908152602091909152604090209061439e565b612f4257612f42615bb7565b6000612f4f8286866144ca565b9050612f6c81612f5d612708565b612f6791906159f6565b614880565b612f7683826148a9565b826001600160a01b0316336001600160a01b0316867f6ad26c5e238e7d002799f9a5db07e81ef14e37386ae03496d7a7ef04713e145b84604051612fbc91815260200190565b60405180910390a45050505050565b80826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b60008315806130265750613023610ebf565b84115b15613047576040516364b4f07960e11b815260048101859052602401610d3c565b6000613051611033565b905083158061305f57508083115b1561308757604051637189425760e01b81526004810185905260248101849052604401610d3c565b80158061309a5750613097611009565b85115b806130a457508284115b156130b35760009150506115df565b6000838152600080516020615e298339815191526020526040902054851061312e57808314156130e657829150506115df565b600080516020615e298339815191526000613102856001615a56565b81526020019081526020016000206000015485101561312457829150506115df565b60009150506115df565b6000848152600080516020615e29833981519152602052604090205485101561315b5760009150506115df565b8360006131696001866159f6565b90505b818111156131d857600060026131828484615a56565b61318d906001615a56565b6131979190615a42565b6000818152600080516020615e29833981519152602052604090205490915088106131c4578092506131d2565b6131cf6001826159f6565b91505b5061316c565b5095945050505050565b7fe21b95c4eb1b99fd548b219e3b5c175a8efb31f910cb76456b20e14eba8cfe4390565b6000610cab825490565b606060006115df8361493e565b60006115df838361499a565b611dd27f6825d6bead7081b4d1ac062bbb771f0e4ade13182688453e79955a721d58c4dd829055565b816001600160a01b0316836001600160a01b031614156132855760405163b06307db60e01b815260040160405180910390fd5b6001600160a01b0383811660008181527fe6a0e71d546599dab4b90490502c456cf7c806a5710690dde406c1a77d7f25e76020908152604080832094871680845294825291829020805460ff1916861515908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050565b815181516000918291829161332691615ae4565b6001600160801b03169150846020015184602001516133459190615ae4565b6001600160801b03169050806133676b033b2e3c9fd0803ce800000084615a0d565b6133719190615a42565b92509250925092565b613382612922565b428110156133a3576040516339e2ec5360e11b815260040160405180910390fd5b600060001982146133c0576133b9826001615a56565b90506133c5565b506000195b610fc0816149c4565b6133d6610ebf565b8311156133f9576040516364b4f07960e11b815260048101849052602401610d3c565b6000613403611009565b9050808411613428576040516364b4f07960e11b815260048101859052602401610d3c565b60006134326131e2565b600083815260209182526040808220815160c08101835281546001600160801b038082168352600160801b9091041694810194909452600101546001600160a01b0381169184019190915264ffffffffff600160a01b82048116606085015260ff600160c81b83041615156080850152600160d01b9091041660a08301529091506134bb6131e2565b600087815260209182526040808220815160c08101835281546001600160801b03808216808452600160801b90920416958201959095526001909101546001600160a01b0381169282019290925264ffffffffff600160a01b83048116606083015260ff600160c81b84041615156080830152600160d01b90920490911660a08201528451909350909161354f9190615ae4565b9050806001600160801b031686111561358d5760405163252dfe8160e01b8152600481018790526001600160801b0382166024820152604401610d3c565b600061359a856001615a56565b905060006135a6611033565b90506040518060400160405280838152602001888152506135d2600080516020615e2983398151915290565b60006135df846001615a56565b8152602080820192909252604001600020825181559101516001918201556136119061360c908390615a56565b614a50565b6136278861361d612708565b612f679190615a56565b61363089614a79565b88827f197874c72af6a06fb0aa4fab45fd39c7cb61ac0992159872dc3295207da7e9eb8a886020015188602001516136689190615ae4565b604080519283526001600160801b039091166020830152429082015260600160405180910390a3505050505050505050565b60006001600160a01b0384163b156137a857604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906136de903390899088908890600401615bcd565b602060405180830381600087803b1580156136f857600080fd5b505af1925050508015613728575060408051601f3d908101601f1916820190925261372591810190615c0a565b60015b61378e573d808015613756576040519150601f19603f3d011682016040523d82523d6000602084013e61375b565b606091505b508051613786576040516309f844e360e01b81526001600160a01b0386166004820152602401610d3c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611557565b506001611557565b6137b8614f76565b8115806137cb57506137c8610ebf565b82115b156137ec576040516364b4f07960e11b815260048101839052602401610d3c565b60006137f66131e2565b600084815260209182526040808220815160c08101835281546001600160801b038082168352600160801b9091041694810194909452600101546001600160a01b0381169184019190915264ffffffffff600160a01b82048116606085015260ff600160c81b83041615156080850152600160d01b9091041660a083015290915061387f6131e2565b600061388c6001876159f6565b81526020808201929092526040908101600020815160c0808201845282546001600160801b038082168452600160801b90910416948201949094526001909101546001600160a01b0381168284015264ffffffffff600160a01b82048116606084015260ff600160c81b83041615156080840152600160d01b9091041660a082015281519283019091528051845191935082916139299190615ae4565b6001600160801b031681526020018260200151846020015161394b9190615ae4565b6001600160801b0316815260200183604001516001600160a01b03168152602001836060015164ffffffffff168152602001613985611009565b861115151581526020018360800151151581525092505050919050565b6139aa614aa2565b6139b5600019613ee6565b6139bf6001614bd8565b6139ca600082612dab565b600019600080516020615dc9833981519152556040516001600160a01b03821681527f20b34d2aaaf6acb4fbbc9c4846858bb824053ab11ff44a59dfba1e22ceb8a509906020015b60405180910390a150565b606060007f5ed9590a548c88ba856b4395a6a115927662fde6fc45814f1c0fa2b0786939438054613a4d9061595d565b80601f0160208091040260200160405190810160405280929190818152602001828054613a799061595d565b8015613ac65780601f10613a9b57610100808354040283529160200191613ac6565b820191906000526020600020905b815481529060010190602001808311613aa957829003601f168201915b50505050509050805160001415613aed575050604080516020810190915260008152919050565b600081604051806040016040528060018152602001602f60f81b815250613b1386614c07565b6040518060400160405280600b81526020016a3f7265717565737465643d60a81b815250613ba2613b426131e2565b6000613b4f60018c6159f6565b81526020810191909152604001600020546001600160801b0316613b716131e2565b60008b81526020919091526040902054613b9491906001600160801b0316615ae4565b6001600160801b0316614c07565b6040518060400160405280600c81526020016b26637265617465645f61743d60a01b815250613bf8613bd26131e2565b60008c81526020919091526040902060010154600160a01b900464ffffffffff16614c07565b604051602001613c0e9796959493929190615c27565b60405160208183030381529060405290506000613c29611009565b851180159150610eb757816040518060400160405280600b81526020016a2666696e616c697a65643d60a81b815250613c77613c7288613c6d8a6001612744611033565b613ca2565b614c07565b604051602001613c8993929190615cb9565b6040516020818303038152906040529150509392505050565b6000821580613cb75750613cb4610ebf565b83115b15613cd8576040516364b4f07960e11b815260048101849052602401610d3c565b613ce0611009565b831115613cef57506000610cab565b6000613cf96131e2565b6000858152602091909152604090206001810154909150600160c81b900460ff1615613d29576000915050610cab565b6115578185856144ca565b6064811015613d595760405163171370f960e31b815260048101829052602401610d3c565b683635c9adc5dea00000811115611dd257604051638ebfb78d60e01b815260048101829052602401610d3c565b6040516323b872dd60e01b8152336004820152306024820152604481018390526000907f0000000000000000000000003f1c547b21f65e10480de3ad8e19faac46c950346001600160a01b0316906323b872dd90606401602060405180830381600087803b158015613df757600080fd5b505af1158015613e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e2f9190615b81565b50604051631920845160e01b8152600481018490526000907f0000000000000000000000003f1c547b21f65e10480de3ad8e19faac46c950346001600160a01b03169063192084519060240160206040518083038186803b158015613e9357600080fd5b505afa158015613ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ecb9190615b9e565b9050613ed884828561412b565b9150611d7f60008484612fcb565b613eee612922565b80613f0c5760405163ad58bfc760e01b815260040160405180910390fd5b6000600019821415613f2157506000196133c5565b613f2b8242615a56565b9050610fc0816149c4565b60006001600160e01b03198216637965db0b60e01b1480610cab57506301ffc9a760e01b6001600160e01b0319831614610cab565b60606000613f7a836002615a0d565b613f85906002615a56565b6001600160401b03811115613f9c57613f9c615511565b6040519080825280601f01601f191660200182016040528015613fc6576020820181803683370190505b509050600360fc1b81600081518110613fe157613fe16158e7565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614010576140106158e7565b60200101906001600160f81b031916908160001a9053506000614034846002615a0d565b61403f906001615a56565b90505b60018111156140b7576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614073576140736158e7565b1a60f81b828281518110614089576140896158e7565b60200101906001600160f81b031916908160001a90535060049490941c936140b081615cfc565b9050614042565b5083156115df5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d3c565b61410e611bde565b610ce35760405163b047186b60e01b815260040160405180910390fd5b600080614136610ebf565b905060006141426131e2565b600083815260209182526040808220815160c08101835281546001600160801b038082168352600160801b909104169481018590526001909101546001600160a01b0381169282019290925264ffffffffff600160a01b83048116606083015260ff600160c81b84041615156080830152600160d01b90920490911660a08201529250906141d1908790615d13565b905060008783600001516141e59190615d13565b90506141f2846001615a56565b94506141fd85614d04565b60006040518060c00160405280836001600160801b03168152602001846001600160801b03168152602001886001600160a01b031681526020014264ffffffffff168152602001600015158152602001614255614d2d565b64ffffffffff1690529050806142696131e2565b6000888152602091825260408082208451858501516001600160801b03908116600160801b02911617815584820151600190910180546060870151608088015160a09098015164ffffffffff908116600160d01b0264ffffffffff60d01b19991515600160c81b029990991665ffffffffffff60c81b1991909216600160a01b026001600160c81b03199093166001600160a01b039586161792909217919091161795909517909455928a168152600080516020615d698339815191529091522061433490876143aa565b61434057614340615bb7565b604080516001600160801b03808c1682528a1660208201526001600160a01b03891691339189917ff0cb471f23fb74ea44b8252eb1881a2dca546288d9f6e90d1a0e82fe0ed342ab910160405180910390a450505050509392505050565b60006115df8383614d57565b60006115df8383614e4a565b6143c082826115e6565b610fc0576000828152600080516020615da9833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60006115df836001600160a01b038416614e4a565b61444b82826115e6565b15610fc0576000828152600080516020615da9833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006115df836001600160a01b038416614d57565b6000816144ed57604051636773bc7160e11b815260048101839052602401610d3c565b60006144f7611033565b90508083111561451d57604051636773bc7160e11b815260048101849052602401610d3c565b6000838152600080516020615e298339815191526020908152604091829020825180840190935280548084526001909101549183019190915285101561457957604051636773bc7160e11b815260048101859052602401610d3c565b818410156145f7576000600080516020615e29833981519152600061459f876001615a56565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050858160000151116145f557604051636773bc7160e11b815260048101869052602401610d3c565b505b60006146016131e2565b600061460e6001896159f6565b81526020019081526020016000206040518060c00160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160149054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681526020016001820160199054906101000a900460ff1615151515815260200160018201601a9054906101000a900464ffffffffff1664ffffffffff1664ffffffffff168152505090506000806000614837848b6040518060c00160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160149054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681526020016001820160199054906101000a900460ff1615151515815260200160018201601a9054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681525050613312565b9250925092508460200151831115614873576b033b2e3c9fd0803ce80000008560200151826148669190615a0d565b6148709190615a42565b91505b5098975050505050505050565b611dd27f0e27eaa2e71c8572ab988fef0b54cd45bbd1740de1e22343fb6cda7536edc12f829055565b804710156148ca57604051638a0d377960e01b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614917576040519150601f19603f3d011682016040523d82523d6000602084013e61491c565b606091505b5050905080610df857604051630f0b498d60e01b815260040160405180910390fd5b60608160000180548060200260200160405190810160405280929190818152602001828054801561498e57602002820191906000526020600020905b81548152602001906001019080831161497a575b50505050509050919050565b60008260000182815481106149b1576149b16158e7565b9060005260206000200154905092915050565b6149db600080516020615de9833981519152829055565b600019811415614a175760405160001981527f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e90602001613a12565b7f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e614a4242836159f6565b604051908152602001613a12565b611dd27f9d8be19d6a54e40bd767aa61b0f462241f5562ef6967d7045485bccac825b240829055565b611dd27f992f2e0c24ce59a21f2dab8bba13b25c2f872129df7f4d45372155e717db0c48829055565b6040805160c08101825260008082526020820181905291810182905264ffffffffff421660608201526001608082015260a0810191909152614ae26131e2565b6000808052602091825260408082208451858501516001600160801b03918216600160801b929091169190910217815584820151600190910180546060870151608088015160a0909801516001600160a01b039094166001600160c81b031990921691909117600160a01b64ffffffffff928316021765ffffffffffff60c81b1916600160c81b9715159790970264ffffffffff60d01b191696909617600160d01b969092169590950217909355825180840190935280835290820152600080516020615e298339815191526000614bb8611033565b815260208082019290925260400160002082518155910151600190910155565b614be0611590565b15614bfe5760405163184e52a160e21b815260040160405180910390fd5b611dd281614e99565b606081614c2b5750506040805180820190915260018152600360fc1b602082015290565b8160005b8115614c555780614c3f81615913565b9150614c4e9050600a83615a42565b9150614c2f565b6000816001600160401b03811115614c6f57614c6f615511565b6040519080825280601f01601f191660200182016040528015614c99576020820181803683370190505b5090505b841561155757614cae6001836159f6565b9150614cbb600a86615d3e565b614cc6906030615a56565b60f81b818381518110614cdb57614cdb6158e7565b60200101906001600160f81b031916908160001a905350614cfd600a86615a42565b9450614c9d565b611dd27f8ee26abbbdf533de3953ccf2204279e845eecb5ab51f8398522746e4ea068041829055565b6000610d107f6825d6bead7081b4d1ac062bbb771f0e4ade13182688453e79955a721d58c4dd5490565b60008181526001830160205260408120548015614e40576000614d7b6001836159f6565b8554909150600090614d8f906001906159f6565b9050818114614df4576000866000018281548110614daf57614daf6158e7565b9060005260206000200154905080876000018481548110614dd257614dd26158e7565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614e0557614e05615d52565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610cab565b6000915050610cab565b6000818152600183016020526040812054614e9157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610cab565b506000610cab565b614ec27f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6829055565b6040518181527ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb90602001613a12565b828054614efe9061595d565b90600052602060002090601f016020900481019282614f205760008555614f66565b82601f10614f395782800160ff19823516178555614f66565b82800160010185558215614f66579182015b82811115614f66578235825591602001919060010190614f4b565b50614f72929150614fe5565b5090565b6040518060c00160405280600081526020016000815260200160006001600160a01b03168152602001600081526020016000151581526020016000151581525090565b6040805160808101825260008082526020820152908101614fd8614ffa565b8152602001600081525090565b5b80821115614f725760008155600101614fe6565b6040518061048001604052806024906020820280368337509192915050565b6001600160e01b031981168114611dd257600080fd5b60006020828403121561504157600080fd5b81356115df81615019565b60005b8381101561506757818101518382015260200161504f565b838111156117f95750506000910152565b6000815180845261509081602086016020860161504c565b601f01601f19169290920160200192915050565b6020815260006115df6020830184615078565b6000602082840312156150c957600080fd5b5035919050565b80356001600160a01b03811681146150e757600080fd5b919050565b600080604083850312156150ff57600080fd5b615108836150d0565b946020939093013593505050565b60008083601f84011261512857600080fd5b5081356001600160401b0381111561513f57600080fd5b6020830191508360208260051b850101111561515a57600080fd5b9250929050565b60008060006040848603121561517657600080fd5b83356001600160401b0381111561518c57600080fd5b61519886828701615116565b90945092506151ab9050602085016150d0565b90509250925092565b6020808252825182820181905260009190848201906040850190845b818110156151ec578351835292840192918401916001016151d0565b50909695505050505050565b60008060006060848603121561520d57600080fd5b615216846150d0565b9250615224602085016150d0565b9150604084013590509250925092565b6000806040838503121561524757600080fd5b82359150615257602084016150d0565b90509250929050565b6000806020838503121561527357600080fd5b82356001600160401b038082111561528a57600080fd5b818501915085601f83011261529e57600080fd5b8135818111156152ad57600080fd5b8660208285010111156152bf57600080fd5b60209290920196919550909350505050565b6000806000806000606086880312156152e957600080fd5b85356001600160401b038082111561530057600080fd5b61530c89838a01615116565b9097509550602088013591508082111561532557600080fd5b5061533288828901615116565b90945092506153459050604087016150d0565b90509295509295909350565b6000806000806060858703121561536757600080fd5b84356001600160401b0381111561537d57600080fd5b61538987828801615116565b90989097506020870135966040013595509350505050565b6000602082840312156153b357600080fd5b6115df826150d0565b60008060008084860360e08112156153d357600080fd5b85356001600160401b038111156153e957600080fd5b6153f588828901615116565b90965094506154089050602087016150d0565b925060a0603f198201121561541c57600080fd5b509295919450926040019150565b6000806040838503121561543d57600080fd5b50508035926020909101359150565b8015158114611dd257600080fd5b60008060006060848603121561546f57600080fd5b833561547a8161544c565b95602085013595506040909401359392505050565b600080604083850312156154a257600080fd5b6154ab836150d0565b915060208301356154bb8161544c565b809150509250929050565b6000806000604084860312156154db57600080fd5b83356001600160401b038111156154f157600080fd5b6154fd86828701615116565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b038111828210171561554957615549615511565b60405290565b60405161048081016001600160401b038111828210171561554957615549615511565b604051601f8201601f191681016001600160401b038111828210171561559a5761559a615511565b604052919050565b60006001600160401b038211156155bb576155bb615511565b50601f01601f191660200190565b600080600080608085870312156155df57600080fd5b6155e8856150d0565b93506155f6602086016150d0565b92506040850135915060608501356001600160401b0381111561561857600080fd5b8501601f8101871361562957600080fd5b803561563c615637826155a2565b615572565b81815288602083850101111561565157600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6000806020838503121561568657600080fd5b82356001600160401b0381111561569c57600080fd5b6156a885828601615116565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b8281101561572c578151805185528681015187860152858101516001600160a01b0316868601526060808201519086015260808082015115159086015260a09081015115159085015260c090930192908501906001016156d1565b5091979650505050505050565b6000806000806040858703121561574f57600080fd5b84356001600160401b038082111561576657600080fd5b61577288838901615116565b9096509450602087013591508082111561578b57600080fd5b5061579887828801615116565b95989497509550505050565b600080604083850312156157b757600080fd5b6157c0836150d0565b9150615257602084016150d0565b6000806000808486036105408112156157e657600080fd5b853594506020808701359450604087013593506104e0605f198301121561580c57600080fd5b615814615527565b915060608701358252608087013561582b8161544c565b8282015260bf8701881361583e57600080fd5b61584661554f565b8061052089018a81111561585957600080fd5b60a08a015b81811015615875578035845292840192840161585e565b50604085019190915235606084015250949793965091945090925050565b815181526020808301511515818301526040808401516104e084019291840160005b60248110156158d2578251825291830191908301906001016158b5565b5050505060608301516104c083015292915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415615927576159276158fd565b5060010190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600181811c9082168061597157607f821691505b60208210811415611ec057634e487b7160e01b600052602260045260246000fd5b6000602082840312156159a457600080fd5b813560ff811681146115df57600080fd5b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600082821015615a0857615a086158fd565b500390565b6000816000190483118215151615615a2757615a276158fd565b500290565b634e487b7160e01b600052601260045260246000fd5b600082615a5157615a51615a2c565b500490565b60008219821115615a6957615a696158fd565b500190565b600060208284031215615a8057600080fd5b81516001600160401b03811115615a9657600080fd5b8201601f81018413615aa757600080fd5b8051615ab5615637826155a2565b818152856020838501011115615aca57600080fd5b615adb82602083016020860161504c565b95945050505050565b60006001600160801b0383811690831681811015615b0457615b046158fd565b039392505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615b4481601785016020880161504c565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615b7581602884016020880161504c565b01602801949350505050565b600060208284031215615b9357600080fd5b81516115df8161544c565b600060208284031215615bb057600080fd5b5051919050565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615c0090830184615078565b9695505050505050565b600060208284031215615c1c57600080fd5b81516115df81615019565b600088516020615c3a8285838e0161504c565b895191840191615c4d8184848e0161504c565b8951920191615c5f8184848d0161504c565b8851920191615c718184848c0161504c565b8751920191615c838184848b0161504c565b8651920191615c958184848a0161504c565b8551920191615ca7818484890161504c565b919091019a9950505050505050505050565b60008451615ccb81846020890161504c565b845190830190615cdf81836020890161504c565b8451910190615cf281836020880161504c565b0195945050505050565b600081615d0b57615d0b6158fd565b506000190190565b60006001600160801b03808316818516808303821115615d3557615d356158fd565b01949350505050565b600082615d4d57615d4d615a2c565b500690565b634e487b7160e01b600052603160045260246000fdfe4b9bfe0774f05ab288bd50bd23f74ae80a797f1d0c82d419d43ebda4fdc2fe1f8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe9a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d31450eb8d0693284079f6627b2c1c6bb2e076066e44df1b18ba6ea7cc507e9bcbe8b012900cb200ee5dfc3b895a32791b67d12891b09f117814f167a237783a02528f2b9d45274be04589d1a9e644321ee1435a867d38a1359d022af391cf7d75445f3cbbc114a35d080f2a1953516d74e74d5106860bc2317840ba265f03b51aa2646970667358221220d3998012b0b3706c6f122ee93e35f4fe6c7801cea03916532cbed1bc2a38b7bc64736f6c63430008090033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000147374455448205769746864726177616c204e46540000000000000000000000000000000000000000000000000000000000000000000000000000000000000007756e737445544800000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _wstETH (address): 0x8d09a4502Cc8Cf1547aD300E066060D043f6982D
Arg [1] : _name (string): stETH Withdrawal NFT
Arg [2] : _symbol (string): unstETH
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000008d09a4502cc8cf1547ad300e066060d043f6982d
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [4] : 7374455448205769746864726177616c204e4654000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [6] : 756e737445544800000000000000000000000000000000000000000000000000
Loading...
Loading
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.