Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 420 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw | 60788745 | 11 days ago | IN | 0 S | 0.00201701 | ||||
| Create_lock | 60219559 | 19 days ago | IN | 0 S | 0.02533569 | ||||
| Withdraw | 60219258 | 19 days ago | IN | 0 S | 0.01898974 | ||||
| Withdraw | 59842382 | 24 days ago | IN | 0 S | 0.01505542 | ||||
| Withdraw | 59351367 | 30 days ago | IN | 0 S | 0.0230395 | ||||
| Withdraw | 58827975 | 38 days ago | IN | 0 S | 0.02043666 | ||||
| Withdraw | 57854584 | 49 days ago | IN | 0 S | 0.01297292 | ||||
| Withdraw | 57742221 | 51 days ago | IN | 0 S | 0.02015229 | ||||
| Create_lock | 56881441 | 61 days ago | IN | 0 S | 0.02458577 | ||||
| Withdraw | 56881032 | 61 days ago | IN | 0 S | 0.020945 | ||||
| Withdraw | 55713350 | 74 days ago | IN | 0 S | 0.02154348 | ||||
| Create_lock | 52859443 | 92 days ago | IN | 0 S | 0.02390609 | ||||
| Withdraw | 52858927 | 92 days ago | IN | 0 S | 0.01490802 | ||||
| Create_lock | 52622737 | 93 days ago | IN | 0 S | 0.03031539 | ||||
| Withdraw | 51658634 | 100 days ago | IN | 0 S | 0.01634523 | ||||
| Create_lock | 51091185 | 105 days ago | IN | 0 S | 0.0229698 | ||||
| Withdraw | 51091134 | 105 days ago | IN | 0 S | 0.0017583 | ||||
| Withdraw | 51090855 | 105 days ago | IN | 0 S | 0.01884234 | ||||
| Withdraw | 49944847 | 114 days ago | IN | 0 S | 0.0228921 | ||||
| Create_lock | 48252627 | 128 days ago | IN | 0 S | 0.02208244 | ||||
| Withdraw | 48252146 | 128 days ago | IN | 0 S | 0.01975153 | ||||
| Withdraw | 46846101 | 139 days ago | IN | 0 S | 0.01490802 | ||||
| Withdraw | 46531271 | 142 days ago | IN | 0 S | 0.01775143 | ||||
| Create_lock | 45970625 | 147 days ago | IN | 0 S | 0.02208244 | ||||
| Withdraw | 45970366 | 147 days ago | IN | 0 S | 0.01283892 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
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 { ERC20 } from "solady/tokens/ERC20.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;
uint8 public immutable tokenDecimals;
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;
tokenDecimals = ERC20(token_addr).decimals();
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 veBTC";
string public constant symbol = "veBTC";
string public constant version = "1.0.0";
uint8 public constant decimals = 18;
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), _scaleDecimals(_value, 18, tokenDecimals)));
}
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, _scaleDecimals(_value, tokenDecimals, 18), 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, _scaleDecimals(_value, tokenDecimals, 18), 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, _scaleDecimals(_value, tokenDecimals, 18), 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, _scaleDecimals(value, 18, tokenDecimals)));
// Burn the NFT
_burn(_tokenId);
emit Withdraw(msg.sender, _tokenId, value, block.timestamp);
emit Supply(supply_before, supply_before - value);
}
function _scaleDecimals(uint256 amount, uint8 from, uint8 to) internal pure returns (uint256) {
if (from == to) {
return amount;
}
if (from > to) {
return amount / (10 ** (from - to));
} else {
return amount * (10 ** (to - from));
}
}
/*///////////////////////////////////////////////////////////////
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: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
/// minting and transferring zero tokens, as well as self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
/// the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The total supply has overflowed.
error TotalSupplyOverflow();
/// @dev The allowance has overflowed.
error AllowanceOverflow();
/// @dev The allowance has underflowed.
error AllowanceUnderflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Insufficient allowance.
error InsufficientAllowance();
/// @dev The permit is invalid.
error InvalidPermit();
/// @dev The permit has expired.
error PermitExpired();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The storage slot for the total supply.
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
/// @dev The balance slot of `owner` is given by:
/// ```
/// mstore(0x0c, _BALANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev The allowance slot of (`owner`, `spender`) is given by:
/// ```
/// mstore(0x20, spender)
/// mstore(0x0c, _ALLOWANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let allowanceSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev The nonce slot of `owner` is given by:
/// ```
/// mstore(0x0c, _NONCES_SLOT_SEED)
/// mstore(0x00, owner)
/// let nonceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 private constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("1")`.
bytes32 private constant _VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
bytes32 private constant _PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns (string memory);
/// @dev Returns the decimals places of the token.
function decimals() public view virtual returns (uint8) {
return 18;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-2612 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For more performance, override to return the constant value
/// of `keccak256(bytes(name()))` if `name()` will never change.
function _constantNameHash() internal view virtual returns (bytes32 result) {}
/// @dev Returns the current nonce for `owner`.
/// This value is used to compute the signature for EIP-2612 permit.
function nonces(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
/// authorized by a signed approval by `owner`.
///
/// Emits a {Approval} event.
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
// Revert if the block timestamp is greater than `deadline`.
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
revert(0x1c, 0x04)
}
let m := mload(0x40) // Grab the free memory pointer.
// Clean the upper 96 bits.
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
// Compute the nonce slot and load its value.
mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
// Prepare the domain separator.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
mstore(0x2e, keccak256(m, 0xa0))
// Prepare the struct hash.
mstore(m, _PERMIT_TYPEHASH)
mstore(add(m, 0x20), owner)
mstore(add(m, 0x40), spender)
mstore(add(m, 0x60), value)
mstore(add(m, 0x80), nonceValue)
mstore(add(m, 0xa0), deadline)
mstore(0x4e, keccak256(m, 0xc0))
// Prepare the ecrecover calldata.
mstore(0x00, keccak256(0x2c, 0x42))
mstore(0x20, and(0xff, v))
mstore(0x40, r)
mstore(0x60, s)
let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
// If the ecrecover fails, the returndatasize will be 0x00,
// `owner` will be checked if it equals the hash at 0x00,
// which evaluates to false (i.e. 0), and we will revert.
// If the ecrecover succeeds, the returndatasize will be 0x20,
// `owner` will be compared against the returned address at 0x20.
if iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
revert(0x1c, 0x04)
}
// Increment and store the updated nonce.
sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
// Compute the allowance slot and store the value.
// The `owner` is already at slot 0x20.
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
// Emit the {Approval} event.
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Grab the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
/// @solidity memory-safe-assembly
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
// Revert if the total supply overflows.
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Subtract and store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Moves `amount` of tokens from `from` to `to`.
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL ALLOWANCE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let owner_ := shl(96, owner)
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any transfer of tokens.
/// This includes minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/// @dev Hook that is called after any transfer of tokens.
/// This includes minting and burning.
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// 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-upgradeable/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": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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":[],"name":"tokenDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"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
60c060409080825234620001f75781816200605e80380380916200002482856200020d565b833981010312620001f7576200003a8162000245565b906200004a602080920162000245565b6001918060ff1994848660065416176006558060805260018060a01b0319338187541617865560018060a01b03809516906002541617600255600487518095819363313ce56760e01b8352165afa801562000203575f90620001be575b6004925060a0525f805260038152845f2043600282015583429101556301ffc9a760e01b5f5252825f2081838254161790556380ac58cd60e01b5f52825f208183825416179055635b5e139f60e01b5f52825f20918254161790556005549051905f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef823083838180a430908280a4615e0390816200025b8239608051818181610ed101528181612c100152818161477401528181614a6501528181614bd101528181614d2f0152614e8c015260a051818181610e520152818161120001528181611a9801528181612a20015281816146ef01528181614a2601528181614b9201528181614cf001528181614e4d01526152ff0152f35b50908181813d8311620001fb575b620001d881836200020d565b81010312620001f757519060ff82168203620001f757600491620000a7565b5f80fd5b503d620001cc565b85513d5f823e3d90fd5b601f909101601f19168101906001600160401b038211908210176200023157604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001f75756fe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146104cf578063047fc9aa146104ca57806306fdde03146104c55780630758c7d8146104c0578063081812fc146104bb578063095cf5c6146104b6578063095ea7b3146104b15780630d6a2033146104ac5780631376f3da146104a757806316b7a011146104a257806318160ddd1461049d5780631c984bc31461049857806320606b701461049357806323b872dd1461048e57806325a58b56146104895780632e1a7d4d146104845780632e720f7d1461047f5780632f745c591461047a578063313ce567146104755780633a46b1a8146104705780633b97e8561461046b57806342842e0e14610466578063430c208114610461578063461f711c1461045c57806346c96aac146104575780634bc2a6571461045257806354fd4d501461044d5780635594a0451461044857806356afe74414610443578063587cde1e1461043e5780635c19a95c146104395780635f5b0c32146104345780636352211e1461042f57806365fc38731461042a5780636f548837146104255780636fcfff451461042057806370a082311461041b5780637116c60c1461040257806371197484146104165780637ecebe001461041157806385f2aef21461040c5780638c2c9baf146104075780638e539e8c146104025780638fbb38ff146103fd578063900cf0cf146103f857806395d89b41146103f3578063981b24d0146103ee578063986b7d8a146103e95780639ab24eb0146103e4578063a183af52146103df578063a22cb465146103da578063a4d855df146103d5578063b29b8830146103d0578063b45a3c0e146103cb578063b88d4fde146103c6578063c1f0fb9f146103c1578063c2c4c5c1146103bc578063c3cda520146103b7578063c87b56dd146103b2578063d1c2babb146103ad578063d1febfb9146103a8578063d2aef457146103a3578063d4e54c3b1461039e578063e0514aba14610399578063e441135c14610394578063e7a324dc1461038f578063e7e242d41461038a578063e9237e1214610385578063e985e9c514610380578063ee99fe281461037b578063ef39933d14610376578063f1127ed814610371578063f8a057631461036c578063fbd3a29d14610367578063fc0c546a146103625763fd4a77f11461035d575f80fd5b612c34565b612be4565b612b89565b612b5c565b612aef565b612a4b565b612992565b612926565b6128e6565b6128c8565b61288e565b612864565b612843565b6127ac565b6126fd565b61269e565b612575565b61241a565b61214c565b611eba565b611e53565b611dcb565b611d95565b611d1d565b611c06565b611b27565b6119fd565b6119da565b611960565b611817565b6117ba565b61179d565b61176e565b61168a565b61174d565b61171a565b6116d5565b6116a8565b611645565b6115fa565b6115d0565b611562565b611522565b611506565b6114bf565b61147e565b6113ef565b6113bc565b6113a1565b611302565b6112d0565b61128e565b61125d565b611224565b6111e7565b611132565b611117565b6110c6565b61105e565b610d37565b610d1d565b610d02565b610c86565b610c4d565b610c2a565b610aaf565b610a43565b6109d1565b610881565b610819565b6107d9565b6107a4565b61072b565b61055c565b610502565b7fffffffff000000000000000000000000000000000000000000000000000000008116036104fe57565b5f80fd5b346104fe5760206003193601126104fe577fffffffff00000000000000000000000000000000000000000000000000000000600435610540816104d4565b165f526004602052602060ff60405f2054166040519015158152f35b346104fe575f6003193601126104fe576020601554604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176105c257604052565b610579565b6080810190811067ffffffffffffffff8211176105c257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105c257604052565b60405190610631826105a6565b565b60405190610631826105c7565b67ffffffffffffffff81116105c257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60405190610687826105a6565b600b82527f52696e67732076654254430000000000000000000000000000000000000000006020830152565b5f5b8381106106c45750505f910152565b81810151838201526020016106b5565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610710815180928187528780880191016106b3565b0116010190565b9060206107289281815201906106d4565b90565b346104fe575f6003193601126104fe5761075a61074661067a565b6040519182916020835260208301906106d4565b0390f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104fe57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104fe57565b346104fe5760406003193601126104fe5760206107cb6107c261075e565b60243590612d42565b63ffffffff60405191168152f35b346104fe5760206003193601126104fe576004355f52600b602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104fe5760206003193601126104fe5761083261075e565b6001549073ffffffffffffffffffffffffffffffffffffffff80831633036104fe577fffffffffffffffffffffffff000000000000000000000000000000000000000091169116176001555f80f35b346104fe5760406003193601126104fe5761089a61075e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe57808316928284146104fe576109516109a3926108ed875f52600760205260405f2090565b5416331460ff6109423361091f8873ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541681156109c9575b50612ee3565b610963855f52600b60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b90505f61094b565b346104fe5760206003193601126104fe576004355f526016602052602060405f2054604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b90633b9aca00811015610a3e5760030201905f90565b6109fb565b346104fe5760406003193601126104fe576024356004355f52601160205260405f2090633b9aca008110156104fe57610a7b91610a28565b508054600182015460029092015460408051600f84810b8252608094851d900b602082015290810193909352606083015290f35b346104fe5760406003193601126104fe5761075a610acb61075e565b602435805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f20541690831691610bd7610b8184841495600960205261091f86610b47610b2e60405f2073ffffffffffffffffffffffffffffffffffffffff90541690565b73ffffffffffffffffffffffffffffffffffffffff1690565b1496610bb1610b2e610b97610b88610b818761091f8d73ffffffffffffffffffffffffffffffffffffffff165f52600a60205260405f2090565b5460ff1690565b985f52600b60205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b149573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b928415610c22575b508315610c1a575b508215610c12575b508115610c0a575b5060405190151581529081906020820190565b90505f610bf7565b91505f610bef565b92505f610be7565b93505f610bdf565b346104fe575f6003193601126104fe576020610c45426132af565b604051908152f35b346104fe5760406003193601126104fe576004355f52601160205260206001610c7b60243560405f20610a28565b500154604051908152f35b346104fe575f6003193601126104fe5760206040517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a8668152f35b60031960609101126104fe5773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104fe579160243590811681036104fe579060443590565b346104fe57610d1b610d1336610cc0565b90339261376f565b005b346104fe575f6003193601126104fe576020604051438152f35b346104fe576020806003193601126104fe57600435600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655610d94610d8f823361389c565b612eea565b610da6815f52601660205260405f2090565b5415801590611043575b61101957610dce610dc9825f52601260205260405f2090565b612f1e565b90828201514210610fef57610eb790610df1610deb8451600f0b90565b600f0b90565b90610e1c610dfd610624565b5f81525f87820152610e17835f52601260205260405f2090565b612f3f565b610e4c60155494610e35610e308588612fbf565b601555565b610e3d610624565b905f82525f8883015283613aca565b84610e777f00000000000000000000000000000000000000000000000000000000000000008461405c565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915293849081906044820190565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1948515610fea577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c95610f7c94610f32925f92610fbd575b5050612eea565b610f3b8161412c565b6040805191825260208201839052429082015233907f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca9490606090a282612fbf565b60408051928352602083019190915290a1610d1b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b610fdc9250803d10610fe3575b610fd481836105e3565b810190612fcc565b5f80610f2b565b503d610fca565b612fe1565b60046040517ff6fafba0000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3ca9d617000000000000000000000000000000000000000000000000000000008152fd5b50611059610b81825f52601760205260405f2090565b610db0565b346104fe5760206003193601126104fe5761107761075e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104fe57167fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002555f80f35b346104fe5760406003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff6110f461075e565b165f52600e60205260405f206024355f52602052602060405f2054604051908152f35b346104fe575f6003193601126104fe57602060405160128152f35b346104fe5760406003193601126104fe5761114b61075e565b6024359073ffffffffffffffffffffffffffffffffffffffff61116e8383612d42565b91165f5260196020526001611195819260405f209063ffffffff165f5260205260405f2090565b01915f9183545f925b8184106111b057604051858152602090f35b909192936111ce826111c28789612fec565b90549060031b1c61429d565b81018091116111e25793830192919061119e565b612c9e565b346104fe575f6003193601126104fe57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104fe5761123236610cc0565b60405191602083019383851067ffffffffffffffff8611176105c257610d1b946040525f8452613403565b346104fe5760406003193601126104fe57602061128461127b61075e565b6024359061389c565b6040519015158152f35b346104fe5760206003193601126104fe576004355f52601060205260206112c060405f20546011835260405f20610a28565b505460801d60405190600f0b8152f35b346104fe575f6003193601126104fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346104fe5760206003193601126104fe5761131b61075e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104fe57167fffffffffffffffffffffffff00000000000000000000000000000000000000005f5416175f555f80f35b60405190611375826105a6565b600582527f312e302e300000000000000000000000000000000000000000000000000000006020830152565b346104fe575f6003193601126104fe5761075a610746611368565b346104fe575f6003193601126104fe57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346104fe5760406003193601126104fe5760043567ffffffffffffffff8082116104fe57366023830112156104fe5781600401359081116105c2578060051b916020926040519261144360208301856105e3565b835260246020840191830101913683116104fe57602401905b82821061146f57610d1b602435856130d0565b8135815290840190840161145c565b346104fe5760206003193601126104fe5760206114a161149c61075e565b613242565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346104fe5760206003193601126104fe576114d861075e565b73ffffffffffffffffffffffffffffffffffffffff8116156114ff575b610d1b9033614f04565b50336114f5565b346104fe575f6003193601126104fe5760206040516104008152f35b346104fe5760206003193601126104fe576004355f526007602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104fe5760406003193601126104fe57600654600160ff8216036104fe5760209060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008092161760065560016115bf336024356004356152a5565b916006541617600655604051908152f35b346104fe5760206003193601126104fe576004355f52600d602052602060405f2054604051908152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff61162861075e565b165f52601a602052602063ffffffff60405f205416604051908152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff61167361075e565b165f526008602052602060405f2054604051908152f35b346104fe5760206003193601126104fe576020610c456004356132af565b346104fe5760206003193601126104fe576004355f526014602052602060405f2054600f0b604051908152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff61170361075e565b165f52601b602052602060405f2054604051908152f35b346104fe575f6003193601126104fe57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346104fe5760406003193601126104fe576020610c4560243560043561543c565b346104fe5760206003193601126104fe576004355f526017602052602060ff60405f2054166040519015158152f35b346104fe575f6003193601126104fe576020601354604051908152f35b346104fe575f6003193601126104fe5761075a6040516117d9816105a6565b600581527f766542544300000000000000000000000000000000000000000000000000000060208201526040519182916020835260208301906106d4565b346104fe5760206003193601126104fe5761075a6118ba60043561183d43821115612eea565b6118b460135461184d81846155fb565b92611868611863855f52600360205260405f2090565b613278565b935f928110156119145761186361188161188f92613001565b5f52600360205260405f2090565b606085019081516060820193845182036118ca575b50505050505b6040830151613030565b90615341565b6040519081529081906020820190565b61190a9550916118f460406118e66118fa946119049796612fbf565b92015160408a015190612fbf565b906130ae565b9251905190612fbf565b906130c1565b5f808080806118a4565b50606084019081519043820361192d575b5050506118aa565b61195893506119426119049261195092612fbf565b6118f4604088015142612fbf565b915143612fbf565b5f8080611925565b346104fe5760206003193601126104fe5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104fe57805f52601660205260405f2054907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111e2575f52601660205260405f20555f80f35b346104fe5760206003193601126104fe576020610c456119f861075e565b6132ce565b346104fe5760406003193601126104fe5760043560243590600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611a58610d8f823361389c565b611a6d610dc9825f52601260205260405f2090565b90611a79831515612eea565b8151600f0b15611af3576020820151421015610fef57611abd611ac3937f0000000000000000000000000000000000000000000000000000000000000000906140cc565b906145c5565b610d1b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b60046040517fba112c93000000000000000000000000000000000000000000000000000000008152fd5b801515036104fe57565b346104fe5760406003193601126104fe57611b4061075e565b602435611b4c81611b1d565b611bd681611ba673ffffffffffffffffffffffffffffffffffffffff851694611b7733871415612eea565b335f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b346104fe5760406003193601126104fe57600435600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611c5d610d8f823361389c565b611c72610dc9825f52601260205260405f2090565b611c92611c8d611c8460243542613030565b62093a80900490565b61307e565b602082014281511115610fef578251600f0b15611af35751811115611cf357611cba4261300f565b8111611cc957611ac39261480c565b60046040517fd6946a43000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd5bb0fd9000000000000000000000000000000000000000000000000000000008152fd5b346104fe5760406003193601126104fe57602060ff611d89611d3d61075e565b73ffffffffffffffffffffffffffffffffffffffff611d5a610781565b91165f52600a845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b346104fe5760206003193601126104fe576004355f5260126020526040805f2060018154600f0b91015482519182526020820152f35b346104fe5760806003193601126104fe57611de461075e565b611dec610781565b6064359167ffffffffffffffff83116104fe57366023840112156104fe57826004013591611e1983610640565b92611e2760405194856105e3565b80845236602482870101116104fe576020815f926024610d1b9801838801378501015260443591613403565b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff5f541633036104fe576004355f52601760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690555f80f35b346104fe575f6003193601126104fe57604051611ed6816105a6565b5f81525f6020809201525f6020604051611eef816105a6565b8281520152611efc613926565b50611f05613926565b5060135490611f12610633565b905f82525f6020830152426040830152606091436060820152918361212f575b60408301918251925f92844211612103575b959390611f5862093a80839594950461307e565b965f975b60ff8910611f7b575b610d1b88611f768961188181601355565b6139ef565b611f8490613020565b5f9390428111156120c45750611ff4611fed610deb95611fde611fd78c611fd2611fca8d611fc4429d8e925b86019b611fbe8d51600f0b90565b93612fbf565b9061394a565b9151600f0b90565b613961565b600f0b8c52565b8351600f0b6139a8565b6139a8565b600f0b8252565b5f612003610deb8a51600f0b90565b126120bc575b5f612018610deb8351600f0b90565b126120b3575b5082948383526120618289019761205a89516120546120466120408a80612fbf565b8b6130ae565b670de0b6b3a7640000900490565b90613030565b8952613001565b984285036120865750505050505050610d1b92611f7691439052905f80808080611f65565b600191929394959697506120a689611f768c5f52600360205260405f2090565b0197959291909493611f5c565b5f90525f61201e565b5f8852612009565b9350611ff4611fed87611fde611fd78c611fd2611fca8b611fc4610deb6120fd6120f6845f52601460205260405f2090565b54600f0b90565b98611fb0565b925061212961211e612119606088015143612fbf565b613096565b611904835142612fbf565b92611f44565b9150612146611863845f52600360205260405f2090565b91611f32565b346104fe5760c06003193601126104fe5761216561075e565b6064359060243560443560ff841684036104fe5773ffffffffffffffffffffffffffffffffffffffff916121a88385166121a133821415612ee3565b1515612ee3565b6121b061067a565b5f81516020809301209661222361230b6123176121cb611368565b805190870120604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818a01908152602081019e909e528d8201929092524660608e01523060808e01529b9093849060a0830190565b03936122557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018352826105e3565b519020928a8c516122c8816122bc8d8d8d8401968760609194939273ffffffffffffffffffffffffffffffffffffffff60808301967fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf845216602083015260408201520152565b038481018352826105e3565b5190208c51938491898301968790916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b039081018352826105e3565b519020885190815260ff919091166020820152608435604082015260a435606082015281805260809060015afa15610fea575f51928316156123f15761237b8373ffffffffffffffffffffffffffffffffffffffff165f52601b60205260405f2090565b9081549161238883613051565b9055036123c857421161239f57610d1b9250614f04565b600483517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b600485517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b346104fe5760206003193601126104fe5760043573ffffffffffffffffffffffffffffffffffffffff612458610b97835f52600760205260405f2090565b161561254b575f612474610dc9835f52601260205260405f2090565b612499610b2e610b2e60025473ffffffffffffffffffffffffffffffffffffffff1690565b6124a3428561429d565b6124b6610deb60208501519451600f0b90565b94612507604051968795869485947fdd9ec149000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b03915afa8015610fea5761075a915f91612529575b5060405191829182610717565b61254591503d805f833e61253d81836105e3565b810190613607565b5f61251c565b60046040517f21f2b69c000000000000000000000000000000000000000000000000000000008152fd5b346104fe5760406003193601126104fe5760043560243561259e825f52601660205260405f2090565b5415801590612683575b61101957816125bc82610d1b941415612ee3565b6125ce6125c9823361389c565b612ee3565b6125db6125c9833361389c565b6125f0610dc9825f52601260205260405f2090565b90612606610dc9845f52601260205260405f2090565b92612676612618610deb8551600f0b90565b9261267160208601516020880151808210155f1461267b5750955b61265961263e610624565b5f81525f6020820152610e17855f52601260205260405f2090565b612661610624565b905f82525f602083015283613aca565b61412c565b614920565b905095612633565b50612699610b81835f52601760205260405f2090565b6125a8565b346104fe5760206003193601126104fe576004355f52600360205260405f20805461075a600260018401549301546040519383859460801d90600f0b85909493926060926080830196600f0b8352600f0b602083015260408201520152565b346104fe5760406003193601126104fe5761271661075e565b60243561272281611b1d565b61277c81611ba673ffffffffffffffffffffffffffffffffffffffff85169461274d33871415612eea565b335f52600a60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60405190151581527fccb38c6086037bf3d363115fc480b43f15aeb617ec6296b6a8ab6c70a1560adc60203392a3005b346104fe5760606003193601126104fe5760443573ffffffffffffffffffffffffffffffffffffffff811681036104fe57600654600160ff8216036104fe57600161282961075a9360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00809516176006556024356004356152a5565b916006541617600655604051918291829190602083019252565b346104fe5760406003193601126104fe576020610c4560243560043561429d565b346104fe5760206003193601126104fe576004355f526010602052602060405f2054604051908152f35b346104fe575f6003193601126104fe5760206040517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b346104fe5760206003193601126104fe576020610c45600435613666565b346104fe5760206003193601126104fe576004355f526009602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104fe5760406003193601126104fe57602060ff611d8961294661075e565b73ffffffffffffffffffffffffffffffffffffffff612963610781565b91165f52600c845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b346104fe5760406003193601126104fe5760043560243590600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006002911617600655805f5260126020526129f560405f20612f1e565b90612a01831515612ee3565b8151600f0b15611af3576020820151421015610fef57612a45611ac3937f0000000000000000000000000000000000000000000000000000000000000000906140cc565b90614ae4565b346104fe5760406003193601126104fe57612a6461075e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe57808316928284146104fe57612ab7612ac9926108ed875f52600760205260405f2090565b610963855f52600960205260405f2090565b7fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad5f80a4005b346104fe5760406003193601126104fe57612b0861075e565b6024359063ffffffff821682036104fe5760209173ffffffffffffffffffffffffffffffffffffffff612b5392165f526019835260405f209063ffffffff165f5260205260405f2090565b54604051908152f35b346104fe5760206003193601126104fe576004355f5260126020526020600160405f200154604051908152f35b346104fe5760206003193601126104fe5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104fe57805f52601660205260405f205490600182018092116111e2575f52601660205260405f20555f80f35b346104fe575f6003193601126104fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff5f541633036104fe576004355f52601760205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790555f80f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff809316019182116111e257565b63ffffffff91821690821603919082116111e257565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b90612d78612d6e8373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b5463ffffffff1690565b9163ffffffff80841615612ecf5782612dcd612db28473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb87612ccb565b63ffffffff165f5260205260405f2090565b541115612ed75782612e0d612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b5f805260205260405f2090565b5411612ecf579190612e1f5f94612ccb565b83851684821611612e31575050505090565b612e53612e4d612e418784612cff565b60011c637fffffff1690565b82612cff565b94612e9386612e808573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b9063ffffffff165f5260205260405f2090565b54848103612ea45750505050505090565b93809596929394105f14612ebd5750935b929190612e1f565b949150612ec990612ccb565b90612eb5565b505050505f90565b50505061072890612ccb565b156104fe57565b15612ef157565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b90604051612f2b816105a6565b6020600182948054600f0b84520154910152565b906020600191612f8b8151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b0151910155565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111e257565b919082039182116111e257565b908160209103126104fe575161072881611b1d565b6040513d5f823e3d90fd5b8054821015610a3e575f5260205f2001905f90565b90600182018092116111e257565b906301dfe20082018092116111e257565b9062093a8082018092116111e257565b919082018092116111e257565b8051821015610a3e5760209160051b010190565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146111e25760010190565b9062093a80918281029281840414901517156111e257565b90670de0b6b3a764000091808302928304036111e257565b818102929181159184041417156111e257565b81156130cb570490565b612d15565b6130e2825f52601660205260405f2090565b5415801590613227575b611019576130fd6125c9833361389c565b613112610b97835f52600760205260405f2090565b90613128610dc9845f52601260205260405f2090565b9260208401519161313d610deb8651600f0b90565b90613149821515612ee3565b613158610e3083601554612fbf565b5f928151965f5b88811061320857509061267161317a9261265961263e610624565b42841115611cf35761318b4261300f565b8411611cc9575f5b8681106131a35750505050505050565b60019061320260056131bd6131b88254613051565b600555565b546131c8818a614345565b50876131e7886131e26131db878a61303d565b518a6130ae565b6130c1565b6131fc610dc9845f52601260205260405f2090565b92614c49565b01613193565b94613220600191613219888761303d565b5190613030565b950161315f565b5061323d610b81835f52601760205260405f2090565b6130ec565b73ffffffffffffffffffffffffffffffffffffffff8082165f52601860205260405f20541680155f14613273575090565b905090565b90604051613285816105c7565b606060028294805480600f0b855260801d600f0b6020850152600181015460408501520154910152565b610728906013545f5260036020526132c960405f20613278565b615341565b73ffffffffffffffffffffffffffffffffffffffff81165f52601a60205263ffffffff60405f20541690811561337d5761333561332b60019273ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb8394612ccb565b015f9181545f925b81841061334b575050505090565b9091929361336961335c8684612fec565b9054429160031b1c61429d565b81018091116111e25793830192919061333d565b50505f90565b908160209103126104fe5751610728816104d4565b9092610728949360809373ffffffffffffffffffffffffffffffffffffffff8092168452166020830152604082015281606082015201906106d4565b3d156133fe573d906133e582610640565b916133f360405193846105e3565b82523d5f602084013e565b606090565b929190916134133383858761376f565b823b613420575b50505050565b613475926020925f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c85523360048601613398565b0393165af15f91816135d6575b506135215761348f6133d4565b8051908161351c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608490fd5b602001fd5b7fffffffff000000000000000000000000000000000000000000000000000000001603613551575f80808061341a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608490fd5b6135f991925060203d602011613600575b6135f181836105e3565b810190613383565b905f613482565b503d6135e7565b6020818303126104fe5780519067ffffffffffffffff82116104fe570181601f820112156104fe57805161363a81610640565b9261364860405194856105e3565b818452602082840101116104fe5761072891602080850191016106b3565b805f52600d60205260405f205443146136845761072890429061429d565b505f90565b805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe576136c6835f52600760205260405f2090565b5416331460ff6136f83361091f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b54168115613767575b50156104fe575f9061371b835f52600960205260405f2090565b7fffffffffffffffffffffffff000000000000000000000000000000000000000081541690557fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8280a4565b90505f613701565b9192835f52601660205260405f205415801590613880575b61101957836137959161389c565b156104fe57825f52600760205273ffffffffffffffffffffffffffffffffffffffff9061380f846138008460405f205416956137d5868216809814612eea565b825f52600b60205260405f208054878116613855575b50506137f683615676565b61149c83826156c8565b61380984613242565b90615a3f565b6138198482615cc1565b4361382c855f52600d60205260405f2090565b5516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b7fffffffffffffffffffffffff00000000000000000000000000000000000000001690555f806137eb565b5060ff613895855f52601760205260405f2090565b5416613787565b905f52600760205273ffffffffffffffffffffffffffffffffffffffff9060ff6139098360405f2054169284811680851495600b60205260405f20541614935f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541690821561391e575b508115613273575090565b91505f613913565b60405190613933826105c7565b5f6060838281528260208201528260408201520152565b90600f0b90600f0b029081600f0b9182036111e257565b90600f0b90600f0b03906f7fffffffffffffffffffffffffffffff82137fffffffffffffffffffffffffffffffff800000000000000000000000000000008312176111e257565b90600f0b90600f0b01907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111e257565b906060600291613a3b8151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b60208101516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000086549260801b169116178455604081015160018501550151910155565b9190613a9e57610631916139ef565b7f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b613ad2613926565b90613adb613926565b925f806013549284151580613f0a575b613af3610633565b945f86526020955f878201524260408201526060904360608201529082613eed575b604082018051915f91834211613ec6575b8a90613b3b62093a808698969594950461307e565b965f975b60ff8910613d95575b5050509050613b7a94508291508c8c87613b64611f7696601355565b613cf0575b5050505f52600360205260405f2090565b613b89575b5050505050505050565b613c0d97613c079685808094019542875111613c9a575b5050500191825190428211613c1a575b5050505050613c02613bd3613bcd835f52601060205260405f2090565b54613001565b9182613be7825f52601060205260405f2090565b554260408601524360608601525f52601160205260405f2090565b610a28565b90613a8f565b5f80808080808080613b7f565b5110613c28575b8080613bb0565b613c45613c5591613c3f613c9295890151600f0b90565b90613961565b91515f52601460205260405f2090565b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b5f8080613c21565b613cad613cb392613cd2940151600f0b90565b906139a8565b83830151865114613cda575b613c5586515f52601460205260405f2090565b5f8281613ba0565b613ceb90613c3f858c0151600f0b90565b613cbf565b613d40613d4a91613c3f611fca85613d39613d32613d23613d178b613d519b0151600f0b90565b878c0151600f0b613c3f565b998d0199611fe88b51600f0b90565b600f0b8952565b51600f0b90565b8651600f0b6139a8565b600f0b8552565b5f613d60610deb8351600f0b90565b12613d8c575b505f613d76610deb8551600f0b90565b12613d84575b888c8c613b69565b5f8352613d7c565b5f90525f613d66565b613d9e90613020565b5f9042811115613e985750611fed613de891611fde613de1613dd742985b8d0196611fc4610deb613dd08a51600f0b90565b928c612fbf565b8c51600f0b613961565b600f0b8b52565b5f613df7610deb8951600f0b90565b12613e90575b5f613e0c610deb8351600f0b90565b12613e87575b5081938282528c613e358289019761205a89516120546120466120408a80612fbf565b98428503613e5a5750505050505050613b7a92611f7691439052905f89818080613b48565b60019192939495969750613e7a89611f768c5f52600360205260405f2090565b0197959092919493613b3f565b5f90525f613e12565b5f8752613dfd565b93613de89150611fed90611fde613de1613dd7613ec06120f68a5f52601460205260405f2090565b93613dbc565b9150613ee7613edc612119606087015143612fbf565b611904845142612fbf565b91613b26565b9050613f04611863835f52600360205260405f2090565b90613b15565b925060208101928351421080614035575b613ff5575b602089019389894287511180613fdf575b613f8f575b5050613f4e6120f682515f52601460205260405f2090565b94519081613f5e575b5050613aeb565b51919350908103613f74575082915b5f80613f57565b6120f6613f89915f52601460205260405f2090565b91613f6d565b613fc3613fd191613d396020613fb9613fac613fd89751600f0b90565b6301dfe20090600f0b0590565b600f0b9201918252565b611fc4610deb428a51612fbf565b600f0b8a52565b8989613f36565b505f613fef610deb8451600f0b90565b13613f31565b61403061402961401b61400c613fac8651600f0b90565b600f0b60208b01908152613d39565b611fc4610deb428951612fbf565b600f0b8852565b613f20565b505f614045610deb8451600f0b90565b13613f1b565b60ff16604d81116111e257600a0a90565b9060ff16806012146140c857806012115f146140915760120360ff81116111e2576140869061404b565b9081156130cb570490565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee019060ff82116111e2576118f46107289261404b565b5090565b9060ff16601281146140c8576012811115614114577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee0160ff81116111e2576140869061404b565b6012039060ff82116111e2576118f46107289261404b565b614136813361389c565b1561427357805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe57339061417a845f52600760205260405f2090565b54161460ff6141ab3361091f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b5416811561426b575b50156104fe575f906141f96141d1845f52600b60205260405f2090565b7fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8282827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258280a461422983613689565b61423b8361423683613242565b6158f8565b61424583826156c8565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4565b90505f6141b4565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b805f52601060205260405f20549081155f146142ba575050505f90565b6142d991613c026142d3925f52601160205260405f2090565b50613278565b6020810151906040810151925f84820394128185128116918513901516176111e257614322611fed614318610deb9461072896600f0b90600f0b61394a565b8351600f0b613961565b5f61432e8251600f0b90565b600f0b1261433d5751600f0b90565b5f8152613d39565b73ffffffffffffffffffffffffffffffffffffffff90828282169261436b841515612eea565b61437483613242565b908116151580806145a4575b6143ba575b505061439091615cc1565b5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600190565b6143c5575b80614385565b90506143f2612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff81161561457157600161443461442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb84612ccb565b015b60018061446d61444586615d98565b612e808773ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b019180549061040061447e83613001565b1161454757908895949392915f5b8281106145105750505050916144da6144b561450a936144b087614390999761588c565b6158e3565b9173ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000825416179055565b916143bf565b8394959697506145396145336145298385969795612fec565b90549060031b1c90565b8761588c565b01908996959493929161448c565b60046040517f8c390496000000000000000000000000000000000000000000000000000000008152fd5b600161459e612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01614436565b50821515614380565b604051906145ba826105a6565b5f6020838281520152565b9190601554916145d8610e308385613030565b6146406145e36145ad565b916145ef8151600f0b90565b926146086020830194855160208401528290600f0b9052565b61462461461d86600f0b611fe88551600f0b90565b600f0b8352565b61463a82610e17895f52601260205260405f2090565b86613aca565b81151580614804575b806147fc575b6146e3575b5160408051948552602085018390526002908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081015b0390a382613030565b604080519283526020830191909152819081015b0390a1565b9061475a9360206147147f00000000000000000000000000000000000000000000000000000000000000008461405c565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481019190915295869081906064820190565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610fea576147d36146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd575b50612eea565b9192509350614654565b6147f6915060203d602011610fe357610fd481836105e3565b5f6147cd565b50600161464f565b506001614649565b601554906148186145ad565b93805190602081019182516020880152600f0b86528051600f0b907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111e2577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c966146de966148bd93835280614918575b506148b782610e17875f52601260205260405f2090565b84613aca565b51604080519283525f60208401526003908301524260608301529033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490608090a360408051828152602081019290925290918291820190565b84525f6148a0565b929161499560155493614936610e308587613030565b61493e6145ad565b8151600f0b9361495c6020840195865160208501528390600f0b9052565b61497861497187600f0b611fe88651600f0b90565b600f0b8452565b80614adc575b5061463a82610e17895f52601260205260405f2090565b81151580614ad5575b80614acd575b614a1a575b5160408051948552602085018390526004908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614a4b9360206147147f00000000000000000000000000000000000000000000000000000000000000008461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610fea57614ac36146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b91925093506149a9565b5060016149a4565b505f61499e565b84525f61497e565b919060155491614af7610e308385613030565b614b026145e36145ad565b81151580614c41575b80614c39575b614b86575b5160408051948552602085018390525f908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614bb79360206147147f00000000000000000000000000000000000000000000000000000000000000008461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610fea57614c2f6146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b9192509350614b16565b506001614b11565b506001614b0b565b9291614c5f60155493614936610e308587613030565b81151580614d9e575b80614d97575b614ce4575b5160408051948552602085018390526005908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614d159360206147147f00000000000000000000000000000000000000000000000000000000000000008461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610fea57614d8d6146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b9192509350614c73565b505f614c6e565b506001614c68565b9291614dbc60155493614936610e308587613030565b81151580614efc575b80614ef4575b614e41575b5160408051948552602085018390526001908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614e729360206147147f00000000000000000000000000000000000000000000000000000000000000008461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610fea57614eea6146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b9192509350614dd0565b506001614dcb565b506001614dc5565b9190614f0f83613242565b73ffffffffffffffffffffffffffffffffffffffff90818516805f526018602052614f788460405f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b838316928281168481847f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f5f80a4848103614fb8575b5050505050509050565b615156575b505050614fcf575b8080808080614fae565b614ffa612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff81161561512357600161503361442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b600190600161504661444586615d98565b0161506f8773ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b54918054906104006150818584613030565b1161454757845f5b83811061510557505050505f5b8281106150ba57505050506150b39293506144b56144da916158e3565b805f614fc5565b806150ff6150f886936150eb8c73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b905f5260205260405f2090565b548461588c565b01615096565b61511b6151156145298386612fec565b8661588c565b018590615089565b6001615150612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615035565b615181612d6e8473ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9163ffffffff8316156152715760016151c46151bb8673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb86612ccb565b01905b60019260016152006151d888615d98565b612e808973ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01918354935f5b85811061522d5750505050505050906144da6144b5615225936158e3565b5f8080614fbd565b8061523c614529899385612fec565b8486615253610b97845f52600760205260405f2090565b1603615261575b5001615207565b61526b908761588c565b5f61525a565b600161529e612e008673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01906151c7565b9042018042116111e25762093a806152bd910461307e565b81156104fe5742811115611cf3576152d44261300f565b8111611cc957615324610728926152ef6131b8600554613051565b6152fc6005548096614345565b507f0000000000000000000000000000000000000000000000000000000000000000906140cc565b9061533a610dc9855f52601260205260405f2090565b9184614da6565b9060408201615357611c8d825162093a80900490565b905f915b60ff83106153a2575b5050505061538d61538d825f615381610deb6107289651600f0b90565b1261433d5751600f0b90565b6fffffffffffffffffffffffffffffffff1690565b6153ab90613020565b5f929084811115615420575083925b60208601906153eb6140296153e16153d38551600f0b90565b611fc4610deb89518b612fbf565b8951600f0b613961565b858514615419578161540861540f92611fe86001969551600f0b90565b600f0b9052565b838352019161535b565b5050615364565b92506154376120f6845f52601460205260405f2090565b6153ba565b61544843831115612eea565b5f9061545c815f52601060205260405f2090565b545f905b6080821061558d575b5050611fed6143186154916142d36155259695613c02613d39965f52601160205260405f2090565b93601354906154a082826155fb565b6154b5611863825f52600360205260405f2090565b92811015615569576118636118816154cc92613001565b906154f360406154e56060850151606087015190612fbf565b930151604085015190612fbf565b905b60408401519383615545575b50505050611fc4610deb6155196020880151600f0b90565b92604088015190612fbf565b5f600f82900b12613684576fffffffffffffffffffffffffffffffff1690565b612054926118f461556096959360606131e294015190612fbf565b5f808080615501565b50615578606083015143612fbf565b90615587604084015142612fbf565b906154f5565b9092818110156155f5576155b26155ac6155a78484613030565b613001565b60011c90565b908560026155cc84613c02885f52601160205260405f2090565b500154116155e15750600190935b0190615460565b9391506155ef600191612f92565b916155da565b92615469565b5f915f915b6080831061560f575b50505090565b90919282811015615670578281018082116111e25760018082018092116111e2571c90826002615647845f52600360205260405f2090565b01541161565c5750600190935b019190615600565b93925061566a600191612f92565b92615654565b92615609565b5f52600960205260405f20805473ffffffffffffffffffffffffffffffffffffffff81166156a2575050565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b815f52600760205273ffffffffffffffffffffffffffffffffffffffff6156fa8160405f205416918316809214612eea565b825f52600760205261573160405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b5f52600860205260405f2054917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019283116111e2576157ef92816157805f935f52600f60205260405f2090565b5480831484146157fd5750826157bb6157ca936150eb8773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b555f52600f60205260405f2090565b5573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b6157f98154612f92565b9055565b6157bb8385926158636158356157ca976150eb8b73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b54806157bb846150eb8d73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b556150eb8773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b8054680100000000000000008110156105c2576158ae91600182018155612fec565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829392549160031b92831b921b1916179055565b90600163ffffffff809316019182116111e257565b90919073ffffffffffffffffffffffffffffffffffffffff811615158080615a36575b615926575b50509050565b615931575b80615920565b61595c612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615615a0257600161599561442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01935b60019060016159a961444586615d98565b018654915f5b8381106159d45750505050506159cd9293506144b56144da916158e3565b805f61592b565b806159e361452987938c612fec565b8381036159f2575b50016159af565b6159fc908561588c565b5f6159eb565b6001615a2f612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0193615998565b5083151561591b565b91909173ffffffffffffffffffffffffffffffffffffffff908184169181168281141580615cb8575b615a74575b5050505050565b615bb8575b50615a87575b808080615a6d565b615ab2612d6e8373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff821615615b85576001615af5615aec8573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb85612ccb565b015b600180615b2e615b0687615d98565b612e808873ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0191805490610400615b3f83613001565b11614547575f5b828110615b6d5750505050615b6693926144b06144da936144b59361588c565b5f80615a7f565b80615b7f614533614529879486612fec565b01615b46565b6001615bb2612e008573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615af7565b615be3612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615615c85576001615c1c61442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b6001906001615c2f61444586615d98565b018154915f5b838110615c57575050505050906144da6144b5615c51936158e3565b5f615a79565b80615c66614529879385612fec565b8a8103615c75575b5001615c35565b615c7f908561588c565b5f615c6e565b6001615cb2612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615c1e565b50831515615a68565b615d8e91805f52602060078152600f73ffffffffffffffffffffffffffffffffffffffff91604092615cf881855f20541615612eea565b845f5260078252615d4686855f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b85165f5260088152825f205493600e8252835f20855f52825280845f20555f52525f205573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b6157f98154613001565b73ffffffffffffffffffffffffffffffffffffffff165f52601a60205263ffffffff60405f20541680151580615dd7575b156107285761072890612ccb565b50601960205260405f20615def4291612dbb84612ccb565b5414615dc956fea164736f6c6343000818000a000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d000000000000000000000000072474981c4932021ec628ca7596b47bbb247a245
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146104cf578063047fc9aa146104ca57806306fdde03146104c55780630758c7d8146104c0578063081812fc146104bb578063095cf5c6146104b6578063095ea7b3146104b15780630d6a2033146104ac5780631376f3da146104a757806316b7a011146104a257806318160ddd1461049d5780631c984bc31461049857806320606b701461049357806323b872dd1461048e57806325a58b56146104895780632e1a7d4d146104845780632e720f7d1461047f5780632f745c591461047a578063313ce567146104755780633a46b1a8146104705780633b97e8561461046b57806342842e0e14610466578063430c208114610461578063461f711c1461045c57806346c96aac146104575780634bc2a6571461045257806354fd4d501461044d5780635594a0451461044857806356afe74414610443578063587cde1e1461043e5780635c19a95c146104395780635f5b0c32146104345780636352211e1461042f57806365fc38731461042a5780636f548837146104255780636fcfff451461042057806370a082311461041b5780637116c60c1461040257806371197484146104165780637ecebe001461041157806385f2aef21461040c5780638c2c9baf146104075780638e539e8c146104025780638fbb38ff146103fd578063900cf0cf146103f857806395d89b41146103f3578063981b24d0146103ee578063986b7d8a146103e95780639ab24eb0146103e4578063a183af52146103df578063a22cb465146103da578063a4d855df146103d5578063b29b8830146103d0578063b45a3c0e146103cb578063b88d4fde146103c6578063c1f0fb9f146103c1578063c2c4c5c1146103bc578063c3cda520146103b7578063c87b56dd146103b2578063d1c2babb146103ad578063d1febfb9146103a8578063d2aef457146103a3578063d4e54c3b1461039e578063e0514aba14610399578063e441135c14610394578063e7a324dc1461038f578063e7e242d41461038a578063e9237e1214610385578063e985e9c514610380578063ee99fe281461037b578063ef39933d14610376578063f1127ed814610371578063f8a057631461036c578063fbd3a29d14610367578063fc0c546a146103625763fd4a77f11461035d575f80fd5b612c34565b612be4565b612b89565b612b5c565b612aef565b612a4b565b612992565b612926565b6128e6565b6128c8565b61288e565b612864565b612843565b6127ac565b6126fd565b61269e565b612575565b61241a565b61214c565b611eba565b611e53565b611dcb565b611d95565b611d1d565b611c06565b611b27565b6119fd565b6119da565b611960565b611817565b6117ba565b61179d565b61176e565b61168a565b61174d565b61171a565b6116d5565b6116a8565b611645565b6115fa565b6115d0565b611562565b611522565b611506565b6114bf565b61147e565b6113ef565b6113bc565b6113a1565b611302565b6112d0565b61128e565b61125d565b611224565b6111e7565b611132565b611117565b6110c6565b61105e565b610d37565b610d1d565b610d02565b610c86565b610c4d565b610c2a565b610aaf565b610a43565b6109d1565b610881565b610819565b6107d9565b6107a4565b61072b565b61055c565b610502565b7fffffffff000000000000000000000000000000000000000000000000000000008116036104fe57565b5f80fd5b346104fe5760206003193601126104fe577fffffffff00000000000000000000000000000000000000000000000000000000600435610540816104d4565b165f526004602052602060ff60405f2054166040519015158152f35b346104fe575f6003193601126104fe576020601554604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176105c257604052565b610579565b6080810190811067ffffffffffffffff8211176105c257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105c257604052565b60405190610631826105a6565b565b60405190610631826105c7565b67ffffffffffffffff81116105c257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60405190610687826105a6565b600b82527f52696e67732076654254430000000000000000000000000000000000000000006020830152565b5f5b8381106106c45750505f910152565b81810151838201526020016106b5565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610710815180928187528780880191016106b3565b0116010190565b9060206107289281815201906106d4565b90565b346104fe575f6003193601126104fe5761075a61074661067a565b6040519182916020835260208301906106d4565b0390f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104fe57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104fe57565b346104fe5760406003193601126104fe5760206107cb6107c261075e565b60243590612d42565b63ffffffff60405191168152f35b346104fe5760206003193601126104fe576004355f52600b602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104fe5760206003193601126104fe5761083261075e565b6001549073ffffffffffffffffffffffffffffffffffffffff80831633036104fe577fffffffffffffffffffffffff000000000000000000000000000000000000000091169116176001555f80f35b346104fe5760406003193601126104fe5761089a61075e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe57808316928284146104fe576109516109a3926108ed875f52600760205260405f2090565b5416331460ff6109423361091f8873ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541681156109c9575b50612ee3565b610963855f52600b60205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b90505f61094b565b346104fe5760206003193601126104fe576004355f526016602052602060405f2054604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b90633b9aca00811015610a3e5760030201905f90565b6109fb565b346104fe5760406003193601126104fe576024356004355f52601160205260405f2090633b9aca008110156104fe57610a7b91610a28565b508054600182015460029092015460408051600f84810b8252608094851d900b602082015290810193909352606083015290f35b346104fe5760406003193601126104fe5761075a610acb61075e565b602435805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f20541690831691610bd7610b8184841495600960205261091f86610b47610b2e60405f2073ffffffffffffffffffffffffffffffffffffffff90541690565b73ffffffffffffffffffffffffffffffffffffffff1690565b1496610bb1610b2e610b97610b88610b818761091f8d73ffffffffffffffffffffffffffffffffffffffff165f52600a60205260405f2090565b5460ff1690565b985f52600b60205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b149573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b928415610c22575b508315610c1a575b508215610c12575b508115610c0a575b5060405190151581529081906020820190565b90505f610bf7565b91505f610bef565b92505f610be7565b93505f610bdf565b346104fe575f6003193601126104fe576020610c45426132af565b604051908152f35b346104fe5760406003193601126104fe576004355f52601160205260206001610c7b60243560405f20610a28565b500154604051908152f35b346104fe575f6003193601126104fe5760206040517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a8668152f35b60031960609101126104fe5773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104fe579160243590811681036104fe579060443590565b346104fe57610d1b610d1336610cc0565b90339261376f565b005b346104fe575f6003193601126104fe576020604051438152f35b346104fe576020806003193601126104fe57600435600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655610d94610d8f823361389c565b612eea565b610da6815f52601660205260405f2090565b5415801590611043575b61101957610dce610dc9825f52601260205260405f2090565b612f1e565b90828201514210610fef57610eb790610df1610deb8451600f0b90565b600f0b90565b90610e1c610dfd610624565b5f81525f87820152610e17835f52601260205260405f2090565b612f3f565b610e4c60155494610e35610e308588612fbf565b601555565b610e3d610624565b905f82525f8883015283613aca565b84610e777f00000000000000000000000000000000000000000000000000000000000000088461405c565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915293849081906044820190565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0165af1948515610fea577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c95610f7c94610f32925f92610fbd575b5050612eea565b610f3b8161412c565b6040805191825260208201839052429082015233907f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca9490606090a282612fbf565b60408051928352602083019190915290a1610d1b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b610fdc9250803d10610fe3575b610fd481836105e3565b810190612fcc565b5f80610f2b565b503d610fca565b612fe1565b60046040517ff6fafba0000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3ca9d617000000000000000000000000000000000000000000000000000000008152fd5b50611059610b81825f52601760205260405f2090565b610db0565b346104fe5760206003193601126104fe5761107761075e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104fe57167fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002555f80f35b346104fe5760406003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff6110f461075e565b165f52600e60205260405f206024355f52602052602060405f2054604051908152f35b346104fe575f6003193601126104fe57602060405160128152f35b346104fe5760406003193601126104fe5761114b61075e565b6024359073ffffffffffffffffffffffffffffffffffffffff61116e8383612d42565b91165f5260196020526001611195819260405f209063ffffffff165f5260205260405f2090565b01915f9183545f925b8184106111b057604051858152602090f35b909192936111ce826111c28789612fec565b90549060031b1c61429d565b81018091116111e25793830192919061119e565b612c9e565b346104fe575f6003193601126104fe57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000008168152f35b346104fe5761123236610cc0565b60405191602083019383851067ffffffffffffffff8611176105c257610d1b946040525f8452613403565b346104fe5760406003193601126104fe57602061128461127b61075e565b6024359061389c565b6040519015158152f35b346104fe5760206003193601126104fe576004355f52601060205260206112c060405f20546011835260405f20610a28565b505460801d60405190600f0b8152f35b346104fe575f6003193601126104fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346104fe5760206003193601126104fe5761131b61075e565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104fe57167fffffffffffffffffffffffff00000000000000000000000000000000000000005f5416175f555f80f35b60405190611375826105a6565b600582527f312e302e300000000000000000000000000000000000000000000000000000006020830152565b346104fe575f6003193601126104fe5761075a610746611368565b346104fe575f6003193601126104fe57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346104fe5760406003193601126104fe5760043567ffffffffffffffff8082116104fe57366023830112156104fe5781600401359081116105c2578060051b916020926040519261144360208301856105e3565b835260246020840191830101913683116104fe57602401905b82821061146f57610d1b602435856130d0565b8135815290840190840161145c565b346104fe5760206003193601126104fe5760206114a161149c61075e565b613242565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346104fe5760206003193601126104fe576114d861075e565b73ffffffffffffffffffffffffffffffffffffffff8116156114ff575b610d1b9033614f04565b50336114f5565b346104fe575f6003193601126104fe5760206040516104008152f35b346104fe5760206003193601126104fe576004355f526007602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104fe5760406003193601126104fe57600654600160ff8216036104fe5760209060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008092161760065560016115bf336024356004356152a5565b916006541617600655604051908152f35b346104fe5760206003193601126104fe576004355f52600d602052602060405f2054604051908152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff61162861075e565b165f52601a602052602063ffffffff60405f205416604051908152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff61167361075e565b165f526008602052602060405f2054604051908152f35b346104fe5760206003193601126104fe576020610c456004356132af565b346104fe5760206003193601126104fe576004355f526014602052602060405f2054600f0b604051908152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff61170361075e565b165f52601b602052602060405f2054604051908152f35b346104fe575f6003193601126104fe57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346104fe5760406003193601126104fe576020610c4560243560043561543c565b346104fe5760206003193601126104fe576004355f526017602052602060ff60405f2054166040519015158152f35b346104fe575f6003193601126104fe576020601354604051908152f35b346104fe575f6003193601126104fe5761075a6040516117d9816105a6565b600581527f766542544300000000000000000000000000000000000000000000000000000060208201526040519182916020835260208301906106d4565b346104fe5760206003193601126104fe5761075a6118ba60043561183d43821115612eea565b6118b460135461184d81846155fb565b92611868611863855f52600360205260405f2090565b613278565b935f928110156119145761186361188161188f92613001565b5f52600360205260405f2090565b606085019081516060820193845182036118ca575b50505050505b6040830151613030565b90615341565b6040519081529081906020820190565b61190a9550916118f460406118e66118fa946119049796612fbf565b92015160408a015190612fbf565b906130ae565b9251905190612fbf565b906130c1565b5f808080806118a4565b50606084019081519043820361192d575b5050506118aa565b61195893506119426119049261195092612fbf565b6118f4604088015142612fbf565b915143612fbf565b5f8080611925565b346104fe5760206003193601126104fe5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104fe57805f52601660205260405f2054907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111e2575f52601660205260405f20555f80f35b346104fe5760206003193601126104fe576020610c456119f861075e565b6132ce565b346104fe5760406003193601126104fe5760043560243590600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611a58610d8f823361389c565b611a6d610dc9825f52601260205260405f2090565b90611a79831515612eea565b8151600f0b15611af3576020820151421015610fef57611abd611ac3937f0000000000000000000000000000000000000000000000000000000000000008906140cc565b906145c5565b610d1b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b60046040517fba112c93000000000000000000000000000000000000000000000000000000008152fd5b801515036104fe57565b346104fe5760406003193601126104fe57611b4061075e565b602435611b4c81611b1d565b611bd681611ba673ffffffffffffffffffffffffffffffffffffffff851694611b7733871415612eea565b335f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b346104fe5760406003193601126104fe57600435600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611c5d610d8f823361389c565b611c72610dc9825f52601260205260405f2090565b611c92611c8d611c8460243542613030565b62093a80900490565b61307e565b602082014281511115610fef578251600f0b15611af35751811115611cf357611cba4261300f565b8111611cc957611ac39261480c565b60046040517fd6946a43000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd5bb0fd9000000000000000000000000000000000000000000000000000000008152fd5b346104fe5760406003193601126104fe57602060ff611d89611d3d61075e565b73ffffffffffffffffffffffffffffffffffffffff611d5a610781565b91165f52600a845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b346104fe5760206003193601126104fe576004355f5260126020526040805f2060018154600f0b91015482519182526020820152f35b346104fe5760806003193601126104fe57611de461075e565b611dec610781565b6064359167ffffffffffffffff83116104fe57366023840112156104fe57826004013591611e1983610640565b92611e2760405194856105e3565b80845236602482870101116104fe576020815f926024610d1b9801838801378501015260443591613403565b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff5f541633036104fe576004355f52601760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690555f80f35b346104fe575f6003193601126104fe57604051611ed6816105a6565b5f81525f6020809201525f6020604051611eef816105a6565b8281520152611efc613926565b50611f05613926565b5060135490611f12610633565b905f82525f6020830152426040830152606091436060820152918361212f575b60408301918251925f92844211612103575b959390611f5862093a80839594950461307e565b965f975b60ff8910611f7b575b610d1b88611f768961188181601355565b6139ef565b611f8490613020565b5f9390428111156120c45750611ff4611fed610deb95611fde611fd78c611fd2611fca8d611fc4429d8e925b86019b611fbe8d51600f0b90565b93612fbf565b9061394a565b9151600f0b90565b613961565b600f0b8c52565b8351600f0b6139a8565b6139a8565b600f0b8252565b5f612003610deb8a51600f0b90565b126120bc575b5f612018610deb8351600f0b90565b126120b3575b5082948383526120618289019761205a89516120546120466120408a80612fbf565b8b6130ae565b670de0b6b3a7640000900490565b90613030565b8952613001565b984285036120865750505050505050610d1b92611f7691439052905f80808080611f65565b600191929394959697506120a689611f768c5f52600360205260405f2090565b0197959291909493611f5c565b5f90525f61201e565b5f8852612009565b9350611ff4611fed87611fde611fd78c611fd2611fca8b611fc4610deb6120fd6120f6845f52601460205260405f2090565b54600f0b90565b98611fb0565b925061212961211e612119606088015143612fbf565b613096565b611904835142612fbf565b92611f44565b9150612146611863845f52600360205260405f2090565b91611f32565b346104fe5760c06003193601126104fe5761216561075e565b6064359060243560443560ff841684036104fe5773ffffffffffffffffffffffffffffffffffffffff916121a88385166121a133821415612ee3565b1515612ee3565b6121b061067a565b5f81516020809301209661222361230b6123176121cb611368565b805190870120604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818a01908152602081019e909e528d8201929092524660608e01523060808e01529b9093849060a0830190565b03936122557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018352826105e3565b519020928a8c516122c8816122bc8d8d8d8401968760609194939273ffffffffffffffffffffffffffffffffffffffff60808301967fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf845216602083015260408201520152565b038481018352826105e3565b5190208c51938491898301968790916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b039081018352826105e3565b519020885190815260ff919091166020820152608435604082015260a435606082015281805260809060015afa15610fea575f51928316156123f15761237b8373ffffffffffffffffffffffffffffffffffffffff165f52601b60205260405f2090565b9081549161238883613051565b9055036123c857421161239f57610d1b9250614f04565b600483517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b600485517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b346104fe5760206003193601126104fe5760043573ffffffffffffffffffffffffffffffffffffffff612458610b97835f52600760205260405f2090565b161561254b575f612474610dc9835f52601260205260405f2090565b612499610b2e610b2e60025473ffffffffffffffffffffffffffffffffffffffff1690565b6124a3428561429d565b6124b6610deb60208501519451600f0b90565b94612507604051968795869485947fdd9ec149000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b03915afa8015610fea5761075a915f91612529575b5060405191829182610717565b61254591503d805f833e61253d81836105e3565b810190613607565b5f61251c565b60046040517f21f2b69c000000000000000000000000000000000000000000000000000000008152fd5b346104fe5760406003193601126104fe5760043560243561259e825f52601660205260405f2090565b5415801590612683575b61101957816125bc82610d1b941415612ee3565b6125ce6125c9823361389c565b612ee3565b6125db6125c9833361389c565b6125f0610dc9825f52601260205260405f2090565b90612606610dc9845f52601260205260405f2090565b92612676612618610deb8551600f0b90565b9261267160208601516020880151808210155f1461267b5750955b61265961263e610624565b5f81525f6020820152610e17855f52601260205260405f2090565b612661610624565b905f82525f602083015283613aca565b61412c565b614920565b905095612633565b50612699610b81835f52601760205260405f2090565b6125a8565b346104fe5760206003193601126104fe576004355f52600360205260405f20805461075a600260018401549301546040519383859460801d90600f0b85909493926060926080830196600f0b8352600f0b602083015260408201520152565b346104fe5760406003193601126104fe5761271661075e565b60243561272281611b1d565b61277c81611ba673ffffffffffffffffffffffffffffffffffffffff85169461274d33871415612eea565b335f52600a60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60405190151581527fccb38c6086037bf3d363115fc480b43f15aeb617ec6296b6a8ab6c70a1560adc60203392a3005b346104fe5760606003193601126104fe5760443573ffffffffffffffffffffffffffffffffffffffff811681036104fe57600654600160ff8216036104fe57600161282961075a9360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00809516176006556024356004356152a5565b916006541617600655604051918291829190602083019252565b346104fe5760406003193601126104fe576020610c4560243560043561429d565b346104fe5760206003193601126104fe576004355f526010602052602060405f2054604051908152f35b346104fe575f6003193601126104fe5760206040517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b346104fe5760206003193601126104fe576020610c45600435613666565b346104fe5760206003193601126104fe576004355f526009602052602073ffffffffffffffffffffffffffffffffffffffff60405f205416604051908152f35b346104fe5760406003193601126104fe57602060ff611d8961294661075e565b73ffffffffffffffffffffffffffffffffffffffff612963610781565b91165f52600c845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b346104fe5760406003193601126104fe5760043560243590600654600160ff8216036104fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006002911617600655805f5260126020526129f560405f20612f1e565b90612a01831515612ee3565b8151600f0b15611af3576020820151421015610fef57612a45611ac3937f0000000000000000000000000000000000000000000000000000000000000008906140cc565b90614ae4565b346104fe5760406003193601126104fe57612a6461075e565b60243590815f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe57808316928284146104fe57612ab7612ac9926108ed875f52600760205260405f2090565b610963855f52600960205260405f2090565b7fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad5f80a4005b346104fe5760406003193601126104fe57612b0861075e565b6024359063ffffffff821682036104fe5760209173ffffffffffffffffffffffffffffffffffffffff612b5392165f526019835260405f209063ffffffff165f5260205260405f2090565b54604051908152f35b346104fe5760206003193601126104fe576004355f5260126020526020600160405f200154604051908152f35b346104fe5760206003193601126104fe5760043573ffffffffffffffffffffffffffffffffffffffff5f541633036104fe57805f52601660205260405f205490600182018092116111e2575f52601660205260405f20555f80f35b346104fe575f6003193601126104fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0168152f35b346104fe5760206003193601126104fe5773ffffffffffffffffffffffffffffffffffffffff5f541633036104fe576004355f52601760205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790555f80f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff809316019182116111e257565b63ffffffff91821690821603919082116111e257565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b90612d78612d6e8373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b5463ffffffff1690565b9163ffffffff80841615612ecf5782612dcd612db28473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb87612ccb565b63ffffffff165f5260205260405f2090565b541115612ed75782612e0d612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b5f805260205260405f2090565b5411612ecf579190612e1f5f94612ccb565b83851684821611612e31575050505090565b612e53612e4d612e418784612cff565b60011c637fffffff1690565b82612cff565b94612e9386612e808573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b9063ffffffff165f5260205260405f2090565b54848103612ea45750505050505090565b93809596929394105f14612ebd5750935b929190612e1f565b949150612ec990612ccb565b90612eb5565b505050505f90565b50505061072890612ccb565b156104fe57565b15612ef157565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b90604051612f2b816105a6565b6020600182948054600f0b84520154910152565b906020600191612f8b8151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b0151910155565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111e257565b919082039182116111e257565b908160209103126104fe575161072881611b1d565b6040513d5f823e3d90fd5b8054821015610a3e575f5260205f2001905f90565b90600182018092116111e257565b906301dfe20082018092116111e257565b9062093a8082018092116111e257565b919082018092116111e257565b8051821015610a3e5760209160051b010190565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146111e25760010190565b9062093a80918281029281840414901517156111e257565b90670de0b6b3a764000091808302928304036111e257565b818102929181159184041417156111e257565b81156130cb570490565b612d15565b6130e2825f52601660205260405f2090565b5415801590613227575b611019576130fd6125c9833361389c565b613112610b97835f52600760205260405f2090565b90613128610dc9845f52601260205260405f2090565b9260208401519161313d610deb8651600f0b90565b90613149821515612ee3565b613158610e3083601554612fbf565b5f928151965f5b88811061320857509061267161317a9261265961263e610624565b42841115611cf35761318b4261300f565b8411611cc9575f5b8681106131a35750505050505050565b60019061320260056131bd6131b88254613051565b600555565b546131c8818a614345565b50876131e7886131e26131db878a61303d565b518a6130ae565b6130c1565b6131fc610dc9845f52601260205260405f2090565b92614c49565b01613193565b94613220600191613219888761303d565b5190613030565b950161315f565b5061323d610b81835f52601760205260405f2090565b6130ec565b73ffffffffffffffffffffffffffffffffffffffff8082165f52601860205260405f20541680155f14613273575090565b905090565b90604051613285816105c7565b606060028294805480600f0b855260801d600f0b6020850152600181015460408501520154910152565b610728906013545f5260036020526132c960405f20613278565b615341565b73ffffffffffffffffffffffffffffffffffffffff81165f52601a60205263ffffffff60405f20541690811561337d5761333561332b60019273ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb8394612ccb565b015f9181545f925b81841061334b575050505090565b9091929361336961335c8684612fec565b9054429160031b1c61429d565b81018091116111e25793830192919061333d565b50505f90565b908160209103126104fe5751610728816104d4565b9092610728949360809373ffffffffffffffffffffffffffffffffffffffff8092168452166020830152604082015281606082015201906106d4565b3d156133fe573d906133e582610640565b916133f360405193846105e3565b82523d5f602084013e565b606090565b929190916134133383858761376f565b823b613420575b50505050565b613475926020925f73ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c85523360048601613398565b0393165af15f91816135d6575b506135215761348f6133d4565b8051908161351c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608490fd5b602001fd5b7fffffffff000000000000000000000000000000000000000000000000000000001603613551575f80808061341a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608490fd5b6135f991925060203d602011613600575b6135f181836105e3565b810190613383565b905f613482565b503d6135e7565b6020818303126104fe5780519067ffffffffffffffff82116104fe570181601f820112156104fe57805161363a81610640565b9261364860405194856105e3565b818452602082840101116104fe5761072891602080850191016106b3565b805f52600d60205260405f205443146136845761072890429061429d565b505f90565b805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe576136c6835f52600760205260405f2090565b5416331460ff6136f83361091f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b54168115613767575b50156104fe575f9061371b835f52600960205260405f2090565b7fffffffffffffffffffffffff000000000000000000000000000000000000000081541690557fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8280a4565b90505f613701565b9192835f52601660205260405f205415801590613880575b61101957836137959161389c565b156104fe57825f52600760205273ffffffffffffffffffffffffffffffffffffffff9061380f846138008460405f205416956137d5868216809814612eea565b825f52600b60205260405f208054878116613855575b50506137f683615676565b61149c83826156c8565b61380984613242565b90615a3f565b6138198482615cc1565b4361382c855f52600d60205260405f2090565b5516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b7fffffffffffffffffffffffff00000000000000000000000000000000000000001690555f806137eb565b5060ff613895855f52601760205260405f2090565b5416613787565b905f52600760205273ffffffffffffffffffffffffffffffffffffffff9060ff6139098360405f2054169284811680851495600b60205260405f20541614935f52600c60205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541690821561391e575b508115613273575090565b91505f613913565b60405190613933826105c7565b5f6060838281528260208201528260408201520152565b90600f0b90600f0b029081600f0b9182036111e257565b90600f0b90600f0b03906f7fffffffffffffffffffffffffffffff82137fffffffffffffffffffffffffffffffff800000000000000000000000000000008312176111e257565b90600f0b90600f0b01907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111e257565b906060600291613a3b8151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b60208101516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000086549260801b169116178455604081015160018501550151910155565b9190613a9e57610631916139ef565b7f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b613ad2613926565b90613adb613926565b925f806013549284151580613f0a575b613af3610633565b945f86526020955f878201524260408201526060904360608201529082613eed575b604082018051915f91834211613ec6575b8a90613b3b62093a808698969594950461307e565b965f975b60ff8910613d95575b5050509050613b7a94508291508c8c87613b64611f7696601355565b613cf0575b5050505f52600360205260405f2090565b613b89575b5050505050505050565b613c0d97613c079685808094019542875111613c9a575b5050500191825190428211613c1a575b5050505050613c02613bd3613bcd835f52601060205260405f2090565b54613001565b9182613be7825f52601060205260405f2090565b554260408601524360608601525f52601160205260405f2090565b610a28565b90613a8f565b5f80808080808080613b7f565b5110613c28575b8080613bb0565b613c45613c5591613c3f613c9295890151600f0b90565b90613961565b91515f52601460205260405f2090565b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b5f8080613c21565b613cad613cb392613cd2940151600f0b90565b906139a8565b83830151865114613cda575b613c5586515f52601460205260405f2090565b5f8281613ba0565b613ceb90613c3f858c0151600f0b90565b613cbf565b613d40613d4a91613c3f611fca85613d39613d32613d23613d178b613d519b0151600f0b90565b878c0151600f0b613c3f565b998d0199611fe88b51600f0b90565b600f0b8952565b51600f0b90565b8651600f0b6139a8565b600f0b8552565b5f613d60610deb8351600f0b90565b12613d8c575b505f613d76610deb8551600f0b90565b12613d84575b888c8c613b69565b5f8352613d7c565b5f90525f613d66565b613d9e90613020565b5f9042811115613e985750611fed613de891611fde613de1613dd742985b8d0196611fc4610deb613dd08a51600f0b90565b928c612fbf565b8c51600f0b613961565b600f0b8b52565b5f613df7610deb8951600f0b90565b12613e90575b5f613e0c610deb8351600f0b90565b12613e87575b5081938282528c613e358289019761205a89516120546120466120408a80612fbf565b98428503613e5a5750505050505050613b7a92611f7691439052905f89818080613b48565b60019192939495969750613e7a89611f768c5f52600360205260405f2090565b0197959092919493613b3f565b5f90525f613e12565b5f8752613dfd565b93613de89150611fed90611fde613de1613dd7613ec06120f68a5f52601460205260405f2090565b93613dbc565b9150613ee7613edc612119606087015143612fbf565b611904845142612fbf565b91613b26565b9050613f04611863835f52600360205260405f2090565b90613b15565b925060208101928351421080614035575b613ff5575b602089019389894287511180613fdf575b613f8f575b5050613f4e6120f682515f52601460205260405f2090565b94519081613f5e575b5050613aeb565b51919350908103613f74575082915b5f80613f57565b6120f6613f89915f52601460205260405f2090565b91613f6d565b613fc3613fd191613d396020613fb9613fac613fd89751600f0b90565b6301dfe20090600f0b0590565b600f0b9201918252565b611fc4610deb428a51612fbf565b600f0b8a52565b8989613f36565b505f613fef610deb8451600f0b90565b13613f31565b61403061402961401b61400c613fac8651600f0b90565b600f0b60208b01908152613d39565b611fc4610deb428951612fbf565b600f0b8852565b613f20565b505f614045610deb8451600f0b90565b13613f1b565b60ff16604d81116111e257600a0a90565b9060ff16806012146140c857806012115f146140915760120360ff81116111e2576140869061404b565b9081156130cb570490565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee019060ff82116111e2576118f46107289261404b565b5090565b9060ff16601281146140c8576012811115614114577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee0160ff81116111e2576140869061404b565b6012039060ff82116111e2576118f46107289261404b565b614136813361389c565b1561427357805f52600760205273ffffffffffffffffffffffffffffffffffffffff8060405f2054169081156104fe57339061417a845f52600760205260405f2090565b54161460ff6141ab3361091f8573ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b5416811561426b575b50156104fe575f906141f96141d1845f52600b60205260405f2090565b7fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8282827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258280a461422983613689565b61423b8361423683613242565b6158f8565b61424583826156c8565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4565b90505f6141b4565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b805f52601060205260405f20549081155f146142ba575050505f90565b6142d991613c026142d3925f52601160205260405f2090565b50613278565b6020810151906040810151925f84820394128185128116918513901516176111e257614322611fed614318610deb9461072896600f0b90600f0b61394a565b8351600f0b613961565b5f61432e8251600f0b90565b600f0b1261433d5751600f0b90565b5f8152613d39565b73ffffffffffffffffffffffffffffffffffffffff90828282169261436b841515612eea565b61437483613242565b908116151580806145a4575b6143ba575b505061439091615cc1565b5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600190565b6143c5575b80614385565b90506143f2612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff81161561457157600161443461442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb84612ccb565b015b60018061446d61444586615d98565b612e808773ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b019180549061040061447e83613001565b1161454757908895949392915f5b8281106145105750505050916144da6144b561450a936144b087614390999761588c565b6158e3565b9173ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000825416179055565b916143bf565b8394959697506145396145336145298385969795612fec565b90549060031b1c90565b8761588c565b01908996959493929161448c565b60046040517f8c390496000000000000000000000000000000000000000000000000000000008152fd5b600161459e612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01614436565b50821515614380565b604051906145ba826105a6565b5f6020838281520152565b9190601554916145d8610e308385613030565b6146406145e36145ad565b916145ef8151600f0b90565b926146086020830194855160208401528290600f0b9052565b61462461461d86600f0b611fe88551600f0b90565b600f0b8352565b61463a82610e17895f52601260205260405f2090565b86613aca565b81151580614804575b806147fc575b6146e3575b5160408051948552602085018390526002908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081015b0390a382613030565b604080519283526020830191909152819081015b0390a1565b9061475a9360206147147f00000000000000000000000000000000000000000000000000000000000000088461405c565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481019190915295869081906064820190565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0165af1928315610fea576147d36146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd575b50612eea565b9192509350614654565b6147f6915060203d602011610fe357610fd481836105e3565b5f6147cd565b50600161464f565b506001614649565b601554906148186145ad565b93805190602081019182516020880152600f0b86528051600f0b907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111e2577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c966146de966148bd93835280614918575b506148b782610e17875f52601260205260405f2090565b84613aca565b51604080519283525f60208401526003908301524260608301529033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62490608090a360408051828152602081019290925290918291820190565b84525f6148a0565b929161499560155493614936610e308587613030565b61493e6145ad565b8151600f0b9361495c6020840195865160208501528390600f0b9052565b61497861497187600f0b611fe88651600f0b90565b600f0b8452565b80614adc575b5061463a82610e17895f52601260205260405f2090565b81151580614ad5575b80614acd575b614a1a575b5160408051948552602085018390526004908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614a4b9360206147147f00000000000000000000000000000000000000000000000000000000000000088461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0165af1928315610fea57614ac36146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b91925093506149a9565b5060016149a4565b505f61499e565b84525f61497e565b919060155491614af7610e308385613030565b614b026145e36145ad565b81151580614c41575b80614c39575b614b86575b5160408051948552602085018390525f908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614bb79360206147147f00000000000000000000000000000000000000000000000000000000000000088461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0165af1928315610fea57614c2f6146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b9192509350614b16565b506001614b11565b506001614b0b565b9291614c5f60155493614936610e308587613030565b81151580614d9e575b80614d97575b614ce4575b5160408051948552602085018390526005908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614d159360206147147f00000000000000000000000000000000000000000000000000000000000000088461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0165af1928315610fea57614d8d6146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b9192509350614c73565b505f614c6e565b506001614c68565b9291614dbc60155493614936610e308587613030565b81151580614efc575b80614ef4575b614e41575b5160408051948552602085018390526001908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c936146ca92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081016146c1565b90614e729360206147147f00000000000000000000000000000000000000000000000000000000000000088461405c565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0165af1928315610fea57614eea6146ca947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c975f916147dd5750612eea565b9192509350614dd0565b506001614dcb565b506001614dc5565b9190614f0f83613242565b73ffffffffffffffffffffffffffffffffffffffff90818516805f526018602052614f788460405f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b838316928281168481847f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f5f80a4848103614fb8575b5050505050509050565b615156575b505050614fcf575b8080808080614fae565b614ffa612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff81161561512357600161503361442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b600190600161504661444586615d98565b0161506f8773ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b54918054906104006150818584613030565b1161454757845f5b83811061510557505050505f5b8281106150ba57505050506150b39293506144b56144da916158e3565b805f614fc5565b806150ff6150f886936150eb8c73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b905f5260205260405f2090565b548461588c565b01615096565b61511b6151156145298386612fec565b8661588c565b018590615089565b6001615150612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615035565b615181612d6e8473ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9163ffffffff8316156152715760016151c46151bb8673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb86612ccb565b01905b60019260016152006151d888615d98565b612e808973ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01918354935f5b85811061522d5750505050505050906144da6144b5615225936158e3565b5f8080614fbd565b8061523c614529899385612fec565b8486615253610b97845f52600760205260405f2090565b1603615261575b5001615207565b61526b908761588c565b5f61525a565b600161529e612e008673ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01906151c7565b9042018042116111e25762093a806152bd910461307e565b81156104fe5742811115611cf3576152d44261300f565b8111611cc957615324610728926152ef6131b8600554613051565b6152fc6005548096614345565b507f0000000000000000000000000000000000000000000000000000000000000008906140cc565b9061533a610dc9855f52601260205260405f2090565b9184614da6565b9060408201615357611c8d825162093a80900490565b905f915b60ff83106153a2575b5050505061538d61538d825f615381610deb6107289651600f0b90565b1261433d5751600f0b90565b6fffffffffffffffffffffffffffffffff1690565b6153ab90613020565b5f929084811115615420575083925b60208601906153eb6140296153e16153d38551600f0b90565b611fc4610deb89518b612fbf565b8951600f0b613961565b858514615419578161540861540f92611fe86001969551600f0b90565b600f0b9052565b838352019161535b565b5050615364565b92506154376120f6845f52601460205260405f2090565b6153ba565b61544843831115612eea565b5f9061545c815f52601060205260405f2090565b545f905b6080821061558d575b5050611fed6143186154916142d36155259695613c02613d39965f52601160205260405f2090565b93601354906154a082826155fb565b6154b5611863825f52600360205260405f2090565b92811015615569576118636118816154cc92613001565b906154f360406154e56060850151606087015190612fbf565b930151604085015190612fbf565b905b60408401519383615545575b50505050611fc4610deb6155196020880151600f0b90565b92604088015190612fbf565b5f600f82900b12613684576fffffffffffffffffffffffffffffffff1690565b612054926118f461556096959360606131e294015190612fbf565b5f808080615501565b50615578606083015143612fbf565b90615587604084015142612fbf565b906154f5565b9092818110156155f5576155b26155ac6155a78484613030565b613001565b60011c90565b908560026155cc84613c02885f52601160205260405f2090565b500154116155e15750600190935b0190615460565b9391506155ef600191612f92565b916155da565b92615469565b5f915f915b6080831061560f575b50505090565b90919282811015615670578281018082116111e25760018082018092116111e2571c90826002615647845f52600360205260405f2090565b01541161565c5750600190935b019190615600565b93925061566a600191612f92565b92615654565b92615609565b5f52600960205260405f20805473ffffffffffffffffffffffffffffffffffffffff81166156a2575050565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b815f52600760205273ffffffffffffffffffffffffffffffffffffffff6156fa8160405f205416918316809214612eea565b825f52600760205261573160405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b5f52600860205260405f2054917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019283116111e2576157ef92816157805f935f52600f60205260405f2090565b5480831484146157fd5750826157bb6157ca936150eb8773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b555f52600f60205260405f2090565b5573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b6157f98154612f92565b9055565b6157bb8385926158636158356157ca976150eb8b73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b54806157bb846150eb8d73ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b556150eb8773ffffffffffffffffffffffffffffffffffffffff165f52600e60205260405f2090565b8054680100000000000000008110156105c2576158ae91600182018155612fec565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829392549160031b92831b921b1916179055565b90600163ffffffff809316019182116111e257565b90919073ffffffffffffffffffffffffffffffffffffffff811615158080615a36575b615926575b50509050565b615931575b80615920565b61595c612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615615a0257600161599561442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01935b60019060016159a961444586615d98565b018654915f5b8381106159d45750505050506159cd9293506144b56144da916158e3565b805f61592b565b806159e361452987938c612fec565b8381036159f2575b50016159af565b6159fc908561588c565b5f6159eb565b6001615a2f612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0193615998565b5083151561591b565b91909173ffffffffffffffffffffffffffffffffffffffff908184169181168281141580615cb8575b615a74575b5050505050565b615bb8575b50615a87575b808080615a6d565b615ab2612d6e8373ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b9063ffffffff821615615b85576001615af5615aec8573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b612dbb85612ccb565b015b600180615b2e615b0687615d98565b612e808873ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b0191805490610400615b3f83613001565b11614547575f5b828110615b6d5750505050615b6693926144b06144da936144b59361588c565b5f80615a7f565b80615b7f614533614529879486612fec565b01615b46565b6001615bb2612e008573ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615af7565b615be3612d6e8273ffffffffffffffffffffffffffffffffffffffff165f52601a60205260405f2090565b63ffffffff811615615c85576001615c1c61442b8473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b015b6001906001615c2f61444586615d98565b018154915f5b838110615c57575050505050906144da6144b5615c51936158e3565b5f615a79565b80615c66614529879385612fec565b8a8103615c75575b5001615c35565b615c7f908561588c565b5f615c6e565b6001615cb2612e008473ffffffffffffffffffffffffffffffffffffffff165f52601960205260405f2090565b01615c1e565b50831515615a68565b615d8e91805f52602060078152600f73ffffffffffffffffffffffffffffffffffffffff91604092615cf881855f20541615612eea565b845f5260078252615d4686855f209073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b85165f5260088152825f205493600e8252835f20855f52825280845f20555f52525f205573ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f2090565b6157f98154613001565b73ffffffffffffffffffffffffffffffffffffffff165f52601a60205263ffffffff60405f20541680151580615dd7575b156107285761072890612ccb565b50601960205260405f20615def4291612dbb84612ccb565b5414615dc956fea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d000000000000000000000000072474981c4932021ec628ca7596b47bbb247a245
-----Decoded View---------------
Arg [0] : token_addr (address): 0xD0851030C94433C261B405fEcbf1DEC5E15948d0
Arg [1] : art_proxy (address): 0x72474981c4932021ec628ca7596B47BBB247a245
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000d0851030c94433c261b405fecbf1dec5e15948d0
Arg [1] : 00000000000000000000000072474981c4932021ec628ca7596b47bbb247a245
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.