Source Code
Overview
S Balance
0 S
Token Holdings
More Info
ContractCreator
TokenTracker
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
VotingEscrow
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 100000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.24; import { IERC721, IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IVeArtProxy } from "./interfaces/IVeArtProxy.sol"; import { IVotingEscrow } from "./interfaces/IVotingEscrow.sol"; /// @title Voting Escrow /// @notice veNFT implementation that escrows ERC-20 tokens in the form of an ERC-721 NFT /// @notice Votes have a weight depending on time, so that users are committed to the future of (whatever they are /// voting for) /// @author Modified from Thena (https://github.com/ThenafiBNB/THENA-Contracts/blob/main/contracts/VotingEscrow.sol) /// @author Modified from Solidly (https://github.com/solidlyexchange/solidly/blob/master/contracts/ve.sol) /// @author Modified from Curve (https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy) /// @author Modified from Nouns DAO /// (https://github.com/withtally/my-nft-dao-project/blob/main/contracts/ERC721Checkpointable.sol) /// @dev Vote weight decays linearly over time. Lock time cannot be more than `MAXTIME` (2 years). contract VotingEscrow is IERC721Metadata, IVotes { enum DepositType { DEPOSIT_FOR_TYPE, CREATE_LOCK_TYPE, INCREASE_LOCK_AMOUNT, INCREASE_UNLOCK_TIME, MERGE_TYPE, SPLIT_TYPE } struct LockedBalance { int128 amount; uint256 end; } struct Point { int128 bias; int128 slope; // # -dweight / dt uint256 ts; uint256 blk; // block } /* We cannot really do block numbers per se b/c slope is per time, not per block * and per block could be fairly bad b/c Ethereum changes blocktimes. * What we can do is to extrapolate ***At functions */ /// @notice A checkpoint for marking delegated tokenIds from a given timestamp struct Checkpoint { uint256 timestamp; uint256[] tokenIds; } /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit( address indexed provider, uint256 tokenId, uint256 value, uint256 indexed locktime, DepositType deposit_type, uint256 ts ); event Withdraw(address indexed provider, uint256 tokenId, uint256 value, uint256 ts); event Supply(uint256 prevSupply, uint256 supply); event VotingApproval(address indexed owner, address indexed operator, uint256 indexed tokenId); event VotingApprovalForAll(address indexed owner, address indexed operator, bool approved); /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ error NotToken(); error AlreadyAttached(); error NotApprovedOrOwner(); error NoLock(); error LockExpired(); error LockInFuture(); error LockTooLong(); error TooManyDelegates(); error InvalidSignature(); error InvalidNonce(); error SignatureExpired(); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ address public immutable token; address public voter; address public team; address public artProxy; mapping(uint256 => Point) public point_history; // epoch -> unsigned point /// @dev Mapping of interface id to bool about whether or not it's supported mapping(bytes4 => bool) internal supportedInterfaces; /// @dev ERC165 interface ID of ERC165 bytes4 internal constant ERC165_INTERFACE_ID = 0x01ffc9a7; /// @dev ERC165 interface ID of ERC721 bytes4 internal constant ERC721_INTERFACE_ID = 0x80ac58cd; /// @dev ERC165 interface ID of ERC721Metadata bytes4 internal constant ERC721_METADATA_INTERFACE_ID = 0x5b5e139f; /// @dev Current count of token uint256 internal tokenId; /// @notice Contract constructor /// @param token_addr `THENA` token address constructor(address token_addr, address art_proxy) { token = token_addr; team = msg.sender; artProxy = art_proxy; point_history[0].blk = block.number; point_history[0].ts = block.timestamp; supportedInterfaces[ERC165_INTERFACE_ID] = true; supportedInterfaces[ERC721_INTERFACE_ID] = true; supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true; // mint-ish emit Transfer(address(0), address(this), tokenId); // burn-ish emit Transfer(address(this), address(0), tokenId); } /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ /// @dev reentrancy guard uint8 internal constant _not_entered = 1; uint8 internal constant _entered = 2; uint8 internal _entered_state = 1; modifier nonreentrant() { require(_entered_state == _not_entered); _entered_state = _entered; _; _entered_state = _not_entered; } /*/////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public constant name = "Rings veUSD"; string public constant symbol = "veUSD"; string public constant version = "1.0.0"; uint8 public constant decimals = 6; function setTeam(address _team) external { require(msg.sender == team); team = _team; } function setArtProxy(address _proxy) external { require(msg.sender == team); artProxy = _proxy; } /// @dev Returns current token URI metadata /// @param _tokenId Token ID to fetch URI for. function tokenURI(uint256 _tokenId) external view returns (string memory) { if (idToOwner[_tokenId] == address(0)) { revert NotToken(); } LockedBalance memory _locked = locked[_tokenId]; return IVeArtProxy(artProxy)._tokenURI( _tokenId, _balanceOfNFT(_tokenId, block.timestamp), _locked.end, uint256(int256(_locked.amount)) ); } /*////////////////////////////////////////////////////////////// ERC721 BALANCE/OWNER STORAGE //////////////////////////////////////////////////////////////*/ /// @dev Mapping from NFT ID to the address that owns it. mapping(uint256 => address) internal idToOwner; /// @dev Mapping from owner address to count of his tokens. mapping(address => uint256) internal ownerToNFTokenCount; /// @dev Returns the address of the owner of the NFT. /// @param _tokenId The identifier for an NFT. function ownerOf(uint256 _tokenId) public view returns (address) { return idToOwner[_tokenId]; } /// @dev Returns the number of NFTs owned by `_owner`. /// Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid. /// @param _owner Address for whom to query the balance. function _balance(address _owner) internal view returns (uint256) { return ownerToNFTokenCount[_owner]; } /// @dev Returns the number of NFTs owned by `_owner`. /// Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid. /// @param _owner Address for whom to query the balance. function balanceOf(address _owner) external view returns (uint256) { return _balance(_owner); } /*////////////////////////////////////////////////////////////// VOTING APPROVAL STORAGE //////////////////////////////////////////////////////////////*/ /// @dev Mapping from NFT ID to voting approved address. mapping(uint256 => address) internal idToVotingApprovals; /// @dev Mapping from owner address to mapping of voting operator addresses. mapping(address => mapping(address => bool)) internal ownerToVotingOperators; /// @dev Get the voting approved address for a single NFT. /// @param _tokenId ID of the NFT to query the voting approval of. function getVotingApproved(uint256 _tokenId) external view returns (address) { return idToVotingApprovals[_tokenId]; } /// @dev Checks if `_operator` is a voting approved operator for `_owner`. /// @param _owner The address that owns the NFTs. /// @param _operator The address that acts on behalf of the owner for voting. function isVotingApprovedForAll(address _owner, address _operator) external view returns (bool) { return (ownerToVotingOperators[_owner])[_operator]; } /// @dev Returns whether the given voter can vote a given token ID /// @param _voter address of the voter to query /// @param _tokenId uint ID of the token to be transferred /// @return bool whether the msg.sender is approved for the given token ID, is an operator of the owner, or is the /// owner of the token function _isVotingApprovedOrOwner(address _voter, uint256 _tokenId) internal view returns (bool) { address owner = idToOwner[_tokenId]; bool voterIsOwner = owner == _voter; bool voterIsApproved = idToVotingApprovals[_tokenId] == _voter; bool voterIsApprovedForAll = (ownerToVotingOperators[owner])[_voter]; bool isApproved = idToApprovals[_tokenId] == _voter; bool _isApprovedForAll = (ownerToOperators[owner])[_voter]; return voterIsOwner || voterIsApproved || voterIsApprovedForAll || isApproved || _isApprovedForAll; } function isVotingApprovedOrOwner(address _voter, uint256 _tokenId) external view returns (bool) { return _isVotingApprovedOrOwner(_voter, _tokenId); } /*////////////////////////////////////////////////////////////// VOTING APPROVAL LOGIC //////////////////////////////////////////////////////////////*/ /// @dev Set or reaffirm the voting approved address for an NFT. The zero address indicates there is no voting /// approved address. /// Throws unless `msg.sender` is the current NFT owner, or an authorized voting operator of the current owner. /// Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP) /// Throws if `_approved` is the current owner. (NOTE: This is not written the EIP) /// @param _approved Address to be approved for the given NFT ID. /// @param _tokenId ID of the token to be approved. function approveVoting(address _approved, uint256 _tokenId) public { address owner = idToOwner[_tokenId]; // Throws if `_tokenId` is not a valid NFT require(owner != address(0)); // Throws if `_approved` is the current owner require(_approved != owner); // Check requirements bool senderIsOwner = (idToOwner[_tokenId] == msg.sender); bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender]; require(senderIsOwner || senderIsApprovedForAll); // Set the approval idToVotingApprovals[_tokenId] = _approved; emit VotingApproval(owner, _approved, _tokenId); } /// @dev Enables or disables voting approval for a third party ("operator") to manage all of /// `msg.sender`'s assets votes. It also emits the VotingApprovalForAll event. /// Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP) /// @notice This works even if sender doesn't own any tokens at the time. /// @param _operator Address to add to the set of authorized voting operators. /// @param _approved True if the voting operators is approved, false to revoke approval. function setVotingApprovalForAll(address _operator, bool _approved) external { // Throws if `_operator` is the `msg.sender` assert(_operator != msg.sender); ownerToVotingOperators[msg.sender][_operator] = _approved; emit VotingApprovalForAll(msg.sender, _operator, _approved); } /* TRANSFER FUNCTIONS */ /// @dev Clear an approval of a given address. Caller should check beforehand if the sender is the owner. function _clearVotingApproval(uint256 _tokenId) internal { if (idToVotingApprovals[_tokenId] != address(0)) { // Reset approvals idToVotingApprovals[_tokenId] = address(0); } } /*////////////////////////////////////////////////////////////// ERC721 APPROVAL STORAGE //////////////////////////////////////////////////////////////*/ /// @dev Mapping from NFT ID to approved address. mapping(uint256 => address) internal idToApprovals; /// @dev Mapping from owner address to mapping of operator addresses. mapping(address => mapping(address => bool)) internal ownerToOperators; mapping(uint256 => uint256) public ownership_change; /// @dev Get the approved address for a single NFT. /// @param _tokenId ID of the NFT to query the approval of. function getApproved(uint256 _tokenId) external view returns (address) { return idToApprovals[_tokenId]; } /// @dev Checks if `_operator` is an approved operator for `_owner`. /// @param _owner The address that owns the NFTs. /// @param _operator The address that acts on behalf of the owner. function isApprovedForAll(address _owner, address _operator) external view returns (bool) { return (ownerToOperators[_owner])[_operator]; } /*////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ /// @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address. /// Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner. /// Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP) /// Throws if `_approved` is the current owner. (NOTE: This is not written the EIP) /// @param _approved Address to be approved for the given NFT ID. /// @param _tokenId ID of the token to be approved. function approve(address _approved, uint256 _tokenId) public { address owner = idToOwner[_tokenId]; // Throws if `_tokenId` is not a valid NFT require(owner != address(0)); // Throws if `_approved` is the current owner require(_approved != owner); // Check requirements bool senderIsOwner = (idToOwner[_tokenId] == msg.sender); bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender]; require(senderIsOwner || senderIsApprovedForAll); // Set the approval idToApprovals[_tokenId] = _approved; emit Approval(owner, _approved, _tokenId); } /// @dev Enables or disables approval for a third party ("operator") to manage all of /// `msg.sender`'s assets. It also emits the ApprovalForAll event. /// Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP) /// @notice This works even if sender doesn't own any tokens at the time. /// @param _operator Address to add to the set of authorized operators. /// @param _approved True if the operators is approved, false to revoke approval. function setApprovalForAll(address _operator, bool _approved) external { // Throws if `_operator` is the `msg.sender` assert(_operator != msg.sender); ownerToOperators[msg.sender][_operator] = _approved; emit ApprovalForAll(msg.sender, _operator, _approved); } /* TRANSFER FUNCTIONS */ /// @dev Clear an approval of a given address /// Throws if `_owner` is not the current owner. function _clearApproval(address _owner, uint256 _tokenId) internal { // Throws if `_owner` is not the current owner assert(idToOwner[_tokenId] == _owner); if (idToApprovals[_tokenId] != address(0)) { // Reset approvals idToApprovals[_tokenId] = address(0); } } /// @dev Returns whether the given spender can transfer a given token ID /// @param _spender address of the spender to query /// @param _tokenId uint ID of the token to be transferred /// @return bool whether the msg.sender is approved for the given token ID, is an operator of the owner, or is the /// owner of the token function _isApprovedOrOwner(address _spender, uint256 _tokenId) internal view returns (bool) { address owner = idToOwner[_tokenId]; bool spenderIsOwner = owner == _spender; bool spenderIsApproved = _spender == idToApprovals[_tokenId]; bool spenderIsApprovedForAll = (ownerToOperators[owner])[_spender]; return spenderIsOwner || spenderIsApproved || spenderIsApprovedForAll; } function isApprovedOrOwner(address _spender, uint256 _tokenId) external view returns (bool) { return _isApprovedOrOwner(_spender, _tokenId); } /// @dev Exeute transfer of a NFT. /// Throws unless `msg.sender` is the current owner, an authorized operator, or the approved /// address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.) /// Throws if `_to` is the zero address. /// Throws if `_from` is not the current owner. /// Throws if `_tokenId` is not a valid NFT. function _transferFrom(address _from, address _to, uint256 _tokenId, address _sender) internal { if (attachments[_tokenId] != 0 || voted[_tokenId]) { revert AlreadyAttached(); } // Check requirements require(_isApprovedOrOwner(_sender, _tokenId)); // Clear approval. Throws if `_from` is not the current owner _clearApproval(_from, _tokenId); // Clear voting approval. _clearVotingApproval(_tokenId); // Remove NFT. Throws if `_tokenId` is not a valid NFT _removeTokenFrom(_from, _tokenId); // auto re-delegate _moveTokenDelegates(delegates(_from), delegates(_to), _tokenId); // Add NFT _addTokenTo(_to, _tokenId); // Set the block of ownership transfer (for Flash NFT protection) ownership_change[_tokenId] = block.number; // Log the transfer emit Transfer(_from, _to, _tokenId); } /// @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved address for this /// NFT. /// Throws if `_from` is not the current owner. /// Throws if `_to` is the zero address. /// Throws if `_tokenId` is not a valid NFT. /// @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else /// they maybe be permanently lost. /// @param _from The current owner of the NFT. /// @param _to The new owner. /// @param _tokenId The NFT to transfer. function transferFrom(address _from, address _to, uint256 _tokenId) external { _transferFrom(_from, _to, _tokenId, msg.sender); } /// @dev Transfers the ownership of an NFT from one address to another address. /// Throws unless `msg.sender` is the current owner, an authorized operator, or the /// approved address for this NFT. /// Throws if `_from` is not the current owner. /// Throws if `_to` is the zero address. /// Throws if `_tokenId` is not a valid NFT. /// If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if /// the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`. /// @param _from The current owner of the NFT. /// @param _to The new owner. /// @param _tokenId The NFT to transfer. function safeTransferFrom(address _from, address _to, uint256 _tokenId) external { safeTransferFrom(_from, _to, _tokenId, ""); } 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 Transfers the ownership of an NFT from one address to another address. /// Throws unless `msg.sender` is the current owner, an authorized operator, or the /// approved address for this NFT. /// Throws if `_from` is not the current owner. /// Throws if `_to` is the zero address. /// Throws if `_tokenId` is not a valid NFT. /// If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if /// the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`. /// @param _from The current owner of the NFT. /// @param _to The new owner. /// @param _tokenId The NFT to transfer. /// @param _data Additional data with no specified format, sent in call to `_to`. function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public { _transferFrom(_from, _to, _tokenId, msg.sender); if (_isContract(_to)) { // Throws if transfer destination is a contract which does not implement 'onERC721Received' try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4 response) { if (response != IERC721Receiver(_to).onERC721Received.selector) { revert("ERC721: ERC721Receiver rejected tokens"); } } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { assembly { revert(add(32, reason), mload(reason)) } } } } } /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ /// @dev Interface identification is specified in ERC-165. /// @param _interfaceID Id of the interface function supportsInterface(bytes4 _interfaceID) external view returns (bool) { return supportedInterfaces[_interfaceID]; } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ /// @dev Mapping from owner address to mapping of index to tokenIds mapping(address => mapping(uint256 => uint256)) internal ownerToNFTokenIdList; /// @dev Mapping from NFT ID to index of owner mapping(uint256 => uint256) internal tokenToOwnerIndex; /// @dev Get token by index function tokenOfOwnerByIndex(address _owner, uint256 _tokenIndex) external view returns (uint256) { return ownerToNFTokenIdList[_owner][_tokenIndex]; } /// @dev Add a NFT to an index mapping to a given address /// @param _to address of the receiver /// @param _tokenId uint ID Of the token to be added function _addTokenToOwnerList(address _to, uint256 _tokenId) internal { uint256 current_count = _balance(_to); ownerToNFTokenIdList[_to][current_count] = _tokenId; tokenToOwnerIndex[_tokenId] = current_count; } /// @dev Add a NFT to a given address /// Throws if `_tokenId` is owned by someone. function _addTokenTo(address _to, uint256 _tokenId) internal { // Throws if `_tokenId` is owned by someone assert(idToOwner[_tokenId] == address(0)); // Change the owner idToOwner[_tokenId] = _to; // Update owner token index tracking _addTokenToOwnerList(_to, _tokenId); // Change count tracking ownerToNFTokenCount[_to] += 1; } /// @dev Function to mint tokens /// Throws if `_to` is zero address. /// Throws if `_tokenId` is owned by someone. /// @param _to The address that will receive the minted tokens. /// @param _tokenId The token id to mint. /// @return A boolean that indicates if the operation was successful. function _mint(address _to, uint256 _tokenId) internal returns (bool) { // Throws if `_to` is zero address assert(_to != address(0)); // checkpoint for gov _moveTokenDelegates(address(0), delegates(_to), _tokenId); // Add NFT. Throws if `_tokenId` is owned by someone _addTokenTo(_to, _tokenId); emit Transfer(address(0), _to, _tokenId); return true; } /// @dev Remove a NFT from an index mapping to a given address /// @param _from address of the sender /// @param _tokenId uint ID Of the token to be removed function _removeTokenFromOwnerList(address _from, uint256 _tokenId) internal { // Delete uint256 current_count = _balance(_from) - 1; uint256 current_index = tokenToOwnerIndex[_tokenId]; if (current_count == current_index) { // update ownerToNFTokenIdList ownerToNFTokenIdList[_from][current_count] = 0; // update tokenToOwnerIndex tokenToOwnerIndex[_tokenId] = 0; } else { uint256 lastTokenId = ownerToNFTokenIdList[_from][current_count]; // Add // update ownerToNFTokenIdList ownerToNFTokenIdList[_from][current_index] = lastTokenId; // update tokenToOwnerIndex tokenToOwnerIndex[lastTokenId] = current_index; // Delete // update ownerToNFTokenIdList ownerToNFTokenIdList[_from][current_count] = 0; // update tokenToOwnerIndex tokenToOwnerIndex[_tokenId] = 0; } } /// @dev Remove a NFT from a given address /// Throws if `_from` is not the current owner. function _removeTokenFrom(address _from, uint256 _tokenId) internal { // Throws if `_from` is not the current owner assert(idToOwner[_tokenId] == _from); // Change the owner idToOwner[_tokenId] = address(0); // Update owner token index tracking _removeTokenFromOwnerList(_from, _tokenId); // Change count tracking ownerToNFTokenCount[_from] -= 1; } function _burn(uint256 _tokenId) internal { if (!_isApprovedOrOwner(msg.sender, _tokenId)) { revert NotApprovedOrOwner(); } address owner = ownerOf(_tokenId); // Clear approval approve(address(0), _tokenId); // Clear voting approval approveVoting(address(0), _tokenId); // checkpoint for gov _moveTokenDelegates(delegates(owner), address(0), _tokenId); // Remove token //_removeTokenFrom(msg.sender, _tokenId); _removeTokenFrom(owner, _tokenId); emit Transfer(owner, address(0), _tokenId); } /*////////////////////////////////////////////////////////////// ESCROW STORAGE //////////////////////////////////////////////////////////////*/ mapping(uint256 => uint256) public user_point_epoch; mapping(uint256 => Point[1_000_000_000]) public user_point_history; // user -> Point[user_epoch] mapping(uint256 => LockedBalance) public locked; uint256 public epoch; mapping(uint256 => int128) public slope_changes; // time -> signed slope change uint256 public supply; uint256 internal constant WEEK = 1 weeks; uint256 internal constant MAXTIME = 52 weeks; int128 internal constant iMAXTIME = 52 weeks; uint256 internal constant MULTIPLIER = 1 ether; /*////////////////////////////////////////////////////////////// ESCROW LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Get the most recently recorded rate of voting power decrease for `_tokenId` /// @param _tokenId token of the NFT /// @return Value of the slope function get_last_user_slope(uint256 _tokenId) external view returns (int128) { uint256 uepoch = user_point_epoch[_tokenId]; return user_point_history[_tokenId][uepoch].slope; } /// @notice Get the timestamp for checkpoint `_idx` for `_tokenId` /// @param _tokenId token of the NFT /// @param _idx User epoch number /// @return Epoch time of the checkpoint function user_point_history__ts(uint256 _tokenId, uint256 _idx) external view returns (uint256) { return user_point_history[_tokenId][_idx].ts; } /// @notice Get timestamp when `_tokenId`'s lock finishes /// @param _tokenId User NFT /// @return Epoch time of the lock end function locked__end(uint256 _tokenId) external view returns (uint256) { return locked[_tokenId].end; } /// @notice Record global and per-user data to checkpoint /// @param _tokenId NFT token ID. No user checkpoint if 0 /// @param old_locked Pevious locked amount / end lock time for the user /// @param new_locked New locked amount / end lock time for the user function _checkpoint(uint256 _tokenId, LockedBalance memory old_locked, LockedBalance memory new_locked) internal { Point memory u_old; Point memory u_new; int128 old_dslope = 0; int128 new_dslope = 0; uint256 _epoch = epoch; if (_tokenId != 0) { // Calculate slopes and biases // Kept at zero when they have to if (old_locked.end > block.timestamp && old_locked.amount > 0) { u_old.slope = old_locked.amount / iMAXTIME; u_old.bias = u_old.slope * int128(int256(old_locked.end - block.timestamp)); } if (new_locked.end > block.timestamp && new_locked.amount > 0) { u_new.slope = new_locked.amount / iMAXTIME; u_new.bias = u_new.slope * int128(int256(new_locked.end - block.timestamp)); } // Read values of scheduled changes in the slope // old_locked.end can be in the past and in the future // new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros old_dslope = slope_changes[old_locked.end]; if (new_locked.end != 0) { if (new_locked.end == old_locked.end) { new_dslope = old_dslope; } else { new_dslope = slope_changes[new_locked.end]; } } } Point memory last_point = Point({ bias: 0, slope: 0, ts: block.timestamp, blk: block.number }); if (_epoch > 0) { last_point = point_history[_epoch]; } uint256 last_checkpoint = last_point.ts; // initial_last_point is used for extrapolation to calculate block number // (approximately, for *At methods) and save them // as we cannot figure that out exactly from inside the contract Point memory initial_last_point = last_point; uint256 block_slope = 0; // dblock/dt if (block.timestamp > last_point.ts) { block_slope = (MULTIPLIER * (block.number - last_point.blk)) / (block.timestamp - last_point.ts); } // If last point is already recorded in this block, slope=0 // But that's ok b/c we know the block in such case // Go over weeks to fill history and calculate what the current point is { uint256 t_i = (last_checkpoint / WEEK) * WEEK; for (uint256 i = 0; i < 255; ++i) { // Hopefully it won't happen that this won't get used in 5 years! // If it does, users will be able to withdraw but vote weight will be broken t_i += WEEK; int128 d_slope = 0; if (t_i > block.timestamp) { t_i = block.timestamp; } else { d_slope = slope_changes[t_i]; } last_point.bias -= last_point.slope * int128(int256(t_i - last_checkpoint)); last_point.slope += d_slope; if (last_point.bias < 0) { // This can happen last_point.bias = 0; } if (last_point.slope < 0) { // This cannot happen - just in case last_point.slope = 0; } last_checkpoint = t_i; last_point.ts = t_i; last_point.blk = initial_last_point.blk + (block_slope * (t_i - initial_last_point.ts)) / MULTIPLIER; _epoch += 1; if (t_i == block.timestamp) { last_point.blk = block.number; break; } else { point_history[_epoch] = last_point; } } } epoch = _epoch; // Now point_history is filled until t=now if (_tokenId != 0) { // If last point was in this block, the slope change has been applied already // But in such case we have 0 slope(s) last_point.slope += (u_new.slope - u_old.slope); last_point.bias += (u_new.bias - u_old.bias); if (last_point.slope < 0) { last_point.slope = 0; } if (last_point.bias < 0) { last_point.bias = 0; } } // Record the changed point into history point_history[_epoch] = last_point; if (_tokenId != 0) { // Schedule the slope changes (slope is going down) // We subtract new_user_slope from [new_locked.end] // and add old_user_slope to [old_locked.end] if (old_locked.end > block.timestamp) { // old_dslope was <something> - u_old.slope, so we cancel that old_dslope += u_old.slope; if (new_locked.end == old_locked.end) { old_dslope -= u_new.slope; // It was a new deposit, not extension } slope_changes[old_locked.end] = old_dslope; } if (new_locked.end > block.timestamp) { if (new_locked.end > old_locked.end) { new_dslope -= u_new.slope; // old slope disappeared at this point slope_changes[new_locked.end] = new_dslope; } // else: we recorded it already in old_dslope } // Now handle user history uint256 user_epoch = user_point_epoch[_tokenId] + 1; user_point_epoch[_tokenId] = user_epoch; u_new.ts = block.timestamp; u_new.blk = block.number; user_point_history[_tokenId][user_epoch] = u_new; } } /// @notice Deposit and lock tokens for a user /// @param _tokenId NFT that holds lock /// @param _value Amount to deposit /// @param unlock_time New time when to unlock the tokens, or 0 if unchanged /// @param locked_balance Previous locked amount / timestamp /// @param deposit_type The type of deposit function _deposit_for( uint256 _tokenId, uint256 _value, uint256 unlock_time, LockedBalance memory locked_balance, DepositType deposit_type ) internal { LockedBalance memory _locked = locked_balance; uint256 supply_before = supply; supply = supply_before + _value; LockedBalance memory old_locked; (old_locked.amount, old_locked.end) = (_locked.amount, _locked.end); // Adding to existing lock, or if a lock is expired - creating a new one _locked.amount += int128(int256(_value)); if (unlock_time != 0) { _locked.end = unlock_time; } locked[_tokenId] = _locked; // Possibilities: // Both old_locked.end could be current or expired (>/< block.timestamp) // value == 0 (extend lock) or value > 0 (add to lock or extend lock) // _locked.end > block.timestamp (always) _checkpoint(_tokenId, old_locked, _locked); address from = msg.sender; if (_value != 0 && deposit_type != DepositType.MERGE_TYPE && deposit_type != DepositType.SPLIT_TYPE) { assert(IERC20(token).transferFrom(from, address(this), _value)); } emit Deposit(from, _tokenId, _value, _locked.end, deposit_type, block.timestamp); emit Supply(supply_before, supply_before + _value); } function block_number() external view returns (uint256) { return block.number; } /// @notice Record global data to checkpoint function checkpoint() external { _checkpoint(0, LockedBalance(0, 0), LockedBalance(0, 0)); } /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock /// @dev Anyone (even a smart contract) can deposit for someone else, but /// cannot extend their locktime and deposit for a brand new user /// @param _tokenId lock NFT /// @param _value Amount to add to user's lock function deposit_for(uint256 _tokenId, uint256 _value) external nonreentrant { LockedBalance memory _locked = locked[_tokenId]; require(_value > 0); // dev: need non-zero value if (_locked.amount == 0) { revert NoLock(); } if (_locked.end <= block.timestamp) { revert LockExpired(); } _deposit_for(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE); } /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration` /// @param _value Amount to deposit /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week) /// @param _to Address to deposit function _create_lock(uint256 _value, uint256 _lock_duration, address _to) internal returns (uint256) { uint256 unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks require(_value > 0); // dev: need non-zero value if (unlock_time <= block.timestamp) { revert LockInFuture(); } if (unlock_time > block.timestamp + MAXTIME) { revert LockTooLong(); } ++tokenId; uint256 _tokenId = tokenId; _mint(_to, _tokenId); _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.CREATE_LOCK_TYPE); return _tokenId; } /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lock_duration` /// @param _value Amount to deposit /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week) function create_lock(uint256 _value, uint256 _lock_duration) external nonreentrant returns (uint256) { return _create_lock(_value, _lock_duration, msg.sender); } /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration` /// @param _value Amount to deposit /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week) /// @param _to Address to deposit function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external nonreentrant returns (uint256) { return _create_lock(_value, _lock_duration, _to); } /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time /// @param _value Amount of tokens to deposit and add to the lock function increase_amount(uint256 _tokenId, uint256 _value) external nonreentrant { assert(_isApprovedOrOwner(msg.sender, _tokenId)); LockedBalance memory _locked = locked[_tokenId]; assert(_value > 0); // dev: need non-zero value if (_locked.amount == 0) { revert NoLock(); } if (_locked.end <= block.timestamp) { revert LockExpired(); } _deposit_for(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT); } /// @notice Extend the unlock time for `_tokenId` /// @param _lock_duration New number of seconds until tokens unlock function increase_unlock_time(uint256 _tokenId, uint256 _lock_duration) external nonreentrant { assert(_isApprovedOrOwner(msg.sender, _tokenId)); LockedBalance memory _locked = locked[_tokenId]; uint256 unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks if (_locked.end <= block.timestamp) { revert LockExpired(); } if (_locked.amount == 0) { revert NoLock(); } if (unlock_time <= _locked.end) { revert LockInFuture(); } if (unlock_time > block.timestamp + MAXTIME) { revert LockTooLong(); } _deposit_for(_tokenId, 0, unlock_time, _locked, DepositType.INCREASE_UNLOCK_TIME); } /// @notice Withdraw all tokens for `_tokenId` /// @dev Only possible if the lock has expired function withdraw(uint256 _tokenId) external nonreentrant { assert(_isApprovedOrOwner(msg.sender, _tokenId)); if (attachments[_tokenId] != 0 || voted[_tokenId]) { revert AlreadyAttached(); } LockedBalance memory _locked = locked[_tokenId]; if (block.timestamp < _locked.end) { revert LockExpired(); } uint256 value = uint256(int256(_locked.amount)); locked[_tokenId] = LockedBalance(0, 0); uint256 supply_before = supply; supply = supply_before - value; // old_locked can have either expired <= timestamp or zero end // _locked has only 0 end // Both can have >= 0 amount _checkpoint(_tokenId, _locked, LockedBalance(0, 0)); assert(IERC20(token).transfer(msg.sender, value)); // Burn the NFT _burn(_tokenId); emit Withdraw(msg.sender, _tokenId, value, block.timestamp); emit Supply(supply_before, supply_before - value); } /*/////////////////////////////////////////////////////////////// GAUGE VOTING STORAGE //////////////////////////////////////////////////////////////*/ // The following ERC20/minime-compatible methods are not real balanceOf and supply! // They measure the weights for the purpose of voting, so they don't represent // real coins. /// @notice Binary search to estimate timestamp for block number /// @param _block Block to find /// @param max_epoch Don't go beyond this epoch /// @return Approximate timestamp for block function _find_block_epoch(uint256 _block, uint256 max_epoch) internal view returns (uint256) { // Binary search uint256 _min = 0; uint256 _max = max_epoch; for (uint256 i = 0; i < 128; ++i) { // Will be always enough for 128-bit numbers if (_min >= _max) { break; } uint256 _mid = (_min + _max + 1) / 2; if (point_history[_mid].blk <= _block) { _min = _mid; } else { _max = _mid - 1; } } return _min; } /// @notice Get the current voting power for `_tokenId` /// @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility /// @param _tokenId NFT for lock /// @param _t Epoch time to return voting power at /// @return User voting power function _balanceOfNFT(uint256 _tokenId, uint256 _t) internal view returns (uint256) { uint256 _epoch = user_point_epoch[_tokenId]; if (_epoch == 0) { return 0; } else { Point memory last_point = user_point_history[_tokenId][_epoch]; last_point.bias -= last_point.slope * int128(int256(_t) - int256(last_point.ts)); if (last_point.bias < 0) { last_point.bias = 0; } return uint256(int256(last_point.bias)); } } function balanceOfNFT(uint256 _tokenId) external view returns (uint256) { if (ownership_change[_tokenId] == block.number) return 0; return _balanceOfNFT(_tokenId, block.timestamp); } function balanceOfNFTAt(uint256 _tokenId, uint256 _t) external view returns (uint256) { return _balanceOfNFT(_tokenId, _t); } /// @notice Measure voting power of `_tokenId` at block height `_block` /// @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime /// @param _tokenId User's wallet NFT /// @param _block Block to calculate the voting power at /// @return Voting power function _balanceOfAtNFT(uint256 _tokenId, uint256 _block) internal view returns (uint256) { // Copying and pasting totalSupply code because Vyper cannot pass by // reference yet assert(_block <= block.number); // Binary search uint256 _min = 0; uint256 _max = user_point_epoch[_tokenId]; for (uint256 i = 0; i < 128; ++i) { // Will be always enough for 128-bit numbers if (_min >= _max) { break; } uint256 _mid = (_min + _max + 1) / 2; if (user_point_history[_tokenId][_mid].blk <= _block) { _min = _mid; } else { _max = _mid - 1; } } Point memory upoint = user_point_history[_tokenId][_min]; uint256 max_epoch = epoch; uint256 _epoch = _find_block_epoch(_block, max_epoch); Point memory point_0 = point_history[_epoch]; uint256 d_block = 0; uint256 d_t = 0; if (_epoch < max_epoch) { Point memory point_1 = point_history[_epoch + 1]; d_block = point_1.blk - point_0.blk; d_t = point_1.ts - point_0.ts; } else { d_block = block.number - point_0.blk; d_t = block.timestamp - point_0.ts; } uint256 block_time = point_0.ts; if (d_block != 0) { block_time += (d_t * (_block - point_0.blk)) / d_block; } upoint.bias -= upoint.slope * int128(int256(block_time - upoint.ts)); if (upoint.bias >= 0) { return uint256(uint128(upoint.bias)); } else { return 0; } } function balanceOfAtNFT(uint256 _tokenId, uint256 _block) external view returns (uint256) { return _balanceOfAtNFT(_tokenId, _block); } /// @notice Calculate total voting power at some point in the past /// @param _block Block to calculate the total voting power at /// @return Total voting power at `_block` function totalSupplyAt(uint256 _block) external view returns (uint256) { assert(_block <= block.number); uint256 _epoch = epoch; uint256 target_epoch = _find_block_epoch(_block, _epoch); Point memory point = point_history[target_epoch]; uint256 dt = 0; if (target_epoch < _epoch) { Point memory point_next = point_history[target_epoch + 1]; if (point.blk != point_next.blk) { dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk); } } else { if (point.blk != block.number) { dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk); } } // Now dt contains info on how far are we beyond point return _supply_at(point, point.ts + dt); } /// @notice Calculate total voting power at some point in the past /// @param point The point (bias/slope) to start search from /// @param t Time to calculate the total voting power at /// @return Total voting power at that time function _supply_at(Point memory point, uint256 t) internal view returns (uint256) { Point memory last_point = point; uint256 t_i = (last_point.ts / WEEK) * WEEK; for (uint256 i = 0; i < 255; ++i) { t_i += WEEK; int128 d_slope = 0; if (t_i > t) { t_i = t; } else { d_slope = slope_changes[t_i]; } last_point.bias -= last_point.slope * int128(int256(t_i - last_point.ts)); if (t_i == t) { break; } last_point.slope += d_slope; last_point.ts = t_i; } if (last_point.bias < 0) { last_point.bias = 0; } return uint256(uint128(last_point.bias)); } function totalSupply() external view returns (uint256) { return totalSupplyAtT(block.timestamp); } /// @notice Calculate total voting power /// @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility /// @return Total voting power function totalSupplyAtT(uint256 t) public view returns (uint256) { uint256 _epoch = epoch; Point memory last_point = point_history[_epoch]; return _supply_at(last_point, t); } /*/////////////////////////////////////////////////////////////// GAUGE VOTING LOGIC //////////////////////////////////////////////////////////////*/ mapping(uint256 => uint256) public attachments; mapping(uint256 => bool) public voted; function setVoter(address _voter) external { require(msg.sender == team); voter = _voter; } function voting(uint256 _tokenId) external { require(msg.sender == voter); voted[_tokenId] = true; } function abstain(uint256 _tokenId) external { require(msg.sender == voter); voted[_tokenId] = false; } function attach(uint256 _tokenId) external { require(msg.sender == voter); attachments[_tokenId] = attachments[_tokenId] + 1; } function detach(uint256 _tokenId) external { require(msg.sender == voter); attachments[_tokenId] = attachments[_tokenId] - 1; } function merge(uint256 _from, uint256 _to) external { if (attachments[_from] != 0 || voted[_from]) { revert AlreadyAttached(); } require(_from != _to); require(_isApprovedOrOwner(msg.sender, _from)); require(_isApprovedOrOwner(msg.sender, _to)); LockedBalance memory _locked0 = locked[_from]; LockedBalance memory _locked1 = locked[_to]; uint256 value0 = uint256(int256(_locked0.amount)); uint256 end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end; locked[_from] = LockedBalance(0, 0); _checkpoint(_from, _locked0, LockedBalance(0, 0)); _burn(_from); _deposit_for(_to, value0, end, _locked1, DepositType.MERGE_TYPE); } /** * @notice split NFT into multiple * @param amounts % of split * @param _tokenId NFTs ID */ function split(uint256[] memory amounts, uint256 _tokenId) external { // check permission and vote if (attachments[_tokenId] != 0 || voted[_tokenId]) { revert AlreadyAttached(); } require(_isApprovedOrOwner(msg.sender, _tokenId)); // save old data and totalWeight address _to = idToOwner[_tokenId]; LockedBalance memory _locked = locked[_tokenId]; uint256 end = _locked.end; uint256 value = uint256(int256(_locked.amount)); require(value > 0); // dev: need non-zero value // reset supply, _deposit_for increase it supply = supply - value; uint256 i; uint256 totalWeight = 0; uint256 length = amounts.length; for (i = 0; i < length; ++i) { totalWeight += amounts[i]; } // remove old data locked[_tokenId] = LockedBalance(0, 0); _checkpoint(_tokenId, _locked, LockedBalance(0, 0)); _burn(_tokenId); // save end uint256 unlock_time = end; if (unlock_time <= block.timestamp) { revert LockInFuture(); } if (unlock_time > block.timestamp + MAXTIME) { revert LockTooLong(); } // mint uint256 _value = 0; for (i = 0; i < length; ++i) { ++tokenId; _tokenId = tokenId; _mint(_to, _tokenId); _value = value * amounts[i] / totalWeight; _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.SPLIT_TYPE); } } /*/////////////////////////////////////////////////////////////// DAO VOTING STORAGE //////////////////////////////////////////////////////////////*/ /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the delegation struct used by the contract bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); /// @notice A record of each accounts delegate mapping(address => address) private _delegates; uint256 public constant MAX_DELEGATES = 1024; // avoid too much gas /// @notice A record of delegated token checkpoints for each account, by index mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; /// @notice The number of checkpoints for each account mapping(address => uint32) public numCheckpoints; /// @notice A record of states for signing / validating signatures mapping(address => uint256) public nonces; /** * @notice Overrides the standard `Comp.sol` delegates mapping to return * the delegator's own address if they haven't delegated. * This avoids having to delegate to oneself. */ function delegates(address delegator) public view returns (address) { address current = _delegates[delegator]; return current == address(0) ? delegator : current; } /** * @notice Gets the current votes balance for `account` * @param account The address to get votes balance * @return The number of current votes for `account` */ function getVotes(address account) external view returns (uint256) { uint32 nCheckpoints = numCheckpoints[account]; if (nCheckpoints == 0) { return 0; } uint256[] storage _tokenIds = checkpoints[account][nCheckpoints - 1].tokenIds; uint256 votes = 0; uint256 length = _tokenIds.length; for (uint256 i = 0; i < length; ++i) { uint256 tId = _tokenIds[i]; votes = votes + _balanceOfNFT(tId, block.timestamp); } return votes; } function getPastVotesIndex(address account, uint256 timestamp) public view returns (uint32) { uint32 nCheckpoints = numCheckpoints[account]; if (nCheckpoints == 0) { return 0; } // First check most recent balance if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) { return (nCheckpoints - 1); } // Next check implicit zero balance if (checkpoints[account][0].timestamp > timestamp) { return 0; } uint32 lower = 0; uint32 upper = nCheckpoints - 1; while (upper > lower) { uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Checkpoint storage cp = checkpoints[account][center]; if (cp.timestamp == timestamp) { return center; } else if (cp.timestamp < timestamp) { lower = center; } else { upper = center - 1; } } return lower; } function getPastVotes(address account, uint256 timestamp) public view returns (uint256) { uint32 _checkIndex = getPastVotesIndex(account, timestamp); // Sum votes uint256[] storage _tokenIds = checkpoints[account][_checkIndex].tokenIds; uint256 votes = 0; uint256 length = _tokenIds.length; for (uint256 i = 0; i < length; ++i) { uint256 tId = _tokenIds[i]; // Use the provided input timestamp here to get the right decay votes = votes + _balanceOfNFT(tId, timestamp); } return votes; } function getPastTotalSupply(uint256 timestamp) external view returns (uint256) { return totalSupplyAtT(timestamp); } /*/////////////////////////////////////////////////////////////// DAO VOTING LOGIC //////////////////////////////////////////////////////////////*/ function _moveTokenDelegates(address srcRep, address dstRep, uint256 _tokenId) internal { if (srcRep != dstRep && _tokenId > 0) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; uint256[] storage srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].tokenIds : checkpoints[srcRep][0].tokenIds; uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep); uint256[] storage srcRepNew = checkpoints[srcRep][nextSrcRepNum].tokenIds; // All the same except _tokenId uint256 length = srcRepOld.length; for (uint256 i = 0; i < length; ++i) { uint256 tId = srcRepOld[i]; if (tId != _tokenId) { srcRepNew.push(tId); } } numCheckpoints[srcRep] = srcRepNum + 1; } if (dstRep != address(0)) { uint32 dstRepNum = numCheckpoints[dstRep]; uint256[] storage dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].tokenIds : checkpoints[dstRep][0].tokenIds; uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep); uint256[] storage dstRepNew = checkpoints[dstRep][nextDstRepNum].tokenIds; // All the same plus _tokenId if (dstRepOld.length + 1 > MAX_DELEGATES) { revert TooManyDelegates(); } uint256 length = dstRepOld.length; for (uint256 i = 0; i < length; ++i) { uint256 tId = dstRepOld[i]; dstRepNew.push(tId); } dstRepNew.push(_tokenId); numCheckpoints[dstRep] = dstRepNum + 1; } } } function _findWhatCheckpointToWrite(address account) internal view returns (uint32) { uint256 _timestamp = block.timestamp; uint32 _nCheckPoints = numCheckpoints[account]; if (_nCheckPoints > 0 && checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp) { return _nCheckPoints - 1; } else { return _nCheckPoints; } } function _moveAllDelegates(address owner, address srcRep, address dstRep) internal { // You can only redelegate what you own if (srcRep != dstRep) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; uint256[] storage srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].tokenIds : checkpoints[srcRep][0].tokenIds; uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep); uint256[] storage srcRepNew = checkpoints[srcRep][nextSrcRepNum].tokenIds; // All the same except what owner owns uint256 length = srcRepOld.length; for (uint256 i = 0; i < length; ++i) { uint256 tId = srcRepOld[i]; if (idToOwner[tId] != owner) { srcRepNew.push(tId); } } numCheckpoints[srcRep] = srcRepNum + 1; } if (dstRep != address(0)) { uint32 dstRepNum = numCheckpoints[dstRep]; uint256[] storage dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].tokenIds : checkpoints[dstRep][0].tokenIds; uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep); uint256[] storage dstRepNew = checkpoints[dstRep][nextDstRepNum].tokenIds; uint256 ownerTokenCount = ownerToNFTokenCount[owner]; if (dstRepOld.length + ownerTokenCount > MAX_DELEGATES) { revert TooManyDelegates(); } // All the same uint256 length = dstRepOld.length; for (uint256 i = 0; i < length; ++i) { uint256 tId = dstRepOld[i]; dstRepNew.push(tId); } // Plus all that's owned for (uint256 i = 0; i < ownerTokenCount; ++i) { uint256 tId = ownerToNFTokenIdList[owner][i]; dstRepNew.push(tId); } numCheckpoints[dstRep] = dstRepNum + 1; } } } function _delegate(address delegator, address delegatee) internal { /// @notice differs from `_delegate()` in `Comp.sol` to use `delegates` override method to simulate /// auto-delegation address currentDelegate = delegates(delegator); _delegates[delegator] = delegatee; emit DelegateChanged(delegator, currentDelegate, delegatee); _moveAllDelegates(delegator, currentDelegate, delegatee); } /** * @notice Delegate votes from `msg.sender` to `delegatee` * @param delegatee The address to delegate votes to */ function delegate(address delegatee) public { if (delegatee == address(0)) delegatee = msg.sender; return _delegate(msg.sender, delegatee); } function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) public { require(delegatee != msg.sender); require(delegatee != address(0)); bytes32 domainSeparator = keccak256( abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes(version)), block.chainid, address(this)) ); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); if (signatory == address(0)) { revert InvalidSignature(); } if (nonce != nonces[signatory]++) { revert InvalidNonce(); } if (block.timestamp > expiry) { revert SignatureExpired(); } return _delegate(signatory, delegatee); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.20; import {IERC721} from "../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 (last updated v5.0.0) (governance/utils/IVotes.sol) pragma solidity ^0.8.20; /** * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. */ interface IVotes { /** * @dev The signature used has expired. */ error VotesExpiredSignature(uint256 expiry); /** * @dev Emitted when an account changes their delegate. */ event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); /** * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units. */ event DelegateVotesChanged(address indexed delegate, uint256 previousVotes, uint256 newVotes); /** * @dev Returns the current amount of votes that `account` has. */ function getVotes(address account) external view returns (uint256); /** * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is * configured to use block numbers, this will return the value at the end of the corresponding block. */ function getPastVotes(address account, uint256 timepoint) external view returns (uint256); /** * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is * configured to use block numbers, this will return the value at the end of the corresponding block. * * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. * Votes that have not been delegated are still part of total supply, even though they would not participate in a * vote. */ function getPastTotalSupply(uint256 timepoint) external view returns (uint256); /** * @dev Returns the delegate that `account` has chosen. */ function delegates(address account) external view returns (address); /** * @dev Delegates votes from the sender to `delegatee`. */ function delegate(address delegatee) external; /** * @dev Delegates votes from signer to `delegatee`. */ function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @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 `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.24; interface IVeArtProxy { function _tokenURI(uint256 _tokenId, uint256 _balanceOf, uint256 _locked_end, uint256 _value) external pure returns (string memory output); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.24; interface IVotingEscrow { struct Point { int128 bias; int128 slope; // # -dweight / dt uint256 ts; uint256 blk; // block } struct LockedBalance { int128 amount; uint256 end; } function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external returns (uint256); function locked(uint256 id) external view returns (LockedBalance memory); function tokenOfOwnerByIndex(address _owner, uint256 _tokenIndex) external view returns (uint256); function token() external view returns (address); function team() external returns (address); function epoch() external view returns (uint256); function point_history(uint256 loc) external view returns (Point memory); function user_point_history(uint256 tokenId, uint256 loc) external view returns (Point memory); function user_point_epoch(uint256 tokenId) external view returns (uint256); function ownerOf(uint256) external view returns (address); function isApprovedOrOwner(address, uint256) external view returns (bool); function transferFrom(address, address, uint256) external; function isVotingApprovedOrOwner(address, uint256) external view returns (bool); function delegateVotingControl(address, uint256) external; function voted(uint256) external view returns (bool); function attachments(uint256) external view returns (uint256); function voting(uint256 tokenId) external; function abstain(uint256 tokenId) external; function attach(uint256 tokenId) external; function detach(uint256 tokenId) external; function checkpoint() external; function deposit_for(uint256 tokenId, uint256 value) external; function balanceOfNFT(uint256 _id) external view returns (uint256); function balanceOf(address _owner) external view returns (uint256); function totalSupply() external view returns (uint256); function supply() external view returns (uint256); function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../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`. * * 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; /** * @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 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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * 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 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 address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @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); }
{ "remappings": [ "ds-test/=lib/solmate/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "solmate/=lib/solmate/src/", "solady/=lib/solady/src/", "src/=src/", "test/=test/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 100000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": true, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"token_addr","type":"address"},{"internalType":"address","name":"art_proxy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyAttached","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"LockExpired","type":"error"},{"inputs":[],"name":"LockInFuture","type":"error"},{"inputs":[],"name":"LockTooLong","type":"error"},{"inputs":[],"name":"NoLock","type":"error"},{"inputs":[],"name":"NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"NotToken","type":"error"},{"inputs":[],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"TooManyDelegates","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"VotesExpiredSignature","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":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousVotes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newVotes","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":false,"internalType":"enum VotingEscrow.DepositType","name":"deposit_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"prevSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"}],"name":"Supply","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":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"VotingApproval","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":"VotingApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DELEGATES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approveVoting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"artProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"attachments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"balanceOfAtNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_t","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"block_number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"create_lock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"create_lock_for","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit_for","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotesIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getVotingApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"get_last_user_slope","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increase_amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"increase_unlock_time","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":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isVotingApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isVotingApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"locked__end","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ownership_change","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","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":"_tokenId","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":"address","name":"_proxy","type":"address"}],"name":"setArtProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_team","type":"address"}],"name":"setTeam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"}],"name":"setVoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setVotingApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slope_changes","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"split","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":[],"name":"team","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"user_point_history__ts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"voting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a034620001605762005da590601f38839003908101601f19168201906001600160401b0382118383101762000164578083916040958694855283398101031262000160576200005d6020620000558362000178565b920162000178565b60019060ff19928284600654161760065560805260018060a01b031990338284541617835560018060a01b03169060025416176002555f80526003602052825f2043600282015581429101556301ffc9a760e01b5f526004602052825f2081838254161790556380ac58cd60e01b5f52825f208183825416179055635b5e139f60e01b5f52825f20918254161790556005549051905f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef823083838180a430908280a4615c1790816200018e8239608051818181610e7301528181612b420152818161457501528181614874015281816149f001528181614b5e01526150820152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001605756fe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146104bf578063047fc9aa146104ba57806306fdde03146104b55780630758c7d8146104b0578063081812fc146104ab578063095cf5c6146104a6578063095ea7b3146104a15780630d6a20331461049c5780631376f3da1461049757806316b7a0111461049257806318160ddd1461048d5780631c984bc31461048857806320606b701461048357806323b872dd1461047e57806325a58b56146104795780632e1a7d4d146104745780632e720f7d1461046f5780632f745c591461046a578063313ce567146104655780633a46b1a81461046057806342842e0e1461045b578063430c208114610456578063461f711c1461045157806346c96aac1461044c5780634bc2a6571461044757806354fd4d50146104425780635594a0451461043d57806356afe74414610438578063587cde1e146104335780635c19a95c1461042e5780635f5b0c32146104295780636352211e1461042457806365fc38731461041f5780636f5488371461041a5780636fcfff451461041557806370a08231146104105780637116c60c146103f7578063711974841461040b5780637ecebe001461040657806385f2aef2146104015780638c2c9baf146103fc5780638e539e8c146103f75780638fbb38ff146103f2578063900cf0cf146103ed57806395d89b41146103e8578063981b24d0146103e3578063986b7d8a146103de5780639ab24eb0146103d9578063a183af52146103d4578063a22cb465146103cf578063a4d855df146103ca578063b29b8830146103c5578063b45a3c0e146103c0578063b88d4fde146103bb578063c1f0fb9f146103b6578063c2c4c5c1146103b1578063c3cda520146103ac578063c87b56dd146103a7578063d1c2babb146103a2578063d1febfb91461039d578063d2aef45714610398578063d4e54c3b14610393578063e0514aba1461038e578063e441135c14610389578063e7a324dc14610384578063e7e242d41461037f578063e9237e121461037a578063e985e9c514610375578063ee99fe2814610370578063ef39933d1461036b578063f1127ed814610366578063f8a0576314610361578063fbd3a29d1461035c578063fc0c546a146103575763fd4a77f114610352575f80fd5b612b66565b612b16565b612abb565b612a8e565b612a21565b61297d565b6128df565b612873565b612833565b612815565b6127db565b6127b1565b612790565b6126f9565b61264a565b6125eb565b6124c2565b612367565b612099565b611e07565b611da0565b611d18565b611ce2565b611c6a565b611b53565b611a74565b611977565b611954565b6118da565b611791565b611734565b611717565b6116e8565b611604565b6116c7565b611694565b61164f565b611622565b6115bf565b611574565b61154a565b6114dc565b61149c565b611480565b611439565b6113f8565b611369565b611336565b61131b565b61127c565b61124a565b611208565b6111d7565b61119e565b6110e9565b6110ce565b61107d565b611015565b610d27565b610d0d565b610cf2565b610c76565b610c3d565b610c1a565b610a9f565b610a33565b6109c1565b610871565b610809565b6107c9565b610794565b61071b565b61054c565b6104f2565b7fffffffff000000000000000000000000000000000000000000000000000000008116036104ee57565b5f80fd5b346104ee5760206003193601126104ee577fffffffff00000000000000000000000000000000000000000000000000000000600435610530816104c4565b165f526004602052602060ff60405f2054166040519015158152f35b346104ee575f6003193601126104ee576020601554604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176105b257604052565b610569565b6080810190811067ffffffffffffffff8211176105b257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105b257604052565b6040519061062182610596565b565b60405190610621826105b7565b67ffffffffffffffff81116105b257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6040519061067782610596565b600b82527f52696e67732076655553440000000000000000000000000000000000000000006020830152565b5f5b8381106106b45750505f910152565b81810151838201526020016106a5565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610700815180928187528780880191016106a3565b0116010190565b9060206107189281815201906106c4565b90565b346104ee575f6003193601126104ee5761074a61073661066a565b6040519182916020835260208301906106c4565b0390f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104ee57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104ee57565b346104ee5760406003193601126104ee5760206107bb6107b261074e565b60243590612c47565b63ffffffff60405191168152f35b346104ee5760206003193601126104ee576004355f52600b602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104ee5760206003193601126104ee5761082261074e565b6001549073ffffffffffffffffffffffffffffffffffffffff80831633036104ee577fffffffffffffffffffffffff000000000000000000000000000000000000000091169116176001555f80f35b346104ee5760406003193601126104ee5761088a61074e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee57808316928284146104ee57610941610993926108dd875f52600760205260405f2090565b5416331460ff6109323361090f8873ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541681156109b9575b50612de8565b610953855f52600b60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b90505f61093b565b346104ee5760206003193601126104ee576004355f526016602052602060405f2054604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b90633b9aca00811015610a2e5760030201905f90565b6109eb565b346104ee5760406003193601126104ee576024356004355f52601160205260405f2090633b9aca008110156104ee57610a6b91610a18565b508054600182015460029092015460408051600f84810b8252608094851d900b602082015290810193909352606083015290f35b346104ee5760406003193601126104ee5761074a610abb61074e565b602435805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f20541690831691610bc7610b7184841495600960205261090f86610b37610b1e60405f2073ffffffffffffffffffffffffffffffffffffffff90541690565b73ffffffffffffffffffffffffffffffffffffffff1690565b1496610ba1610b1e610b87610b78610b718761090f8d73ffffffffffffffffffffffffffffffffffffffff165f52600a60205260405f2090565b5460ff1690565b985f52600b60205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b149573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b928415610c12575b508315610c0a575b508215610c02575b508115610bfa575b5060405190151581529081906020820190565b90505f610be7565b91505f610bdf565b92505f610bd7565b93505f610bcf565b346104ee575f6003193601126104ee576020610c35426131dc565b604051908152f35b346104ee5760406003193601126104ee576004355f52601160205260206001610c6b60243560405f20610a18565b500154604051908152f35b346104ee575f6003193601126104ee5760206040517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a8668152f35b60031960609101126104ee5773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104ee579160243590811681036104ee579060443590565b346104ee57610d0b610d0336610cb0565b90339261369c565b005b346104ee575f6003193601126104ee576020604051438152f35b346104ee576020806003193601126104ee57600435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655610d84610d7f82336137c9565b612def565b610d96815f52601660205260405f2090565b5415801590610ffa575b610fd057610dbe610db9825f52601260205260405f2090565b612e23565b90828201514210610fa657610ddd610dd78351600f0b90565b600f0b90565b610e07610de8610614565b5f81525f86820152610e02845f52601260205260405f2090565b612e44565b610e3760155493610e20610e1b8487612ec4565b601555565b610e28610614565b905f82525f87830152846139f7565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018290529184836044815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1948515610fa1577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c95610f3394610ee9925f92610f74575b5050612def565b610ef281613f78565b6040805191825260208201839052429082015233907f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca9490606090a282612ec4565b60408051928352602083019190915290a1610d0b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b610f939250803d10610f9a575b610f8b81836105d3565b810190612ed1565b5f80610ee2565b503d610f81565b612ee6565b60046040517ff6fafba0000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3ca9d617000000000000000000000000000000000000000000000000000000008152fd5b50611010610b71825f52601760205260405f2090565b610da0565b346104ee5760206003193601126104ee5761102e61074e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104ee57167fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002555f80f35b346104ee5760406003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff6110ab61074e565b165f52600e60205260405f206024355f52602052602060405f2054604051908152f35b346104ee575f6003193601126104ee57602060405160068152f35b346104ee5760406003193601126104ee5761110261074e565b6024359073ffffffffffffffffffffffffffffffffffffffff6111258383612c47565b91165f526019602052600161114c819260405f209063ffffffff165f5260205260405f2090565b01915f9183545f925b81841061116757604051858152602090f35b90919293611185826111798789612ef1565b90549060031b1c6140e9565b810180911161119957938301929190611155565b612bd0565b346104ee576111ac36610cb0565b60405191602083019383851067ffffffffffffffff8611176105b257610d0b946040525f8452613330565b346104ee5760406003193601126104ee5760206111fe6111f561074e565b602435906137c9565b6040519015158152f35b346104ee5760206003193601126104ee576004355f526010602052602061123a60405f20546011835260405f20610a18565b505460801d60405190600f0b8152f35b346104ee575f6003193601126104ee57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346104ee5760206003193601126104ee5761129561074e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104ee57167fffffffffffffffffffffffff00000000000000000000000000000000000000005f5416175f555f80f35b604051906112ef82610596565b600582527f312e302e300000000000000000000000000000000000000000000000000000006020830152565b346104ee575f6003193601126104ee5761074a6107366112e2565b346104ee575f6003193601126104ee57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346104ee5760406003193601126104ee5760043567ffffffffffffffff8082116104ee57366023830112156104ee5781600401359081116105b2578060051b91602092604051926113bd60208301856105d3565b835260246020840191830101913683116104ee57602401905b8282106113e957610d0b60243585612ffd565b813581529084019084016113d6565b346104ee5760206003193601126104ee57602061141b61141661074e565b61316f565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346104ee5760206003193601126104ee5761145261074e565b73ffffffffffffffffffffffffffffffffffffffff811615611479575b610d0b9033614bea565b503361146f565b346104ee575f6003193601126104ee5760206040516104008152f35b346104ee5760206003193601126104ee576004355f526007602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104ee5760406003193601126104ee57600654600160ff8216036104ee5760209060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0080921617600655600161153933602435600435614f8b565b916006541617600655604051908152f35b346104ee5760206003193601126104ee576004355f52600d602052602060405f2054604051908152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff6115a261074e565b165f52601a602052602063ffffffff60405f205416604051908152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff6115ed61074e565b165f526008602052602060405f2054604051908152f35b346104ee5760206003193601126104ee576020610c356004356131dc565b346104ee5760206003193601126104ee576004355f526014602052602060405f2054600f0b604051908152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff61167d61074e565b165f52601b602052602060405f2054604051908152f35b346104ee575f6003193601126104ee57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346104ee5760406003193601126104ee576020610c35602435600435615250565b346104ee5760206003193601126104ee576004355f526017602052602060ff60405f2054166040519015158152f35b346104ee575f6003193601126104ee576020601354604051908152f35b346104ee575f6003193601126104ee5761074a60405161175381610596565b600581527f766555534400000000000000000000000000000000000000000000000000000060208201526040519182916020835260208301906106c4565b346104ee5760206003193601126104ee5761074a6118346004356117b743821115612def565b61182e6013546117c7818461540f565b926117e26117dd855f52600360205260405f2090565b6131a5565b935f9281101561188e576117dd6117fb61180992612f06565b5f52600360205260405f2090565b60608501908151606082019384518203611844575b50505050505b6040830151612f35565b90615155565b6040519081529081906020820190565b61188495509161186e60406118606118749461187e9796612ec4565b92015160408a015190612ec4565b90612fb3565b9251905190612ec4565b90612fc6565b5f8080808061181e565b5060608401908151904382036118a7575b505050611824565b6118d293506118bc61187e926118ca92612ec4565b61186e604088015142612ec4565b915143612ec4565b5f808061189f565b346104ee5760206003193601126104ee5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104ee57805f52601660205260405f2054907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611199575f52601660205260405f20555f80f35b346104ee5760206003193601126104ee576020610c3561197261074e565b6131fb565b346104ee5760406003193601126104ee57600435602435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002176006556119d1610d7f83336137c9565b815f5260126020526119e560405f20612e23565b906119f1811515612def565b8151600f0b15611a40576020820151421015610fa657611a1092614411565b610d0b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b60046040517fba112c93000000000000000000000000000000000000000000000000000000008152fd5b801515036104ee57565b346104ee5760406003193601126104ee57611a8d61074e565b602435611a9981611a6a565b611b2381611af373ffffffffffffffffffffffffffffffffffffffff851694611ac433871415612def565b335f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b346104ee5760406003193601126104ee57600435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611baa610d7f82336137c9565b611bbf610db9825f52601260205260405f2090565b611bdf611bda611bd160243542612f35565b62093a80900490565b612f83565b602082014281511115610fa6578251600f0b15611a405751811115611c4057611c0742612f14565b8111611c1657611a1092614622565b60046040517fd6946a43000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd5bb0fd9000000000000000000000000000000000000000000000000000000008152fd5b346104ee5760406003193601126104ee57602060ff611cd6611c8a61074e565b73ffffffffffffffffffffffffffffffffffffffff611ca7610771565b91165f52600a845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b346104ee5760206003193601126104ee576004355f5260126020526040805f2060018154600f0b91015482519182526020820152f35b346104ee5760806003193601126104ee57611d3161074e565b611d39610771565b6064359167ffffffffffffffff83116104ee57366023840112156104ee57826004013591611d6683610630565b92611d7460405194856105d3565b80845236602482870101116104ee576020815f926024610d0b9801838801378501015260443591613330565b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff5f541633036104ee576004355f52601760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690555f80f35b346104ee575f6003193601126104ee57604051611e2381610596565b5f81525f6020809201525f6020604051611e3c81610596565b8281520152611e49613853565b50611e52613853565b5060135490611e5f610623565b905f82525f6020830152426040830152606091436060820152918361207c575b60408301918251925f92844211612050575b959390611ea562093a808395949504612f83565b965f975b60ff8910611ec8575b610d0b88611ec3896117fb81601355565b61391c565b611ed190612f25565b5f9390428111156120115750611f41611f3a610dd795611f2b611f248c611f1f611f178d611f11429d8e925b86019b611f0b8d51600f0b90565b93612ec4565b90613877565b9151600f0b90565b61388e565b600f0b8c52565b8351600f0b6138d5565b6138d5565b600f0b8252565b5f611f50610dd78a51600f0b90565b12612009575b5f611f65610dd78351600f0b90565b12612000575b508294838352611fae82890197611fa78951611fa1611f93611f8d8a80612ec4565b8b612fb3565b670de0b6b3a7640000900490565b90612f35565b8952612f06565b98428503611fd35750505050505050610d0b92611ec391439052905f80808080611eb2565b60019192939495969750611ff389611ec38c5f52600360205260405f2090565b0197959291909493611ea9565b5f90525f611f6b565b5f8852611f56565b9350611f41611f3a87611f2b611f248c611f1f611f178b611f11610dd761204a612043845f52601460205260405f2090565b54600f0b90565b98611efd565b925061207661206b612066606088015143612ec4565b612f9b565b61187e835142612ec4565b92611e91565b91506120936117dd845f52600360205260405f2090565b91611e7f565b346104ee5760c06003193601126104ee576120b261074e565b6064359060243560443560ff841684036104ee5773ffffffffffffffffffffffffffffffffffffffff916120f58385166120ee33821415612de8565b1515612de8565b6120fd61066a565b5f8151602080930120966121706122586122646121186112e2565b805190870120604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818a01908152602081019e909e528d8201929092524660608e01523060808e01529b9093849060a0830190565b03936121a27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018352826105d3565b519020928a8c51612215816122098d8d8d8401968760609194939273ffffffffffffffffffffffffffffffffffffffff60808301967fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf845216602083015260408201520152565b038481018352826105d3565b5190208c51938491898301968790916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b039081018352826105d3565b519020885190815260ff919091166020820152608435604082015260a435606082015281805260809060015afa15610fa1575f519283161561233e576122c88373ffffffffffffffffffffffffffffffffffffffff165f52601b60205260405f2090565b908154916122d583612f56565b9055036123155742116122ec57610d0b9250614bea565b600483517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b600485517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b346104ee5760206003193601126104ee5760043573ffffffffffffffffffffffffffffffffffffffff6123a5610b87835f52600760205260405f2090565b1615612498575f6123c1610db9835f52601260205260405f2090565b6123e6610b1e610b1e60025473ffffffffffffffffffffffffffffffffffffffff1690565b6123f042856140e9565b612403610dd760208501519451600f0b90565b94612454604051968795869485947fdd9ec149000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b03915afa8015610fa15761074a915f91612476575b5060405191829182610707565b61249291503d805f833e61248a81836105d3565b810190613534565b5f612469565b60046040517f21f2b69c000000000000000000000000000000000000000000000000000000008152fd5b346104ee5760406003193601126104ee576004356024356124eb825f52601660205260405f2090565b54158015906125d0575b610fd0578161250982610d0b941415612de8565b61251b61251682336137c9565b612de8565b61252861251683336137c9565b61253d610db9825f52601260205260405f2090565b90612553610db9845f52601260205260405f2090565b926125c3612565610dd78551600f0b90565b926125be60208601516020880151808210155f146125c85750955b6125a661258b610614565b5f81525f6020820152610e02855f52601260205260405f2090565b6125ae610614565b905f82525f6020830152836139f7565b613f78565b614734565b905095612580565b506125e6610b71835f52601760205260405f2090565b6124f5565b346104ee5760206003193601126104ee576004355f52600360205260405f20805461074a600260018401549301546040519383859460801d90600f0b85909493926060926080830196600f0b8352600f0b602083015260408201520152565b346104ee5760406003193601126104ee5761266361074e565b60243561266f81611a6a565b6126c981611af373ffffffffffffffffffffffffffffffffffffffff85169461269a33871415612def565b335f52600a60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60405190151581527fccb38c6086037bf3d363115fc480b43f15aeb617ec6296b6a8ab6c70a1560adc60203392a3005b346104ee5760606003193601126104ee5760443573ffffffffffffffffffffffffffffffffffffffff811681036104ee57600654600160ff8216036104ee57600161277661074a9360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0080951617600655602435600435614f8b565b916006541617600655604051918291829190602083019252565b346104ee5760406003193601126104ee576020610c356024356004356140e9565b346104ee5760206003193601126104ee576004355f526010602052602060405f2054604051908152f35b346104ee575f6003193601126104ee5760206040517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b346104ee5760206003193601126104ee576020610c35600435613593565b346104ee5760206003193601126104ee576004355f526009602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104ee5760406003193601126104ee57602060ff611cd661289361074e565b73ffffffffffffffffffffffffffffffffffffffff6128b0610771565b91165f52600c845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b346104ee5760406003193601126104ee57600435602435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006002911617600655815f52601260205260405f20906040519161294783610596565b60018154600f0b9182855201549060208401918252612967831515612de8565b15611a405751421015610fa657611a1092614908565b346104ee5760406003193601126104ee5761299661074e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee57808316928284146104ee576129e96129fb926108dd875f52600760205260405f2090565b610953855f52600960205260405f2090565b7fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad5f80a4005b346104ee5760406003193601126104ee57612a3a61074e565b6024359063ffffffff821682036104ee5760209173ffffffffffffffffffffffffffffffffffffffff612a8592165f526019835260405f209063ffffffff165f5260205260405f2090565b54604051908152f35b346104ee5760206003193601126104ee576004355f5260126020526020600160405f200154604051908152f35b346104ee5760206003193601126104ee5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104ee57805f52601660205260405f20549060018201809211611199575f52601660205260405f20555f80f35b346104ee575f6003193601126104ee57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff5f541633036104ee576004355f52601760205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790555f80f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff8093160191821161119957565b63ffffffff918216908216039190821161119957565b90612c7d612c738373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b5463ffffffff1690565b9163ffffffff80841615612dd45782612cd2612cb78473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc087612bfd565b63ffffffff165f5260205260405f2090565b541115612ddc5782612d12612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b5f805260205260405f2090565b5411612dd4579190612d245f94612bfd565b83851684821611612d36575050505090565b612d58612d52612d468784612c31565b60011c637fffffff1690565b82612c31565b94612d9886612d858573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b9063ffffffff165f5260205260405f2090565b54848103612da95750505050505090565b93809596929394105f14612dc25750935b929190612d24565b949150612dce90612bfd565b90612dba565b505050505f90565b50505061071890612bfd565b156104ee57565b15612df657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b90604051612e3081610596565b6020600182948054600f0b84520154910152565b906020600191612e908151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b0151910155565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161119957565b9190820391821161119957565b908160209103126104ee575161071881611a6a565b6040513d5f823e3d90fd5b8054821015610a2e575f5260205f2001905f90565b906001820180921161119957565b906301dfe200820180921161119957565b9062093a80820180921161119957565b9190820180921161119957565b8051821015610a2e5760209160051b010190565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146111995760010190565b9062093a809182810292818404149015171561119957565b90670de0b6b3a7640000918083029283040361119957565b8181029291811591840414171561119957565b8115612fd0570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b61300f825f52601660205260405f2090565b5415801590613154575b610fd05761302a61251683336137c9565b61303f610b87835f52600760205260405f2090565b90613055610db9845f52601260205260405f2090565b9260208401519161306a610dd78651600f0b90565b90613076821515612de8565b613085610e1b83601554612ec4565b5f928151965f5b8881106131355750906125be6130a7926125a661258b610614565b42841115611c40576130b842612f14565b8411611c16575f5b8681106130d05750505050505050565b60019061312f60056130ea6130e58254612f56565b600555565b546130f5818a614191565b50876131148861310f613108878a612f42565b518a612fb3565b612fc6565b613129610db9845f52601260205260405f2090565b92614a7d565b016130c0565b9461314d6001916131468887612f42565b5190612f35565b950161308c565b5061316a610b71835f52601760205260405f2090565b613019565b73ffffffffffffffffffffffffffffffffffffffff8082165f52601860205260405f20541680155f146131a0575090565b905090565b906040516131b2816105b7565b606060028294805480600f0b855260801d600f0b6020850152600181015460408501520154910152565b610718906013545f5260036020526131f660405f206131a5565b615155565b73ffffffffffffffffffffffffffffffffffffffff81165f52601a60205263ffffffff60405f2054169081156132aa5761326261325860019273ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc08394612bfd565b015f9181545f925b818410613278575050505090565b909192936132966132898684612ef1565b9054429160031b1c6140e9565b81018091116111995793830192919061326a565b50505f90565b908160209103126104ee5751610718816104c4565b9092610718949360809373ffffffffffffffffffffffffffffffffffffffff8092168452166020830152604082015281606082015201906106c4565b3d1561332b573d9061331282610630565b9161332060405193846105d3565b82523d5f602084013e565b606090565b929190916133403383858761369c565b823b61334d575b50505050565b6133a2926020925f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c855233600486016132c5565b0393165af15f9181613503575b5061344e576133bc613301565b80519081613449576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608490fd5b602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000160361347e575f808080613347565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608490fd5b61352691925060203d60201161352d575b61351e81836105d3565b8101906132b0565b905f6133af565b503d613514565b6020818303126104ee5780519067ffffffffffffffff82116104ee570181601f820112156104ee57805161356781610630565b9261357560405194856105d3565b818452602082840101116104ee5761071891602080850191016106a3565b805f52600d60205260405f205443146135b1576107189042906140e9565b505f90565b805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee576135f3835f52600760205260405f2090565b5416331460ff6136253361090f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b54168115613694575b50156104ee575f90613648835f52600960205260405f2090565b7fffffffffffffffffffffffff000000000000000000000000000000000000000081541690557fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8280a4565b90505f61362e565b9192835f52601660205260405f2054158015906137ad575b610fd057836136c2916137c9565b156104ee57825f52600760205273ffffffffffffffffffffffffffffffffffffffff9061373c8461372d8460405f20541695613702868216809814612def565b825f52600b60205260405f208054878116613782575b50506137238361548a565b61141683826154dc565b6137368461316f565b90615853565b6137468482615ad5565b43613759855f52600d60205260405f2090565b5516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b7fffffffffffffffffffffffff00000000000000000000000000000000000000001690555f80613718565b5060ff6137c2855f52601760205260405f2090565b54166136b4565b905f52600760205273ffffffffffffffffffffffffffffffffffffffff9060ff6138368360405f2054169284811680851495600b60205260405f20541614935f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541690821561384b575b5081156131a0575090565b91505f613840565b60405190613860826105b7565b5f6060838281528260208201528260408201520152565b90600f0b90600f0b029081600f0b91820361119957565b90600f0b90600f0b03906f7fffffffffffffffffffffffffffffff82137fffffffffffffffffffffffffffffffff8000000000000000000000000000000083121761119957565b90600f0b90600f0b01907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff83131761119957565b9060606002916139688151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b60208101516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000086549260801b169116178455604081015160018501550151910155565b91906139cb576106219161391c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6139ff613853565b90613a08613853565b925f806013549284151580613e37575b613a20610623565b945f86526020955f878201524260408201526060904360608201529082613e1a575b604082018051915f91834211613df3575b8a90613a6862093a8086989695949504612f83565b965f975b60ff8910613cc2575b5050509050613aa794508291508c8c87613a91611ec396601355565b613c1d575b5050505f52600360205260405f2090565b613ab6575b5050505050505050565b613b3a97613b349685808094019542875111613bc7575b5050500191825190428211613b47575b5050505050613b2f613b00613afa835f52601060205260405f2090565b54612f06565b9182613b14825f52601060205260405f2090565b554260408601524360608601525f52601160205260405f2090565b610a18565b906139bc565b5f80808080808080613aac565b5110613b55575b8080613add565b613b72613b8291613b6c613bbf95890151600f0b90565b9061388e565b91515f52601460205260405f2090565b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b5f8080613b4e565b613bda613be092613bff940151600f0b90565b906138d5565b83830151865114613c07575b613b8286515f52601460205260405f2090565b5f8281613acd565b613c1890613b6c858c0151600f0b90565b613bec565b613c6d613c7791613b6c611f1785613c66613c5f613c50613c448b613c7e9b0151600f0b90565b878c0151600f0b613b6c565b998d0199611f358b51600f0b90565b600f0b8952565b51600f0b90565b8651600f0b6138d5565b600f0b8552565b5f613c8d610dd78351600f0b90565b12613cb9575b505f613ca3610dd78551600f0b90565b12613cb1575b888c8c613a96565b5f8352613ca9565b5f90525f613c93565b613ccb90612f25565b5f9042811115613dc55750611f3a613d1591611f2b613d0e613d0442985b8d0196611f11610dd7613cfd8a51600f0b90565b928c612ec4565b8c51600f0b61388e565b600f0b8b52565b5f613d24610dd78951600f0b90565b12613dbd575b5f613d39610dd78351600f0b90565b12613db4575b5081938282528c613d6282890197611fa78951611fa1611f93611f8d8a80612ec4565b98428503613d875750505050505050613aa792611ec391439052905f89818080613a75565b60019192939495969750613da789611ec38c5f52600360205260405f2090565b0197959092919493613a6c565b5f90525f613d3f565b5f8752613d2a565b93613d159150611f3a90611f2b613d0e613d04613ded6120438a5f52601460205260405f2090565b93613ce9565b9150613e14613e09612066606087015143612ec4565b61187e845142612ec4565b91613a53565b9050613e316117dd835f52600360205260405f2090565b90613a42565b925060208101928351421080613f62575b613f22575b602089019389894287511180613f0c575b613ebc575b5050613e7b61204382515f52601460205260405f2090565b94519081613e8b575b5050613a18565b51919350908103613ea1575082915b5f80613e84565b612043613eb6915f52601460205260405f2090565b91613e9a565b613ef0613efe91613c666020613ee6613ed9613f059751600f0b90565b6301dfe20090600f0b0590565b600f0b9201918252565b611f11610dd7428a51612ec4565b600f0b8a52565b8989613e63565b505f613f1c610dd78451600f0b90565b13613e5e565b613f5d613f56613f48613f39613ed98651600f0b90565b600f0b60208b01908152613c66565b611f11610dd7428951612ec4565b600f0b8852565b613e4d565b505f613f72610dd78451600f0b90565b13613e48565b613f8281336137c9565b156140bf57805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee573390613fc6845f52600760205260405f2090565b54161460ff613ff73361090f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b541681156140b7575b50156104ee575f9061404561401d845f52600b60205260405f2090565b7fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8282827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258280a4614075836135b6565b614087836140828361316f565b61570c565b61409183826154dc565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4565b90505f614000565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b805f52601060205260405f20549081155f14614106575050505f90565b61412591613b2f61411f925f52601160205260405f2090565b506131a5565b6020810151906040810151925f84820394128185128116918513901516176111995761416e611f3a614164610dd79461071896600f0b90600f0b613877565b8351600f0b61388e565b5f61417a8251600f0b90565b600f0b126141895751600f0b90565b5f8152613c66565b73ffffffffffffffffffffffffffffffffffffffff9082828216926141b7841515612def565b6141c08361316f565b908116151580806143f0575b614206575b50506141dc91615ad5565b5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600190565b614211575b806141d1565b905061423e612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff8116156143bd5760016142806142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc084612bfd565b015b6001806142b961429186615bac565b612d858773ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01918054906104006142ca83612f06565b1161439357908895949392915f5b82811061435c575050505091614326614301614356936142fc876141dc99976156a0565b6156f7565b9173ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000825416179055565b9161420b565b83949596975061438561437f6143758385969795612ef1565b90549060031b1c90565b876156a0565b0190899695949392916142d8565b60046040517f8c390496000000000000000000000000000000000000000000000000000000008152fd5b60016143ea612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01614282565b508215156141cc565b6040519061440682610596565b5f6020838281520152565b919060155491614424610e1b8385612f35565b61448c61442f6143f9565b9161443b8151600f0b90565b926144546020830194855160208401528290600f0b9052565b61447061446986600f0b611f358551600f0b90565b600f0b8352565b61448682610e02895f52601260205260405f2090565b866139f7565b8115158061461a575b80614612575b61452f575b5160408051948552602085018390526002908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081015b0390a382612f35565b604080519283526020830191909152819081015b0390a1565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fa1576145e9614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f3575b50612def565b91925093506144a0565b61460c915060203d602011610f9a57610f8b81836105d3565b5f6145e3565b50600161449b565b506001614495565b6015549061462e6143f9565b93805190602081019182516020880152600f0b86528051600f0b907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff831317611199577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9661452a966146d39383528061472c575b506146cd82610e02875f52601260205260405f2090565b846139f7565b51906040519081525f6020820152600360408201524260608201527fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62460803392a360408051828152602081019290925290918291820190565b84525f6146b6565b92916147a96015549361474a610e1b8587612f35565b6147526143f9565b8151600f0b936147706020840195865160208501528390600f0b9052565b61478c61478587600f0b611f358651600f0b90565b600f0b8452565b80614900575b5061448682610e02895f52601260205260405f2090565b811515806148f9575b806148f1575b61482e575b5160408051948552602085018390526004908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490806080810161450d565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fa1576148e7614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f35750612def565b91925093506147bd565b5060016147b8565b505f6147b2565b84525f614792565b91906015549161491b610e1b8385612f35565b61492661442f6143f9565b81151580614a75575b80614a6d575b6149aa575b5160408051948552602085018390525f908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490806080810161450d565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fa157614a63614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f35750612def565b919250935061493a565b506001614935565b50600161492f565b9291614a936015549361474a610e1b8587612f35565b81151580614be2575b80614bdb575b614b18575b5160408051948552602085018390526005908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490806080810161450d565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fa157614bd1614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f35750612def565b9192509350614aa7565b505f614aa2565b506001614a9c565b9190614bf58361316f565b73ffffffffffffffffffffffffffffffffffffffff90818516805f526018602052614c5e8460405f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b838316928281168481847f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f5f80a4848103614c9e575b5050505050509050565b614e3c575b505050614cb5575b8080808080614c94565b614ce0612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615614e09576001614d196142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b6001906001614d2c61429186615bac565b01614d558773ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b5491805490610400614d678584612f35565b1161439357845f5b838110614deb57505050505f5b828110614da05750505050614d99929350614301614326916156f7565b805f614cab565b80614de5614dde8693614dd18c73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b905f5260205260405f2090565b54846156a0565b01614d7c565b614e01614dfb6143758386612ef1565b866156a0565b018590614d6f565b6001614e36612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01614d1b565b614e67612c738473ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9163ffffffff831615614f57576001614eaa614ea18673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc086612bfd565b01905b6001926001614ee6614ebe88615bac565b612d858973ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01918354935f5b858110614f13575050505050505090614326614301614f0b936156f7565b5f8080614ca3565b80614f22614375899385612ef1565b8486614f39610b87845f52600760205260405f2090565b1603614f47575b5001614eed565b614f5190876156a0565b5f614f40565b6001614f84612d058673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0190614ead565b9042018042116111995762093a80614fa39104612f83565b81156104ee5742811115611c4057614fba42612f14565b8111611c1657614fd9614fce600554612f56565b809481600555614191565b50614fef610db9845f52601260205260405f2090565b9061503f60155492615004610e1b8686612f35565b61500c6143f9565b8151600f0b9361502a6020840195865160208501528390600f0b9052565b61478c61478588600f0b611f358651600f0b90565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101849052926020846064815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1918215610fa1576150f5615141937f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c965f916145f35750612def565b516040517fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62433918061450d42878c84606091949392608082019582526020820152600160408201520152565b60408051928352602083019190915290a190565b906040820161516b611bda825162093a80900490565b905f915b60ff83106151b6575b505050506151a16151a1825f615195610dd76107189651600f0b90565b126141895751600f0b90565b6fffffffffffffffffffffffffffffffff1690565b6151bf90612f25565b5f929084811115615234575083925b60208601906151ff613f566151f56151e78551600f0b90565b611f11610dd789518b612ec4565b8951600f0b61388e565b85851461522d578161521c61522392611f356001969551600f0b90565b600f0b9052565b838352019161516f565b5050615178565b925061524b612043845f52601460205260405f2090565b6151ce565b61525c43831115612def565b5f90615270815f52601060205260405f2090565b545f905b608082106153a1575b5050611f3a6141646152a561411f6153399695613b2f613c66965f52601160205260405f2090565b93601354906152b4828261540f565b6152c96117dd825f52600360205260405f2090565b9281101561537d576117dd6117fb6152e092612f06565b9061530760406152f96060850151606087015190612ec4565b930151604085015190612ec4565b905b60408401519383615359575b50505050611f11610dd761532d6020880151600f0b90565b92604088015190612ec4565b5f600f82900b126135b1576fffffffffffffffffffffffffffffffff1690565b611fa19261186e615374969593606061310f94015190612ec4565b5f808080615315565b5061538c606083015143612ec4565b9061539b604084015142612ec4565b90615309565b909281811015615409576153c66153c06153bb8484612f35565b612f06565b60011c90565b908560026153e084613b2f885f52601160205260405f2090565b500154116153f55750600190935b0190615274565b939150615403600191612e97565b916153ee565b9261527d565b5f915f915b60808310615423575b50505090565b9091928281101561548457828101808211611199576001808201809211611199571c9082600261545b845f52600360205260405f2090565b0154116154705750600190935b019190615414565b93925061547e600191612e97565b92615468565b9261541d565b5f52600960205260405f20805473ffffffffffffffffffffffffffffffffffffffff81166154b6575050565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b815f52600760205273ffffffffffffffffffffffffffffffffffffffff61550e8160405f205416918316809214612def565b825f52600760205261554560405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b5f52600860205260405f2054917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019283116111995761560392816155945f935f52600f60205260405f2090565b5480831484146156115750826155cf6155de93614dd18773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b555f52600f60205260405f2090565b5573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b61560d8154612e97565b9055565b6155cf8385926156776156496155de97614dd18b73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b54806155cf84614dd18d73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b55614dd18773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b8054680100000000000000008110156105b2576156c291600182018155612ef1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829392549160031b92831b921b1916179055565b90600163ffffffff8093160191821161119957565b90919073ffffffffffffffffffffffffffffffffffffffff81161515808061584a575b61573a575b50509050565b615745575b80615734565b615770612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff8116156158165760016157a96142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01935b60019060016157bd61429186615bac565b018654915f5b8381106157e85750505050506157e1929350614301614326916156f7565b805f61573f565b806157f761437587938c612ef1565b838103615806575b50016157c3565b61581090856156a0565b5f6157ff565b6001615843612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01936157ac565b5083151561572f565b91909173ffffffffffffffffffffffffffffffffffffffff908184169181168281141580615acc575b615888575b5050505050565b6159cc575b5061589b575b808080615881565b6158c6612c738373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff8216156159995760016159096159008573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc085612bfd565b015b60018061594261591a87615bac565b612d858873ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b019180549061040061595383612f06565b11614393575f5b828110615981575050505061597a93926142fc61432693614301936156a0565b5f80615893565b8061599361437f614375879486612ef1565b0161595a565b60016159c6612d058573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0161590b565b6159f7612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615615a99576001615a306142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b6001906001615a4361429186615bac565b018154915f5b838110615a6b57505050505090614326614301615a65936156f7565b5f61588d565b80615a7a614375879385612ef1565b8a8103615a89575b5001615a49565b615a9390856156a0565b5f615a82565b6001615ac6612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615a32565b5083151561587c565b615ba291805f52602060078152600f73ffffffffffffffffffffffffffffffffffffffff91604092615b0c81855f20541615612def565b845f5260078252615b5a86855f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b85165f5260088152825f205493600e8252835f20855f52825280845f20555f52525f205573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b61560d8154612f06565b73ffffffffffffffffffffffffffffffffffffffff165f52601a60205263ffffffff60405f20541680151580615beb575b156107185761071890612bfd565b50601960205260405f20615c034291612cc084612bfd565b5414615bdd56fea164736f6c6343000818000a0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba00000000000000000000000011e23dfb5a9b4d52dba1d6a17086e796e408f51e
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146104bf578063047fc9aa146104ba57806306fdde03146104b55780630758c7d8146104b0578063081812fc146104ab578063095cf5c6146104a6578063095ea7b3146104a15780630d6a20331461049c5780631376f3da1461049757806316b7a0111461049257806318160ddd1461048d5780631c984bc31461048857806320606b701461048357806323b872dd1461047e57806325a58b56146104795780632e1a7d4d146104745780632e720f7d1461046f5780632f745c591461046a578063313ce567146104655780633a46b1a81461046057806342842e0e1461045b578063430c208114610456578063461f711c1461045157806346c96aac1461044c5780634bc2a6571461044757806354fd4d50146104425780635594a0451461043d57806356afe74414610438578063587cde1e146104335780635c19a95c1461042e5780635f5b0c32146104295780636352211e1461042457806365fc38731461041f5780636f5488371461041a5780636fcfff451461041557806370a08231146104105780637116c60c146103f7578063711974841461040b5780637ecebe001461040657806385f2aef2146104015780638c2c9baf146103fc5780638e539e8c146103f75780638fbb38ff146103f2578063900cf0cf146103ed57806395d89b41146103e8578063981b24d0146103e3578063986b7d8a146103de5780639ab24eb0146103d9578063a183af52146103d4578063a22cb465146103cf578063a4d855df146103ca578063b29b8830146103c5578063b45a3c0e146103c0578063b88d4fde146103bb578063c1f0fb9f146103b6578063c2c4c5c1146103b1578063c3cda520146103ac578063c87b56dd146103a7578063d1c2babb146103a2578063d1febfb91461039d578063d2aef45714610398578063d4e54c3b14610393578063e0514aba1461038e578063e441135c14610389578063e7a324dc14610384578063e7e242d41461037f578063e9237e121461037a578063e985e9c514610375578063ee99fe2814610370578063ef39933d1461036b578063f1127ed814610366578063f8a0576314610361578063fbd3a29d1461035c578063fc0c546a146103575763fd4a77f114610352575f80fd5b612b66565b612b16565b612abb565b612a8e565b612a21565b61297d565b6128df565b612873565b612833565b612815565b6127db565b6127b1565b612790565b6126f9565b61264a565b6125eb565b6124c2565b612367565b612099565b611e07565b611da0565b611d18565b611ce2565b611c6a565b611b53565b611a74565b611977565b611954565b6118da565b611791565b611734565b611717565b6116e8565b611604565b6116c7565b611694565b61164f565b611622565b6115bf565b611574565b61154a565b6114dc565b61149c565b611480565b611439565b6113f8565b611369565b611336565b61131b565b61127c565b61124a565b611208565b6111d7565b61119e565b6110e9565b6110ce565b61107d565b611015565b610d27565b610d0d565b610cf2565b610c76565b610c3d565b610c1a565b610a9f565b610a33565b6109c1565b610871565b610809565b6107c9565b610794565b61071b565b61054c565b6104f2565b7fffffffff000000000000000000000000000000000000000000000000000000008116036104ee57565b5f80fd5b346104ee5760206003193601126104ee577fffffffff00000000000000000000000000000000000000000000000000000000600435610530816104c4565b165f526004602052602060ff60405f2054166040519015158152f35b346104ee575f6003193601126104ee576020601554604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176105b257604052565b610569565b6080810190811067ffffffffffffffff8211176105b257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105b257604052565b6040519061062182610596565b565b60405190610621826105b7565b67ffffffffffffffff81116105b257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6040519061067782610596565b600b82527f52696e67732076655553440000000000000000000000000000000000000000006020830152565b5f5b8381106106b45750505f910152565b81810151838201526020016106a5565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610700815180928187528780880191016106a3565b0116010190565b9060206107189281815201906106c4565b90565b346104ee575f6003193601126104ee5761074a61073661066a565b6040519182916020835260208301906106c4565b0390f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104ee57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104ee57565b346104ee5760406003193601126104ee5760206107bb6107b261074e565b60243590612c47565b63ffffffff60405191168152f35b346104ee5760206003193601126104ee576004355f52600b602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104ee5760206003193601126104ee5761082261074e565b6001549073ffffffffffffffffffffffffffffffffffffffff80831633036104ee577fffffffffffffffffffffffff000000000000000000000000000000000000000091169116176001555f80f35b346104ee5760406003193601126104ee5761088a61074e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee57808316928284146104ee57610941610993926108dd875f52600760205260405f2090565b5416331460ff6109323361090f8873ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541681156109b9575b50612de8565b610953855f52600b60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b90505f61093b565b346104ee5760206003193601126104ee576004355f526016602052602060405f2054604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b90633b9aca00811015610a2e5760030201905f90565b6109eb565b346104ee5760406003193601126104ee576024356004355f52601160205260405f2090633b9aca008110156104ee57610a6b91610a18565b508054600182015460029092015460408051600f84810b8252608094851d900b602082015290810193909352606083015290f35b346104ee5760406003193601126104ee5761074a610abb61074e565b602435805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f20541690831691610bc7610b7184841495600960205261090f86610b37610b1e60405f2073ffffffffffffffffffffffffffffffffffffffff90541690565b73ffffffffffffffffffffffffffffffffffffffff1690565b1496610ba1610b1e610b87610b78610b718761090f8d73ffffffffffffffffffffffffffffffffffffffff165f52600a60205260405f2090565b5460ff1690565b985f52600b60205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b149573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b928415610c12575b508315610c0a575b508215610c02575b508115610bfa575b5060405190151581529081906020820190565b90505f610be7565b91505f610bdf565b92505f610bd7565b93505f610bcf565b346104ee575f6003193601126104ee576020610c35426131dc565b604051908152f35b346104ee5760406003193601126104ee576004355f52601160205260206001610c6b60243560405f20610a18565b500154604051908152f35b346104ee575f6003193601126104ee5760206040517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a8668152f35b60031960609101126104ee5773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104ee579160243590811681036104ee579060443590565b346104ee57610d0b610d0336610cb0565b90339261369c565b005b346104ee575f6003193601126104ee576020604051438152f35b346104ee576020806003193601126104ee57600435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655610d84610d7f82336137c9565b612def565b610d96815f52601660205260405f2090565b5415801590610ffa575b610fd057610dbe610db9825f52601260205260405f2090565b612e23565b90828201514210610fa657610ddd610dd78351600f0b90565b600f0b90565b610e07610de8610614565b5f81525f86820152610e02845f52601260205260405f2090565b612e44565b610e3760155493610e20610e1b8487612ec4565b601555565b610e28610614565b905f82525f87830152846139f7565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018290529184836044815f7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1948515610fa1577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c95610f3394610ee9925f92610f74575b5050612def565b610ef281613f78565b6040805191825260208201839052429082015233907f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca9490606090a282612ec4565b60408051928352602083019190915290a1610d0b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b610f939250803d10610f9a575b610f8b81836105d3565b810190612ed1565b5f80610ee2565b503d610f81565b612ee6565b60046040517ff6fafba0000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3ca9d617000000000000000000000000000000000000000000000000000000008152fd5b50611010610b71825f52601760205260405f2090565b610da0565b346104ee5760206003193601126104ee5761102e61074e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104ee57167fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002555f80f35b346104ee5760406003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff6110ab61074e565b165f52600e60205260405f206024355f52602052602060405f2054604051908152f35b346104ee575f6003193601126104ee57602060405160068152f35b346104ee5760406003193601126104ee5761110261074e565b6024359073ffffffffffffffffffffffffffffffffffffffff6111258383612c47565b91165f526019602052600161114c819260405f209063ffffffff165f5260205260405f2090565b01915f9183545f925b81841061116757604051858152602090f35b90919293611185826111798789612ef1565b90549060031b1c6140e9565b810180911161119957938301929190611155565b612bd0565b346104ee576111ac36610cb0565b60405191602083019383851067ffffffffffffffff8611176105b257610d0b946040525f8452613330565b346104ee5760406003193601126104ee5760206111fe6111f561074e565b602435906137c9565b6040519015158152f35b346104ee5760206003193601126104ee576004355f526010602052602061123a60405f20546011835260405f20610a18565b505460801d60405190600f0b8152f35b346104ee575f6003193601126104ee57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346104ee5760206003193601126104ee5761129561074e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104ee57167fffffffffffffffffffffffff00000000000000000000000000000000000000005f5416175f555f80f35b604051906112ef82610596565b600582527f312e302e300000000000000000000000000000000000000000000000000000006020830152565b346104ee575f6003193601126104ee5761074a6107366112e2565b346104ee575f6003193601126104ee57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346104ee5760406003193601126104ee5760043567ffffffffffffffff8082116104ee57366023830112156104ee5781600401359081116105b2578060051b91602092604051926113bd60208301856105d3565b835260246020840191830101913683116104ee57602401905b8282106113e957610d0b60243585612ffd565b813581529084019084016113d6565b346104ee5760206003193601126104ee57602061141b61141661074e565b61316f565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346104ee5760206003193601126104ee5761145261074e565b73ffffffffffffffffffffffffffffffffffffffff811615611479575b610d0b9033614bea565b503361146f565b346104ee575f6003193601126104ee5760206040516104008152f35b346104ee5760206003193601126104ee576004355f526007602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104ee5760406003193601126104ee57600654600160ff8216036104ee5760209060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0080921617600655600161153933602435600435614f8b565b916006541617600655604051908152f35b346104ee5760206003193601126104ee576004355f52600d602052602060405f2054604051908152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff6115a261074e565b165f52601a602052602063ffffffff60405f205416604051908152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff6115ed61074e565b165f526008602052602060405f2054604051908152f35b346104ee5760206003193601126104ee576020610c356004356131dc565b346104ee5760206003193601126104ee576004355f526014602052602060405f2054600f0b604051908152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff61167d61074e565b165f52601b602052602060405f2054604051908152f35b346104ee575f6003193601126104ee57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346104ee5760406003193601126104ee576020610c35602435600435615250565b346104ee5760206003193601126104ee576004355f526017602052602060ff60405f2054166040519015158152f35b346104ee575f6003193601126104ee576020601354604051908152f35b346104ee575f6003193601126104ee5761074a60405161175381610596565b600581527f766555534400000000000000000000000000000000000000000000000000000060208201526040519182916020835260208301906106c4565b346104ee5760206003193601126104ee5761074a6118346004356117b743821115612def565b61182e6013546117c7818461540f565b926117e26117dd855f52600360205260405f2090565b6131a5565b935f9281101561188e576117dd6117fb61180992612f06565b5f52600360205260405f2090565b60608501908151606082019384518203611844575b50505050505b6040830151612f35565b90615155565b6040519081529081906020820190565b61188495509161186e60406118606118749461187e9796612ec4565b92015160408a015190612ec4565b90612fb3565b9251905190612ec4565b90612fc6565b5f8080808061181e565b5060608401908151904382036118a7575b505050611824565b6118d293506118bc61187e926118ca92612ec4565b61186e604088015142612ec4565b915143612ec4565b5f808061189f565b346104ee5760206003193601126104ee5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104ee57805f52601660205260405f2054907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611199575f52601660205260405f20555f80f35b346104ee5760206003193601126104ee576020610c3561197261074e565b6131fb565b346104ee5760406003193601126104ee57600435602435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002176006556119d1610d7f83336137c9565b815f5260126020526119e560405f20612e23565b906119f1811515612def565b8151600f0b15611a40576020820151421015610fa657611a1092614411565b610d0b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b60046040517fba112c93000000000000000000000000000000000000000000000000000000008152fd5b801515036104ee57565b346104ee5760406003193601126104ee57611a8d61074e565b602435611a9981611a6a565b611b2381611af373ffffffffffffffffffffffffffffffffffffffff851694611ac433871415612def565b335f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b346104ee5760406003193601126104ee57600435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611baa610d7f82336137c9565b611bbf610db9825f52601260205260405f2090565b611bdf611bda611bd160243542612f35565b62093a80900490565b612f83565b602082014281511115610fa6578251600f0b15611a405751811115611c4057611c0742612f14565b8111611c1657611a1092614622565b60046040517fd6946a43000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd5bb0fd9000000000000000000000000000000000000000000000000000000008152fd5b346104ee5760406003193601126104ee57602060ff611cd6611c8a61074e565b73ffffffffffffffffffffffffffffffffffffffff611ca7610771565b91165f52600a845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b346104ee5760206003193601126104ee576004355f5260126020526040805f2060018154600f0b91015482519182526020820152f35b346104ee5760806003193601126104ee57611d3161074e565b611d39610771565b6064359167ffffffffffffffff83116104ee57366023840112156104ee57826004013591611d6683610630565b92611d7460405194856105d3565b80845236602482870101116104ee576020815f926024610d0b9801838801378501015260443591613330565b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff5f541633036104ee576004355f52601760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690555f80f35b346104ee575f6003193601126104ee57604051611e2381610596565b5f81525f6020809201525f6020604051611e3c81610596565b8281520152611e49613853565b50611e52613853565b5060135490611e5f610623565b905f82525f6020830152426040830152606091436060820152918361207c575b60408301918251925f92844211612050575b959390611ea562093a808395949504612f83565b965f975b60ff8910611ec8575b610d0b88611ec3896117fb81601355565b61391c565b611ed190612f25565b5f9390428111156120115750611f41611f3a610dd795611f2b611f248c611f1f611f178d611f11429d8e925b86019b611f0b8d51600f0b90565b93612ec4565b90613877565b9151600f0b90565b61388e565b600f0b8c52565b8351600f0b6138d5565b6138d5565b600f0b8252565b5f611f50610dd78a51600f0b90565b12612009575b5f611f65610dd78351600f0b90565b12612000575b508294838352611fae82890197611fa78951611fa1611f93611f8d8a80612ec4565b8b612fb3565b670de0b6b3a7640000900490565b90612f35565b8952612f06565b98428503611fd35750505050505050610d0b92611ec391439052905f80808080611eb2565b60019192939495969750611ff389611ec38c5f52600360205260405f2090565b0197959291909493611ea9565b5f90525f611f6b565b5f8852611f56565b9350611f41611f3a87611f2b611f248c611f1f611f178b611f11610dd761204a612043845f52601460205260405f2090565b54600f0b90565b98611efd565b925061207661206b612066606088015143612ec4565b612f9b565b61187e835142612ec4565b92611e91565b91506120936117dd845f52600360205260405f2090565b91611e7f565b346104ee5760c06003193601126104ee576120b261074e565b6064359060243560443560ff841684036104ee5773ffffffffffffffffffffffffffffffffffffffff916120f58385166120ee33821415612de8565b1515612de8565b6120fd61066a565b5f8151602080930120966121706122586122646121186112e2565b805190870120604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818a01908152602081019e909e528d8201929092524660608e01523060808e01529b9093849060a0830190565b03936121a27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018352826105d3565b519020928a8c51612215816122098d8d8d8401968760609194939273ffffffffffffffffffffffffffffffffffffffff60808301967fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf845216602083015260408201520152565b038481018352826105d3565b5190208c51938491898301968790916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b039081018352826105d3565b519020885190815260ff919091166020820152608435604082015260a435606082015281805260809060015afa15610fa1575f519283161561233e576122c88373ffffffffffffffffffffffffffffffffffffffff165f52601b60205260405f2090565b908154916122d583612f56565b9055036123155742116122ec57610d0b9250614bea565b600483517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b600485517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b346104ee5760206003193601126104ee5760043573ffffffffffffffffffffffffffffffffffffffff6123a5610b87835f52600760205260405f2090565b1615612498575f6123c1610db9835f52601260205260405f2090565b6123e6610b1e610b1e60025473ffffffffffffffffffffffffffffffffffffffff1690565b6123f042856140e9565b612403610dd760208501519451600f0b90565b94612454604051968795869485947fdd9ec149000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b03915afa8015610fa15761074a915f91612476575b5060405191829182610707565b61249291503d805f833e61248a81836105d3565b810190613534565b5f612469565b60046040517f21f2b69c000000000000000000000000000000000000000000000000000000008152fd5b346104ee5760406003193601126104ee576004356024356124eb825f52601660205260405f2090565b54158015906125d0575b610fd0578161250982610d0b941415612de8565b61251b61251682336137c9565b612de8565b61252861251683336137c9565b61253d610db9825f52601260205260405f2090565b90612553610db9845f52601260205260405f2090565b926125c3612565610dd78551600f0b90565b926125be60208601516020880151808210155f146125c85750955b6125a661258b610614565b5f81525f6020820152610e02855f52601260205260405f2090565b6125ae610614565b905f82525f6020830152836139f7565b613f78565b614734565b905095612580565b506125e6610b71835f52601760205260405f2090565b6124f5565b346104ee5760206003193601126104ee576004355f52600360205260405f20805461074a600260018401549301546040519383859460801d90600f0b85909493926060926080830196600f0b8352600f0b602083015260408201520152565b346104ee5760406003193601126104ee5761266361074e565b60243561266f81611a6a565b6126c981611af373ffffffffffffffffffffffffffffffffffffffff85169461269a33871415612def565b335f52600a60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60405190151581527fccb38c6086037bf3d363115fc480b43f15aeb617ec6296b6a8ab6c70a1560adc60203392a3005b346104ee5760606003193601126104ee5760443573ffffffffffffffffffffffffffffffffffffffff811681036104ee57600654600160ff8216036104ee57600161277661074a9360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0080951617600655602435600435614f8b565b916006541617600655604051918291829190602083019252565b346104ee5760406003193601126104ee576020610c356024356004356140e9565b346104ee5760206003193601126104ee576004355f526010602052602060405f2054604051908152f35b346104ee575f6003193601126104ee5760206040517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b346104ee5760206003193601126104ee576020610c35600435613593565b346104ee5760206003193601126104ee576004355f526009602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104ee5760406003193601126104ee57602060ff611cd661289361074e565b73ffffffffffffffffffffffffffffffffffffffff6128b0610771565b91165f52600c845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b346104ee5760406003193601126104ee57600435602435600654600160ff8216036104ee577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006002911617600655815f52601260205260405f20906040519161294783610596565b60018154600f0b9182855201549060208401918252612967831515612de8565b15611a405751421015610fa657611a1092614908565b346104ee5760406003193601126104ee5761299661074e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee57808316928284146104ee576129e96129fb926108dd875f52600760205260405f2090565b610953855f52600960205260405f2090565b7fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad5f80a4005b346104ee5760406003193601126104ee57612a3a61074e565b6024359063ffffffff821682036104ee5760209173ffffffffffffffffffffffffffffffffffffffff612a8592165f526019835260405f209063ffffffff165f5260205260405f2090565b54604051908152f35b346104ee5760206003193601126104ee576004355f5260126020526020600160405f200154604051908152f35b346104ee5760206003193601126104ee5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104ee57805f52601660205260405f20549060018201809211611199575f52601660205260405f20555f80f35b346104ee575f6003193601126104ee57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba168152f35b346104ee5760206003193601126104ee5773ffffffffffffffffffffffffffffffffffffffff5f541633036104ee576004355f52601760205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790555f80f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff8093160191821161119957565b63ffffffff918216908216039190821161119957565b90612c7d612c738373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b5463ffffffff1690565b9163ffffffff80841615612dd45782612cd2612cb78473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc087612bfd565b63ffffffff165f5260205260405f2090565b541115612ddc5782612d12612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b5f805260205260405f2090565b5411612dd4579190612d245f94612bfd565b83851684821611612d36575050505090565b612d58612d52612d468784612c31565b60011c637fffffff1690565b82612c31565b94612d9886612d858573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b9063ffffffff165f5260205260405f2090565b54848103612da95750505050505090565b93809596929394105f14612dc25750935b929190612d24565b949150612dce90612bfd565b90612dba565b505050505f90565b50505061071890612bfd565b156104ee57565b15612df657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b90604051612e3081610596565b6020600182948054600f0b84520154910152565b906020600191612e908151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b0151910155565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161119957565b9190820391821161119957565b908160209103126104ee575161071881611a6a565b6040513d5f823e3d90fd5b8054821015610a2e575f5260205f2001905f90565b906001820180921161119957565b906301dfe200820180921161119957565b9062093a80820180921161119957565b9190820180921161119957565b8051821015610a2e5760209160051b010190565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146111995760010190565b9062093a809182810292818404149015171561119957565b90670de0b6b3a7640000918083029283040361119957565b8181029291811591840414171561119957565b8115612fd0570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b61300f825f52601660205260405f2090565b5415801590613154575b610fd05761302a61251683336137c9565b61303f610b87835f52600760205260405f2090565b90613055610db9845f52601260205260405f2090565b9260208401519161306a610dd78651600f0b90565b90613076821515612de8565b613085610e1b83601554612ec4565b5f928151965f5b8881106131355750906125be6130a7926125a661258b610614565b42841115611c40576130b842612f14565b8411611c16575f5b8681106130d05750505050505050565b60019061312f60056130ea6130e58254612f56565b600555565b546130f5818a614191565b50876131148861310f613108878a612f42565b518a612fb3565b612fc6565b613129610db9845f52601260205260405f2090565b92614a7d565b016130c0565b9461314d6001916131468887612f42565b5190612f35565b950161308c565b5061316a610b71835f52601760205260405f2090565b613019565b73ffffffffffffffffffffffffffffffffffffffff8082165f52601860205260405f20541680155f146131a0575090565b905090565b906040516131b2816105b7565b606060028294805480600f0b855260801d600f0b6020850152600181015460408501520154910152565b610718906013545f5260036020526131f660405f206131a5565b615155565b73ffffffffffffffffffffffffffffffffffffffff81165f52601a60205263ffffffff60405f2054169081156132aa5761326261325860019273ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc08394612bfd565b015f9181545f925b818410613278575050505090565b909192936132966132898684612ef1565b9054429160031b1c6140e9565b81018091116111995793830192919061326a565b50505f90565b908160209103126104ee5751610718816104c4565b9092610718949360809373ffffffffffffffffffffffffffffffffffffffff8092168452166020830152604082015281606082015201906106c4565b3d1561332b573d9061331282610630565b9161332060405193846105d3565b82523d5f602084013e565b606090565b929190916133403383858761369c565b823b61334d575b50505050565b6133a2926020925f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c855233600486016132c5565b0393165af15f9181613503575b5061344e576133bc613301565b80519081613449576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608490fd5b602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000160361347e575f808080613347565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608490fd5b61352691925060203d60201161352d575b61351e81836105d3565b8101906132b0565b905f6133af565b503d613514565b6020818303126104ee5780519067ffffffffffffffff82116104ee570181601f820112156104ee57805161356781610630565b9261357560405194856105d3565b818452602082840101116104ee5761071891602080850191016106a3565b805f52600d60205260405f205443146135b1576107189042906140e9565b505f90565b805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee576135f3835f52600760205260405f2090565b5416331460ff6136253361090f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b54168115613694575b50156104ee575f90613648835f52600960205260405f2090565b7fffffffffffffffffffffffff000000000000000000000000000000000000000081541690557fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8280a4565b90505f61362e565b9192835f52601660205260405f2054158015906137ad575b610fd057836136c2916137c9565b156104ee57825f52600760205273ffffffffffffffffffffffffffffffffffffffff9061373c8461372d8460405f20541695613702868216809814612def565b825f52600b60205260405f208054878116613782575b50506137238361548a565b61141683826154dc565b6137368461316f565b90615853565b6137468482615ad5565b43613759855f52600d60205260405f2090565b5516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b7fffffffffffffffffffffffff00000000000000000000000000000000000000001690555f80613718565b5060ff6137c2855f52601760205260405f2090565b54166136b4565b905f52600760205273ffffffffffffffffffffffffffffffffffffffff9060ff6138368360405f2054169284811680851495600b60205260405f20541614935f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541690821561384b575b5081156131a0575090565b91505f613840565b60405190613860826105b7565b5f6060838281528260208201528260408201520152565b90600f0b90600f0b029081600f0b91820361119957565b90600f0b90600f0b03906f7fffffffffffffffffffffffffffffff82137fffffffffffffffffffffffffffffffff8000000000000000000000000000000083121761119957565b90600f0b90600f0b01907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff83131761119957565b9060606002916139688151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b60208101516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000086549260801b169116178455604081015160018501550151910155565b91906139cb576106219161391c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6139ff613853565b90613a08613853565b925f806013549284151580613e37575b613a20610623565b945f86526020955f878201524260408201526060904360608201529082613e1a575b604082018051915f91834211613df3575b8a90613a6862093a8086989695949504612f83565b965f975b60ff8910613cc2575b5050509050613aa794508291508c8c87613a91611ec396601355565b613c1d575b5050505f52600360205260405f2090565b613ab6575b5050505050505050565b613b3a97613b349685808094019542875111613bc7575b5050500191825190428211613b47575b5050505050613b2f613b00613afa835f52601060205260405f2090565b54612f06565b9182613b14825f52601060205260405f2090565b554260408601524360608601525f52601160205260405f2090565b610a18565b906139bc565b5f80808080808080613aac565b5110613b55575b8080613add565b613b72613b8291613b6c613bbf95890151600f0b90565b9061388e565b91515f52601460205260405f2090565b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b5f8080613b4e565b613bda613be092613bff940151600f0b90565b906138d5565b83830151865114613c07575b613b8286515f52601460205260405f2090565b5f8281613acd565b613c1890613b6c858c0151600f0b90565b613bec565b613c6d613c7791613b6c611f1785613c66613c5f613c50613c448b613c7e9b0151600f0b90565b878c0151600f0b613b6c565b998d0199611f358b51600f0b90565b600f0b8952565b51600f0b90565b8651600f0b6138d5565b600f0b8552565b5f613c8d610dd78351600f0b90565b12613cb9575b505f613ca3610dd78551600f0b90565b12613cb1575b888c8c613a96565b5f8352613ca9565b5f90525f613c93565b613ccb90612f25565b5f9042811115613dc55750611f3a613d1591611f2b613d0e613d0442985b8d0196611f11610dd7613cfd8a51600f0b90565b928c612ec4565b8c51600f0b61388e565b600f0b8b52565b5f613d24610dd78951600f0b90565b12613dbd575b5f613d39610dd78351600f0b90565b12613db4575b5081938282528c613d6282890197611fa78951611fa1611f93611f8d8a80612ec4565b98428503613d875750505050505050613aa792611ec391439052905f89818080613a75565b60019192939495969750613da789611ec38c5f52600360205260405f2090565b0197959092919493613a6c565b5f90525f613d3f565b5f8752613d2a565b93613d159150611f3a90611f2b613d0e613d04613ded6120438a5f52601460205260405f2090565b93613ce9565b9150613e14613e09612066606087015143612ec4565b61187e845142612ec4565b91613a53565b9050613e316117dd835f52600360205260405f2090565b90613a42565b925060208101928351421080613f62575b613f22575b602089019389894287511180613f0c575b613ebc575b5050613e7b61204382515f52601460205260405f2090565b94519081613e8b575b5050613a18565b51919350908103613ea1575082915b5f80613e84565b612043613eb6915f52601460205260405f2090565b91613e9a565b613ef0613efe91613c666020613ee6613ed9613f059751600f0b90565b6301dfe20090600f0b0590565b600f0b9201918252565b611f11610dd7428a51612ec4565b600f0b8a52565b8989613e63565b505f613f1c610dd78451600f0b90565b13613e5e565b613f5d613f56613f48613f39613ed98651600f0b90565b600f0b60208b01908152613c66565b611f11610dd7428951612ec4565b600f0b8852565b613e4d565b505f613f72610dd78451600f0b90565b13613e48565b613f8281336137c9565b156140bf57805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104ee573390613fc6845f52600760205260405f2090565b54161460ff613ff73361090f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b541681156140b7575b50156104ee575f9061404561401d845f52600b60205260405f2090565b7fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8282827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258280a4614075836135b6565b614087836140828361316f565b61570c565b61409183826154dc565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4565b90505f614000565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b805f52601060205260405f20549081155f14614106575050505f90565b61412591613b2f61411f925f52601160205260405f2090565b506131a5565b6020810151906040810151925f84820394128185128116918513901516176111995761416e611f3a614164610dd79461071896600f0b90600f0b613877565b8351600f0b61388e565b5f61417a8251600f0b90565b600f0b126141895751600f0b90565b5f8152613c66565b73ffffffffffffffffffffffffffffffffffffffff9082828216926141b7841515612def565b6141c08361316f565b908116151580806143f0575b614206575b50506141dc91615ad5565b5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600190565b614211575b806141d1565b905061423e612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff8116156143bd5760016142806142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc084612bfd565b015b6001806142b961429186615bac565b612d858773ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01918054906104006142ca83612f06565b1161439357908895949392915f5b82811061435c575050505091614326614301614356936142fc876141dc99976156a0565b6156f7565b9173ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000825416179055565b9161420b565b83949596975061438561437f6143758385969795612ef1565b90549060031b1c90565b876156a0565b0190899695949392916142d8565b60046040517f8c390496000000000000000000000000000000000000000000000000000000008152fd5b60016143ea612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01614282565b508215156141cc565b6040519061440682610596565b5f6020838281520152565b919060155491614424610e1b8385612f35565b61448c61442f6143f9565b9161443b8151600f0b90565b926144546020830194855160208401528290600f0b9052565b61447061446986600f0b611f358551600f0b90565b600f0b8352565b61448682610e02895f52601260205260405f2090565b866139f7565b8115158061461a575b80614612575b61452f575b5160408051948552602085018390526002908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081015b0390a382612f35565b604080519283526020830191909152819081015b0390a1565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fa1576145e9614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f3575b50612def565b91925093506144a0565b61460c915060203d602011610f9a57610f8b81836105d3565b5f6145e3565b50600161449b565b506001614495565b6015549061462e6143f9565b93805190602081019182516020880152600f0b86528051600f0b907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff831317611199577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9661452a966146d39383528061472c575b506146cd82610e02875f52601260205260405f2090565b846139f7565b51906040519081525f6020820152600360408201524260608201527fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62460803392a360408051828152602081019290925290918291820190565b84525f6146b6565b92916147a96015549361474a610e1b8587612f35565b6147526143f9565b8151600f0b936147706020840195865160208501528390600f0b9052565b61478c61478587600f0b611f358651600f0b90565b600f0b8452565b80614900575b5061448682610e02895f52601260205260405f2090565b811515806148f9575b806148f1575b61482e575b5160408051948552602085018390526004908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490806080810161450d565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fa1576148e7614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f35750612def565b91925093506147bd565b5060016147b8565b505f6147b2565b84525f614792565b91906015549161491b610e1b8385612f35565b61492661442f6143f9565b81151580614a75575b80614a6d575b6149aa575b5160408051948552602085018390525f908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490806080810161450d565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fa157614a63614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f35750612def565b919250935061493a565b506001614935565b50600161492f565b9291614a936015549361474a610e1b8587612f35565b81151580614be2575b80614bdb575b614b18575b5160408051948552602085018390526005908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361451692919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490806080810161450d565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101839052939091906020856064815f7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fa157614bd1614516947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916145f35750612def565b9192509350614aa7565b505f614aa2565b506001614a9c565b9190614bf58361316f565b73ffffffffffffffffffffffffffffffffffffffff90818516805f526018602052614c5e8460405f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b838316928281168481847f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f5f80a4848103614c9e575b5050505050509050565b614e3c575b505050614cb5575b8080808080614c94565b614ce0612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615614e09576001614d196142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b6001906001614d2c61429186615bac565b01614d558773ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b5491805490610400614d678584612f35565b1161439357845f5b838110614deb57505050505f5b828110614da05750505050614d99929350614301614326916156f7565b805f614cab565b80614de5614dde8693614dd18c73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b905f5260205260405f2090565b54846156a0565b01614d7c565b614e01614dfb6143758386612ef1565b866156a0565b018590614d6f565b6001614e36612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01614d1b565b614e67612c738473ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9163ffffffff831615614f57576001614eaa614ea18673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc086612bfd565b01905b6001926001614ee6614ebe88615bac565b612d858973ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01918354935f5b858110614f13575050505050505090614326614301614f0b936156f7565b5f8080614ca3565b80614f22614375899385612ef1565b8486614f39610b87845f52600760205260405f2090565b1603614f47575b5001614eed565b614f5190876156a0565b5f614f40565b6001614f84612d058673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0190614ead565b9042018042116111995762093a80614fa39104612f83565b81156104ee5742811115611c4057614fba42612f14565b8111611c1657614fd9614fce600554612f56565b809481600555614191565b50614fef610db9845f52601260205260405f2090565b9061503f60155492615004610e1b8686612f35565b61500c6143f9565b8151600f0b9361502a6020840195865160208501528390600f0b9052565b61478c61478588600f0b611f358651600f0b90565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101849052926020846064815f7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1918215610fa1576150f5615141937f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c965f916145f35750612def565b516040517fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62433918061450d42878c84606091949392608082019582526020820152600160408201520152565b60408051928352602083019190915290a190565b906040820161516b611bda825162093a80900490565b905f915b60ff83106151b6575b505050506151a16151a1825f615195610dd76107189651600f0b90565b126141895751600f0b90565b6fffffffffffffffffffffffffffffffff1690565b6151bf90612f25565b5f929084811115615234575083925b60208601906151ff613f566151f56151e78551600f0b90565b611f11610dd789518b612ec4565b8951600f0b61388e565b85851461522d578161521c61522392611f356001969551600f0b90565b600f0b9052565b838352019161516f565b5050615178565b925061524b612043845f52601460205260405f2090565b6151ce565b61525c43831115612def565b5f90615270815f52601060205260405f2090565b545f905b608082106153a1575b5050611f3a6141646152a561411f6153399695613b2f613c66965f52601160205260405f2090565b93601354906152b4828261540f565b6152c96117dd825f52600360205260405f2090565b9281101561537d576117dd6117fb6152e092612f06565b9061530760406152f96060850151606087015190612ec4565b930151604085015190612ec4565b905b60408401519383615359575b50505050611f11610dd761532d6020880151600f0b90565b92604088015190612ec4565b5f600f82900b126135b1576fffffffffffffffffffffffffffffffff1690565b611fa19261186e615374969593606061310f94015190612ec4565b5f808080615315565b5061538c606083015143612ec4565b9061539b604084015142612ec4565b90615309565b909281811015615409576153c66153c06153bb8484612f35565b612f06565b60011c90565b908560026153e084613b2f885f52601160205260405f2090565b500154116153f55750600190935b0190615274565b939150615403600191612e97565b916153ee565b9261527d565b5f915f915b60808310615423575b50505090565b9091928281101561548457828101808211611199576001808201809211611199571c9082600261545b845f52600360205260405f2090565b0154116154705750600190935b019190615414565b93925061547e600191612e97565b92615468565b9261541d565b5f52600960205260405f20805473ffffffffffffffffffffffffffffffffffffffff81166154b6575050565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b815f52600760205273ffffffffffffffffffffffffffffffffffffffff61550e8160405f205416918316809214612def565b825f52600760205261554560405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b5f52600860205260405f2054917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019283116111995761560392816155945f935f52600f60205260405f2090565b5480831484146156115750826155cf6155de93614dd18773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b555f52600f60205260405f2090565b5573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b61560d8154612e97565b9055565b6155cf8385926156776156496155de97614dd18b73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b54806155cf84614dd18d73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b55614dd18773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b8054680100000000000000008110156105b2576156c291600182018155612ef1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829392549160031b92831b921b1916179055565b90600163ffffffff8093160191821161119957565b90919073ffffffffffffffffffffffffffffffffffffffff81161515808061584a575b61573a575b50509050565b615745575b80615734565b615770612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff8116156158165760016157a96142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01935b60019060016157bd61429186615bac565b018654915f5b8381106157e85750505050506157e1929350614301614326916156f7565b805f61573f565b806157f761437587938c612ef1565b838103615806575b50016157c3565b61581090856156a0565b5f6157ff565b6001615843612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01936157ac565b5083151561572f565b91909173ffffffffffffffffffffffffffffffffffffffff908184169181168281141580615acc575b615888575b5050505050565b6159cc575b5061589b575b808080615881565b6158c6612c738373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff8216156159995760016159096159008573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612cc085612bfd565b015b60018061594261591a87615bac565b612d858873ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b019180549061040061595383612f06565b11614393575f5b828110615981575050505061597a93926142fc61432693614301936156a0565b5f80615893565b8061599361437f614375879486612ef1565b0161595a565b60016159c6612d058573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0161590b565b6159f7612c738273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615615a99576001615a306142778473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b6001906001615a4361429186615bac565b018154915f5b838110615a6b57505050505090614326614301615a65936156f7565b5f61588d565b80615a7a614375879385612ef1565b8a8103615a89575b5001615a49565b615a9390856156a0565b5f615a82565b6001615ac6612d058473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615a32565b5083151561587c565b615ba291805f52602060078152600f73ffffffffffffffffffffffffffffffffffffffff91604092615b0c81855f20541615612def565b845f5260078252615b5a86855f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b85165f5260088152825f205493600e8252835f20855f52825280845f20555f52525f205573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b61560d8154612f06565b73ffffffffffffffffffffffffffffffffffffffff165f52601a60205263ffffffff60405f20541680151580615beb575b156107185761071890612bfd565b50601960205260405f20615c034291612cc084612bfd565b5414615bdd56fea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba00000000000000000000000011e23dfb5a9b4d52dba1d6a17086e796e408f51e
-----Decoded View---------------
Arg [0] : token_addr (address): 0x4D85bA8c3918359c78Ed09581E5bc7578ba932ba
Arg [1] : art_proxy (address): 0x11E23Dfb5A9b4D52DBa1D6A17086e796e408f51e
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba
Arg [1] : 00000000000000000000000011e23dfb5a9b4d52dba1d6a17086e796e408f51e
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.