Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 1,569 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer | 23700058 | 5 days ago | IN | 0 ETH | 0.00000975 | ||||
| Approve | 23693620 | 6 days ago | IN | 0 ETH | 0.00000689 | ||||
| Approve | 23659024 | 10 days ago | IN | 0 ETH | 0.00003468 | ||||
| Transfer | 23515155 | 31 days ago | IN | 0 ETH | 0.00001188 | ||||
| Transfer | 23511231 | 31 days ago | IN | 0 ETH | 0.00001191 | ||||
| Transfer | 23511211 | 31 days ago | IN | 0 ETH | 0.00001171 | ||||
| Transfer | 23422606 | 44 days ago | IN | 0 ETH | 0.00001477 | ||||
| Approve | 23404487 | 46 days ago | IN | 0 ETH | 0.00001666 | ||||
| Approve | 23399488 | 47 days ago | IN | 0 ETH | 0.00001886 | ||||
| Approve | 23399485 | 47 days ago | IN | 0 ETH | 0.00001866 | ||||
| Approve | 23399483 | 47 days ago | IN | 0 ETH | 0.00001835 | ||||
| Approve | 23399479 | 47 days ago | IN | 0 ETH | 0.0000162 | ||||
| Approve | 23399468 | 47 days ago | IN | 0 ETH | 0.00001692 | ||||
| Approve | 23399466 | 47 days ago | IN | 0 ETH | 0.00001851 | ||||
| Approve | 23399464 | 47 days ago | IN | 0 ETH | 0.00001901 | ||||
| Approve | 23399462 | 47 days ago | IN | 0 ETH | 0.00001942 | ||||
| Approve | 23399460 | 47 days ago | IN | 0 ETH | 0.00001862 | ||||
| Approve | 23399458 | 47 days ago | IN | 0 ETH | 0.00001899 | ||||
| Approve | 23399456 | 47 days ago | IN | 0 ETH | 0.00001904 | ||||
| Approve | 23399454 | 47 days ago | IN | 0 ETH | 0.0000174 | ||||
| Approve | 23399450 | 47 days ago | IN | 0 ETH | 0.00001892 | ||||
| Approve | 23399448 | 47 days ago | IN | 0 ETH | 0.00001855 | ||||
| Approve | 23399446 | 47 days ago | IN | 0 ETH | 0.00001701 | ||||
| Approve | 23399444 | 47 days ago | IN | 0 ETH | 0.00001598 | ||||
| Approve | 23399441 | 47 days ago | IN | 0 ETH | 0.00001761 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Add Liquidity ET... | 23141026 | 83 days ago | 1.5 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Crome
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/**
* @title IERC6551Account
* @dev Interface for ERC6551 token bound accounts
*/
interface IERC6551Account {
receive() external payable;
function token()
external
view
returns (uint256 chainId, address tokenContract, uint256 tokenId);
function state() external view returns (uint256);
function isValidSigner(
address signer,
bytes calldata context
) external view returns (bytes4 magicValue);
}
/**
* @title IERC6551Executable
* @dev Interface for ERC6551 execution
*/
interface IERC6551Executable {
function execute(
address to,
uint256 value,
bytes calldata data,
uint8 operation
) external payable returns (bytes memory);
}
/**
* @title ERC6551BytecodeLib
* @dev Library for ERC6551 bytecode operations
*/
library ERC6551BytecodeLib {
/**
* @dev Returns the creation code of the token bound account for a non-fungible token.
*
* @return result The creation code of the token bound account
*/
function getCreationCode(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) internal pure returns (bytes memory result) {
assembly {
result := mload(0x40) // Grab the free memory pointer
// Layout the variables and bytecode backwards
mstore(add(result, 0xb7), tokenId)
mstore(add(result, 0x97), shr(96, shl(96, tokenContract)))
mstore(add(result, 0x77), chainId)
mstore(add(result, 0x57), salt)
mstore(add(result, 0x37), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(result, 0x28), implementation)
mstore(
add(result, 0x14),
0x3d60ad80600a3d3981f3363d3d373d3d3d363d73
)
mstore(result, 0xb7) // Store the length
mstore(0x40, add(result, 0xd7)) // Allocate the memory
}
}
/**
* @dev Returns the create2 address computed from `salt`, `bytecodeHash`, `deployer`.
*
* @return result The create2 address computed from `salt`, `bytecodeHash`, `deployer`
*/
function computeAddress(
bytes32 salt,
bytes32 bytecodeHash,
address deployer
) internal pure returns (address result) {
assembly {
result := mload(0x40) // Grab the free memory pointer
mstore8(result, 0xff)
mstore(add(result, 0x35), bytecodeHash)
mstore(add(result, 0x01), shl(96, deployer))
mstore(add(result, 0x15), salt)
result := keccak256(result, 0x55)
}
}
}
/**
* @title ERC6551AccountLib
* @dev Library for ERC6551 account operations
*/
library ERC6551AccountLib {
function computeAddress(
address registry,
address _implementation,
bytes32 _salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) internal pure returns (address) {
bytes32 bytecodeHash = keccak256(
ERC6551BytecodeLib.getCreationCode(
_implementation,
_salt,
chainId,
tokenContract,
tokenId
)
);
return Create2.computeAddress(_salt, bytecodeHash, registry);
}
function isERC6551Account(
address account,
address expectedImplementation,
address registry
) internal view returns (bool) {
// invalid bytecode size
if (account.code.length != 0xAD) return false;
address _implementation = implementation(account);
// implementation does not exist
if (_implementation.code.length == 0) return false;
// invalid implementation
if (_implementation != expectedImplementation) return false;
(
bytes32 _salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) = context(account);
return
account ==
computeAddress(
registry,
_implementation,
_salt,
chainId,
tokenContract,
tokenId
);
}
function implementation(
address account
) internal view returns (address _implementation) {
assembly {
// copy proxy implementation (0x14 bytes)
extcodecopy(account, 0xC, 0xA, 0x14)
_implementation := mload(0x00)
}
}
function implementation() internal view returns (address _implementation) {
return implementation(address(this));
}
function token(
address account
) internal view returns (uint256, address, uint256) {
bytes memory encodedData = new bytes(0x60);
assembly {
// copy 0x60 bytes from end of context
extcodecopy(account, add(encodedData, 0x20), 0x4d, 0x60)
}
return abi.decode(encodedData, (uint256, address, uint256));
}
function token() internal view returns (uint256, address, uint256) {
return token(address(this));
}
function salt(address account) internal view returns (bytes32) {
bytes memory encodedData = new bytes(0x20);
assembly {
// copy 0x20 bytes from beginning of context
extcodecopy(account, add(encodedData, 0x20), 0x2d, 0x20)
}
return abi.decode(encodedData, (bytes32));
}
function salt() internal view returns (bytes32) {
return salt(address(this));
}
function context(
address account
) internal view returns (bytes32, uint256, address, uint256) {
bytes memory encodedData = new bytes(0x80);
assembly {
// copy full context (0x80 bytes)
extcodecopy(account, add(encodedData, 0x20), 0x2D, 0x80)
}
return abi.decode(encodedData, (bytes32, uint256, address, uint256));
}
function context()
internal
view
returns (bytes32, uint256, address, uint256)
{
return context(address(this));
}
}
/**
* @title ERC6551Account
* @dev Implementation of the ERC6551 token bound account
*/
contract ERC6551Account is
IERC165,
IERC1271,
IERC6551Account,
IERC6551Executable,
IERC721Receiver,
IERC1155Receiver
{
uint256 public state;
receive() external payable {}
function execute(
address to,
uint256 value,
bytes calldata data,
uint8 operation
) external payable virtual returns (bytes memory result) {
require(_isValidSigner(msg.sender), "Invalid signer");
require(operation == 0, "Only call operations are supported");
++state;
bool success;
(success, result) = to.call{value: value}(data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}
function isValidSigner(
address signer,
bytes calldata
) external view virtual returns (bytes4) {
if (_isValidSigner(signer)) {
return IERC6551Account.isValidSigner.selector;
}
return bytes4(0);
}
function isValidSignature(
bytes32 hash,
bytes memory signature
) external view virtual returns (bytes4 magicValue) {
bool isValid = SignatureChecker.isValidSignatureNow(
owner(),
hash,
signature
);
if (isValid) {
return IERC1271.isValidSignature.selector;
}
return bytes4(0);
}
function onERC721Received(
address,
address,
uint256 receivedTokenId,
bytes memory
) external view virtual returns (bytes4) {
_revertIfOwnershipCycle(msg.sender, receivedTokenId);
return IERC721Receiver.onERC721Received.selector;
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) external view virtual returns (bytes4) {
return IERC1155Receiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) external pure virtual returns (bytes4) {
return IERC1155Receiver.onERC1155BatchReceived.selector;
}
function supportsInterface(
bytes4 interfaceId
) public pure virtual returns (bool) {
return (interfaceId == type(IERC6551Account).interfaceId ||
interfaceId == type(IERC6551Executable).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC721Receiver).interfaceId ||
interfaceId == type(IERC165).interfaceId);
}
function token()
public
view
virtual
override
returns (uint256, address, uint256)
{
return ERC6551AccountLib.token();
}
function owner() public view virtual returns (address) {
(uint256 chainId, address contractAddress, uint256 tokenId) = token();
if (chainId != block.chainid) return address(0);
return IERCAI(contractAddress).ownerOf(tokenId);
}
function _isValidSigner(
address signer
) internal view virtual returns (bool) {
return signer == owner();
}
/**
* @dev Helper method to check if a received token is in the ownership chain of the wallet.
* @param receivedTokenAddress The address of the token being received.
* @param receivedTokenId The ID of the token being received.
*/
function _revertIfOwnershipCycle(
address receivedTokenAddress,
uint256 receivedTokenId
) internal view virtual {
(
uint256 _chainId,
address _contractAddress,
uint256 _tokenId
) = token();
require(
_chainId != block.chainid ||
receivedTokenAddress != _contractAddress ||
receivedTokenId != _tokenId,
"Cannot own yourself"
);
address currentOwner = owner();
require(currentOwner != address(this), "Token in ownership chain");
uint256 depth = 0;
while (currentOwner.code.length > 0) {
try IERC6551Account(payable(currentOwner)).token() returns (
uint256 chainId,
address contractAddress,
uint256 tokenId
) {
require(
chainId != block.chainid ||
contractAddress != receivedTokenAddress ||
tokenId != receivedTokenId,
"Token in ownership chain"
);
// Advance up the ownership chain
currentOwner = IERCAI(contractAddress).ownerOf(tokenId);
require(
currentOwner != address(this),
"Token in ownership chain"
);
} catch {
break;
}
unchecked {
++depth;
}
if (depth == 5) revert("Ownership chain too deep");
}
}
}
/**
* @title IERC6551Registry
* @dev Interface for ERC6551 registry
*/
interface IERC6551Registry {
/**
* @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation.
*/
event ERC6551AccountCreated(
address account,
address indexed implementation,
bytes32 salt,
uint256 chainId,
address indexed tokenContract,
uint256 indexed tokenId
);
/**
* @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails.
*/
error AccountCreationFailed();
/**
* @dev Creates a token bound account for a non-fungible token.
*
* If account has already been created, returns the account address without calling create2.
*
* Emits ERC6551AccountCreated event.
*
* @return account The address of the token bound account
*/
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address account);
/**
* @dev Returns the computed token bound account address for a non-fungible token.
*
* @return account The address of the token bound account
*/
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address account);
}
/**
* @title ERC6551Registry
* @dev Implementation of the ERC6551 registry
*/
contract ERC6551Registry is IERC6551Registry, Ownable {
constructor() Ownable(msg.sender) {}
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address) {
assembly {
// Memory Layout:
// ----
// 0x00 0xff (1 byte)
// 0x01 registry (address) (20 bytes)
// 0x15 salt (bytes32) (32 bytes)
// 0x35 Bytecode Hash (bytes32) (32 bytes)
// ----
// 0x55 ERC-1167 Constructor + Header (20 bytes)
// 0x69 implementation (address) (20 bytes)
// 0x5D ERC-1167 Footer (15 bytes)
// 0x8C salt (uint256) (32 bytes)
// 0xAC chainId (uint256) (32 bytes)
// 0xCC tokenContract (address) (32 bytes)
// 0xEC tokenId (uint256) (32 bytes)
// Silence unused variable warnings
pop(chainId)
// Copy bytecode + constant data to memory
calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId
mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
mstore(0x5d, implementation) // implementation
mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header
// Copy create2 computation data to memory
mstore8(0x00, 0xff) // 0xFF
mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode)
mstore(0x01, shl(96, address())) // registry address
mstore(0x15, salt) // salt
// Compute account address
let computed := keccak256(0x00, 0x55)
// If the account has not yet been deployed
if iszero(extcodesize(computed)) {
// Deploy account contract
let deployed := create2(0, 0x55, 0xb7, salt)
// Revert if the deployment fails
if iszero(deployed) {
mstore(0x00, 0x20188a59) // `AccountCreationFailed()`
revert(0x1c, 0x04)
}
// Store account address in memory before salt and chainId
mstore(0x6c, deployed)
// Emit the ERC6551AccountCreated event
log4(
0x6c,
0x60,
// `ERC6551AccountCreated(address,address,bytes32,uint256,address,uint256)`
0x79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf88722,
implementation,
tokenContract,
tokenId
)
// Return the account address
return(0x6c, 0x20)
}
// Otherwise, return the computed account address
mstore(0x00, shr(96, shl(96, computed)))
return(0x00, 0x20)
}
}
function cid() external view returns (uint256) {
return block.chainid;
}
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address) {
assembly {
// Silence unused variable warnings
pop(chainId)
pop(tokenContract)
pop(tokenId)
// Copy bytecode + constant data to memory
calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId
mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
mstore(0x5d, implementation) // implementation
mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header
// Copy create2 computation data to memory
mstore8(0x00, 0xff) // 0xFF
mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode)
mstore(0x01, shl(96, address())) // registry address
mstore(0x15, salt) // salt
// Store computed account address in memory
mstore(0x00, shr(96, shl(96, keccak256(0x00, 0x55))))
// Return computed account address
return(0x00, 0x20)
}
}
}
/**
* @title IERCAI
* @dev Interface for ERCAI token
*/
interface IERCAI is IERC165 {
error NotFound();
error InvalidTokenId();
error AlreadyExists();
error InvalidRecipient();
error InvalidSender();
error InvalidSpender();
error InvalidOperator();
error UnsafeRecipient();
error RecipientIsERC721TransferExempt();
error Unauthorized();
error InsufficientAllowance();
error DecimalsTooLow();
error PermitDeadlineExpired();
error InvalidSigner();
error InvalidApproval();
error OwnedIndexOverflow();
error MintLimitReached();
error InvalidExemption();
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function erc20TotalSupply() external view returns (uint256);
function erc721TotalSupply() external view returns (uint256);
function balanceOf(address owner_) external view returns (uint256);
function erc721BalanceOf(address owner_) external view returns (uint256);
function erc20BalanceOf(address owner_) external view returns (uint256);
function erc721TransferExempt(
address account_
) external view returns (bool);
function isApprovedForAll(
address owner_,
address operator_
) external view returns (bool);
function allowance(
address owner_,
address spender_
) external view returns (uint256);
function owned(address owner_) external view returns (uint256[] memory);
function ownerOf(uint256 id_) external view returns (address erc721Owner);
function tokenURI(uint256 id_) external view returns (string memory);
function approve(
address spender_,
uint256 valueOrId_
) external returns (bool);
function erc20Approve(
address spender_,
uint256 value_
) external returns (bool);
function erc721Approve(
address spender_,
uint256 id_
) external returns (bool);
function setApprovalForAll(address operator_, bool approved_) external;
function transferFrom(
address from_,
address to_,
uint256 valueOrId_
) external returns (bool);
function erc20TransferFrom(
address from_,
address to_,
uint256 value_
) external returns (bool);
function erc721TransferFrom(
address from_,
address to_,
uint256 id_
) external;
function transfer(address to_, uint256 amount_) external returns (bool);
function getERC721QueueLength() external view returns (uint256);
function getERC721TokensInQueue(
uint256 start_,
uint256 count_
) external view returns (uint256[] memory);
function setSelfERC721TransferExempt(bool state_) external;
function safeTransferFrom(address from_, address to_, uint256 id_) external;
function safeTransferFrom(
address from_,
address to_,
uint256 id_,
bytes calldata data_
) external;
function DOMAIN_SEPARATOR() external view returns (bytes32);
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external;
}
/**
* @title PackedDoubleEndedQueue
* @dev Library for managing double-ended queues with packed uint16 values
*/
library PackedDoubleEndedQueue {
uint128 constant SLOT_MASK = (1 << 64) - 1;
uint128 constant INDEX_MASK = SLOT_MASK << 64;
uint256 constant SLOT_DATA_MASK = (1 << 16) - 1;
/**
* @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
*/
error QueueEmpty();
/**
* @dev A push operation couldn't be completed due to the queue being full.
*/
error QueueFull();
/**
* @dev An operation (e.g. {at}) couldn't be completed due to an index being out of bounds.
*/
error QueueOutOfBounds();
/**
* @dev Invalid slot.
*/
error InvalidSlot();
/**
* @dev Indices and slots are 64 bits to fit within a single storage slot.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* The first item is at data[begin] and the last item is at data[end - 1]. This range can wrap around.
*/
struct Uint16Deque {
uint64 _beginIndex;
uint64 _beginSlot;
uint64 _endIndex;
uint64 _endSlot;
mapping(uint64 index => uint256) _data;
}
/**
* @dev Removes the item at the end of the queue and returns it.
*
* Reverts with {QueueEmpty} if the queue is empty.
*/
function popBack(
Uint16Deque storage deque
) internal returns (uint16 value) {
unchecked {
uint64 backIndex = deque._endIndex;
uint64 backSlot = deque._endSlot;
if (backIndex == deque._beginIndex && backSlot == deque._beginSlot)
revert QueueEmpty();
if (backSlot == 0) {
--backIndex;
backSlot = 15;
} else {
--backSlot;
}
uint256 data = deque._data[backIndex];
value = _getEntry(data, backSlot);
deque._data[backIndex] = _setData(data, backSlot, 0);
deque._endIndex = backIndex;
deque._endSlot = backSlot;
}
}
/**
* @dev Inserts an item at the beginning of the queue.
*
* Reverts with {QueueFull} if the queue is full.
*/
function pushFront(Uint16Deque storage deque, uint16 value_) internal {
unchecked {
uint64 frontIndex = deque._beginIndex;
uint64 frontSlot = deque._beginSlot;
if (frontSlot == 0) {
--frontIndex;
frontSlot = 15;
} else {
--frontSlot;
}
if (frontIndex == deque._endIndex && frontSlot == deque._endSlot)
revert QueueFull();
deque._data[frontIndex] = _setData(
deque._data[frontIndex],
frontSlot,
value_
);
deque._beginIndex = frontIndex;
deque._beginSlot = frontSlot;
}
}
/**
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
* `length(deque) - 1`.
*
* Reverts with `QueueOutOfBounds` if the index is out of bounds.
*/
function at(
Uint16Deque storage deque,
uint256 index_
) internal view returns (uint16 value) {
if (index_ >= length(deque) * 16) revert QueueOutOfBounds();
unchecked {
return
_getEntry(
deque._data[
deque._beginIndex +
uint64(deque._beginSlot + (index_ % 16)) /
16 +
uint64(index_ / 16)
],
uint64(((deque._beginSlot + index_) % 16))
);
}
}
/**
* @dev Returns the number of items in the queue.
*/
function length(Uint16Deque storage deque) internal view returns (uint256) {
unchecked {
return
(16 - deque._beginSlot) +
deque._endSlot +
deque._endIndex *
16 -
deque._beginIndex *
16 -
16;
}
}
/**
* @dev Returns true if the queue is empty.
*/
function empty(Uint16Deque storage deque) internal view returns (bool) {
return
deque._endSlot == deque._beginSlot &&
deque._endIndex == deque._beginIndex;
}
function _setData(
uint256 data_,
uint64 slot_,
uint16 value
) private pure returns (uint256) {
return
(data_ & (~_getSlotMask(slot_))) + (uint256(value) << (16 * slot_));
}
function _getEntry(
uint256 data,
uint64 slot_
) private pure returns (uint16) {
return uint16((data & _getSlotMask(slot_)) >> (16 * slot_));
}
function _getSlotMask(uint64 slot_) private pure returns (uint256) {
return SLOT_DATA_MASK << (slot_ * 16);
}
}
/**
* @title ERC721Events
* @dev Events for ERC721 token standard
*/
library ERC721Events {
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
event Approval(
address indexed owner,
address indexed spender,
uint256 indexed id
);
event Transfer(
address indexed from,
address indexed to,
uint256 indexed id
);
}
/**
* @title ERC20Events
* @dev Events for ERC20 token standard
*/
library ERC20Events {
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 amount);
}
/**
* @title ERCAI Core
* @dev Core implementation of the ERCAI standard combining ERC20, ERC721, and ERC6551
*/
abstract contract ERCAICore is IERCAI, ReentrancyGuard, Ownable {
using PackedDoubleEndedQueue for PackedDoubleEndedQueue.Uint16Deque;
// Agent task and performance tracking
struct Task {
string description;
address requester;
uint256 reward;
uint256 deadline;
bool completed;
bytes32 outcome;
}
struct AgentPerformance {
uint256 tasksCompleted;
uint256 rewardsEarned;
uint256 lastActiveTimestamp;
}
struct AgentMessage {
uint256 fromTokenId;
bytes32 message;
uint256 timestamp;
}
struct ScheduledAction {
address to;
uint256 value;
bytes data;
uint8 operation;
uint256 nextExecutionTime;
uint256 interval; // 0 for one-time, >0 for recurring (in seconds)
bool active;
}
/// @dev The queue of ERC-721 tokens stored in the contract.
PackedDoubleEndedQueue.Uint16Deque private _storedERC721Ids;
/// @dev Token name
string public name;
/// @dev Token symbol
string public symbol;
/// @dev Decimals for ERC-20 representation
uint8 public immutable decimals;
/// @dev Units for ERC-20 representation
uint256 public immutable units;
/// @dev Total supply in ERC-20 representation
uint256 public totalSupply;
/// @dev Current mint counter which also represents the highest
/// minted id, monotonically increasing to ensure accurate ownership
uint256 public minted;
/// @dev Initial chain id for EIP-2612 support
uint256 internal immutable _INITIAL_CHAIN_ID;
uint256 constant MAX_BATCH_SIZE = 50;
uint256 constant MAX_TASKS_PER_AGENT = 100;
uint256 constant MAX_MESSAGES_PER_AGENT = 1000;
/// @dev Initial domain separator for EIP-2612 support
bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR;
/// @dev Balance of user in ERC-20 representation
mapping(address => uint256) public balanceOf;
/// @dev Allowance of user in ERC-20 representation
mapping(address => mapping(address => uint256)) public allowance;
/// @dev Approval in ERC-721 representaion
mapping(uint256 => address) public getApproved;
/// @dev Approval for all in ERC-721 representation
mapping(address => mapping(address => bool)) public isApprovedForAll;
/// @dev Packed representation of ownerOf and owned indices
mapping(uint256 => uint256) internal _ownedData;
/// @dev Array of owned ids in ERC-721 representation
mapping(address => uint16[]) internal _owned;
/// @dev Addresses that are exempt from ERC-721 transfer, typically for gas savings (pairs, routers, etc)
mapping(address => bool) internal _erc721TransferExempt;
/// @dev EIP-2612 nonces
mapping(address => uint256) public nonces;
/// @dev Address bitmask for packed ownership data
uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
/// @dev Owned index bitmask for packed ownership data
uint256 private constant _BITMASK_OWNED_INDEX = ((1 << 96) - 1) << 160;
/// @dev Constant for token id encoding
uint256 public constant ID_ENCODING_PREFIX = 1 << 255;
/// @dev Agent authorization system
mapping(uint256 => mapping(address => bool)) public authorizedAgents;
/// @dev Agent instruction storage
mapping(uint256 => bytes32) public agentInstructions;
/// @dev Agent last action tracking
mapping(uint256 => uint256) public lastActionTimestamp;
/// @dev Task management
mapping(uint256 => Task) public tasks;
mapping(uint256 => uint256[]) public agentAssignedTasks;
uint256 public nextTaskId;
/// @dev Agent performance tracking
mapping(uint256 => AgentPerformance) public agentPerformance;
/// @dev Agent communication
mapping(uint256 => AgentMessage[]) public agentMessages;
/// @dev Scheduled actions
mapping(uint256 => mapping(uint256 => ScheduledAction))
public scheduledActions;
mapping(uint256 => uint256) public nextActionId;
mapping(uint256 => mapping(uint256 => bool)) public agentTaskAssignments; // taskId => agentId => isAssigned
/// @dev struct for changeable 6551 setups
struct ERCAISetup {
ERC6551Account implementation;
ERC6551Registry registry;
bytes32 salt;
}
/// @dev storage for each 6551 setup
ERCAISetup[] public setup;
/// @dev 6551 setup set for each NFT
mapping(uint256 => uint256) public nft_setup_set;
// Events for agent functionality
event AccountCreationFailed(uint256 tokenId, address implementation, bytes32 salt);
event TaskCreated(
uint256 indexed taskId,
address indexed requester,
string description
);
event TaskAssigned(uint256 indexed taskId, uint256 indexed agentId);
event TaskCompleted(
uint256 indexed taskId,
uint256 indexed agentId,
bytes32 outcome
);
event AgentMessageSent(
uint256 indexed fromTokenId,
uint256 indexed toTokenId,
bytes32 message
);
event AgentAuthorized(
uint256 indexed tokenId,
address indexed agent,
bool status
);
event AgentActionExecuted(
uint256 indexed tokenId,
address indexed executor,
bytes4 functionSelector
);
event ActionScheduled(
uint256 indexed tokenId,
uint256 actionId,
uint256 nextExecutionTime
);
event ActionCancelled(uint256 indexed tokenId, uint256 actionId);
event ActionExecuted(
uint256 indexed tokenId,
uint256 actionId,
bool success
);
constructor(string memory name_, string memory symbol_, uint8 decimals_) {
name = name_;
symbol = symbol_;
if (decimals_ < 18) {
revert DecimalsTooLow();
}
decimals = decimals_;
units = 10 ** decimals;
// EIP-2612 initialization
_INITIAL_CHAIN_ID = block.chainid;
_INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
/**
* @notice Get the token bound account address for a token ID
* @param id_ The token ID
* @return The token bound account address
*/
function account(uint256 id_) public view returns (address) {
ERCAISetup memory s = setup[nft_setup_set[id_]];
return
s.registry.account(
address(s.implementation),
s.salt,
block.chainid,
address(this),
id_
);
}
/**
* @notice Execute an action through a token bound account
* @param id_ The token ID
* @param to The target address
* @param value The value to send
* @param data The call data
* @param operation The operation type
* @return result The result of the execution
*/
function execute(
uint256 id_,
address to,
uint256 value,
bytes calldata data,
uint8 operation
) external payable nonReentrant returns (bytes memory result) {
require(
msg.sender == _getOwnerOf(id_ + ID_ENCODING_PREFIX),
"Not the token owner"
);
return
ERC6551Account(payable(account(id_))).execute(
to,
value,
data,
operation
);
}
/**
* @notice Authorize an agent to act on behalf of a token
* @param tokenId The token ID
* @param agent The agent address
* @param authorized Whether to authorize or deauthorize
*/
function authorizeAgent(
uint256 tokenId,
address agent,
bool authorized
) external {
require(
_getOwnerOf(tokenId + ID_ENCODING_PREFIX) == msg.sender,
"Not token owner"
);
authorizedAgents[tokenId][agent] = authorized;
emit AgentAuthorized(tokenId, agent, authorized);
}
/**
* @notice Execute an action as an authorized agent
* @param tokenId The token ID
* @param to The target address
* @param value The value to send
* @param data The call data
* @param operation The operation type
* @return result The result of the execution
*/
function executeAsAgent(
uint256 tokenId,
address to,
uint256 value,
bytes calldata data,
uint8 operation
) external nonReentrant returns (bytes memory result) {
require(authorizedAgents[tokenId][msg.sender], "Agent not authorized");
// Update last action timestamp
lastActionTimestamp[tokenId] = block.timestamp;
// Execute the action through the token bound account
address tba = account(tokenId);
// Emit event for monitoring
emit AgentActionExecuted(tokenId, msg.sender, bytes4(data[:4]));
// Execute the call through the token bound account
return ERC6551Account(payable(tba)).execute(to, value, data, operation);
}
/**
* @notice Create a new task
* @param description The task description
* @param deadline The task deadline
* @return taskId The ID of the created task
*/
function createTask(
string calldata description,
uint256 deadline
) external payable nonReentrant returns (uint256) {
require(deadline > block.timestamp, "Deadline must be in the future");
uint256 taskId = nextTaskId++;
Task storage newTask = tasks[taskId];
newTask.description = description;
newTask.requester = msg.sender;
newTask.reward = msg.value;
newTask.deadline = deadline;
newTask.completed = false;
emit TaskCreated(taskId, msg.sender, description);
return taskId;
}
/**
* @notice Assign a task to an agent
* @param taskId The task ID
* @param agentId The agent (token) ID
*/
function assignTask(uint256 taskId, uint256 agentId) external {
require(agentAssignedTasks[agentId].length < MAX_TASKS_PER_AGENT, "Too many tasks for agent");
require(
_getOwnerOf(agentId + ID_ENCODING_PREFIX) == msg.sender,
"Not agent owner"
);
Task storage task = tasks[taskId];
require(!task.completed, "Task already completed");
require(block.timestamp < task.deadline, "Task deadline passed");
agentAssignedTasks[agentId].push(taskId);
agentTaskAssignments[taskId][agentId] = true;
emit TaskAssigned(taskId, agentId);
}
/**
* @notice Complete a task
* @param taskId The task ID
* @param agentId The agent (token) ID
* @param outcome The task outcome
*/
function completeTask(
uint256 taskId,
uint256 agentId,
bytes32 outcome
) external nonReentrant {
address tba = account(agentId);
require(
msg.sender == tba ||
authorizedAgents[agentId][msg.sender] ||
_getOwnerOf(agentId + ID_ENCODING_PREFIX) == msg.sender,
"Not authorized"
);
Task storage task = tasks[taskId];
require(agentTaskAssignments[taskId][agentId], "Agent not assigned to task");
require(!task.completed, "Task already completed");
require(block.timestamp <= task.deadline, "Task deadline passed");
// Mark task as completed
task.completed = true;
task.outcome = outcome;
// Update agent performance
AgentPerformance storage performance = agentPerformance[agentId];
performance.tasksCompleted++;
performance.rewardsEarned += task.reward;
performance.lastActiveTimestamp = block.timestamp;
// Transfer reward to the token-bound account
(bool success, ) = tba.call{value: task.reward}("");
require(success, "Reward transfer failed");
emit TaskCompleted(taskId, agentId, outcome);
}
/**
* @notice Send a message from one agent to another
* @param fromTokenId The sender token ID
* @param toTokenId The recipient token ID
* @param message The message content
*/
function sendAgentMessage(
uint256 fromTokenId,
uint256 toTokenId,
bytes32 message
) external {
// Verify recipient exists
require(toTokenId <= minted, "Recipient token does not exist");
address fromTba = account(fromTokenId);
require(agentMessages[toTokenId].length < MAX_MESSAGES_PER_AGENT, "Too many messages for recipient");
require(
msg.sender == fromTba ||
authorizedAgents[fromTokenId][msg.sender] ||
_getOwnerOf(fromTokenId + ID_ENCODING_PREFIX) == msg.sender,
"Not authorized"
);
agentMessages[toTokenId].push(
AgentMessage({
fromTokenId: fromTokenId,
message: message,
timestamp: block.timestamp
})
);
emit AgentMessageSent(fromTokenId, toTokenId, message);
}
/**
* @notice Get messages for a token
* @param tokenId The token ID
* @param startIndex The starting index
* @param count The number of messages to return
* @return messages The requested messages
*/
function getAgentMessages(
uint256 tokenId,
uint256 startIndex,
uint256 count
) external view returns (AgentMessage[] memory) {
// Limit maximum messages per request
count = count > 100 ? 100 : count;
uint256 totalMessages = agentMessages[tokenId].length;
if (startIndex >= totalMessages) {
return new AgentMessage[](0);
}
uint256 endIndex = startIndex + count;
if (endIndex > totalMessages) {
endIndex = totalMessages;
}
uint256 resultCount = endIndex - startIndex;
AgentMessage[] memory messages = new AgentMessage[](resultCount);
for (uint256 i = 0; i < resultCount; i++) {
messages[i] = agentMessages[tokenId][startIndex + i];
}
return messages;
}
/**
* @notice Schedule an action for future execution
* @param tokenId The token ID
* @param to The target address
* @param value The value to send
* @param data The call data
* @param operation The operation type
* @param executionTime The execution time
* @param interval The interval for recurring actions (0 for one-time)
* @return actionId The ID of the scheduled action
*/
function scheduleAction(
uint256 tokenId,
address to,
uint256 value,
bytes calldata data,
uint8 operation,
uint256 executionTime,
uint256 interval
) external returns (uint256) {
require(
_getOwnerOf(tokenId + ID_ENCODING_PREFIX) == msg.sender ||
authorizedAgents[tokenId][msg.sender],
"Not authorized"
);
require(
executionTime > block.timestamp,
"Execution time must be in the future"
);
uint256 actionId = nextActionId[tokenId]++;
scheduledActions[tokenId][actionId] = ScheduledAction({
to: to,
value: value,
data: data,
operation: operation,
nextExecutionTime: executionTime,
interval: interval,
active: true
});
emit ActionScheduled(tokenId, actionId, executionTime);
return actionId;
}
/**
* @notice Cancel a scheduled action
* @param tokenId The token ID
* @param actionId The action ID
*/
function cancelScheduledAction(uint256 tokenId, uint256 actionId) external {
require(
_getOwnerOf(tokenId + ID_ENCODING_PREFIX) == msg.sender ||
authorizedAgents[tokenId][msg.sender],
"Not authorized"
);
scheduledActions[tokenId][actionId].active = false;
emit ActionCancelled(tokenId, actionId);
}
/**
* @notice Execute a scheduled action
* @param tokenId The token ID
* @param actionId The action ID
* @return success Whether the execution was successful
*/
function executeScheduledAction(
uint256 tokenId,
uint256 actionId
) external nonReentrant returns (bool success) {
ScheduledAction storage action = scheduledActions[tokenId][actionId];
require(action.active, "Action not active");
require(
block.timestamp >= action.nextExecutionTime,
"Not yet time to execute"
);
address tba = account(tokenId);
// Attempt to execute the action
try
ERC6551Account(payable(tba)).execute(
action.to,
action.value,
action.data,
action.operation
)
{
success = true;
} catch {
success = false;
}
// Update for recurring actions
if (action.interval > 0 && success) {
action.nextExecutionTime = block.timestamp + action.interval;
emit ActionScheduled(tokenId, actionId, action.nextExecutionTime);
} else if (success) {
// One-time action completed, deactivate
action.active = false;
}
emit ActionExecuted(tokenId, actionId, success);
return success;
}
/// @notice Function to find owner of a given ERC-721 token
function ownerOf(
uint256 id_
) public view virtual returns (address erc721Owner) {
id_ += ID_ENCODING_PREFIX;
erc721Owner = _getOwnerOf(id_);
if (!_isValidTokenId(id_)) {
revert InvalidTokenId();
}
if (erc721Owner == address(0)) {
revert NotFound();
}
}
function owned(
address owner_
) public view virtual returns (uint256[] memory) {
uint256[] memory ownedAsU256 = new uint256[](_owned[owner_].length);
for (uint256 i = 0; i < _owned[owner_].length; ) {
ownedAsU256[i] = _owned[owner_][i];
unchecked {
++i;
}
}
return ownedAsU256;
}
function erc721BalanceOf(
address owner_
) public view virtual returns (uint256) {
return _owned[owner_].length;
}
function erc20BalanceOf(
address owner_
) public view virtual returns (uint256) {
return balanceOf[owner_];
}
function erc20TotalSupply() public view virtual returns (uint256) {
return totalSupply;
}
function erc721TotalSupply() public view virtual returns (uint256) {
return minted;
}
function getERC721QueueLength() public view virtual returns (uint256) {
return _storedERC721Ids.length();
}
function getERC721TokensInQueue(
uint256 start_,
uint256 count_
) public view virtual returns (uint256[] memory) {
uint256[] memory tokensInQueue = new uint256[](count_);
for (uint256 i = start_; i < start_ + count_; ) {
tokensInQueue[i - start_] = _storedERC721Ids.at(i);
unchecked {
++i;
}
}
return tokensInQueue;
}
/// @notice tokenURI must be implemented by child contract
function tokenURI(uint256 id_) public view virtual returns (string memory);
/// @notice Function for token approvals
/// @dev This function assumes the operator is attempting to approve an ERC-721
/// if valueOrId is less than the minted count. Unlike setApprovalForAll,
/// spender_ must be allowed to be 0x0 so that approval can be revoked.
function approve(
address spender_,
uint256 valueOrId_
) public virtual returns (bool) {
// The ERC-721 tokens are 1-indexed, so 0 is not a valid id and indicates that
// operator is attempting to set the ERC-20 allowance to 0.
if (valueOrId_ >= ID_ENCODING_PREFIX)
return erc20Approve(spender_, valueOrId_);
if (_isValidTokenId(valueOrId_ + ID_ENCODING_PREFIX)) {
bool auth = erc721Approve(spender_, valueOrId_);
// If ERC-721 exists but sender is not authorised then default to ERC-20
if (!auth) return erc20Approve(spender_, valueOrId_);
} else {
return erc20Approve(spender_, valueOrId_);
}
return true;
}
function erc721Approve(
address spender_,
uint256 id_
) public virtual returns (bool) {
// Intention is to approve as ERC-721 token (id).
id_ += ID_ENCODING_PREFIX;
address erc721Owner = _getOwnerOf(id_);
if (
msg.sender != erc721Owner &&
!isApprovedForAll[erc721Owner][msg.sender]
) {
return false;
}
getApproved[id_] = spender_;
emit ERC721Events.Approval(
erc721Owner,
spender_,
id_ - ID_ENCODING_PREFIX
);
return true;
}
/// @dev Providing type(uint256).max for approval value results in an
/// unlimited approval that is not deducted from on transfers.
function erc20Approve(
address spender_,
uint256 value_
) public virtual returns (bool) {
// Prevent granting 0x0 an ERC-20 allowance.
if (spender_ == address(0)) {
revert InvalidSpender();
}
// Intention is to approve as ERC-20 token (value).
allowance[msg.sender][spender_] = value_;
emit ERC20Events.Approval(msg.sender, spender_, value_);
return true;
}
/// @notice Function for ERC-721 approvals
function setApprovalForAll(
address operator_,
bool approved_
) public virtual {
// Prevent approvals to 0x0.
if (operator_ == address(0)) {
revert InvalidOperator();
}
isApprovedForAll[msg.sender][operator_] = approved_;
emit ERC721Events.ApprovalForAll(msg.sender, operator_, approved_);
}
/// @notice Function for mixed transfers from an operator that may be different than 'from'.
/// @dev This function assumes the operator is attempting to transfer an ERC-721
/// if valueOrId is less than or equal to current max id.
function transferFrom(
address from_,
address to_,
uint256 valueOrId_
) public virtual returns (bool) {
if (_isValidTokenId(valueOrId_ + ID_ENCODING_PREFIX)) {
if (from_ != _getOwnerOf(valueOrId_ + ID_ENCODING_PREFIX))
return erc20TransferFrom(from_, to_, valueOrId_);
else erc721TransferFrom(from_, to_, valueOrId_);
} else {
// Intention is to transfer as ERC-20 token (value).
return erc20TransferFrom(from_, to_, valueOrId_);
}
return true;
}
/// @notice Function for ERC-721 transfers from.
/// @dev This function is recommended for ERC721 transfers
function erc721TransferFrom(
address from_,
address to_,
uint256 id_
) public virtual {
id_ += ID_ENCODING_PREFIX;
// Prevent transferring tokens from 0x0.
if (from_ == address(0)) {
revert InvalidSender();
}
// Prevent burning tokens to 0x0.
if (to_ == address(0)) {
revert InvalidRecipient();
}
if (from_ != _getOwnerOf(id_)) {
revert Unauthorized();
}
// Check that the operator is either the sender or approved for the transfer.
if (
msg.sender != from_ &&
!isApprovedForAll[from_][msg.sender] &&
msg.sender != getApproved[id_]
) {
revert Unauthorized();
}
if (erc721TransferExempt(to_)) {
revert RecipientIsERC721TransferExempt();
}
// Transfer 1 * units ERC-20 and 1 ERC-721 token.
// ERC-721 transfer exemptions handled above. Can't make it to this point if either is transfer exempt.
_transferERC20(from_, to_, units);
_transferERC721(from_, to_, id_);
}
/// @notice Function for ERC-20 transfers from.
/// @dev This function is recommended for ERC20 transfers
function erc20TransferFrom(
address from_,
address to_,
uint256 value_
) public virtual returns (bool) {
// Prevent transferring tokens from 0x0.
if (from_ == address(0)) {
revert InvalidSender();
}
// Prevent burning tokens to 0x0.
if (to_ == address(0)) {
revert InvalidRecipient();
}
// Intention is to transfer as ERC-20 token (value).
uint256 allowed = allowance[from_][msg.sender];
// Check that the operator has sufficient allowance.
if (allowed != type(uint256).max) {
if (allowed < value_) {
revert InsufficientAllowance();
}
allowance[from_][msg.sender] = allowed - value_;
}
// Transferring ERC-20s directly requires the _transfer function.
// Handles ERC-721 exemptions internally.
return _transferERC20WithERC721(from_, to_, value_);
}
/// @notice Function for ERC-20 transfers.
/// @dev This function assumes the operator is attempting to transfer as ERC-20
/// given this function is only supported on the ERC-20 interface.
/// Treats even small amounts that are valid ERC-721 ids as ERC-20s.
function transfer(
address to_,
uint256 value_
) public virtual returns (bool) {
// Prevent burning tokens to 0x0.
if (to_ == address(0)) {
revert InvalidRecipient();
}
// Transferring ERC-20s directly requires the _transfer function.
// Handles ERC-721 exemptions internally.
return _transferERC20WithERC721(msg.sender, to_, value_);
}
/// @notice Function for ERC-721 transfers with contract support.
/// This function only supports moving valid ERC-721 ids, as it does not exist on the ERC-20
/// spec and will revert otherwise.
function safeTransferFrom(
address from_,
address to_,
uint256 id_
) public virtual {
safeTransferFrom(from_, to_, id_, "");
}
/// @notice Function for ERC-721 transfers with contract support and callback data.
/// This function only supports moving valid ERC-721 ids, as it does not exist on the
/// ERC-20 spec and will revert otherwise.
function safeTransferFrom(
address from_,
address to_,
uint256 id_,
bytes memory data_
) public virtual {
if (!_isValidTokenId(id_ + ID_ENCODING_PREFIX)) {
revert InvalidTokenId();
}
transferFrom(from_, to_, id_);
if (
to_.code.length != 0 &&
IERC721Receiver(to_).onERC721Received(
msg.sender,
from_,
id_,
data_
) !=
IERC721Receiver.onERC721Received.selector
) {
revert UnsafeRecipient();
}
}
/// @notice Function for EIP-2612 permits
/// @dev Providing type(uint256).max for permit value results in an
/// unlimited approval that is not deducted from on transfers.
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
uint8 v_,
bytes32 r_,
bytes32 s_
) public virtual {
if (deadline_ < block.timestamp) {
revert PermitDeadlineExpired();
}
if (_isValidTokenId(value_)) {
revert InvalidApproval();
}
if (spender_ == address(0)) {
revert InvalidSpender();
}
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner_,
spender_,
value_,
nonces[owner_]++,
deadline_
)
)
)
),
v_,
r_,
s_
);
if (recoveredAddress == address(0) || recoveredAddress != owner_) {
revert InvalidSigner();
}
allowance[recoveredAddress][spender_] = value_;
}
emit ERC20Events.Approval(owner_, spender_, value_);
}
/// @notice Returns domain initial domain separator, or recomputes if chain id is not equal to initial chain id
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return
block.chainid == _INITIAL_CHAIN_ID
? _INITIAL_DOMAIN_SEPARATOR
: _computeDomainSeparator();
}
function supportsInterface(
bytes4 interfaceId
) public view virtual returns (bool) {
return
interfaceId == type(IERCAI).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
/// @notice Function for self-exemption
function setSelfERC721TransferExempt(bool state_) public virtual {
_setERC721TransferExempt(msg.sender, state_);
}
/// @notice Function to check if address is transfer exempt
function erc721TransferExempt(
address target_
) public view virtual returns (bool) {
return target_ == address(0) || _erc721TransferExempt[target_];
}
/// @notice For a token token id to be considered valid, it just needs
/// to fall within the range of possible token ids, it does not
/// necessarily have to be minted yet.
function _isValidTokenId(uint256 id_) internal pure returns (bool) {
return id_ > ID_ENCODING_PREFIX && id_ != type(uint256).max;
}
/// @notice Internal function to compute domain separator for EIP-2612 permits
function _computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/// @notice This is the lowest level ERC-20 transfer function, which
/// should be used for both normal ERC-20 transfers as well as minting.
/// Note that this function allows transfers to and from 0x0.
function _transferERC20(
address from_,
address to_,
uint256 value_
) internal virtual {
// Minting is a special case for which we should not check the balance of
// the sender, and we should increase the total supply.
if (from_ == address(0)) {
totalSupply += value_;
} else {
// Deduct value from sender's balance.
balanceOf[from_] -= value_;
}
// Update the recipient's balance.
// Can be unchecked because on mint, adding to totalSupply is checked, and on transfer balance deduction is checked.
unchecked {
balanceOf[to_] += value_;
}
emit ERC20Events.Transfer(from_, to_, value_);
}
/// @notice Consolidated record keeping function for transferring ERC-721s.
/// @dev Assign the token to the new owner, and remove from the old owner.
/// Note that this function allows transfers to and from 0x0.
/// Does not handle ERC-721 exemptions.
function _transferERC721(
address from_,
address to_,
uint256 id_
) internal virtual {
// If this is not a mint, handle record keeping for transfer from previous owner.
if (from_ != address(0)) {
// On transfer of an NFT, any previous approval is reset.
delete getApproved[id_];
uint256 updatedId = ID_ENCODING_PREFIX +
_owned[from_][_owned[from_].length - 1];
if (updatedId != id_) {
uint256 updatedIndex = _getOwnedIndex(id_);
// update _owned for sender
_owned[from_][updatedIndex] = uint16(updatedId);
// update index for the moved id
_setOwnedIndex(updatedId, updatedIndex);
}
// pop
_owned[from_].pop();
}
// Check if this is a burn.
if (to_ != address(0)) {
// If not a burn, update the owner of the token to the new owner.
// Update owner of the token to the new owner.
_setOwnerOf(id_, to_);
// Push token onto the new owner's stack.
_owned[to_].push(uint16(id_));
// Update index for new owner's stack.
_setOwnedIndex(id_, _owned[to_].length - 1);
} else {
// If this is a burn, reset the owner of the token to 0x0 by deleting the token from _ownedData.
delete _ownedData[id_];
}
emit ERC721Events.Transfer(from_, to_, id_ - ID_ENCODING_PREFIX);
}
/// @notice Internal function for ERC-20 transfers. Also handles any ERC-721 transfers that may be required.
// Handles ERC-721 exemptions.
function _transferERC20WithERC721(
address from_,
address to_,
uint256 value_
) internal virtual returns (bool) {
uint256 erc20BalanceOfSenderBefore = erc20BalanceOf(from_);
uint256 erc20BalanceOfReceiverBefore = erc20BalanceOf(to_);
_transferERC20(from_, to_, value_);
// Preload for gas savings on branches
bool isFromERC721TransferExempt = erc721TransferExempt(from_);
bool isToERC721TransferExempt = erc721TransferExempt(to_);
// Skip _withdrawAndStoreERC721 and/or _retrieveOrMintERC721 for ERC-721 transfer exempt addresses
// 1) to save gas
// 2) because ERC-721 transfer exempt addresses won't always have/need ERC-721s corresponding to their ERC20s.
if (isFromERC721TransferExempt && isToERC721TransferExempt) {
// Case 1) Both sender and recipient are ERC-721 transfer exempt. No ERC-721s need to be transferred.
// NOOP.
} else if (isFromERC721TransferExempt) {
// Case 2) The sender is ERC-721 transfer exempt, but the recipient is not. Contract should not attempt
// to transfer ERC-721s from the sender, but the recipient should receive ERC-721s
// from the bank/minted for any whole number increase in their balance.
// Only cares about whole number increments.
uint256 tokensToRetrieveOrMint = (balanceOf[to_] / units) -
(erc20BalanceOfReceiverBefore / units);
// Check to prevent excessive loop iterations
if (tokensToRetrieveOrMint > MAX_BATCH_SIZE) {
revert("Retrieval batch size too large");
}
for (uint256 i = 0; i < tokensToRetrieveOrMint; ) {
_retrieveOrMintERC721(to_);
unchecked {
++i;
}
}
} else if (isToERC721TransferExempt) {
// Case 3) The sender is not ERC-721 transfer exempt, but the recipient is. Contract should attempt
// to withdraw and store ERC-721s from the sender, but the recipient should not
// receive ERC-721s from the bank/minted.
// Only cares about whole number increments.
uint256 tokensToWithdrawAndStore = (erc20BalanceOfSenderBefore /
units) - (balanceOf[from_] / units);
// Check to prevent excessive loop iterations
if (tokensToWithdrawAndStore > MAX_BATCH_SIZE) {
revert("Withdrawal batch size too large");
}
for (uint256 i = 0; i < tokensToWithdrawAndStore; ) {
_withdrawAndStoreERC721(from_);
unchecked {
++i;
}
}
} else {
// Case 4) Neither the sender nor the recipient are ERC-721 transfer exempt.
// Strategy:
// 1. First deal with the whole tokens. These are easy and will just be transferred.
// 2. Look at the fractional part of the value:
// a) If it causes the sender to lose a whole token that was represented by an NFT due to a
// fractional part being transferred, withdraw and store an additional NFT from the sender.
// b) If it causes the receiver to gain a whole new token that should be represented by an NFT
// due to receiving a fractional part that completes a whole token, retrieve or mint an NFT to the recevier.
// Whole tokens worth of ERC-20s get transferred as ERC-721s without any burning/minting.
uint256 nftsToTransfer = value_ / units;
if (nftsToTransfer > MAX_BATCH_SIZE) {
revert("Batch size too large");
}
for (uint256 i = 0; i < nftsToTransfer; ) {
// Pop from sender's ERC-721 stack and transfer them (LIFO)
uint256 indexOfLastToken = _owned[from_].length - 1;
uint256 tokenId = ID_ENCODING_PREFIX +
_owned[from_][indexOfLastToken];
_transferERC721(from_, to_, tokenId);
unchecked {
++i;
}
}
// If the sender's transaction changes their holding from a fractional to a non-fractional
// amount (or vice versa), adjust ERC-721s.
//
// Check if the send causes the sender to lose a whole token that was represented by an ERC-721
// due to a fractional part being transferred.
if (
erc20BalanceOfSenderBefore /
units -
erc20BalanceOf(from_) /
units >
nftsToTransfer
) {
_withdrawAndStoreERC721(from_);
}
if (
erc20BalanceOf(to_) /
units -
erc20BalanceOfReceiverBefore /
units >
nftsToTransfer
) {
_retrieveOrMintERC721(to_);
}
}
return true;
}
/// @notice Internal function for ERC20 minting
/// @dev This function will allow minting of new ERC20s.
/// If mintCorrespondingERC721s_ is true, and the recipient is not ERC-721 exempt, it will
/// also mint the corresponding ERC721s.
/// Handles ERC-721 exemptions.
function _mintERC20(address to_, uint256 value_) internal virtual {
/// You cannot mint to the zero address (you can't mint and immediately burn in the same transfer).
if (to_ == address(0)) {
revert InvalidRecipient();
}
if (totalSupply + value_ > ID_ENCODING_PREFIX) {
revert MintLimitReached();
}
_transferERC20WithERC721(address(0), to_, value_);
}
/// @notice Internal function for ERC-721 minting and retrieval from the bank.
/// @dev This function will allow minting of new ERC-721s up to the total fractional supply. It will
/// first try to pull from the bank, and if the bank is empty, it will mint a new token.
/// Does not handle ERC-721 exemptions.
function _retrieveOrMintERC721(address to_) internal virtual {
if (to_ == address(0)) {
revert InvalidRecipient();
}
uint256 id;
if (!_storedERC721Ids.empty()) {
// If there are any tokens in the bank, use those first.
// Pop off the end of the queue (FIFO).
id = ID_ENCODING_PREFIX + _storedERC721Ids.popBack();
} else {
// Otherwise, mint a new token, should not be able to go over the total fractional supply.
++minted;
// Reserve max uint256 for approvals
if (minted == type(uint256).max) {
revert MintLimitReached();
}
id = ID_ENCODING_PREFIX + minted;
// Create 6551 account for new minted NFT using the latest setup data
uint256 sl = setup.length - 1;
nft_setup_set[minted] = sl;
_createAccount(sl, minted);
}
address erc721Owner = _getOwnerOf(id);
// The token should not already belong to anyone besides 0x0 or this contract.
// If it does, something is wrong, as this should never happen.
if (erc721Owner != address(0)) {
revert AlreadyExists();
}
// Transfer the token to the recipient, either transferring from the contract's bank or minting.
// Does not handle ERC-721 exemptions.
_transferERC721(erc721Owner, to_, id);
}
/// @notice Internal function for ERC-721 deposits to bank (this contract).
/// @dev This function will allow depositing of ERC-721s to the bank, which can be retrieved by future minters.
// Does not handle ERC-721 exemptions.
function _withdrawAndStoreERC721(address from_) internal virtual {
if (from_ == address(0)) {
revert InvalidSender();
}
// Retrieve the latest token added to the owner's stack (LIFO).
uint256 id = ID_ENCODING_PREFIX +
_owned[from_][_owned[from_].length - 1];
// Transfer to 0x0.
// Does not handle ERC-721 exemptions.
_transferERC721(from_, address(0), id);
// Record the token in the contract's bank queue.
_storedERC721Ids.pushFront(uint16(id));
}
/// @notice Initialization function to set pairs / etc, saving gas by avoiding mint / burn on unnecessary targets
function _setERC721TransferExempt(
address target_,
bool state_
) internal virtual {
if (target_ == address(0)) {
revert InvalidExemption();
}
// Adjust the ERC721 balances of the target to respect exemption rules.
// Despite this logic, it is still recommended practice to exempt prior to the target
// having an active balance.
if (state_) {
_clearERC721Balance(target_);
} else {
_reinstateERC721Balance(target_);
}
_erc721TransferExempt[target_] = state_;
}
/// @notice Function to reinstate balance on exemption removal
function _reinstateERC721Balance(address target_) private {
uint256 expectedERC721Balance = erc20BalanceOf(target_) / units;
uint256 actualERC721Balance = erc721BalanceOf(target_);
for (uint256 i = 0; i < expectedERC721Balance - actualERC721Balance; ) {
// Transfer ERC721 balance in from pool
_retrieveOrMintERC721(target_);
unchecked {
++i;
}
}
}
/// @notice Function to clear balance on exemption inclusion
function _clearERC721Balance(address target_) private {
uint256 erc721Balance = erc721BalanceOf(target_);
for (uint256 i = 0; i < erc721Balance; ) {
// Transfer out ERC721 balance
_withdrawAndStoreERC721(target_);
unchecked {
++i;
}
}
}
function _getOwnerOf(
uint256 id_
) internal view virtual returns (address ownerOf_) {
uint256 data = _ownedData[id_];
assembly {
ownerOf_ := and(data, _BITMASK_ADDRESS)
}
}
function _setOwnerOf(uint256 id_, address owner_) internal virtual {
uint256 data = _ownedData[id_];
assembly {
data := add(
and(data, _BITMASK_OWNED_INDEX),
and(owner_, _BITMASK_ADDRESS)
)
}
_ownedData[id_] = data;
}
function _getOwnedIndex(
uint256 id_
) internal view virtual returns (uint256 ownedIndex_) {
uint256 data = _ownedData[id_];
assembly {
ownedIndex_ := shr(160, data)
}
}
function _setOwnedIndex(uint256 id_, uint256 index_) internal virtual {
uint256 data = _ownedData[id_];
if (index_ > _BITMASK_OWNED_INDEX >> 160) {
revert OwnedIndexOverflow();
}
assembly {
data := add(
and(data, _BITMASK_ADDRESS),
and(shl(160, index_), _BITMASK_OWNED_INDEX)
)
}
_ownedData[id_] = data;
}
function _createAccount(uint256 setupId_, uint256 tokenId_) internal virtual {
ERCAISetup memory s = setup[setupId_];
try s.registry.createAccount(
address(s.implementation),
s.salt,
block.chainid,
address(this),
tokenId_
) {} catch {
emit AccountCreationFailed(tokenId_, address(s.implementation), s.salt);
}
}
}
/**
* @title ERCAI
* @dev Implementation of the ERCAI token with AI agent capabilities
*/
contract Crome is ERCAICore {
IUniswapV2Router02 immutable uniswapV2Router_;
string baseURI =
"rose-select-pheasant-446.mypinata.cloud/ipfs/bafkreicvh2cdfdu7uwh5tog7puzxdikuuolovkict32rxub5rjdl2ahhgu/";
bool public nftsMinting;
uint256 public maxWallet;
bool public allowExempt;
address public uniswapV2Pair;
// Tax system variables
address public developmentWallet;
bool public taxesActive = true;
bool private inSwapProcess;
uint256 public tokensForSwap;
uint256 public buyTax;
uint256 public sellTax;
uint256 public transferTax;
uint256 private lastSwapBlock;
uint256 public launchBlock;
mapping(address => bool) public excludedFromFees;
mapping(address => bool) public marketPairs;
modifier lockSwapProcess() {
inSwapProcess = true;
_;
inSwapProcess = false;
}
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_,
ERC6551Registry registry_,
ERC6551Account implementation_,
bytes32 salt_
) ERCAICore(name_, symbol_, decimals_) Ownable(msg.sender) {
uniswapV2Router_ = IUniswapV2Router02(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
);
_setERC721TransferExempt(address(uniswapV2Router_), true);
setup.push(
ERCAISetup({
implementation: implementation_,
registry: registry_,
salt: salt_
})
);
developmentWallet = 0x3f57924D96bF4737475a6Ea2DA3b0a51ACe36Cdb;
buyTax = 0;
sellTax = 0;
transferTax = 0;
excludedFromFees[address(this)] = true;
}
function tokenURI(
uint256 id_
) public view override returns (string memory) {
return string.concat(baseURI, Strings.toString(id_));
}
function updateURI(string memory uri) external onlyOwner {
baseURI = uri;
}
function setERC721TransferExempt(
address account_,
bool value_
) external onlyOwner {
_setERC721TransferExempt(account_, value_);
}
function add6551Setup(
ERC6551Registry registry_,
ERC6551Account implementation_,
bytes32 salt_
) external onlyOwner {
setup.push(
ERCAISetup({
implementation: implementation_,
registry: registry_,
salt: salt_
})
);
}
function upgrade6551Setup(uint256 setupId_, uint256 tokenId_) external {
require(
_getOwnerOf(tokenId_ + ID_ENCODING_PREFIX) == msg.sender,
"Not token owner"
);
require(setupId_ < setup.length, "Invalid setup");
nft_setup_set[tokenId_] = setupId_;
_createAccount(setupId_, tokenId_);
}
function enableTrading(uint256 supply721, bool create) external payable onlyOwner {
require(erc20TotalSupply() == 0, "Already launched");
require(msg.value > 0, "ETH required for liquidity");
_setERC721TransferExempt(address(this), true);
uint256 supply = supply721 * units;
maxWallet = supply;
_mintERC20(address(this), supply);
tokensForSwap = supply/1000;
launchBlock = block.number;
buyTax = 5;
sellTax = 5;
// Set exemptions for owner and router
_setERC721TransferExempt(msg.sender, true);
excludedFromFees[msg.sender] = true; // Exempt owner from fees
uint256 ownerTokens = supply * 20 / 100; // 20% to owner
uint256 liquidityTokens = supply * 80 / 100; // 80% for liquidity
_transferERC20(address(this), msg.sender, ownerTokens);
// Approve router to spend liquidity tokens
allowance[address(this)][address(uniswapV2Router_)] = type(uint256).max;
if (create) {
uniswapV2Pair = IUniswapV2Factory(uniswapV2Router_.factory())
.createPair(address(this), uniswapV2Router_.WETH());
_setERC721TransferExempt(uniswapV2Pair, true);
marketPairs[uniswapV2Pair] = true;
}
uniswapV2Router_.addLiquidityETH{value: msg.value}(
address(this),
liquidityTokens,
0, // Accept any amount of tokens
0, // Accept any amount of ETH
msg.sender, // LP tokens go to owner
block.timestamp + 600 // 10 minute deadline
);
maxWallet = supply * 2/ 100;
}
function _transferERC20WithERC721(
address from_,
address to_,
uint256 value_
) internal override returns (bool) {
if (!nftsMinting) _setERC721TransferExempt(to_, true);
if (
to_ != uniswapV2Pair &&
maxWallet < erc20TotalSupply() &&
to_ != address(0)
) {
uint256 bal = erc20BalanceOf(to_);
require(bal + value_ <= maxWallet, "Too many tokens");
}
bool applyFee = taxesActive &&
!inSwapProcess &&
!(excludedFromFees[from_] || excludedFromFees[to_]);
if (applyFee) {
uint256 feeAmount = 0;
uint256 currentBuyTax = buyTax;
uint256 currentSellTax = sellTax;
if (block.number == launchBlock) {
currentBuyTax = 38;
currentSellTax = 38;
}
if (marketPairs[to_] && currentBuyTax > 0) {
// Buy tax
feeAmount = (value_ * currentBuyTax) / 100;
} else if (marketPairs[from_] && currentSellTax > 0) {
// Sell tax
feeAmount = (value_ * currentSellTax) / 100;
} else if (!marketPairs[to_] && !marketPairs[from_] && transferTax > 0) {
// Transfer tax
feeAmount = (value_ * transferTax) / 100;
}
if (feeAmount > 0) {
value_ -= feeAmount;
super._transferERC20WithERC721(from_, address(this), feeAmount);
}
}
uint256 contractTokenBalance = erc20BalanceOf(address(this));
bool canSwap = contractTokenBalance >= tokensForSwap;
if (applyFee && !marketPairs[from_] && canSwap) {
if (block.number > lastSwapBlock) {
_swapTokens(contractTokenBalance);
lastSwapBlock = block.number;
}
}
return super._transferERC20WithERC721(from_, to_, value_);
}
function setSelfERC721TransferExempt(bool state_) public override {
require(allowExempt, "Please wait until feature enabled");
super.setSelfERC721TransferExempt(state_);
}
function mintNFTs() external onlyOwner {
nftsMinting = true;
}
function removeMaxWallet() external onlyOwner {
maxWallet = erc20TotalSupply();
}
function allowSelfExempts() external onlyOwner {
allowExempt = true;
}
function _swapTokens(uint256 tokenAmount) internal virtual lockSwapProcess {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router_.WETH();
uint256 maxSwapAmount = tokensForSwap * 3;
if (tokenAmount > maxSwapAmount) {
tokenAmount = maxSwapAmount;
}
uniswapV2Router_.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0,
path,
developmentWallet,
block.timestamp
);
}
// Admin functions for tax management
function setTaxesActive(bool value) external onlyOwner {
taxesActive = value;
}
function setDevelopmentWallet(address newWallet) external onlyOwner {
developmentWallet = newWallet;
}
function setBuyTax(uint256 newTax) external onlyOwner {
require(newTax <= 5, "Tax too high (max 5%)");
buyTax = newTax;
}
function setSellTax(uint256 newTax) external onlyOwner {
require(newTax <= 5, "Tax too high (max 5%)");
sellTax = newTax;
}
function setTransferTax(uint256 newTax) external onlyOwner {
require(newTax <= 5, "Tax too high (max 5%)");
transferTax = newTax;
}
function setTokensForSwap(uint256 newAmount) external onlyOwner {
tokensForSwap = newAmount;
}
function excludeFromFees(address account, bool value) external onlyOwner {
excludedFromFees[account] = value;
}
function setMarketPair(address pair, bool value) external onlyOwner {
marketPairs[pair] = value;
}
}
interface IUniswapV2Factory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint256
);
function createPair(
address tokenA,
address tokenB
) external returns (address pair);
}
interface IUniswapV2Pair {
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function getAmountsIn(
uint256 amountOut,
address[] calldata path
) external view returns (uint256[] memory amounts);
function getAmountsOut(
uint256 amountIn,
address[] calldata path
) external view returns (uint256[] memory amounts);
}
interface IUniswapV2Router02 is IUniswapV2Router01 {
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// 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
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// 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/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Not enough balance for performing a CREATE2 deploy.
*/
error Create2InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev There's no code to deploy.
*/
error Create2EmptyBytecode();
/**
* @dev The deployment failed.
*/
error Create2FailedDeployment();
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
if (address(this).balance < amount) {
revert Create2InsufficientBalance(address(this).balance, amount);
}
if (bytecode.length == 0) {
revert Create2EmptyBytecode();
}
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
if (addr == address(0)) {
revert Create2FailedDeployment();
}
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := keccak256(start, 85)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"contract ERC6551Registry","name":"registry_","type":"address"},{"internalType":"contract ERC6551Account","name":"implementation_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyExists","type":"error"},{"inputs":[],"name":"DecimalsTooLow","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InvalidApproval","type":"error"},{"inputs":[],"name":"InvalidExemption","type":"error"},{"inputs":[],"name":"InvalidOperator","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidSpender","type":"error"},{"inputs":[],"name":"InvalidTokenId","type":"error"},{"inputs":[],"name":"MintLimitReached","type":"error"},{"inputs":[],"name":"NotFound","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnedIndexOverflow","type":"error"},{"inputs":[],"name":"PermitDeadlineExpired","type":"error"},{"inputs":[],"name":"QueueEmpty","type":"error"},{"inputs":[],"name":"QueueFull","type":"error"},{"inputs":[],"name":"QueueOutOfBounds","type":"error"},{"inputs":[],"name":"RecipientIsERC721TransferExempt","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnsafeRecipient","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"AccountCreationFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actionId","type":"uint256"}],"name":"ActionCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actionId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"ActionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextExecutionTime","type":"uint256"}],"name":"ActionScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"bytes4","name":"functionSelector","type":"bytes4"}],"name":"AgentActionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AgentAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"message","type":"bytes32"}],"name":"AgentMessageSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","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":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"taskId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"agentId","type":"uint256"}],"name":"TaskAssigned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"taskId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"agentId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"outcome","type":"bytes32"}],"name":"TaskCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"taskId","type":"uint256"},{"indexed":true,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"string","name":"description","type":"string"}],"name":"TaskCreated","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":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ID_ENCODING_PREFIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"account","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC6551Registry","name":"registry_","type":"address"},{"internalType":"contract ERC6551Account","name":"implementation_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"}],"name":"add6551Setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"agentAssignedTasks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"agentInstructions","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"agentMessages","outputs":[{"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"internalType":"bytes32","name":"message","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"agentPerformance","outputs":[{"internalType":"uint256","name":"tasksCompleted","type":"uint256"},{"internalType":"uint256","name":"rewardsEarned","type":"uint256"},{"internalType":"uint256","name":"lastActiveTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"agentTaskAssignments","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowSelfExempts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"valueOrId_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"taskId","type":"uint256"},{"internalType":"uint256","name":"agentId","type":"uint256"}],"name":"assignTask","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"agent","type":"address"},{"internalType":"bool","name":"authorized","type":"bool"}],"name":"authorizeAgent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"authorizedAgents","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"actionId","type":"uint256"}],"name":"cancelScheduledAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"taskId","type":"uint256"},{"internalType":"uint256","name":"agentId","type":"uint256"},{"internalType":"bytes32","name":"outcome","type":"bytes32"}],"name":"completeTask","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"description","type":"string"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"createTask","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"developmentWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supply721","type":"uint256"},{"internalType":"bool","name":"create","type":"bool"}],"name":"enableTrading","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"erc20Approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"erc20BalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc20TotalSupply","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":"value_","type":"uint256"}],"name":"erc20TransferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"erc721Approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"erc721BalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc721TotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"}],"name":"erc721TransferExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"erc721TransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"value","type":"bool"}],"name":"excludeFromFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"excludedFromFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint8","name":"operation","type":"uint8"}],"name":"execute","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint8","name":"operation","type":"uint8"}],"name":"executeAsAgent","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"actionId","type":"uint256"}],"name":"executeScheduledAction","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"getAgentMessages","outputs":[{"components":[{"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"internalType":"bytes32","name":"message","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct ERCAICore.AgentMessage[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getERC721QueueLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"start_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"}],"name":"getERC721TokensInQueue","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastActionTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"launchBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"marketPairs","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxWallet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nextActionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTaskId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nft_setup_set","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftsMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"owner_","type":"address"}],"name":"owned","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"erc721Owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeMaxWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"id_","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":"id_","type":"uint256"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint8","name":"operation","type":"uint8"},{"internalType":"uint256","name":"executionTime","type":"uint256"},{"internalType":"uint256","name":"interval","type":"uint256"}],"name":"scheduleAction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"scheduledActions","outputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint8","name":"operation","type":"uint8"},{"internalType":"uint256","name":"nextExecutionTime","type":"uint256"},{"internalType":"uint256","name":"interval","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sellTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"internalType":"uint256","name":"toTokenId","type":"uint256"},{"internalType":"bytes32","name":"message","type":"bytes32"}],"name":"sendAgentMessage","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":"uint256","name":"newTax","type":"uint256"}],"name":"setBuyTax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWallet","type":"address"}],"name":"setDevelopmentWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"bool","name":"value_","type":"bool"}],"name":"setERC721TransferExempt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"},{"internalType":"bool","name":"value","type":"bool"}],"name":"setMarketPair","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state_","type":"bool"}],"name":"setSelfERC721TransferExempt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTax","type":"uint256"}],"name":"setSellTax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setTaxesActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAmount","type":"uint256"}],"name":"setTokensForSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTax","type":"uint256"}],"name":"setTransferTax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"setup","outputs":[{"internalType":"contract ERC6551Account","name":"implementation","type":"address"},{"internalType":"contract ERC6551Registry","name":"registry","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"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":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tasks","outputs":[{"internalType":"string","name":"description","type":"string"},{"internalType":"address","name":"requester","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"completed","type":"bool"},{"internalType":"bytes32","name":"outcome","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxesActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensForSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"valueOrId_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Pair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"units","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"updateURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"setupId_","type":"uint256"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"upgrade6551Setup","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101c060405260696101208181529061724e61014039601d906100229082610d5a565b506021805460ff60a01b1916600160a01b179055348015610041575f5ffd5b506040516172b73803806172b783398101604081905261006091610eb4565b60015f55858585338061008c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b61009581610241565b5060046100a28482610d5a565b5060056100af8382610d5a565b5060128160ff1610156100d5576040516398790fd560e01b815260040160405180910390fd5b60ff811660808190526100e990600a61105a565b60a0524660c0526100f8610292565b60e0525050737a250d5630b4cf539739df2c5dacb4c659f2488d6101008190526101249150600161032b565b604080516060810182526001600160a01b0393841681529383166020808601918252858301938452601b8054600181810183555f928352975160039091027f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc1810180549289166001600160a01b031993841617905593517f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc285018054919098169082161790965593517f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc39092019190915560218054909416733f57924d96bf4737475a6ea2da3b0a51ace36cdb17909355602382905560248290556025829055308252602890925220805460ff19169091179055506111a9915050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60046040516102c3919061106f565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b0382166103525760405163a41e3d3f60e01b815260040160405180910390fd5b80156103665761036182610399565b61036f565b61036f826103d1565b6001600160a01b03919091165f908152600e60205260409020805460ff1916911515919091179055565b6001600160a01b0381165f908152600d6020526040812054905b818110156103cc576103c48361044b565b6001016103b3565b505050565b60a0515f906103f4836001600160a01b03165f9081526008602052604090205490565b6103fe91906110e0565b90505f61041f836001600160a01b03165f908152600d602052604090205490565b90505f5b61042d82846110ff565b8110156104455761043d846104f0565b600101610423565b50505050565b6001600160a01b03811661047257604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b0381165f908152600d602052604081208054610497906001906110ff565b815481106104a7576104a7611112565b5f91825260209091206010820401546104d491600f166002026101000a900461ffff16600160ff1b611126565b90506104e1825f83610605565b6104ec60028261086f565b5050565b6001600160a01b03811661051757604051634e46966960e11b815260040160405180910390fd5b5f610522600261095b565b6105495761053060026109a5565b6105429061ffff16600160ff1b611126565b90506105c8565b60075f815461055790611139565b9091555060075460010161057e5760405163303b682f60e01b815260040160405180910390fd5b60075461058f90600160ff1b611126565b601b549091505f906105a3906001906110ff565b600780545f908152601c60205260409020829055549091506105c6908290610aae565b505b5f818152600c60205260409020546001600160a01b031680156105fe5760405163119b4fd360e11b815260040160405180910390fd5b6103cc8184845b6001600160a01b03831615610769575f818152600a6020908152604080832080546001600160a01b03191690556001600160a01b0386168352600d90915281208054610653906001906110ff565b8154811061066357610663611112565b5f918252602090912060108204015461069091600f166002026101000a900461ffff16600160ff1b611126565b9050818114610717575f828152600c602052604081205460a01c6001600160a01b0386165f908152600d6020526040902080549192508391839081106106d8576106d8611112565b905f5260205f2090601091828204019190066002026101000a81548161ffff021916908361ffff1602179055506107158282610bd360201b60201c565b505b6001600160a01b0384165f908152600d6020526040902080548061073d5761073d611151565b5f8281526020902060105f1990920191820401805461ffff6002600f8516026101000a02191690559055505b6001600160a01b0382161561080d575f818152600c6020526040902080546001600160a01b0319166001600160a01b0384160190556001600160a01b0382165f818152600d60209081526040822080546001808201835582855292842060108204018054600f9092166002026101000a61ffff818102199093169288160291909117905592909152905461080891839161080391906110ff565b610bd3565b61081c565b5f818152600c60205260408120555b61082a600160ff1b826110ff565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b81546001600160401b0380821691680100000000000000009004165f81900361089d57505f1901600f6108a1565b5f19015b83546001600160401b03838116600160801b909204161480156108d7575083546001600160401b03828116600160c01b90920416145b156108f557604051638acb5f2760e01b815260040160405180910390fd5b6001600160401b0382165f90815260018501602052604090205461091a908285610c38565b6001600160401b039283165f81815260018701602052604090209190915584546001600160801b031916176801000000000000000091909216021790915550565b80545f90600160c01b81046001600160401b03908116680100000000000000009092041614801561099f57508154600160801b81046001600160401b039081169116145b92915050565b80545f906001600160401b03600160801b8204811691600160c01b810482169116821480156109ec575083546001600160401b038281166801000000000000000090920416145b15610a0a576040516375e52f4f60e01b815260040160405180910390fd5b806001600160401b03165f03610a2557505f1901600f610a29565b5f19015b6001600160401b0382165f908152600185016020526040902054610a4d8183610c78565b9350610a5a81835f610c38565b6001600160401b039384165f81815260018801602052604090209190915585546001600160801b0316600160801b9091026001600160c01b031617600160c01b929093169190910291909117909255919050565b5f601b8381548110610ac257610ac2611112565b5f9182526020918290206040805160608101825260039390930290910180546001600160a01b0390811680855260018301549091169484018590526002909101548383018190529151638a54c52f60e01b81526004810191909152602481019190915246604482015230606482015260848101859052909250638a54c52f9060a4016020604051808303815f875af1925050508015610b7e575060408051601f3d908101601f19168201909252610b7b91810190611165565b60015b61044557805160408083015181518581526001600160a01b03909316602084015282820152517fa270d820fae88ac2cdc56236bb65d488686ef9c572896ea62d95d0875a6666389181900360600190a1505050565b5f828152600c60205260409020546001600160601b03821115610c0957604051633f2cd0e360e21b815260040160405180910390fd5b5f928352600c60205260409092206001600160a01b039290921660a09190911b6001600160a01b031916019055565b5f610c44836010611180565b6001600160401b03168261ffff16901b610c6384610ca260201b60201c565b198516610c709190611126565b949350505050565b5f610c84826010611180565b6001600160401b0316610c9683610ca2565b8416901c905092915050565b5f610cae826010611180565b6001600160401b031661ffff901b9050919050565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680610ceb57607f821691505b602082108103610d0957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156103cc57805f5260205f20601f840160051c81016020851015610d345750805b601f840160051c820191505b81811015610d53575f8155600101610d40565b5050505050565b81516001600160401b03811115610d7357610d73610cc3565b610d8781610d818454610cd7565b84610d0f565b6020601f821160018114610db9575f8315610da25750848201515b5f19600385901b1c1916600184901b178455610d53565b5f84815260208120601f198516915b82811015610de85787850151825560209485019460019092019101610dc8565b5084821015610e0557868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f82601f830112610e23575f5ffd5b81516001600160401b03811115610e3c57610e3c610cc3565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610e6a57610e6a610cc3565b604052818152838201602001851015610e81575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b6001600160a01b0381168114610eb1575f5ffd5b50565b5f5f5f5f5f5f60c08789031215610ec9575f5ffd5b86516001600160401b03811115610ede575f5ffd5b610eea89828a01610e14565b602089015190975090506001600160401b03811115610f07575f5ffd5b610f1389828a01610e14565b955050604087015160ff81168114610f29575f5ffd5b6060880151909450610f3a81610e9d565b6080880151909350610f4b81610e9d565b60a09790970151959894975092959194919391925050565b634e487b7160e01b5f52601160045260245ffd5b6001815b6001841115610fb257808504811115610f9657610f96610f63565b6001841615610fa457908102905b60019390931c928002610f7b565b935093915050565b5f82610fc85750600161099f565b81610fd457505f61099f565b8160018114610fea5760028114610ff457611010565b600191505061099f565b60ff84111561100557611005610f63565b50506001821b61099f565b5060208310610133831016604e8410600b8410161715611033575081810a61099f565b61103f5f198484610f77565b805f190482111561105257611052610f63565b029392505050565b5f61106860ff841683610fba565b9392505050565b5f5f835461107c81610cd7565b60018216801561109357600181146110a8576110d5565b60ff19831686528115158202860193506110d5565b865f5260205f205f5b838110156110cd578154888201526001909101906020016110b1565b505081860193505b509195945050505050565b5f826110fa57634e487b7160e01b5f52601260045260245ffd5b500490565b8181038181111561099f5761099f610f63565b634e487b7160e01b5f52603260045260245ffd5b8082018082111561099f5761099f610f63565b5f6001820161114a5761114a610f63565b5060010190565b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215611175575f5ffd5b815161106881610e9d565b6001600160401b0381811683821602908116908181146111a2576111a2610f63565b5092915050565b60805160a05160c05160e05161010051615ff161125d5f395f81816137c1015281816137fc0152818161388b015281816139e201528181614a210152614b0a01525f611c2301525f611bf301525f8181610c1c01528181613438015281816136f9015281816145d101528181614614015281816146dc01528181614706015281816147a9015281816148bc015281816148f3015281816149370152818161495e0152614bc001525f6108060152615ff15ff3fe6080604052600436106104f1575f3560e01c806389fb4c661161028e578063c879b96911610160578063dfabc033116100c9578063f8b45b0511610083578063f8b45b05146110b3578063fb29d015146110c8578063fcbe1a3e146110db578063fd4fc0bd146110f4578063fdc3d8d71461111f578063ff64704314611134575f5ffd5b8063dfabc03314610fd3578063e985e9c514610ff2578063f1bd3b7b1461102b578063f2fde38b1461104a578063f4644f4114611069578063f780bc1a14611094575f5ffd5b8063dbe66ca01161011a578063dbe66ca014610ee4578063dc07b61714610f12578063dc1052e214610f26578063dc48646214610f45578063dd62ed3e14610f7e578063dd63769914610fb4575f5ffd5b8063c879b96914610e23578063c87b56dd14610e5d578063cc1776d314610e7c578063d00efb2f14610e91578063d505accf14610ea6578063d96ca0b914610ec5575f5ffd5b8063a9059cbb11610202578063c0246668116101bc578063c024666814610d74578063c04a541414610d93578063c16dd4a414610db2578063c30f4a5a14610dd1578063c5ab3ba614610df0578063c6e672b914610e04575f5ffd5b8063a9059cbb14610c7c578063b156807314610c9b578063b1ab931714610cc7578063b3f9ea3414610cf3578063b4b11b9514610d27578063b88d4fde14610d55575f5ffd5b80638da5cb5b116102535780638da5cb5b14610bbb578063947abb9d14610bd857806395d89b4114610bf7578063976a843514610c0b578063a22cb46514610c3e578063a49a910f14610c5d575f5ffd5b806389fb4c6614610b195780638a696e5014610b2d5780638b52590314610b4c5780638cd09d5014610b6b5780638d97767214610b8a575f5ffd5b80633bb7bf1d116103c757806368e8fe6d1161033b578063744140cb116102f5578063744140cb14610a6357806377465ade14610a825780637d6e157814610aa15780637ecebe0014610ac05780638124f7ac14610aeb57806385ffd38f14610b00575f5ffd5b806368e8fe6d146109a45780636e8f624b146109cf57806370a08231146109e657806370dba90b14610a11578063715018a614610a3057806372ac248614610a44575f5ffd5b80634d6313601161038c5780634d631360146109095780634d9660721461091d5780634f02c4201461093c5780634f7041a5146109515780636352211e14610966578063669b797a14610985575f5ffd5b80633bb7bf1d1461084e57806342842e0e1461086d5780634313b9e51461088c57806349bd5a5e146108d15780634af57f9d146108f5575f5ffd5b806310d0c3031161046957806321030ff21161042357806321030ff21461074c57806323b872dd146107855780632ae0268a146107a45780632dd7c658146107d6578063313ce567146107f55780633644e5151461083a575f5ffd5b806310d0c3031461067f578063135ffd401461069e57806318160ddd146106bd5780631ac5cfe5146106d25780631c199e211461070c5780631ecd7d6e14610737575f5ffd5b806306fdde03116104ba57806306fdde03146105ae578063081812fc146105c2578063095ea7b31461060e57806309674eb01461062d57806309c862cd1461064157806309f0ef6514610660575f5ffd5b8062773040146104f557806301612c401461051e57806301ffc9a71461053f57806302519da31461056e57806304fe2b341461059b575b5f5ffd5b61050861050336600461525a565b611154565b60405161051591906152fb565b60405180910390f35b348015610529575f5ffd5b5061053d61053836600461530d565b611277565b005b34801561054a575f5ffd5b5061055e610559366004615342565b611443565b6040519015158152602001610515565b348015610579575f5ffd5b5061058d61058836600461535d565b611479565b604051908152602001610515565b61058d6105a9366004615378565b611493565b3480156105b9575f5ffd5b5061050861159b565b3480156105cd575f5ffd5b506105f66105dc3660046153bf565b600a6020525f90815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610515565b348015610619575f5ffd5b5061055e6106283660046153d6565b611627565b348015610638575f5ffd5b5061058d61169c565b34801561064c575f5ffd5b5061053d61065b366004615400565b6116ac565b34801561066b575f5ffd5b5061055e61067a36600461535d565b61186b565b34801561068a575f5ffd5b5061058d61069936600461530d565b61189b565b3480156106a9575f5ffd5b5061053d6106b836600461530d565b6118c6565b3480156106c8575f5ffd5b5061058d60065481565b3480156106dd575f5ffd5b506106f16106ec36600461530d565b61197b565b60408051938452602084019290925290820152606001610515565b348015610717575f5ffd5b5061058d6107263660046153bf565b60116020525f908152604090205481565b348015610742575f5ffd5b5061058d60225481565b348015610757575f5ffd5b5061055e610766366004615429565b601060209081525f928352604080842090915290825290205460ff1681565b348015610790575f5ffd5b5061055e61079f366004615457565b6119b9565b3480156107af575f5ffd5b506107c36107be36600461530d565b611a30565b6040516105159796959493929190615495565b3480156107e1575f5ffd5b506105f66107f03660046153bf565b611b0c565b348015610800575f5ffd5b506108287f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610515565b348015610845575f5ffd5b5061058d611bf0565b348015610859575f5ffd5b5061053d610868366004615457565b611c45565b348015610878575f5ffd5b5061053d610887366004615457565b611d1d565b348015610897575f5ffd5b506108ab6108a63660046153bf565b611d3c565b604080516001600160a01b03948516815293909216602084015290820152606001610515565b3480156108dc575f5ffd5b506020546105f69061010090046001600160a01b031681565b348015610900575f5ffd5b5061053d611d7a565b348015610914575f5ffd5b5061053d611d91565b348015610928575f5ffd5b5061055e6109373660046153d6565b611da8565b348015610947575f5ffd5b5061058d60075481565b34801561095c575f5ffd5b5061058d60235481565b348015610971575f5ffd5b506105f66109803660046153bf565b611e33565b348015610990575f5ffd5b5061053d61099f366004615400565b611eb0565b3480156109af575f5ffd5b5061058d6109be3660046153bf565b601c6020525f908152604090205481565b3480156109da575f5ffd5b5061058d600160ff1b81565b3480156109f1575f5ffd5b5061058d610a0036600461535d565b60086020525f908152604090205481565b348015610a1c575f5ffd5b5061053d610a2b3660046154f5565b612178565b348015610a3b575f5ffd5b5061053d61223d565b348015610a4f575f5ffd5b5061053d610a5e36600461535d565b612250565b348015610a6e575f5ffd5b5061053d610a7d36600461530d565b61227a565b348015610a8d575f5ffd5b5061055e610a9c36600461530d565b612333565b348015610aac575f5ffd5b5061058d610abb366004615530565b61255c565b348015610acb575f5ffd5b5061058d610ada36600461535d565b600f6020525f908152604090205481565b348015610af6575f5ffd5b5061058d60255481565b348015610b0b575f5ffd5b50601e5461055e9060ff1681565b348015610b24575f5ffd5b5060065461058d565b348015610b38575f5ffd5b5061053d610b473660046155b6565b612789565b348015610b57575f5ffd5b5061053d610b663660046153bf565b6127f1565b348015610b76575f5ffd5b5061053d610b853660046153bf565b61281f565b348015610b95575f5ffd5b50610ba9610ba43660046153bf565b61284d565b604051610515969594939291906155cf565b348015610bc6575f5ffd5b506001546001600160a01b03166105f6565b348015610be3575f5ffd5b50610508610bf236600461525a565b612915565b348015610c02575f5ffd5b50610508612a70565b348015610c16575f5ffd5b5061058d7f000000000000000000000000000000000000000000000000000000000000000081565b348015610c49575f5ffd5b5061053d610c58366004615615565b612a7d565b348015610c68575f5ffd5b5061053d610c773660046153bf565b612b0f565b348015610c87575f5ffd5b5061055e610c963660046153d6565b612b1c565b348015610ca6575f5ffd5b50610cba610cb5366004615400565b612b4f565b6040516105159190615648565b348015610cd2575f5ffd5b50610ce6610ce136600461535d565b612cf5565b60405161051591906156a6565b348015610cfe575f5ffd5b5061058d610d0d36600461535d565b6001600160a01b03165f908152600d602052604090205490565b348015610d32575f5ffd5b5061055e610d4136600461535d565b60296020525f908152604090205460ff1681565b348015610d60575f5ffd5b5061053d610d6f366004615782565b612df0565b348015610d7f575f5ffd5b5061053d610d8e366004615615565b612ee3565b348015610d9e575f5ffd5b506021546105f6906001600160a01b031681565b348015610dbd575f5ffd5b5061053d610dcc366004615615565b612f15565b348015610ddc575f5ffd5b5061053d610deb3660046157fc565b612f47565b348015610dfb575f5ffd5b5060075461058d565b348015610e0f575f5ffd5b5061053d610e1e366004615615565b612f5b565b348015610e2e575f5ffd5b506106f1610e3d3660046153bf565b60166020525f908152604090208054600182015460029092015490919083565b348015610e68575f5ffd5b50610508610e773660046153bf565b612f6d565b348015610e87575f5ffd5b5061058d60245481565b348015610e9c575f5ffd5b5061058d60275481565b348015610eb1575f5ffd5b5061053d610ec0366004615840565b612fa1565b348015610ed0575f5ffd5b5061055e610edf366004615457565b6131de565b348015610eef575f5ffd5b5061055e610efe36600461535d565b60286020525f908152604090205460ff1681565b348015610f1d575f5ffd5b5061053d6132bb565b348015610f31575f5ffd5b5061053d610f403660046153bf565b6132cb565b348015610f50575f5ffd5b5061055e610f5f36600461530d565b601a60209081525f928352604080842090915290825290205460ff1681565b348015610f89575f5ffd5b5061058d610f983660046158aa565b600960209081525f928352604080842090915290825290205481565b348015610fbf575f5ffd5b5061053d610fce366004615457565b6132f9565b348015610fde575f5ffd5b5061055e610fed3660046153d6565b613467565b348015610ffd575f5ffd5b5061055e61100c3660046158aa565b600b60209081525f928352604080842090915290825290205460ff1681565b348015611036575f5ffd5b5061053d6110453660046155b6565b61354f565b348015611055575f5ffd5b5061053d61106436600461535d565b613575565b348015611074575f5ffd5b5061058d6110833660046153bf565b60196020525f908152604090205481565b34801561109f575f5ffd5b50610ce66110ae36600461530d565b6135af565b3480156110be575f5ffd5b5061058d601f5481565b61053d6110d63660046158d6565b61364e565b3480156110e6575f5ffd5b5060205461055e9060ff1681565b3480156110ff575f5ffd5b5061058d61110e3660046153bf565b60126020525f908152604090205481565b34801561112a575f5ffd5b5061058d60155481565b34801561113f575f5ffd5b5060215461055e90600160a01b900460ff1681565b606061115e613acf565b61118961116f600160ff1b8961590b565b5f908152600c60205260409020546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146111e45760405162461bcd60e51b81526020600482015260136024820152722737ba103a3432903a37b5b2b71037bbb732b960691b60448201526064015b60405180910390fd5b6111ed87611b0c565b6001600160a01b0316635194544787878787876040518663ffffffff1660e01b8152600401611220959493929190615946565b5f604051808303815f875af115801561123b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526112629190810190615982565b905061126d60015f55565b9695505050505050565b5f818152601460205260409020546064116112d45760405162461bcd60e51b815260206004820152601860248201527f546f6f206d616e79207461736b7320666f72206167656e74000000000000000060448201526064016111db565b336112e661116f600160ff1b8461590b565b6001600160a01b03161461132e5760405162461bcd60e51b815260206004820152600f60248201526e2737ba1030b3b2b73a1037bbb732b960891b60448201526064016111db565b5f828152601360205260409020600481015460ff16156113895760405162461bcd60e51b815260206004820152601660248201527515185cdac8185b1c9958591e4818dbdb5c1b195d195960521b60448201526064016111db565b806003015442106113d35760405162461bcd60e51b815260206004820152601460248201527315185cdac8191958591b1a5b99481c185cdcd95960621b60448201526064016111db565b5f82815260146020908152604080832080546001818101835591855283852001879055868452601a8352818420868552909252808320805460ff191690921790915551839185917f25e5c814bf9313a0efe86f97f5d6ad2921089c376aa35ab12fc2c495546755ee9190a3505050565b5f6001600160e01b0319821663caf91ff560e01b148061147357506001600160e01b031982166301ffc9a760e01b145b92915050565b6001600160a01b03165f9081526008602052604090205490565b5f61149c613acf565b4282116114eb5760405162461bcd60e51b815260206004820152601e60248201527f446561646c696e65206d75737420626520696e2074686520667574757265000060448201526064016111db565b601580545f91826114fb836159f6565b909155505f8181526013602052604090209091508061151b868883615a91565b506001810180546001600160a01b031916339081179091553460028301556003820185905560048201805460ff1916905560405183907f5b2493258cbc7169ad9389516998320ce25dc27637cea28ddd0d033af122bfa690611580908a908a90615b4a565b60405180910390a350905061159460015f55565b9392505050565b600480546115a890615a0e565b80601f01602080910402602001604051908101604052809291908181526020018280546115d490615a0e565b801561161f5780601f106115f65761010080835404028352916020019161161f565b820191905f5260205f20905b81548152906001019060200180831161160257829003601f168201915b505050505081565b5f600160ff1b82106116445761163d8383611da8565b9050611473565b61165a611655600160ff1b8461590b565b613af7565b15611689575f61166a8484613467565b9050806116835761167b8484611da8565b915050611473565b50611693565b61163d8383611da8565b50600192915050565b5f6116a76002613b0e565b905090565b6007548211156116fe5760405162461bcd60e51b815260206004820152601e60248201527f526563697069656e7420746f6b656e20646f6573206e6f74206578697374000060448201526064016111db565b5f61170884611b0c565b5f848152601760205260409020549091506103e8116117695760405162461bcd60e51b815260206004820152601f60248201527f546f6f206d616e79206d6573736167657320666f7220726563697069656e740060448201526064016111db565b336001600160a01b038216148061179857505f84815260106020908152604080832033845290915290205460ff165b806117bb5750336117b061116f600160ff1b8761590b565b6001600160a01b0316145b6117d75760405162461bcd60e51b81526004016111db90615b5d565b5f83815260176020908152604080832081516060810183528881528084018781524282850190815283546001818101865594885295909620915160039095029091019384555190830155915160029091015551839085907f58f76f0de54ae383f9426c81a1ebabe061a939ba8307bfc74426e84ea67be1db9061185d9086815260200190565b60405180910390a350505050565b5f6001600160a01b03821615806114735750506001600160a01b03165f908152600e602052604090205460ff1690565b6014602052815f5260405f2081815481106118b4575f80fd5b905f5260205f20015f91509150505481565b336118d861116f600160ff1b8561590b565b6001600160a01b0316148061190557505f82815260106020908152604080832033845290915290205460ff165b6119215760405162461bcd60e51b81526004016111db90615b5d565b5f828152601860209081526040808320848452825291829020600601805460ff19169055905182815283917fb06cc13dfc427822729928fa6cfdbdefc78e001061fe9d6a6c634484e77ebb6b910160405180910390a25050565b6017602052815f5260405f208181548110611994575f80fd5b5f91825260209091206003909102018054600182015460029092015490935090915083565b5f6119cb611655600160ff1b8461590b565b15611a1b576119e161116f600160ff1b8461590b565b6001600160a01b0316846001600160a01b031614611a0b57611a048484846131de565b9050611594565b611a168484846132f9565b611a26565b611a048484846131de565b5060019392505050565b601860209081525f92835260408084209091529082529020805460018201546002830180546001600160a01b03909316939192611a6c90615a0e565b80601f0160208091040260200160405190810160405280929190818152602001828054611a9890615a0e565b8015611ae35780601f10611aba57610100808354040283529160200191611ae3565b820191905f5260205f20905b815481529060010190602001808311611ac657829003601f168201915b505050600384015460048501546005860154600690960154949560ff9283169591945092501687565b5f818152601c6020526040812054601b80548392908110611b2f57611b2f615b85565b5f9182526020918290206040805160608101825260039390930290910180546001600160a01b039081168085526001830154909116948401859052600290910154838301819052915163246a002160e01b8152600481019190915260248101919091524660448201523060648201526084810186905290925063246a00219060a401602060405180830381865afa158015611bcc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115949190615b99565b5f7f00000000000000000000000000000000000000000000000000000000000000004614611c20576116a7613b51565b507f000000000000000000000000000000000000000000000000000000000000000090565b611c4d613bea565b604080516060810182526001600160a01b03938416815293831660208501908152908401918252601b80546001810182555f91909152935160039094027f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc1810180549585166001600160a01b031996871617905590517f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc28201805491909416941693909317909155517f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc390910155565b611d3783838360405180602001604052805f815250612df0565b505050565b601b8181548110611d4b575f80fd5b5f9182526020909120600390910201805460018201546002909201546001600160a01b03918216935091169083565b611d82613bea565b601e805460ff19166001179055565b611d99613bea565b6020805460ff19166001179055565b5f6001600160a01b038316611dd057604051635461585f60e01b815260040160405180910390fd5b335f8181526009602090815260408083206001600160a01b03881680855290835292819020869055518581529192917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350600192915050565b5f611e42600160ff1b8361590b565b5f818152600c60205260409020549092506001600160a01b03169050611e6782613af7565b611e84576040516307ed98ed60e31b815260040160405180910390fd5b6001600160a01b038116611eab5760405163c5723b5160e01b815260040160405180910390fd5b919050565b611eb8613acf565b5f611ec283611b0c565b9050336001600160a01b0382161480611ef357505f83815260106020908152604080832033845290915290205460ff165b80611f16575033611f0b61116f600160ff1b8661590b565b6001600160a01b0316145b611f325760405162461bcd60e51b81526004016111db90615b5d565b5f848152601360209081526040808320601a83528184208785529092529091205460ff16611fa25760405162461bcd60e51b815260206004820152601a60248201527f4167656e74206e6f742061737369676e656420746f207461736b00000000000060448201526064016111db565b600481015460ff1615611ff05760405162461bcd60e51b815260206004820152601660248201527515185cdac8185b1c9958591e4818dbdb5c1b195d195960521b60448201526064016111db565b806003015442111561203b5760405162461bcd60e51b815260206004820152601460248201527315185cdac8191958591b1a5b99481c185cdcd95960621b60448201526064016111db565b60048101805460ff19166001179055600581018390555f84815260166020526040812080549091829061206d836159f6565b91905055508160020154816001015f828254612089919061590b565b9091555050426002808301919091558201546040515f916001600160a01b038616918381818185875af1925050503d805f81146120e1576040519150601f19603f3d011682016040523d82523d5f602084013e6120e6565b606091505b50509050806121305760405162461bcd60e51b815260206004820152601660248201527514995dd85c99081d1c985b9cd9995c8819985a5b195960521b60448201526064016111db565b85877f339ba61d0494cd9480ac692252c889cdd0184f891856a80a25fc39c7d9f88f7e8760405161216391815260200190565b60405180910390a350505050611d3760015f55565b3361218a61116f600160ff1b8661590b565b6001600160a01b0316146121d25760405162461bcd60e51b815260206004820152600f60248201526e2737ba103a37b5b2b71037bbb732b960891b60448201526064016111db565b5f8381526010602090815260408083206001600160a01b03861680855290835292819020805460ff1916851515908117909155905190815285917fdaaa206caa4ddb9f2b93bf33507d65bc89857c7c11bf52bfbfe83c83043ba6b291015b60405180910390a3505050565b612245613bea565b61224e5f613c17565b565b612258613bea565b602180546001600160a01b0319166001600160a01b0392909216919091179055565b3361228c61116f600160ff1b8461590b565b6001600160a01b0316146122d45760405162461bcd60e51b815260206004820152600f60248201526e2737ba103a37b5b2b71037bbb732b960891b60448201526064016111db565b601b5482106123155760405162461bcd60e51b815260206004820152600d60248201526c0496e76616c696420736574757609c1b60448201526064016111db565b5f818152601c6020526040902082905561232f8282613c68565b5050565b5f61233c613acf565b5f8381526018602090815260408083208584529091529020600681015460ff1661239c5760405162461bcd60e51b8152602060048201526011602482015270416374696f6e206e6f742061637469766560781b60448201526064016111db565b80600401544210156123f05760405162461bcd60e51b815260206004820152601760248201527f4e6f74207965742074696d6520746f206578656375746500000000000000000060448201526064016111db565b5f6123fa85611b0c565b825460018401546003850154604051635194544760e01b81529394506001600160a01b038086169463519454479461244294921692600289019160ff90911690600401615bb4565b5f604051808303815f875af192505050801561247f57506040513d5f823e601f3d908101601f1916820160405261247c9190810190615982565b60015b61248b575f9250612491565b50600192505b5f82600501541180156124a15750825b156125025760058201546124b5904261590b565b6004830181905560405186917f7d2b37830491f9385a6dbe76832e1224c5f344498e963836ef2ffd590aaa986a916124f591888252602082015260400190565b60405180910390a2612515565b82156125155760068201805460ff191690555b60408051858152841515602082015286917f2abcaabda6b6086dffe7c6dba92c8208fba28f2ad62bb562e3ea785849be5e7f910160405180910390a2505061147360015f55565b5f3361256f61116f600160ff1b8c61590b565b6001600160a01b0316148061259c57505f89815260106020908152604080832033845290915290205460ff165b6125b85760405162461bcd60e51b81526004016111db90615b5d565b4283116126135760405162461bcd60e51b8152602060048201526024808201527f457865637574696f6e2074696d65206d75737420626520696e207468652066756044820152637475726560e01b60648201526084016111db565b5f8981526019602052604081208054908261262d836159f6565b9190505590506040518060e001604052808a6001600160a01b0316815260200189815260200188888080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92018290525093855250505060ff8816602080840191909152604080840189905260608401889052600160809094018490528e835260188252808320868452825291829020845181546001600160a01b0319166001600160a01b03909116178155908401519281019290925582015160028201906126fc9082615c5f565b50606082015160038201805460ff90921660ff199283161790556080830151600483015560a0830151600583015560c090920151600690910180549115159190921617905560408051828152602081018690528b917f7d2b37830491f9385a6dbe76832e1224c5f344498e963836ef2ffd590aaa986a910160405180910390a29998505050505050505050565b60205460ff166127e55760405162461bcd60e51b815260206004820152602160248201527f506c65617365207761697420756e74696c206665617475726520656e61626c656044820152601960fa1b60648201526084016111db565b6127ee81613d8d565b50565b6127f9613bea565b600581111561281a5760405162461bcd60e51b81526004016111db90615d19565b602555565b612827613bea565b60058111156128485760405162461bcd60e51b81526004016111db90615d19565b602455565b60136020525f908152604090208054819061286790615a0e565b80601f016020809104026020016040519081016040528092919081815260200182805461289390615a0e565b80156128de5780601f106128b5576101008083540402835291602001916128de565b820191905f5260205f20905b8154815290600101906020018083116128c157829003601f168201915b50505050600183015460028401546003850154600486015460059096015494956001600160a01b03909316949193509160ff169086565b606061291f613acf565b5f87815260106020908152604080832033845290915290205460ff1661297e5760405162461bcd60e51b81526020600482015260146024820152731059d95b9d081b9bdd08185d5d1a1bdc9a5e995960621b60448201526064016111db565b5f87815260126020526040812042905561299788611b0c565b905033887f389089e27675162c5830a44541b405635429787635f585fa156f33768cc66d8d6129c960045f898b615d48565b6129d291615d6f565b6040516001600160e01b0319909116815260200160405180910390a3604051635194544760e01b81526001600160a01b03821690635194544790612a22908a908a908a908a908a90600401615946565b5f604051808303815f875af1158015612a3d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612a649190810190615982565b91505061126d60015f55565b600580546115a890615a0e565b6001600160a01b038216612aa45760405163ccea9e6f60e01b815260040160405180910390fd5b335f818152600b602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b612b17613bea565b602255565b5f6001600160a01b038316612b4457604051634e46966960e11b815260040160405180910390fd5b611594338484613d97565b606060648211612b5f5781612b62565b60645b5f85815260176020526040902054909250808410612bcc57604080515f8082526020820190925290612bc3565b612bb060405180606001604052805f81526020015f81526020015f81525090565b815260200190600190039081612b8f5790505b50915050611594565b5f612bd7848661590b565b905081811115612be45750805b5f612bef8683615da5565b90505f816001600160401b03811115612c0a57612c0a6156dd565b604051908082528060200260200182016040528015612c5c57816020015b612c4960405180606001604052805f81526020015f81526020015f81525090565b815260200190600190039081612c285790505b5090505f5b82811015612ce9575f898152601760205260409020612c80828a61590b565b81548110612c9057612c90615b85565b905f5260205f2090600302016040518060600160405290815f820154815260200160018201548152602001600282015481525050828281518110612cd657612cd6615b85565b6020908102919091010152600101612c61565b50979650505050505050565b6001600160a01b0381165f908152600d6020526040812054606091906001600160401b03811115612d2857612d286156dd565b604051908082528060200260200182016040528015612d51578160200160208202803683370190505b5090505f5b6001600160a01b0384165f908152600d6020526040902054811015612de9576001600160a01b0384165f908152600d60205260409020805482908110612d9e57612d9e615b85565b905f5260205f2090601091828204019190066002029054906101000a900461ffff1661ffff16828281518110612dd657612dd6615b85565b6020908102919091010152600101612d56565b5092915050565b612e01611655600160ff1b8461590b565b612e1e576040516307ed98ed60e31b815260040160405180910390fd5b612e298484846119b9565b506001600160a01b0383163b15801590612ebf5750604051630a85bd0160e11b808252906001600160a01b0385169063150b7a0290612e72903390899088908890600401615db8565b6020604051808303815f875af1158015612e8e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eb29190615dea565b6001600160e01b03191614155b15612edd57604051633da6393160e01b815260040160405180910390fd5b50505050565b612eeb613bea565b6001600160a01b03919091165f908152602860205260409020805460ff1916911515919091179055565b612f1d613bea565b6001600160a01b03919091165f908152602960205260409020805460ff1916911515919091179055565b612f4f613bea565b601d61232f8282615c5f565b612f63613bea565b61232f828261405b565b6060601d612f7a836140c9565b604051602001612f8b929190615e73565b6040516020818303038152906040529050919050565b42841015612fc2576040516305787bdf60e01b815260040160405180910390fd5b612fcb85613af7565b15612fe9576040516303e7c1bd60e31b815260040160405180910390fd5b6001600160a01b03861661301057604051635461585f60e01b815260040160405180910390fd5b5f600161301b611bf0565b6001600160a01b038a81165f818152600f602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015613123573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b03811615806131585750876001600160a01b0316816001600160a01b031614155b1561317657604051632057875960e21b815260040160405180910390fd5b6001600160a01b039081165f9081526009602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b5f6001600160a01b03841661320657604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b03831661322d57604051634e46966960e11b815260040160405180910390fd5b6001600160a01b0384165f9081526009602090815260408083203384529091529020545f1981146132a75782811015613279576040516313be252b60e01b815260040160405180910390fd5b6132838382615da5565b6001600160a01b0386165f9081526009602090815260408083203384529091529020555b6132b2858585613d97565b95945050505050565b6132c3613bea565b600654601f55565b6132d3613bea565b60058111156132f45760405162461bcd60e51b81526004016111db90615d19565b602355565b613307600160ff1b8261590b565b90506001600160a01b03831661333057604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b03821661335757604051634e46966960e11b815260040160405180910390fd5b5f818152600c60205260409020546001600160a01b0384811691161461338f576040516282b42960e81b815260040160405180910390fd5b336001600160a01b038416148015906133cb57506001600160a01b0383165f908152600b6020908152604080832033845290915290205460ff16155b80156133ed57505f818152600a60205260409020546001600160a01b03163314155b1561340a576040516282b42960e81b815260040160405180910390fd5b6134138261186b565b1561343157604051635ce7539760e01b815260040160405180910390fd5b61345c83837f0000000000000000000000000000000000000000000000000000000000000000614158565b611d37838383614204565b5f613476600160ff1b8361590b565b5f818152600c60205260409020549092506001600160a01b03163381148015906134c357506001600160a01b0381165f908152600b6020908152604080832033845290915290205460ff16155b156134d1575f915050611473565b5f838152600a6020526040902080546001600160a01b0319166001600160a01b038616179055613505600160ff1b84615da5565b846001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45060019392505050565b613557613bea565b60218054911515600160a01b0260ff60a01b19909216919091179055565b61357d613bea565b6001600160a01b0381166135a657604051631e4fbdf760e01b81525f60048201526024016111db565b6127ee81613c17565b60605f826001600160401b038111156135ca576135ca6156dd565b6040519080825280602002602001820160405280156135f3578160200160208202803683370190505b509050835b613602848661590b565b81101561364657613614600282614468565b61ffff16826136238784615da5565b8151811061363357613633615b85565b60209081029190910101526001016135f8565b509392505050565b613656613bea565b600654156136995760405162461bcd60e51b815260206004820152601060248201526f105b1c9958591e481b185d5b98da195960821b60448201526064016111db565b5f34116136e85760405162461bcd60e51b815260206004820152601a60248201527f45544820726571756972656420666f72206c697175696469747900000000000060448201526064016111db565b6136f330600161405b565b5f61371e7f000000000000000000000000000000000000000000000000000000000000000084615e95565b601f819055905061372f3082614517565b61373b6103e882615ec0565b602255436027556005602381905560245561375733600161405b565b335f908152602860205260408120805460ff19166001179055606461377d836014615e95565b6137879190615ec0565b90505f6064613797846050615e95565b6137a19190615ec0565b90506137ae303384614158565b305f9081526009602090815260408083207f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316845290915290205f19905583156139d8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015613856573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061387a9190615b99565b6001600160a01b031663c9c65396307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138e5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139099190615b99565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201526044016020604051808303815f875af1158015613953573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139779190615b99565b60208054610100600160a81b0319166101006001600160a01b03938416810291909117918290556139ac92910416600161405b565b602080546001600160a01b03610100909104165f908152602990915260409020805460ff191660011790555b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f305d7193430845f8033613a194261025861590b565b60405160e089901b6001600160e01b03191681526001600160a01b039687166004820152602481019590955260448501939093526064840191909152909216608482015260a481019190915260c40160606040518083038185885af1158015613a84573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190613aa99190615edf565b5050506064836002613abb9190615e95565b613ac59190615ec0565b601f555050505050565b60025f5403613af157604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b5f600160ff1b821180156114735750505f19141590565b54600f196001600160401b038083166010908102600160401b850483168203600160c01b8604841601600160801b90950483169091029390930192909203011690565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6004604051613b829190615f0a565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001546001600160a01b0316331461224e5760405163118cdaa760e01b81523360048201526024016111db565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f601b8381548110613c7c57613c7c615b85565b5f9182526020918290206040805160608101825260039390930290910180546001600160a01b0390811680855260018301549091169484018590526002909101548383018190529151638a54c52f60e01b81526004810191909152602481019190915246604482015230606482015260848101859052909250638a54c52f9060a4016020604051808303815f875af1925050508015613d38575060408051601f3d908101601f19168201909252613d3591810190615b99565b60015b612edd57805160408083015181518581526001600160a01b03909316602084015282820152517fa270d820fae88ac2cdc56236bb65d488686ef9c572896ea62d95d0875a6666389181900360600190a1505050565b6127ee338261405b565b601e545f9060ff16613dae57613dae83600161405b565b6020546001600160a01b038481166101009092041614801590613dd45750600654601f54105b8015613de857506001600160a01b03831615155b15613e49575f613df784611479565b601f54909150613e07848361590b565b1115613e475760405162461bcd60e51b815260206004820152600f60248201526e546f6f206d616e7920746f6b656e7360881b60448201526064016111db565b505b6021545f90600160a01b900460ff168015613e6e5750602154600160a81b900460ff16155b8015613eb457506001600160a01b0385165f9081526028602052604090205460ff1680613eb257506001600160a01b0384165f9081526028602052604090205460ff165b155b90508015613fe7576023546024546027545f9291904303613ed6575060269050805b6001600160a01b0387165f9081526029602052604090205460ff168015613efc57505f82115b15613f1e576064613f0d8388615e95565b613f179190615ec0565b9250613fc4565b6001600160a01b0388165f9081526029602052604090205460ff168015613f4457505f81115b15613f55576064613f0d8288615e95565b6001600160a01b0387165f9081526029602052604090205460ff16158015613f9557506001600160a01b0388165f9081526029602052604090205460ff16155b8015613fa257505f602554115b15613fc457606460255487613fb79190615e95565b613fc19190615ec0565b92505b8215613fe357613fd48387615da5565b9550613fe188308561457b565b505b5050505b5f613ff130611479565b60225490915081101582801561401f57506001600160a01b0387165f9081526029602052604090205460ff16155b80156140285750805b156140455760265443111561404557614040826149b9565b436026555b61405087878761457b565b979650505050505050565b6001600160a01b0382166140825760405163a41e3d3f60e01b815260040160405180910390fd5b80156140965761409182614b8a565b61409f565b61409f82614bbd565b6001600160a01b03919091165f908152600e60205260409020805460ff1916911515919091179055565b60605f6140d583614c39565b60010190505f816001600160401b038111156140f3576140f36156dd565b6040519080825280601f01601f19166020018201604052801561411d576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461412757509392505050565b6001600160a01b038316614182578060065f828254614177919061590b565b909155506141af9050565b6001600160a01b0383165f90815260086020526040812080548392906141a9908490615da5565b90915550505b6001600160a01b038083165f81815260086020526040908190208054850190555190918516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906122309085815260200190565b6001600160a01b03831615614362575f818152600a6020908152604080832080546001600160a01b03191690556001600160a01b0386168352600d9091528120805461425290600190615da5565b8154811061426257614262615b85565b5f918252602090912060108204015461428f91600f166002026101000a900461ffff16600160ff1b61590b565b9050818114614310575f828152600c602052604081205460a01c6001600160a01b0386165f908152600d6020526040902080549192508391839081106142d7576142d7615b85565b905f5260205f2090601091828204019190066002026101000a81548161ffff021916908361ffff16021790555061430e8282614d10565b505b6001600160a01b0384165f908152600d6020526040902080548061433657614336615f15565b5f8281526020902060105f1990920191820401805461ffff6002600f8516026101000a02191690559055505b6001600160a01b03821615614406575f818152600c6020526040902080546001600160a01b0319166001600160a01b0384160190556001600160a01b0382165f818152600d60209081526040822080546001808201835582855292842060108204018054600f9092166002026101000a61ffff81810219909316928816029190911790559290915290546144019183916143fc9190615da5565b614d10565b614415565b5f818152600c60205260408120555b614423600160ff1b82615da5565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b5f61447283613b0e565b61447d906010615e95565b821061449c5760405163580821e760e01b815260040160405180910390fd5b611594600184015f601085046010808789546001600160401b03600160401b9091048116929091069190910116816144d6576144d6615eac565b88549190046001600160401b03808316919091019290920182168352602083019390935260409091015f205491601091600160401b90910416850106614d7a565b6001600160a01b03821661453e57604051634e46966960e11b815260040160405180910390fd5b600160ff1b81600654614551919061590b565b11156145705760405163303b682f60e01b815260040160405180910390fd5b611d375f8383613d97565b5f5f61458685611479565b90505f61459285611479565b905061459f868686614158565b5f6145a98761186b565b90505f6145b58761186b565b90508180156145c15750805b6149ab5781156146b8575f6145f67f000000000000000000000000000000000000000000000000000000000000000085615ec0565b6001600160a01b0389165f90815260086020526040902054614639907f000000000000000000000000000000000000000000000000000000000000000090615ec0565b6146439190615da5565b905060328111156146965760405162461bcd60e51b815260206004820152601e60248201527f52657472696576616c2062617463682073697a6520746f6f206c61726765000060448201526064016111db565b5f5b818110156146b1576146a989614da4565b600101614698565b50506149ab565b80156147a3576001600160a01b0388165f90815260086020526040812054614701907f000000000000000000000000000000000000000000000000000000000000000090615ec0565b61472b7f000000000000000000000000000000000000000000000000000000000000000087615ec0565b6147359190615da5565b905060328111156147885760405162461bcd60e51b815260206004820152601f60248201527f5769746864726177616c2062617463682073697a6520746f6f206c617267650060448201526064016111db565b5f5b818110156146b15761479b8a614ebd565b60010161478a565b5f6147ce7f000000000000000000000000000000000000000000000000000000000000000088615ec0565b905060328111156148185760405162461bcd60e51b815260206004820152601460248201527342617463682073697a6520746f6f206c6172676560601b60448201526064016111db565b5f5b818110156148b8576001600160a01b038a165f908152600d602052604081205461484690600190615da5565b6001600160a01b038c165f908152600d60205260408120805492935090918390811061487457614874615b85565b5f91825260209091206010820401546148a191600f166002026101000a900461ffff16600160ff1b61590b565b90506148ae8c8c83614204565b505060010161481a565b50807f00000000000000000000000000000000000000000000000000000000000000006148e48b611479565b6148ee9190615ec0565b6149187f000000000000000000000000000000000000000000000000000000000000000088615ec0565b6149229190615da5565b11156149315761493189614ebd565b8061495c7f000000000000000000000000000000000000000000000000000000000000000086615ec0565b7f00000000000000000000000000000000000000000000000000000000000000006149868b611479565b6149909190615ec0565b61499a9190615da5565b11156149a9576149a988614da4565b505b506001979650505050505050565b6021805460ff60a81b1916600160a81b1790556040805160028082526060820183525f9260208301908036833701905050905030815f815181106149ff576149ff615b85565b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa158015614a7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614a9f9190615b99565b81600181518110614ab257614ab2615b85565b60200260200101906001600160a01b031690816001600160a01b0316815250505f6022546003614ae29190615e95565b905080831115614af0578092505b60215460405163791ac94760e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263791ac94792614b4b9288925f92899291909116904290600401615f29565b5f604051808303815f87803b158015614b62575f5ffd5b505af1158015614b74573d5f5f3e3d5ffd5b50506021805460ff60a81b191690555050505050565b6001600160a01b0381165f908152600d6020526040812054905b81811015611d3757614bb583614ebd565b600101614ba4565b5f7f0000000000000000000000000000000000000000000000000000000000000000614be883611479565b614bf29190615ec0565b90505f614c13836001600160a01b03165f908152600d602052604090205490565b90505f5b614c218284615da5565b811015612edd57614c3184614da4565b600101614c17565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310614c775772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310614ca3576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310614cc157662386f26fc10000830492506010015b6305f5e1008310614cd9576305f5e100830492506008015b6127108310614ced57612710830492506004015b60648310614cff576064830492506002015b600a83106114735760010192915050565b5f828152600c60205260409020546bffffffffffffffffffffffff821115614d4b57604051633f2cd0e360e21b815260040160405180910390fd5b5f928352600c60205260409092206001600160a01b039290921660a09190911b6001600160a01b031916019055565b5f614d86826010615f99565b6001600160401b0316614d9883614f5e565b8416901c905092915050565b6001600160a01b038116614dcb57604051634e46966960e11b815260040160405180910390fd5b5f614dd66002614f7f565b614dfd57614de46002614fc0565b614df69061ffff16600160ff1b61590b565b9050614e7c565b60075f8154614e0b906159f6565b90915550600754600101614e325760405163303b682f60e01b815260040160405180910390fd5b600754614e4390600160ff1b61590b565b601b549091505f90614e5790600190615da5565b600780545f908152601c6020526040902082905554909150614e7a908290613c68565b505b5f818152600c60205260409020546001600160a01b03168015614eb25760405163119b4fd360e11b815260040160405180910390fd5b611d37818484614204565b6001600160a01b038116614ee457604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b0381165f908152600d602052604081208054614f0990600190615da5565b81548110614f1957614f19615b85565b5f9182526020909120601082040154614f4691600f166002026101000a900461ffff16600160ff1b61590b565b9050614f53825f83614204565b61232f6002826150cd565b5f614f6a826010615f99565b6001600160401b031661ffff901b9050919050565b80545f90600160c01b81046001600160401b03908116600160401b90920416148015611473575050546001600160401b03808216600160801b909204161490565b80545f906001600160401b03600160801b8204811691600160c01b81048216911682148015615002575083546001600160401b03828116600160401b90920416145b15615020576040516375e52f4f60e01b815260040160405180910390fd5b806001600160401b03165f0361503b57505f1901600f61503f565b5f19015b6001600160401b0382165f9081526001850160205260409020546150638183614d7a565b935061507081835f6151b8565b6001600160401b039384165f81815260018801602052604090209190915585546fffffffffffffffffffffffffffffffff16600160801b9091026001600160c01b031617600160c01b929093169190910291909117909255919050565b81546001600160401b0380821691600160401b9004165f8190036150f657505f1901600f6150fa565b5f19015b83546001600160401b03838116600160801b90920416148015615130575083546001600160401b03828116600160c01b90920416145b1561514e57604051638acb5f2760e01b815260040160405180910390fd5b6001600160401b0382165f9081526001850160205260409020546151739082856151b8565b6001600160401b039283165f81815260018701602052604090209190915584546fffffffffffffffffffffffffffffffff191617600160401b91909216021790915550565b5f6151c4836010615f99565b6001600160401b03168261ffff16901b6151dd84614f5e565b1985166151ea919061590b565b949350505050565b6001600160a01b03811681146127ee575f5ffd5b5f5f83601f840112615216575f5ffd5b5081356001600160401b0381111561522c575f5ffd5b602083019150836020828501011115615243575f5ffd5b9250929050565b803560ff81168114611eab575f5ffd5b5f5f5f5f5f5f60a0878903121561526f575f5ffd5b863595506020870135615281816151f2565b94506040870135935060608701356001600160401b038111156152a2575f5ffd5b6152ae89828a01615206565b90945092506152c190506080880161524a565b90509295509295509295565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61159460208301846152cd565b5f5f6040838503121561531e575f5ffd5b50508035926020909101359150565b6001600160e01b0319811681146127ee575f5ffd5b5f60208284031215615352575f5ffd5b81356115948161532d565b5f6020828403121561536d575f5ffd5b8135611594816151f2565b5f5f5f6040848603121561538a575f5ffd5b83356001600160401b0381111561539f575f5ffd5b6153ab86828701615206565b909790965060209590950135949350505050565b5f602082840312156153cf575f5ffd5b5035919050565b5f5f604083850312156153e7575f5ffd5b82356153f2816151f2565b946020939093013593505050565b5f5f5f60608486031215615412575f5ffd5b505081359360208301359350604090920135919050565b5f5f6040838503121561543a575f5ffd5b82359150602083013561544c816151f2565b809150509250929050565b5f5f5f60608486031215615469575f5ffd5b8335615474816151f2565b92506020840135615484816151f2565b929592945050506040919091013590565b60018060a01b038816815286602082015260e060408201525f6154bb60e08301886152cd565b905060ff861660608301528460808301528360a083015282151560c083015298975050505050505050565b80358015158114611eab575f5ffd5b5f5f5f60608486031215615507575f5ffd5b833592506020840135615519816151f2565b9150615527604085016154e6565b90509250925092565b5f5f5f5f5f5f5f5f60e0898b031215615547575f5ffd5b883597506020890135615559816151f2565b96506040890135955060608901356001600160401b0381111561557a575f5ffd5b6155868b828c01615206565b9096509450615599905060808a0161524a565b979a969950949793969295929450505060a08201359160c0013590565b5f602082840312156155c6575f5ffd5b611594826154e6565b60c081525f6155e160c08301896152cd565b6001600160a01b0397909716602083015250604081019490945260608401929092521515608083015260a090910152919050565b5f5f60408385031215615626575f5ffd5b8235615631816151f2565b915061563f602084016154e6565b90509250929050565b602080825282518282018190525f918401906040840190835b8181101561569b57835180518452602081015160208501526040810151604085015250606083019250602084019350600181019050615661565b509095945050505050565b602080825282518282018190525f918401906040840190835b8181101561569b5783518352602093840193909201916001016156bf565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715615719576157196156dd565b604052919050565b5f6001600160401b03821115615739576157396156dd565b50601f01601f191660200190565b5f61575961575484615721565b6156f1565b905082815283838301111561576c575f5ffd5b828260208301375f602084830101529392505050565b5f5f5f5f60808587031215615795575f5ffd5b84356157a0816151f2565b935060208501356157b0816151f2565b92506040850135915060608501356001600160401b038111156157d1575f5ffd5b8501601f810187136157e1575f5ffd5b6157f087823560208401615747565b91505092959194509250565b5f6020828403121561580c575f5ffd5b81356001600160401b03811115615821575f5ffd5b8201601f81018413615831575f5ffd5b6151ea84823560208401615747565b5f5f5f5f5f5f5f60e0888a031215615856575f5ffd5b8735615861816151f2565b96506020880135615871816151f2565b9550604088013594506060880135935061588d6080890161524a565b9699959850939692959460a0840135945060c09093013592915050565b5f5f604083850312156158bb575f5ffd5b82356158c6816151f2565b9150602083013561544c816151f2565b5f5f604083850312156158e7575f5ffd5b8235915061563f602084016154e6565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611473576114736158f7565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60018060a01b0386168152846020820152608060408201525f61596d60808301858761591e565b905060ff831660608301529695505050505050565b5f60208284031215615992575f5ffd5b81516001600160401b038111156159a7575f5ffd5b8201601f810184136159b7575f5ffd5b80516159c561575482615721565b8181528560208385010111156159d9575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f60018201615a0757615a076158f7565b5060010190565b600181811c90821680615a2257607f821691505b602082108103615a4057634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115611d3757805f5260205f20601f840160051c81016020851015615a6b5750805b601f840160051c820191505b81811015615a8a575f8155600101615a77565b5050505050565b6001600160401b03831115615aa857615aa86156dd565b615abc83615ab68354615a0e565b83615a46565b5f601f841160018114615aed575f8515615ad65750838201355b5f19600387901b1c1916600186901b178355615a8a565b5f83815260208120601f198716915b82811015615b1c5786850135825560209485019460019092019101615afc565b5086821015615b38575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b602081525f6151ea60208301848661591e565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615ba9575f5ffd5b8151611594816151f2565b6001600160a01b0385168152602081018490526080604082015282545f908190615bdd81615a0e565b806080860152600182165f8114615bfb5760018114615c1757615c48565b60ff19831660a087015260a082151560051b8701019350615c48565b875f5260205f205f5b83811015615c3f57815488820160a00152600190910190602001615c20565b870160a0019450505b50505060ff84166060840152905095945050505050565b81516001600160401b03811115615c7857615c786156dd565b615c8c81615c868454615a0e565b84615a46565b6020601f821160018114615cbe575f8315615ca75750848201515b5f19600385901b1c1916600184901b178455615a8a565b5f84815260208120601f198516915b82811015615ced5787850151825560209485019460019092019101615ccd565b5084821015615d0a57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b60208082526015908201527454617820746f6f206869676820286d61782035252960581b604082015260600190565b5f5f85851115615d56575f5ffd5b83861115615d62575f5ffd5b5050820193919092039150565b80356001600160e01b03198116906004841015612de9576001600160e01b031960049490940360031b84901b1690921692915050565b81810381811115611473576114736158f7565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f9061126d908301846152cd565b5f60208284031215615dfa575f5ffd5b81516115948161532d565b5f8154615e1181615a0e565b600182168015615e285760018114615e3d57615e6a565b60ff1983168652811515820286019350615e6a565b845f5260205f205f5b83811015615e6257815488820152600190910190602001615e46565b505081860193505b50505092915050565b5f615e7e8285615e05565b83518060208601835e5f9101908152949350505050565b8082028115828204841417611473576114736158f7565b634e487b7160e01b5f52601260045260245ffd5b5f82615eda57634e487b7160e01b5f52601260045260245ffd5b500490565b5f5f5f60608486031215615ef1575f5ffd5b5050815160208301516040909301519094929350919050565b5f6115948284615e05565b634e487b7160e01b5f52603160045260245ffd5b5f60a0820187835286602084015260a0604084015280865180835260c0850191506020880192505f5b81811015615f795783516001600160a01b0316835260209384019390920191600101615f52565b50506001600160a01b039590951660608401525050608001529392505050565b6001600160401b038181168382160290811690818114612de957612de96158f756fea264697066735822122076f1288d8d28b93e428f5c0ffc28bd2eba3a171469bad28792537f9b7518f8d164736f6c634300081c0033726f73652d73656c6563742d7068656173616e742d3434362e6d7970696e6174612e636c6f75642f697066732f6261666b7265696376683263646664753775776835746f673770757a7864696b75756f6c6f766b69637433327278756235726a646c3261686867752f00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000f0cc32453580ea0964d65493341d5c0f285362840000000000000000000000002e3e9bf7eaf07763d9a0cb01408126dbda841b6deb715e0af3d8f25f74c385f0a171b281db3515ebe6636d2c565588adb46f29e7000000000000000000000000000000000000000000000000000000000000000543726f6d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000543524f4d45000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106104f1575f3560e01c806389fb4c661161028e578063c879b96911610160578063dfabc033116100c9578063f8b45b0511610083578063f8b45b05146110b3578063fb29d015146110c8578063fcbe1a3e146110db578063fd4fc0bd146110f4578063fdc3d8d71461111f578063ff64704314611134575f5ffd5b8063dfabc03314610fd3578063e985e9c514610ff2578063f1bd3b7b1461102b578063f2fde38b1461104a578063f4644f4114611069578063f780bc1a14611094575f5ffd5b8063dbe66ca01161011a578063dbe66ca014610ee4578063dc07b61714610f12578063dc1052e214610f26578063dc48646214610f45578063dd62ed3e14610f7e578063dd63769914610fb4575f5ffd5b8063c879b96914610e23578063c87b56dd14610e5d578063cc1776d314610e7c578063d00efb2f14610e91578063d505accf14610ea6578063d96ca0b914610ec5575f5ffd5b8063a9059cbb11610202578063c0246668116101bc578063c024666814610d74578063c04a541414610d93578063c16dd4a414610db2578063c30f4a5a14610dd1578063c5ab3ba614610df0578063c6e672b914610e04575f5ffd5b8063a9059cbb14610c7c578063b156807314610c9b578063b1ab931714610cc7578063b3f9ea3414610cf3578063b4b11b9514610d27578063b88d4fde14610d55575f5ffd5b80638da5cb5b116102535780638da5cb5b14610bbb578063947abb9d14610bd857806395d89b4114610bf7578063976a843514610c0b578063a22cb46514610c3e578063a49a910f14610c5d575f5ffd5b806389fb4c6614610b195780638a696e5014610b2d5780638b52590314610b4c5780638cd09d5014610b6b5780638d97767214610b8a575f5ffd5b80633bb7bf1d116103c757806368e8fe6d1161033b578063744140cb116102f5578063744140cb14610a6357806377465ade14610a825780637d6e157814610aa15780637ecebe0014610ac05780638124f7ac14610aeb57806385ffd38f14610b00575f5ffd5b806368e8fe6d146109a45780636e8f624b146109cf57806370a08231146109e657806370dba90b14610a11578063715018a614610a3057806372ac248614610a44575f5ffd5b80634d6313601161038c5780634d631360146109095780634d9660721461091d5780634f02c4201461093c5780634f7041a5146109515780636352211e14610966578063669b797a14610985575f5ffd5b80633bb7bf1d1461084e57806342842e0e1461086d5780634313b9e51461088c57806349bd5a5e146108d15780634af57f9d146108f5575f5ffd5b806310d0c3031161046957806321030ff21161042357806321030ff21461074c57806323b872dd146107855780632ae0268a146107a45780632dd7c658146107d6578063313ce567146107f55780633644e5151461083a575f5ffd5b806310d0c3031461067f578063135ffd401461069e57806318160ddd146106bd5780631ac5cfe5146106d25780631c199e211461070c5780631ecd7d6e14610737575f5ffd5b806306fdde03116104ba57806306fdde03146105ae578063081812fc146105c2578063095ea7b31461060e57806309674eb01461062d57806309c862cd1461064157806309f0ef6514610660575f5ffd5b8062773040146104f557806301612c401461051e57806301ffc9a71461053f57806302519da31461056e57806304fe2b341461059b575b5f5ffd5b61050861050336600461525a565b611154565b60405161051591906152fb565b60405180910390f35b348015610529575f5ffd5b5061053d61053836600461530d565b611277565b005b34801561054a575f5ffd5b5061055e610559366004615342565b611443565b6040519015158152602001610515565b348015610579575f5ffd5b5061058d61058836600461535d565b611479565b604051908152602001610515565b61058d6105a9366004615378565b611493565b3480156105b9575f5ffd5b5061050861159b565b3480156105cd575f5ffd5b506105f66105dc3660046153bf565b600a6020525f90815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610515565b348015610619575f5ffd5b5061055e6106283660046153d6565b611627565b348015610638575f5ffd5b5061058d61169c565b34801561064c575f5ffd5b5061053d61065b366004615400565b6116ac565b34801561066b575f5ffd5b5061055e61067a36600461535d565b61186b565b34801561068a575f5ffd5b5061058d61069936600461530d565b61189b565b3480156106a9575f5ffd5b5061053d6106b836600461530d565b6118c6565b3480156106c8575f5ffd5b5061058d60065481565b3480156106dd575f5ffd5b506106f16106ec36600461530d565b61197b565b60408051938452602084019290925290820152606001610515565b348015610717575f5ffd5b5061058d6107263660046153bf565b60116020525f908152604090205481565b348015610742575f5ffd5b5061058d60225481565b348015610757575f5ffd5b5061055e610766366004615429565b601060209081525f928352604080842090915290825290205460ff1681565b348015610790575f5ffd5b5061055e61079f366004615457565b6119b9565b3480156107af575f5ffd5b506107c36107be36600461530d565b611a30565b6040516105159796959493929190615495565b3480156107e1575f5ffd5b506105f66107f03660046153bf565b611b0c565b348015610800575f5ffd5b506108287f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610515565b348015610845575f5ffd5b5061058d611bf0565b348015610859575f5ffd5b5061053d610868366004615457565b611c45565b348015610878575f5ffd5b5061053d610887366004615457565b611d1d565b348015610897575f5ffd5b506108ab6108a63660046153bf565b611d3c565b604080516001600160a01b03948516815293909216602084015290820152606001610515565b3480156108dc575f5ffd5b506020546105f69061010090046001600160a01b031681565b348015610900575f5ffd5b5061053d611d7a565b348015610914575f5ffd5b5061053d611d91565b348015610928575f5ffd5b5061055e6109373660046153d6565b611da8565b348015610947575f5ffd5b5061058d60075481565b34801561095c575f5ffd5b5061058d60235481565b348015610971575f5ffd5b506105f66109803660046153bf565b611e33565b348015610990575f5ffd5b5061053d61099f366004615400565b611eb0565b3480156109af575f5ffd5b5061058d6109be3660046153bf565b601c6020525f908152604090205481565b3480156109da575f5ffd5b5061058d600160ff1b81565b3480156109f1575f5ffd5b5061058d610a0036600461535d565b60086020525f908152604090205481565b348015610a1c575f5ffd5b5061053d610a2b3660046154f5565b612178565b348015610a3b575f5ffd5b5061053d61223d565b348015610a4f575f5ffd5b5061053d610a5e36600461535d565b612250565b348015610a6e575f5ffd5b5061053d610a7d36600461530d565b61227a565b348015610a8d575f5ffd5b5061055e610a9c36600461530d565b612333565b348015610aac575f5ffd5b5061058d610abb366004615530565b61255c565b348015610acb575f5ffd5b5061058d610ada36600461535d565b600f6020525f908152604090205481565b348015610af6575f5ffd5b5061058d60255481565b348015610b0b575f5ffd5b50601e5461055e9060ff1681565b348015610b24575f5ffd5b5060065461058d565b348015610b38575f5ffd5b5061053d610b473660046155b6565b612789565b348015610b57575f5ffd5b5061053d610b663660046153bf565b6127f1565b348015610b76575f5ffd5b5061053d610b853660046153bf565b61281f565b348015610b95575f5ffd5b50610ba9610ba43660046153bf565b61284d565b604051610515969594939291906155cf565b348015610bc6575f5ffd5b506001546001600160a01b03166105f6565b348015610be3575f5ffd5b50610508610bf236600461525a565b612915565b348015610c02575f5ffd5b50610508612a70565b348015610c16575f5ffd5b5061058d7f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b348015610c49575f5ffd5b5061053d610c58366004615615565b612a7d565b348015610c68575f5ffd5b5061053d610c773660046153bf565b612b0f565b348015610c87575f5ffd5b5061055e610c963660046153d6565b612b1c565b348015610ca6575f5ffd5b50610cba610cb5366004615400565b612b4f565b6040516105159190615648565b348015610cd2575f5ffd5b50610ce6610ce136600461535d565b612cf5565b60405161051591906156a6565b348015610cfe575f5ffd5b5061058d610d0d36600461535d565b6001600160a01b03165f908152600d602052604090205490565b348015610d32575f5ffd5b5061055e610d4136600461535d565b60296020525f908152604090205460ff1681565b348015610d60575f5ffd5b5061053d610d6f366004615782565b612df0565b348015610d7f575f5ffd5b5061053d610d8e366004615615565b612ee3565b348015610d9e575f5ffd5b506021546105f6906001600160a01b031681565b348015610dbd575f5ffd5b5061053d610dcc366004615615565b612f15565b348015610ddc575f5ffd5b5061053d610deb3660046157fc565b612f47565b348015610dfb575f5ffd5b5060075461058d565b348015610e0f575f5ffd5b5061053d610e1e366004615615565b612f5b565b348015610e2e575f5ffd5b506106f1610e3d3660046153bf565b60166020525f908152604090208054600182015460029092015490919083565b348015610e68575f5ffd5b50610508610e773660046153bf565b612f6d565b348015610e87575f5ffd5b5061058d60245481565b348015610e9c575f5ffd5b5061058d60275481565b348015610eb1575f5ffd5b5061053d610ec0366004615840565b612fa1565b348015610ed0575f5ffd5b5061055e610edf366004615457565b6131de565b348015610eef575f5ffd5b5061055e610efe36600461535d565b60286020525f908152604090205460ff1681565b348015610f1d575f5ffd5b5061053d6132bb565b348015610f31575f5ffd5b5061053d610f403660046153bf565b6132cb565b348015610f50575f5ffd5b5061055e610f5f36600461530d565b601a60209081525f928352604080842090915290825290205460ff1681565b348015610f89575f5ffd5b5061058d610f983660046158aa565b600960209081525f928352604080842090915290825290205481565b348015610fbf575f5ffd5b5061053d610fce366004615457565b6132f9565b348015610fde575f5ffd5b5061055e610fed3660046153d6565b613467565b348015610ffd575f5ffd5b5061055e61100c3660046158aa565b600b60209081525f928352604080842090915290825290205460ff1681565b348015611036575f5ffd5b5061053d6110453660046155b6565b61354f565b348015611055575f5ffd5b5061053d61106436600461535d565b613575565b348015611074575f5ffd5b5061058d6110833660046153bf565b60196020525f908152604090205481565b34801561109f575f5ffd5b50610ce66110ae36600461530d565b6135af565b3480156110be575f5ffd5b5061058d601f5481565b61053d6110d63660046158d6565b61364e565b3480156110e6575f5ffd5b5060205461055e9060ff1681565b3480156110ff575f5ffd5b5061058d61110e3660046153bf565b60126020525f908152604090205481565b34801561112a575f5ffd5b5061058d60155481565b34801561113f575f5ffd5b5060215461055e90600160a01b900460ff1681565b606061115e613acf565b61118961116f600160ff1b8961590b565b5f908152600c60205260409020546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146111e45760405162461bcd60e51b81526020600482015260136024820152722737ba103a3432903a37b5b2b71037bbb732b960691b60448201526064015b60405180910390fd5b6111ed87611b0c565b6001600160a01b0316635194544787878787876040518663ffffffff1660e01b8152600401611220959493929190615946565b5f604051808303815f875af115801561123b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526112629190810190615982565b905061126d60015f55565b9695505050505050565b5f818152601460205260409020546064116112d45760405162461bcd60e51b815260206004820152601860248201527f546f6f206d616e79207461736b7320666f72206167656e74000000000000000060448201526064016111db565b336112e661116f600160ff1b8461590b565b6001600160a01b03161461132e5760405162461bcd60e51b815260206004820152600f60248201526e2737ba1030b3b2b73a1037bbb732b960891b60448201526064016111db565b5f828152601360205260409020600481015460ff16156113895760405162461bcd60e51b815260206004820152601660248201527515185cdac8185b1c9958591e4818dbdb5c1b195d195960521b60448201526064016111db565b806003015442106113d35760405162461bcd60e51b815260206004820152601460248201527315185cdac8191958591b1a5b99481c185cdcd95960621b60448201526064016111db565b5f82815260146020908152604080832080546001818101835591855283852001879055868452601a8352818420868552909252808320805460ff191690921790915551839185917f25e5c814bf9313a0efe86f97f5d6ad2921089c376aa35ab12fc2c495546755ee9190a3505050565b5f6001600160e01b0319821663caf91ff560e01b148061147357506001600160e01b031982166301ffc9a760e01b145b92915050565b6001600160a01b03165f9081526008602052604090205490565b5f61149c613acf565b4282116114eb5760405162461bcd60e51b815260206004820152601e60248201527f446561646c696e65206d75737420626520696e2074686520667574757265000060448201526064016111db565b601580545f91826114fb836159f6565b909155505f8181526013602052604090209091508061151b868883615a91565b506001810180546001600160a01b031916339081179091553460028301556003820185905560048201805460ff1916905560405183907f5b2493258cbc7169ad9389516998320ce25dc27637cea28ddd0d033af122bfa690611580908a908a90615b4a565b60405180910390a350905061159460015f55565b9392505050565b600480546115a890615a0e565b80601f01602080910402602001604051908101604052809291908181526020018280546115d490615a0e565b801561161f5780601f106115f65761010080835404028352916020019161161f565b820191905f5260205f20905b81548152906001019060200180831161160257829003601f168201915b505050505081565b5f600160ff1b82106116445761163d8383611da8565b9050611473565b61165a611655600160ff1b8461590b565b613af7565b15611689575f61166a8484613467565b9050806116835761167b8484611da8565b915050611473565b50611693565b61163d8383611da8565b50600192915050565b5f6116a76002613b0e565b905090565b6007548211156116fe5760405162461bcd60e51b815260206004820152601e60248201527f526563697069656e7420746f6b656e20646f6573206e6f74206578697374000060448201526064016111db565b5f61170884611b0c565b5f848152601760205260409020549091506103e8116117695760405162461bcd60e51b815260206004820152601f60248201527f546f6f206d616e79206d6573736167657320666f7220726563697069656e740060448201526064016111db565b336001600160a01b038216148061179857505f84815260106020908152604080832033845290915290205460ff165b806117bb5750336117b061116f600160ff1b8761590b565b6001600160a01b0316145b6117d75760405162461bcd60e51b81526004016111db90615b5d565b5f83815260176020908152604080832081516060810183528881528084018781524282850190815283546001818101865594885295909620915160039095029091019384555190830155915160029091015551839085907f58f76f0de54ae383f9426c81a1ebabe061a939ba8307bfc74426e84ea67be1db9061185d9086815260200190565b60405180910390a350505050565b5f6001600160a01b03821615806114735750506001600160a01b03165f908152600e602052604090205460ff1690565b6014602052815f5260405f2081815481106118b4575f80fd5b905f5260205f20015f91509150505481565b336118d861116f600160ff1b8561590b565b6001600160a01b0316148061190557505f82815260106020908152604080832033845290915290205460ff165b6119215760405162461bcd60e51b81526004016111db90615b5d565b5f828152601860209081526040808320848452825291829020600601805460ff19169055905182815283917fb06cc13dfc427822729928fa6cfdbdefc78e001061fe9d6a6c634484e77ebb6b910160405180910390a25050565b6017602052815f5260405f208181548110611994575f80fd5b5f91825260209091206003909102018054600182015460029092015490935090915083565b5f6119cb611655600160ff1b8461590b565b15611a1b576119e161116f600160ff1b8461590b565b6001600160a01b0316846001600160a01b031614611a0b57611a048484846131de565b9050611594565b611a168484846132f9565b611a26565b611a048484846131de565b5060019392505050565b601860209081525f92835260408084209091529082529020805460018201546002830180546001600160a01b03909316939192611a6c90615a0e565b80601f0160208091040260200160405190810160405280929190818152602001828054611a9890615a0e565b8015611ae35780601f10611aba57610100808354040283529160200191611ae3565b820191905f5260205f20905b815481529060010190602001808311611ac657829003601f168201915b505050600384015460048501546005860154600690960154949560ff9283169591945092501687565b5f818152601c6020526040812054601b80548392908110611b2f57611b2f615b85565b5f9182526020918290206040805160608101825260039390930290910180546001600160a01b039081168085526001830154909116948401859052600290910154838301819052915163246a002160e01b8152600481019190915260248101919091524660448201523060648201526084810186905290925063246a00219060a401602060405180830381865afa158015611bcc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115949190615b99565b5f7f00000000000000000000000000000000000000000000000000000000000000014614611c20576116a7613b51565b507f6486f784571aff1bc79b8c7d3bc6f23669b9341f1a77d00ddcfccd5ea1f1db9b90565b611c4d613bea565b604080516060810182526001600160a01b03938416815293831660208501908152908401918252601b80546001810182555f91909152935160039094027f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc1810180549585166001600160a01b031996871617905590517f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc28201805491909416941693909317909155517f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc390910155565b611d3783838360405180602001604052805f815250612df0565b505050565b601b8181548110611d4b575f80fd5b5f9182526020909120600390910201805460018201546002909201546001600160a01b03918216935091169083565b611d82613bea565b601e805460ff19166001179055565b611d99613bea565b6020805460ff19166001179055565b5f6001600160a01b038316611dd057604051635461585f60e01b815260040160405180910390fd5b335f8181526009602090815260408083206001600160a01b03881680855290835292819020869055518581529192917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350600192915050565b5f611e42600160ff1b8361590b565b5f818152600c60205260409020549092506001600160a01b03169050611e6782613af7565b611e84576040516307ed98ed60e31b815260040160405180910390fd5b6001600160a01b038116611eab5760405163c5723b5160e01b815260040160405180910390fd5b919050565b611eb8613acf565b5f611ec283611b0c565b9050336001600160a01b0382161480611ef357505f83815260106020908152604080832033845290915290205460ff165b80611f16575033611f0b61116f600160ff1b8661590b565b6001600160a01b0316145b611f325760405162461bcd60e51b81526004016111db90615b5d565b5f848152601360209081526040808320601a83528184208785529092529091205460ff16611fa25760405162461bcd60e51b815260206004820152601a60248201527f4167656e74206e6f742061737369676e656420746f207461736b00000000000060448201526064016111db565b600481015460ff1615611ff05760405162461bcd60e51b815260206004820152601660248201527515185cdac8185b1c9958591e4818dbdb5c1b195d195960521b60448201526064016111db565b806003015442111561203b5760405162461bcd60e51b815260206004820152601460248201527315185cdac8191958591b1a5b99481c185cdcd95960621b60448201526064016111db565b60048101805460ff19166001179055600581018390555f84815260166020526040812080549091829061206d836159f6565b91905055508160020154816001015f828254612089919061590b565b9091555050426002808301919091558201546040515f916001600160a01b038616918381818185875af1925050503d805f81146120e1576040519150601f19603f3d011682016040523d82523d5f602084013e6120e6565b606091505b50509050806121305760405162461bcd60e51b815260206004820152601660248201527514995dd85c99081d1c985b9cd9995c8819985a5b195960521b60448201526064016111db565b85877f339ba61d0494cd9480ac692252c889cdd0184f891856a80a25fc39c7d9f88f7e8760405161216391815260200190565b60405180910390a350505050611d3760015f55565b3361218a61116f600160ff1b8661590b565b6001600160a01b0316146121d25760405162461bcd60e51b815260206004820152600f60248201526e2737ba103a37b5b2b71037bbb732b960891b60448201526064016111db565b5f8381526010602090815260408083206001600160a01b03861680855290835292819020805460ff1916851515908117909155905190815285917fdaaa206caa4ddb9f2b93bf33507d65bc89857c7c11bf52bfbfe83c83043ba6b291015b60405180910390a3505050565b612245613bea565b61224e5f613c17565b565b612258613bea565b602180546001600160a01b0319166001600160a01b0392909216919091179055565b3361228c61116f600160ff1b8461590b565b6001600160a01b0316146122d45760405162461bcd60e51b815260206004820152600f60248201526e2737ba103a37b5b2b71037bbb732b960891b60448201526064016111db565b601b5482106123155760405162461bcd60e51b815260206004820152600d60248201526c0496e76616c696420736574757609c1b60448201526064016111db565b5f818152601c6020526040902082905561232f8282613c68565b5050565b5f61233c613acf565b5f8381526018602090815260408083208584529091529020600681015460ff1661239c5760405162461bcd60e51b8152602060048201526011602482015270416374696f6e206e6f742061637469766560781b60448201526064016111db565b80600401544210156123f05760405162461bcd60e51b815260206004820152601760248201527f4e6f74207965742074696d6520746f206578656375746500000000000000000060448201526064016111db565b5f6123fa85611b0c565b825460018401546003850154604051635194544760e01b81529394506001600160a01b038086169463519454479461244294921692600289019160ff90911690600401615bb4565b5f604051808303815f875af192505050801561247f57506040513d5f823e601f3d908101601f1916820160405261247c9190810190615982565b60015b61248b575f9250612491565b50600192505b5f82600501541180156124a15750825b156125025760058201546124b5904261590b565b6004830181905560405186917f7d2b37830491f9385a6dbe76832e1224c5f344498e963836ef2ffd590aaa986a916124f591888252602082015260400190565b60405180910390a2612515565b82156125155760068201805460ff191690555b60408051858152841515602082015286917f2abcaabda6b6086dffe7c6dba92c8208fba28f2ad62bb562e3ea785849be5e7f910160405180910390a2505061147360015f55565b5f3361256f61116f600160ff1b8c61590b565b6001600160a01b0316148061259c57505f89815260106020908152604080832033845290915290205460ff165b6125b85760405162461bcd60e51b81526004016111db90615b5d565b4283116126135760405162461bcd60e51b8152602060048201526024808201527f457865637574696f6e2074696d65206d75737420626520696e207468652066756044820152637475726560e01b60648201526084016111db565b5f8981526019602052604081208054908261262d836159f6565b9190505590506040518060e001604052808a6001600160a01b0316815260200189815260200188888080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92018290525093855250505060ff8816602080840191909152604080840189905260608401889052600160809094018490528e835260188252808320868452825291829020845181546001600160a01b0319166001600160a01b03909116178155908401519281019290925582015160028201906126fc9082615c5f565b50606082015160038201805460ff90921660ff199283161790556080830151600483015560a0830151600583015560c090920151600690910180549115159190921617905560408051828152602081018690528b917f7d2b37830491f9385a6dbe76832e1224c5f344498e963836ef2ffd590aaa986a910160405180910390a29998505050505050505050565b60205460ff166127e55760405162461bcd60e51b815260206004820152602160248201527f506c65617365207761697420756e74696c206665617475726520656e61626c656044820152601960fa1b60648201526084016111db565b6127ee81613d8d565b50565b6127f9613bea565b600581111561281a5760405162461bcd60e51b81526004016111db90615d19565b602555565b612827613bea565b60058111156128485760405162461bcd60e51b81526004016111db90615d19565b602455565b60136020525f908152604090208054819061286790615a0e565b80601f016020809104026020016040519081016040528092919081815260200182805461289390615a0e565b80156128de5780601f106128b5576101008083540402835291602001916128de565b820191905f5260205f20905b8154815290600101906020018083116128c157829003601f168201915b50505050600183015460028401546003850154600486015460059096015494956001600160a01b03909316949193509160ff169086565b606061291f613acf565b5f87815260106020908152604080832033845290915290205460ff1661297e5760405162461bcd60e51b81526020600482015260146024820152731059d95b9d081b9bdd08185d5d1a1bdc9a5e995960621b60448201526064016111db565b5f87815260126020526040812042905561299788611b0c565b905033887f389089e27675162c5830a44541b405635429787635f585fa156f33768cc66d8d6129c960045f898b615d48565b6129d291615d6f565b6040516001600160e01b0319909116815260200160405180910390a3604051635194544760e01b81526001600160a01b03821690635194544790612a22908a908a908a908a908a90600401615946565b5f604051808303815f875af1158015612a3d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612a649190810190615982565b91505061126d60015f55565b600580546115a890615a0e565b6001600160a01b038216612aa45760405163ccea9e6f60e01b815260040160405180910390fd5b335f818152600b602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b612b17613bea565b602255565b5f6001600160a01b038316612b4457604051634e46966960e11b815260040160405180910390fd5b611594338484613d97565b606060648211612b5f5781612b62565b60645b5f85815260176020526040902054909250808410612bcc57604080515f8082526020820190925290612bc3565b612bb060405180606001604052805f81526020015f81526020015f81525090565b815260200190600190039081612b8f5790505b50915050611594565b5f612bd7848661590b565b905081811115612be45750805b5f612bef8683615da5565b90505f816001600160401b03811115612c0a57612c0a6156dd565b604051908082528060200260200182016040528015612c5c57816020015b612c4960405180606001604052805f81526020015f81526020015f81525090565b815260200190600190039081612c285790505b5090505f5b82811015612ce9575f898152601760205260409020612c80828a61590b565b81548110612c9057612c90615b85565b905f5260205f2090600302016040518060600160405290815f820154815260200160018201548152602001600282015481525050828281518110612cd657612cd6615b85565b6020908102919091010152600101612c61565b50979650505050505050565b6001600160a01b0381165f908152600d6020526040812054606091906001600160401b03811115612d2857612d286156dd565b604051908082528060200260200182016040528015612d51578160200160208202803683370190505b5090505f5b6001600160a01b0384165f908152600d6020526040902054811015612de9576001600160a01b0384165f908152600d60205260409020805482908110612d9e57612d9e615b85565b905f5260205f2090601091828204019190066002029054906101000a900461ffff1661ffff16828281518110612dd657612dd6615b85565b6020908102919091010152600101612d56565b5092915050565b612e01611655600160ff1b8461590b565b612e1e576040516307ed98ed60e31b815260040160405180910390fd5b612e298484846119b9565b506001600160a01b0383163b15801590612ebf5750604051630a85bd0160e11b808252906001600160a01b0385169063150b7a0290612e72903390899088908890600401615db8565b6020604051808303815f875af1158015612e8e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eb29190615dea565b6001600160e01b03191614155b15612edd57604051633da6393160e01b815260040160405180910390fd5b50505050565b612eeb613bea565b6001600160a01b03919091165f908152602860205260409020805460ff1916911515919091179055565b612f1d613bea565b6001600160a01b03919091165f908152602960205260409020805460ff1916911515919091179055565b612f4f613bea565b601d61232f8282615c5f565b612f63613bea565b61232f828261405b565b6060601d612f7a836140c9565b604051602001612f8b929190615e73565b6040516020818303038152906040529050919050565b42841015612fc2576040516305787bdf60e01b815260040160405180910390fd5b612fcb85613af7565b15612fe9576040516303e7c1bd60e31b815260040160405180910390fd5b6001600160a01b03861661301057604051635461585f60e01b815260040160405180910390fd5b5f600161301b611bf0565b6001600160a01b038a81165f818152600f602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015613123573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b03811615806131585750876001600160a01b0316816001600160a01b031614155b1561317657604051632057875960e21b815260040160405180910390fd5b6001600160a01b039081165f9081526009602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b5f6001600160a01b03841661320657604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b03831661322d57604051634e46966960e11b815260040160405180910390fd5b6001600160a01b0384165f9081526009602090815260408083203384529091529020545f1981146132a75782811015613279576040516313be252b60e01b815260040160405180910390fd5b6132838382615da5565b6001600160a01b0386165f9081526009602090815260408083203384529091529020555b6132b2858585613d97565b95945050505050565b6132c3613bea565b600654601f55565b6132d3613bea565b60058111156132f45760405162461bcd60e51b81526004016111db90615d19565b602355565b613307600160ff1b8261590b565b90506001600160a01b03831661333057604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b03821661335757604051634e46966960e11b815260040160405180910390fd5b5f818152600c60205260409020546001600160a01b0384811691161461338f576040516282b42960e81b815260040160405180910390fd5b336001600160a01b038416148015906133cb57506001600160a01b0383165f908152600b6020908152604080832033845290915290205460ff16155b80156133ed57505f818152600a60205260409020546001600160a01b03163314155b1561340a576040516282b42960e81b815260040160405180910390fd5b6134138261186b565b1561343157604051635ce7539760e01b815260040160405180910390fd5b61345c83837f0000000000000000000000000000000000000000000000000de0b6b3a7640000614158565b611d37838383614204565b5f613476600160ff1b8361590b565b5f818152600c60205260409020549092506001600160a01b03163381148015906134c357506001600160a01b0381165f908152600b6020908152604080832033845290915290205460ff16155b156134d1575f915050611473565b5f838152600a6020526040902080546001600160a01b0319166001600160a01b038616179055613505600160ff1b84615da5565b846001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45060019392505050565b613557613bea565b60218054911515600160a01b0260ff60a01b19909216919091179055565b61357d613bea565b6001600160a01b0381166135a657604051631e4fbdf760e01b81525f60048201526024016111db565b6127ee81613c17565b60605f826001600160401b038111156135ca576135ca6156dd565b6040519080825280602002602001820160405280156135f3578160200160208202803683370190505b509050835b613602848661590b565b81101561364657613614600282614468565b61ffff16826136238784615da5565b8151811061363357613633615b85565b60209081029190910101526001016135f8565b509392505050565b613656613bea565b600654156136995760405162461bcd60e51b815260206004820152601060248201526f105b1c9958591e481b185d5b98da195960821b60448201526064016111db565b5f34116136e85760405162461bcd60e51b815260206004820152601a60248201527f45544820726571756972656420666f72206c697175696469747900000000000060448201526064016111db565b6136f330600161405b565b5f61371e7f0000000000000000000000000000000000000000000000000de0b6b3a764000084615e95565b601f819055905061372f3082614517565b61373b6103e882615ec0565b602255436027556005602381905560245561375733600161405b565b335f908152602860205260408120805460ff19166001179055606461377d836014615e95565b6137879190615ec0565b90505f6064613797846050615e95565b6137a19190615ec0565b90506137ae303384614158565b305f9081526009602090815260408083207f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b0316845290915290205f19905583156139d8577f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015613856573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061387a9190615b99565b6001600160a01b031663c9c65396307f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138e5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139099190615b99565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201526044016020604051808303815f875af1158015613953573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139779190615b99565b60208054610100600160a81b0319166101006001600160a01b03938416810291909117918290556139ac92910416600161405b565b602080546001600160a01b03610100909104165f908152602990915260409020805460ff191660011790555b6001600160a01b037f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d1663f305d7193430845f8033613a194261025861590b565b60405160e089901b6001600160e01b03191681526001600160a01b039687166004820152602481019590955260448501939093526064840191909152909216608482015260a481019190915260c40160606040518083038185885af1158015613a84573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190613aa99190615edf565b5050506064836002613abb9190615e95565b613ac59190615ec0565b601f555050505050565b60025f5403613af157604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b5f600160ff1b821180156114735750505f19141590565b54600f196001600160401b038083166010908102600160401b850483168203600160c01b8604841601600160801b90950483169091029390930192909203011690565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6004604051613b829190615f0a565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001546001600160a01b0316331461224e5760405163118cdaa760e01b81523360048201526024016111db565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f601b8381548110613c7c57613c7c615b85565b5f9182526020918290206040805160608101825260039390930290910180546001600160a01b0390811680855260018301549091169484018590526002909101548383018190529151638a54c52f60e01b81526004810191909152602481019190915246604482015230606482015260848101859052909250638a54c52f9060a4016020604051808303815f875af1925050508015613d38575060408051601f3d908101601f19168201909252613d3591810190615b99565b60015b612edd57805160408083015181518581526001600160a01b03909316602084015282820152517fa270d820fae88ac2cdc56236bb65d488686ef9c572896ea62d95d0875a6666389181900360600190a1505050565b6127ee338261405b565b601e545f9060ff16613dae57613dae83600161405b565b6020546001600160a01b038481166101009092041614801590613dd45750600654601f54105b8015613de857506001600160a01b03831615155b15613e49575f613df784611479565b601f54909150613e07848361590b565b1115613e475760405162461bcd60e51b815260206004820152600f60248201526e546f6f206d616e7920746f6b656e7360881b60448201526064016111db565b505b6021545f90600160a01b900460ff168015613e6e5750602154600160a81b900460ff16155b8015613eb457506001600160a01b0385165f9081526028602052604090205460ff1680613eb257506001600160a01b0384165f9081526028602052604090205460ff165b155b90508015613fe7576023546024546027545f9291904303613ed6575060269050805b6001600160a01b0387165f9081526029602052604090205460ff168015613efc57505f82115b15613f1e576064613f0d8388615e95565b613f179190615ec0565b9250613fc4565b6001600160a01b0388165f9081526029602052604090205460ff168015613f4457505f81115b15613f55576064613f0d8288615e95565b6001600160a01b0387165f9081526029602052604090205460ff16158015613f9557506001600160a01b0388165f9081526029602052604090205460ff16155b8015613fa257505f602554115b15613fc457606460255487613fb79190615e95565b613fc19190615ec0565b92505b8215613fe357613fd48387615da5565b9550613fe188308561457b565b505b5050505b5f613ff130611479565b60225490915081101582801561401f57506001600160a01b0387165f9081526029602052604090205460ff16155b80156140285750805b156140455760265443111561404557614040826149b9565b436026555b61405087878761457b565b979650505050505050565b6001600160a01b0382166140825760405163a41e3d3f60e01b815260040160405180910390fd5b80156140965761409182614b8a565b61409f565b61409f82614bbd565b6001600160a01b03919091165f908152600e60205260409020805460ff1916911515919091179055565b60605f6140d583614c39565b60010190505f816001600160401b038111156140f3576140f36156dd565b6040519080825280601f01601f19166020018201604052801561411d576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461412757509392505050565b6001600160a01b038316614182578060065f828254614177919061590b565b909155506141af9050565b6001600160a01b0383165f90815260086020526040812080548392906141a9908490615da5565b90915550505b6001600160a01b038083165f81815260086020526040908190208054850190555190918516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906122309085815260200190565b6001600160a01b03831615614362575f818152600a6020908152604080832080546001600160a01b03191690556001600160a01b0386168352600d9091528120805461425290600190615da5565b8154811061426257614262615b85565b5f918252602090912060108204015461428f91600f166002026101000a900461ffff16600160ff1b61590b565b9050818114614310575f828152600c602052604081205460a01c6001600160a01b0386165f908152600d6020526040902080549192508391839081106142d7576142d7615b85565b905f5260205f2090601091828204019190066002026101000a81548161ffff021916908361ffff16021790555061430e8282614d10565b505b6001600160a01b0384165f908152600d6020526040902080548061433657614336615f15565b5f8281526020902060105f1990920191820401805461ffff6002600f8516026101000a02191690559055505b6001600160a01b03821615614406575f818152600c6020526040902080546001600160a01b0319166001600160a01b0384160190556001600160a01b0382165f818152600d60209081526040822080546001808201835582855292842060108204018054600f9092166002026101000a61ffff81810219909316928816029190911790559290915290546144019183916143fc9190615da5565b614d10565b614415565b5f818152600c60205260408120555b614423600160ff1b82615da5565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b5f61447283613b0e565b61447d906010615e95565b821061449c5760405163580821e760e01b815260040160405180910390fd5b611594600184015f601085046010808789546001600160401b03600160401b9091048116929091069190910116816144d6576144d6615eac565b88549190046001600160401b03808316919091019290920182168352602083019390935260409091015f205491601091600160401b90910416850106614d7a565b6001600160a01b03821661453e57604051634e46966960e11b815260040160405180910390fd5b600160ff1b81600654614551919061590b565b11156145705760405163303b682f60e01b815260040160405180910390fd5b611d375f8383613d97565b5f5f61458685611479565b90505f61459285611479565b905061459f868686614158565b5f6145a98761186b565b90505f6145b58761186b565b90508180156145c15750805b6149ab5781156146b8575f6145f67f0000000000000000000000000000000000000000000000000de0b6b3a764000085615ec0565b6001600160a01b0389165f90815260086020526040902054614639907f0000000000000000000000000000000000000000000000000de0b6b3a764000090615ec0565b6146439190615da5565b905060328111156146965760405162461bcd60e51b815260206004820152601e60248201527f52657472696576616c2062617463682073697a6520746f6f206c61726765000060448201526064016111db565b5f5b818110156146b1576146a989614da4565b600101614698565b50506149ab565b80156147a3576001600160a01b0388165f90815260086020526040812054614701907f0000000000000000000000000000000000000000000000000de0b6b3a764000090615ec0565b61472b7f0000000000000000000000000000000000000000000000000de0b6b3a764000087615ec0565b6147359190615da5565b905060328111156147885760405162461bcd60e51b815260206004820152601f60248201527f5769746864726177616c2062617463682073697a6520746f6f206c617267650060448201526064016111db565b5f5b818110156146b15761479b8a614ebd565b60010161478a565b5f6147ce7f0000000000000000000000000000000000000000000000000de0b6b3a764000088615ec0565b905060328111156148185760405162461bcd60e51b815260206004820152601460248201527342617463682073697a6520746f6f206c6172676560601b60448201526064016111db565b5f5b818110156148b8576001600160a01b038a165f908152600d602052604081205461484690600190615da5565b6001600160a01b038c165f908152600d60205260408120805492935090918390811061487457614874615b85565b5f91825260209091206010820401546148a191600f166002026101000a900461ffff16600160ff1b61590b565b90506148ae8c8c83614204565b505060010161481a565b50807f0000000000000000000000000000000000000000000000000de0b6b3a76400006148e48b611479565b6148ee9190615ec0565b6149187f0000000000000000000000000000000000000000000000000de0b6b3a764000088615ec0565b6149229190615da5565b11156149315761493189614ebd565b8061495c7f0000000000000000000000000000000000000000000000000de0b6b3a764000086615ec0565b7f0000000000000000000000000000000000000000000000000de0b6b3a76400006149868b611479565b6149909190615ec0565b61499a9190615da5565b11156149a9576149a988614da4565b505b506001979650505050505050565b6021805460ff60a81b1916600160a81b1790556040805160028082526060820183525f9260208301908036833701905050905030815f815181106149ff576149ff615b85565b60200260200101906001600160a01b031690816001600160a01b0316815250507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa158015614a7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614a9f9190615b99565b81600181518110614ab257614ab2615b85565b60200260200101906001600160a01b031690816001600160a01b0316815250505f6022546003614ae29190615e95565b905080831115614af0578092505b60215460405163791ac94760e01b81526001600160a01b037f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d81169263791ac94792614b4b9288925f92899291909116904290600401615f29565b5f604051808303815f87803b158015614b62575f5ffd5b505af1158015614b74573d5f5f3e3d5ffd5b50506021805460ff60a81b191690555050505050565b6001600160a01b0381165f908152600d6020526040812054905b81811015611d3757614bb583614ebd565b600101614ba4565b5f7f0000000000000000000000000000000000000000000000000de0b6b3a7640000614be883611479565b614bf29190615ec0565b90505f614c13836001600160a01b03165f908152600d602052604090205490565b90505f5b614c218284615da5565b811015612edd57614c3184614da4565b600101614c17565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310614c775772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310614ca3576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310614cc157662386f26fc10000830492506010015b6305f5e1008310614cd9576305f5e100830492506008015b6127108310614ced57612710830492506004015b60648310614cff576064830492506002015b600a83106114735760010192915050565b5f828152600c60205260409020546bffffffffffffffffffffffff821115614d4b57604051633f2cd0e360e21b815260040160405180910390fd5b5f928352600c60205260409092206001600160a01b039290921660a09190911b6001600160a01b031916019055565b5f614d86826010615f99565b6001600160401b0316614d9883614f5e565b8416901c905092915050565b6001600160a01b038116614dcb57604051634e46966960e11b815260040160405180910390fd5b5f614dd66002614f7f565b614dfd57614de46002614fc0565b614df69061ffff16600160ff1b61590b565b9050614e7c565b60075f8154614e0b906159f6565b90915550600754600101614e325760405163303b682f60e01b815260040160405180910390fd5b600754614e4390600160ff1b61590b565b601b549091505f90614e5790600190615da5565b600780545f908152601c6020526040902082905554909150614e7a908290613c68565b505b5f818152600c60205260409020546001600160a01b03168015614eb25760405163119b4fd360e11b815260040160405180910390fd5b611d37818484614204565b6001600160a01b038116614ee457604051636edaef2f60e11b815260040160405180910390fd5b6001600160a01b0381165f908152600d602052604081208054614f0990600190615da5565b81548110614f1957614f19615b85565b5f9182526020909120601082040154614f4691600f166002026101000a900461ffff16600160ff1b61590b565b9050614f53825f83614204565b61232f6002826150cd565b5f614f6a826010615f99565b6001600160401b031661ffff901b9050919050565b80545f90600160c01b81046001600160401b03908116600160401b90920416148015611473575050546001600160401b03808216600160801b909204161490565b80545f906001600160401b03600160801b8204811691600160c01b81048216911682148015615002575083546001600160401b03828116600160401b90920416145b15615020576040516375e52f4f60e01b815260040160405180910390fd5b806001600160401b03165f0361503b57505f1901600f61503f565b5f19015b6001600160401b0382165f9081526001850160205260409020546150638183614d7a565b935061507081835f6151b8565b6001600160401b039384165f81815260018801602052604090209190915585546fffffffffffffffffffffffffffffffff16600160801b9091026001600160c01b031617600160c01b929093169190910291909117909255919050565b81546001600160401b0380821691600160401b9004165f8190036150f657505f1901600f6150fa565b5f19015b83546001600160401b03838116600160801b90920416148015615130575083546001600160401b03828116600160c01b90920416145b1561514e57604051638acb5f2760e01b815260040160405180910390fd5b6001600160401b0382165f9081526001850160205260409020546151739082856151b8565b6001600160401b039283165f81815260018701602052604090209190915584546fffffffffffffffffffffffffffffffff191617600160401b91909216021790915550565b5f6151c4836010615f99565b6001600160401b03168261ffff16901b6151dd84614f5e565b1985166151ea919061590b565b949350505050565b6001600160a01b03811681146127ee575f5ffd5b5f5f83601f840112615216575f5ffd5b5081356001600160401b0381111561522c575f5ffd5b602083019150836020828501011115615243575f5ffd5b9250929050565b803560ff81168114611eab575f5ffd5b5f5f5f5f5f5f60a0878903121561526f575f5ffd5b863595506020870135615281816151f2565b94506040870135935060608701356001600160401b038111156152a2575f5ffd5b6152ae89828a01615206565b90945092506152c190506080880161524a565b90509295509295509295565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61159460208301846152cd565b5f5f6040838503121561531e575f5ffd5b50508035926020909101359150565b6001600160e01b0319811681146127ee575f5ffd5b5f60208284031215615352575f5ffd5b81356115948161532d565b5f6020828403121561536d575f5ffd5b8135611594816151f2565b5f5f5f6040848603121561538a575f5ffd5b83356001600160401b0381111561539f575f5ffd5b6153ab86828701615206565b909790965060209590950135949350505050565b5f602082840312156153cf575f5ffd5b5035919050565b5f5f604083850312156153e7575f5ffd5b82356153f2816151f2565b946020939093013593505050565b5f5f5f60608486031215615412575f5ffd5b505081359360208301359350604090920135919050565b5f5f6040838503121561543a575f5ffd5b82359150602083013561544c816151f2565b809150509250929050565b5f5f5f60608486031215615469575f5ffd5b8335615474816151f2565b92506020840135615484816151f2565b929592945050506040919091013590565b60018060a01b038816815286602082015260e060408201525f6154bb60e08301886152cd565b905060ff861660608301528460808301528360a083015282151560c083015298975050505050505050565b80358015158114611eab575f5ffd5b5f5f5f60608486031215615507575f5ffd5b833592506020840135615519816151f2565b9150615527604085016154e6565b90509250925092565b5f5f5f5f5f5f5f5f60e0898b031215615547575f5ffd5b883597506020890135615559816151f2565b96506040890135955060608901356001600160401b0381111561557a575f5ffd5b6155868b828c01615206565b9096509450615599905060808a0161524a565b979a969950949793969295929450505060a08201359160c0013590565b5f602082840312156155c6575f5ffd5b611594826154e6565b60c081525f6155e160c08301896152cd565b6001600160a01b0397909716602083015250604081019490945260608401929092521515608083015260a090910152919050565b5f5f60408385031215615626575f5ffd5b8235615631816151f2565b915061563f602084016154e6565b90509250929050565b602080825282518282018190525f918401906040840190835b8181101561569b57835180518452602081015160208501526040810151604085015250606083019250602084019350600181019050615661565b509095945050505050565b602080825282518282018190525f918401906040840190835b8181101561569b5783518352602093840193909201916001016156bf565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715615719576157196156dd565b604052919050565b5f6001600160401b03821115615739576157396156dd565b50601f01601f191660200190565b5f61575961575484615721565b6156f1565b905082815283838301111561576c575f5ffd5b828260208301375f602084830101529392505050565b5f5f5f5f60808587031215615795575f5ffd5b84356157a0816151f2565b935060208501356157b0816151f2565b92506040850135915060608501356001600160401b038111156157d1575f5ffd5b8501601f810187136157e1575f5ffd5b6157f087823560208401615747565b91505092959194509250565b5f6020828403121561580c575f5ffd5b81356001600160401b03811115615821575f5ffd5b8201601f81018413615831575f5ffd5b6151ea84823560208401615747565b5f5f5f5f5f5f5f60e0888a031215615856575f5ffd5b8735615861816151f2565b96506020880135615871816151f2565b9550604088013594506060880135935061588d6080890161524a565b9699959850939692959460a0840135945060c09093013592915050565b5f5f604083850312156158bb575f5ffd5b82356158c6816151f2565b9150602083013561544c816151f2565b5f5f604083850312156158e7575f5ffd5b8235915061563f602084016154e6565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611473576114736158f7565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60018060a01b0386168152846020820152608060408201525f61596d60808301858761591e565b905060ff831660608301529695505050505050565b5f60208284031215615992575f5ffd5b81516001600160401b038111156159a7575f5ffd5b8201601f810184136159b7575f5ffd5b80516159c561575482615721565b8181528560208385010111156159d9575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f60018201615a0757615a076158f7565b5060010190565b600181811c90821680615a2257607f821691505b602082108103615a4057634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115611d3757805f5260205f20601f840160051c81016020851015615a6b5750805b601f840160051c820191505b81811015615a8a575f8155600101615a77565b5050505050565b6001600160401b03831115615aa857615aa86156dd565b615abc83615ab68354615a0e565b83615a46565b5f601f841160018114615aed575f8515615ad65750838201355b5f19600387901b1c1916600186901b178355615a8a565b5f83815260208120601f198716915b82811015615b1c5786850135825560209485019460019092019101615afc565b5086821015615b38575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b602081525f6151ea60208301848661591e565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615ba9575f5ffd5b8151611594816151f2565b6001600160a01b0385168152602081018490526080604082015282545f908190615bdd81615a0e565b806080860152600182165f8114615bfb5760018114615c1757615c48565b60ff19831660a087015260a082151560051b8701019350615c48565b875f5260205f205f5b83811015615c3f57815488820160a00152600190910190602001615c20565b870160a0019450505b50505060ff84166060840152905095945050505050565b81516001600160401b03811115615c7857615c786156dd565b615c8c81615c868454615a0e565b84615a46565b6020601f821160018114615cbe575f8315615ca75750848201515b5f19600385901b1c1916600184901b178455615a8a565b5f84815260208120601f198516915b82811015615ced5787850151825560209485019460019092019101615ccd565b5084821015615d0a57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b60208082526015908201527454617820746f6f206869676820286d61782035252960581b604082015260600190565b5f5f85851115615d56575f5ffd5b83861115615d62575f5ffd5b5050820193919092039150565b80356001600160e01b03198116906004841015612de9576001600160e01b031960049490940360031b84901b1690921692915050565b81810381811115611473576114736158f7565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f9061126d908301846152cd565b5f60208284031215615dfa575f5ffd5b81516115948161532d565b5f8154615e1181615a0e565b600182168015615e285760018114615e3d57615e6a565b60ff1983168652811515820286019350615e6a565b845f5260205f205f5b83811015615e6257815488820152600190910190602001615e46565b505081860193505b50505092915050565b5f615e7e8285615e05565b83518060208601835e5f9101908152949350505050565b8082028115828204841417611473576114736158f7565b634e487b7160e01b5f52601260045260245ffd5b5f82615eda57634e487b7160e01b5f52601260045260245ffd5b500490565b5f5f5f60608486031215615ef1575f5ffd5b5050815160208301516040909301519094929350919050565b5f6115948284615e05565b634e487b7160e01b5f52603160045260245ffd5b5f60a0820187835286602084015260a0604084015280865180835260c0850191506020880192505f5b81811015615f795783516001600160a01b0316835260209384019390920191600101615f52565b50506001600160a01b039590951660608401525050608001529392505050565b6001600160401b038181168382160290811690818114612de957612de96158f756fea264697066735822122076f1288d8d28b93e428f5c0ffc28bd2eba3a171469bad28792537f9b7518f8d164736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000f0cc32453580ea0964d65493341d5c0f285362840000000000000000000000002e3e9bf7eaf07763d9a0cb01408126dbda841b6deb715e0af3d8f25f74c385f0a171b281db3515ebe6636d2c565588adb46f29e7000000000000000000000000000000000000000000000000000000000000000543726f6d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000543524f4d45000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : name_ (string): Crome
Arg [1] : symbol_ (string): CROME
Arg [2] : decimals_ (uint8): 18
Arg [3] : registry_ (address): 0xf0Cc32453580Ea0964d65493341d5C0f28536284
Arg [4] : implementation_ (address): 0x2e3E9bf7eaf07763d9A0Cb01408126dBdA841b6D
Arg [5] : salt_ (bytes32): 0xeb715e0af3d8f25f74c385f0a171b281db3515ebe6636d2c565588adb46f29e7
-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [3] : 000000000000000000000000f0cc32453580ea0964d65493341d5c0f28536284
Arg [4] : 0000000000000000000000002e3e9bf7eaf07763d9a0cb01408126dbda841b6d
Arg [5] : eb715e0af3d8f25f74c385f0a171b281db3515ebe6636d2c565588adb46f29e7
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [7] : 43726f6d65000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [9] : 43524f4d45000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.