Overview
APE Balance
APE Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 6 from a total of 6 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Read | 9584014 | 4 days ago | IN | 0.20509714 APE | 0.00754686 | ||||
Read | 9527388 | 5 days ago | IN | 0.3 APE | 0.00772504 | ||||
Set Approval For... | 9474858 | 6 days ago | IN | 0 APE | 0.00117092 | ||||
Read | 9470474 | 6 days ago | IN | 0.24098337 APE | 0.00781653 | ||||
Enable Shadow Mo... | 9354391 | 8 days ago | IN | 0 APE | 0.00072593 | ||||
Grant Roles | 9354373 | 8 days ago | IN | 0 APE | 0.00121704 |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Q00nicornShadow
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.28; import {NFTShadow} from "../NFTShadow.sol"; /** * @title Q00nicornShadow */ contract Q00nicornShadow is NFTShadow { constructor() NFTShadow(0x00000000000087c6dbaDC090d39BC10316f20658) { initialize( 0xe880c31C8118103aB49B604E281283E489a69780, "Q00nicorns", "q00nicorns", "", 0x7Df5c32827f0Fa448392Ce58231Ce4f378ce56e5, address(0), 0 ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.28; import {ERC721} from "solady/tokens/ERC721.sol"; import {LibString} from "solady/utils/LibString.sol"; import {OwnableRoles} from "solady/auth/OwnableRoles.sol"; import {ERC2981} from "solady/tokens/ERC2981.sol"; import {IERC5192} from "./interfaces/IERC5192.sol"; import {IBeacon} from "./interfaces/IBeacon.sol"; import {ICreatorToken} from "./interfaces/ICreatorToken.sol"; import {ITransferValidator} from "./interfaces/ITransferValidator.sol"; import {IShadowCallbackReceiver} from "./interfaces/IShadowCallbackReceiver.sol"; /** * @title NFTShadow * @author @0xQuit * @notice A contract for soulbound Shadow NFTs that follow ownership on a remote chain. * @notice This contract is designed to be used with a Beacon contract (see IBeacon.sol) to maintain ownership records * through LayerZero's lzRead protocol. * @notice Soulbound tokens can be unlocked by locking them on their source chain. * @notice Contracts have shadow mode disabled by default. In this state, they function very much like a typical oNFT. They * can be minted by locking the native asset, and the native asset can be unlocked by burning the Shadow NFT. * @notice When tokens are locked, ONLY the Beacon contract can transfer them. They will follow the canonical owner, * as resolved by the ExclusiveDelegateResolver (the "Resolver") contract. Users should only issue Resolver compatible * delegations for assets if they own the delegatee. Though the delegatee can not claim ownership of the underlying asset, * they may be able to claim airdrops, participate in activations, or otherwise interact with the asset using the Shadow NFT. */ contract NFTShadow is ERC721, ERC2981, IERC5192, ICreatorToken, OwnableRoles { // Only the Beacon contract can perform admin actions when a token is in Shadow mode error CallerNotBeacon(); // ERC-4906 BatchMetadataUpdate event event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); // The token is currently in Shadow mode error TokenLocked(); // The token is not in Shadow mode error TokenNotLocked(); // The function selector is not recognized error FnSelectorNotRecognized(); // Emitted when a new metadata renderer is set event MetadataRendererSet(address indexed metadataRenderer); // Emitted when shadow mode is enabled event ShadowModeEnabled(); // The address of the Beacon contract address public immutable BEACON_CONTRACT_ADDRESS; // Admin management roles uint256 public constant SHADOW_MODE_MANAGER_ROLE = _ROLE_13; uint256 public constant METADATA_MANAGER_ROLE = _ROLE_42; uint256 public constant TRANSFER_VALIDATOR_MANAGER_ROLE = _ROLE_69; uint256 public constant ROYALTY_MANAGER_ROLE = _ROLE_88; // The address of the ERC721C transfer validator, address(0) for none address public transferValidator; // Mapping to store callback target for each GUID mapping(bytes32 guid => address callbackTarget) public callbacks; // _extraData flags to indicate if a token is in Shadow mode (locked) or not (unlocked) uint96 private constant LOCKED = 0; uint96 private constant UNLOCKED = 1; // The base URI for the token metadata string private _baseTokenUri; // The name of the collection string private _name; // The symbol of the collection string private _symbol; // The address of the MetadataRenderer contract (address(0) if not set) address public metadataRenderer; // If false, the token can only be minted by locking it on the source chain bool public shadowModeEnabled; /** * @param _beaconContract The address of the Beacon contract */ constructor(address _beaconContract) { BEACON_CONTRACT_ADDRESS = _beaconContract; } /// @dev Fallback function for calls from Beacon contract. fallback() external { _shadowFallback(); } /** * @notice Initializes the NFTShadow contract. * @param _initialOwner The address of the initial owner. * @param _collectionName The name of the collection. * @param _collectionSymbol The symbol of the collection. * @param _baseTokenURI The base URI for the token metadata. * @param _metadataRenderer The address of the MetadataRenderer contract. * @param _transferValidator The address of the transfer validator. * @param _royaltyFeeNumerator The ERC2981 royalty fee numerator. */ function initialize( address _initialOwner, string memory _collectionName, string memory _collectionSymbol, string memory _baseTokenURI, // can be left empty if metadataRenderer is set address _metadataRenderer, // address(0) if not set address _transferValidator, uint96 _royaltyFeeNumerator ) public { _baseTokenUri = _baseTokenURI; _name = _collectionName; _symbol = _collectionSymbol; metadataRenderer = _metadataRenderer; _initializeOwner(_initialOwner); transferValidator = _transferValidator; _setDefaultRoyalty(_initialOwner, _royaltyFeeNumerator); } /** * @notice Transfers a token from one address to another. * @param from The address of the sender. * @param to The address of the recipient. * @param tokenId The token ID to transfer. * @dev If the token is locked, only the Beacon contract can transfer it. * @dev If the token is unlocked, only the owner or approved address can transfer it. */ function transferFrom(address from, address to, uint256 tokenId) public payable virtual override { if (msg.sender == BEACON_CONTRACT_ADDRESS) { if (_getExtraData(tokenId) == UNLOCKED) revert TokenNotLocked(); _transfer(address(0), from, to, tokenId); } else { super.transferFrom(from, to, tokenId); } } /** * @notice Triggers an ownership update for the tokens. * @param tokenIds The token IDs to update ownership for. * @param eids The EIDs to read ownership from. If token is locked on the provided EID, no update will occur. */ function read(uint256[] calldata tokenIds, uint32[] calldata eids) public payable virtual returns (bytes32) { return _read(tokenIds, eids, 0); } /** * @notice Triggers an ownership update with custom options. Helpful if a component of the update or callback is particularly expensive. * @param tokenIds The token IDs to update ownership for. * @param eids The EIDs to read ownership from. If token is locked on the provided EID, no update will occur. * @param callbackGasLimit The gas limit for the callback. * @return guid The GUID of the callback. * @dev Any excess fees provided to LayerZero will be returned to the caller. Potential callers are responsible for ensuring * that they are able to receive native tokens. */ function readWithCallback(uint256[] calldata tokenIds, uint32[] calldata eids, uint128 callbackGasLimit) external payable virtual returns (bytes32) { bytes32 guid = _read(tokenIds, eids, callbackGasLimit); callbacks[guid] = msg.sender; return guid; } /** * @notice Unlocks and sends tokens to the beneficiary on the specified EID. * @param dstEid The EID to unlock and send the tokens on. * @param tokenIds The token IDs to unlock and send. * @param beneficiary The address of the recipient on the target chain. * @param refundRecipient The address to refund the native fee to. * @param supplementalGasLimit The gas limit for the callback. * @dev Must be owner or approved, and the token must be unlocked (not in Shadow mode). Tokens released on the target chain will be locked on this chain. */ function send( uint32 dstEid, uint256[] calldata tokenIds, address beneficiary, address refundRecipient, uint128 supplementalGasLimit ) external payable virtual { for (uint256 i = 0; i < tokenIds.length; i++) { if (!_isApprovedOrOwner(msg.sender, tokenIds[i])) revert NotOwnerNorApproved(); if (_locked(tokenIds[i])) revert TokenLocked(); if (!shadowModeEnabled) { _burn(tokenIds[i]); } _setExtraData(tokenIds[i], LOCKED); emit Locked(tokenIds[i]); } address baseCollectionAddress = IBeacon(BEACON_CONTRACT_ADDRESS).shadowToBase(address(this)); IBeacon(BEACON_CONTRACT_ADDRESS).send{value: msg.value}( dstEid, baseCollectionAddress, tokenIds, beneficiary, refundRecipient, supplementalGasLimit ); } /** * @notice Enables shadow mode for the token. * @dev If shadow mode is enabled, shadows can be minted by the Beacon contract. * @dev Once enabled, shadow mode cannot be disabled. */ function enableShadowMode() external onlyRoles(SHADOW_MODE_MANAGER_ROLE) { shadowModeEnabled = true; } /** * @notice Sets the base URI for the token metadata. * @param uri The base URI for the token metadata. * emits BatchMetadataUpdate event */ function setTokenURI(string memory uri) external onlyRoles(METADATA_MANAGER_ROLE) { _baseTokenUri = uri; emit BatchMetadataUpdate(0, type(uint256).max); } /** * @notice Sets the metadata renderer for the collection. * @param _metadataRenderer The address of the metadata renderer. */ function setMetadataRenderer(address _metadataRenderer) external onlyRoles(METADATA_MANAGER_ROLE) { metadataRenderer = _metadataRenderer; emit MetadataRendererSet(_metadataRenderer); emit BatchMetadataUpdate(0, type(uint256).max); } /** * @notice Sets the transfer validator for the collection. * @param transferValidator_ The address of the transfer validator. * @dev address(0) to disable validation. */ function setTransferValidator(address transferValidator_) external onlyRoles(TRANSFER_VALIDATOR_MANAGER_ROLE) { address oldValidator = transferValidator; transferValidator = transferValidator_; emit TransferValidatorUpdated(oldValidator, transferValidator_); } /** * @dev Sets the ERC2981 royalty information for the token collection. * @param receiver The address of the royalty recipient. * @param feeNumerator The royalty fee numerator. */ function setRoyaltyInfo(address receiver, uint96 feeNumerator) external onlyRoles(ROYALTY_MANAGER_ROLE) { _setDefaultRoyalty(receiver, feeNumerator); } /** * @notice Burns a token. * @param tokenId The token ID. * @dev Only the Beacon contract can burn locked tokens, enforced by _beforeTokenTransfer * @dev If the token is unlocked, the caller must be the owner or approved. */ function burn(uint256 tokenId) external virtual { if (_locked(tokenId)) { _burn(tokenId); } else { _burn(msg.sender, tokenId); } } /** * @notice Executes a callback for the specified GUID. * @param guid The GUID of the callback. */ function executeCallback(bytes32 guid) external virtual { if (msg.sender != BEACON_CONTRACT_ADDRESS) revert CallerNotBeacon(); address callbackTarget = callbacks[guid]; if (callbackTarget == address(0)) return; delete callbacks[guid]; IShadowCallbackReceiver(callbackTarget).executeCallback(guid); } /** * @notice Returns whether the token is locked. * @param tokenId The token ID. * @return bool Whether the token is locked. */ function locked(uint256 tokenId) external view returns (bool) { return _locked(tokenId); } /** * @notice Returns whether the contract supports the specified interface. * @param interfaceId The interface ID. * @return bool Whether the contract supports the interface. * @dev ERC721 and ERC2981 are supported. */ function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC2981) returns (bool) { return interfaceId == type(IERC5192).interfaceId || ERC721.supportsInterface(interfaceId) || ERC2981.supportsInterface(interfaceId) || interfaceId == bytes4(0x49064906); } /** * @notice Returns the token URI for the specified token ID. * @param id The token ID. * @return The token URI. * @dev If the metadataRenderer is set, it will be used to render the token URI. */ function tokenURI(uint256 id) public view virtual override returns (string memory) { if (!_exists(id)) revert TokenDoesNotExist(); if (metadataRenderer != address(0)) { return ERC721(metadataRenderer).tokenURI(id); } return LibString.concat(_baseTokenUri, LibString.toString(id)); } /** * @notice Returns the name of the collection. * @return _name the name of the collection. */ function name() public view virtual override returns (string memory) { return _name; } /** * @notice Returns the symbol of the collection. * @return _symbol the symbol of the collection. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @notice Returns the token IDs of the tokens owned by the specified owner in a range. * @dev 10k collections should be able to get all their tokens in a single call, larger collections may need pagination. * @dev Intended for offchain use, do not use onchain. * @param owner The address of the owner. * @param start The range start. * @param stop The range stop. * @return array of token IDs owned by the specified owner. */ function tokensOfOwnerIn(address owner, uint256 start, uint256 stop) external view returns (uint256[] memory) { return _tokensOfOwnerIn(owner, start, stop); } /** * @notice Returns the ERC721C transfer validator address. address(0) indicates no validator is set. * @return address The address of the transfer validator. */ function getTransferValidator() external view returns (address) { return transferValidator; } /** * @notice Returns the ERC721C transfer validation function signature and whether it is a view function. * @return functionSignature The 4 byte function signature. * @return isViewFunction Bool indicating if the function is a view function. */ function getTransferValidationFunction() external pure returns (bytes4 functionSignature, bool isViewFunction) { functionSignature = 0xcaee23ea; isViewFunction = true; } function _read(uint256[] calldata tokenIds, uint32[] calldata dstEids, uint128 callbackGasLimit) internal returns (bytes32) { address baseCollectionAddress = IBeacon(BEACON_CONTRACT_ADDRESS).shadowToBase(address(this)); return IBeacon(BEACON_CONTRACT_ADDRESS).read{value: msg.value}( baseCollectionAddress, tokenIds, dstEids, msg.sender, callbackGasLimit ); } /** * @notice Unlocks the specified token ID. * @param tokenId The token ID to unlock. * @param beneficiary The address of the beneficiary of the unlocked token, in case ownership is stale. * @dev Only the Beacon contract can unlock tokens */ function _unlockToken(uint256 tokenId, address beneficiary) internal { if (_locked(tokenId)) { address owner = _ownerOf(tokenId); // mint if there is no previous owner, otherwise transfer if (owner == address(0)) { _mint(beneficiary, tokenId); } else if (beneficiary != owner) { _transfer(address(0), owner, beneficiary, tokenId); } _setExtraData(tokenId, UNLOCKED); emit Unlocked(tokenId); } else { revert TokenNotLocked(); } } function _tokensOfOwnerIn(address owner, uint256 start, uint256 stop) internal view returns (uint256[] memory) { uint256 numberOfTokens = balanceOf(owner); uint256 tokenIdIdx = 0; uint256[] memory tokenIds = new uint256[](numberOfTokens); for (uint256 i = start; i <= stop; i++) { if (_ownerOf(i) == owner) { tokenIds[tokenIdIdx] = i; unchecked { tokenIdIdx++; } if (tokenIdIdx == numberOfTokens) { return tokenIds; } } } // in case we did not find all the tokens within this start/stop, we need to resize the array uint256[] memory result = new uint256[](tokenIdIdx); for (uint256 i = 0; i < tokenIdIdx; i++) { result[i] = tokenIds[i]; } return result; } // tokens can only be transferred if they are locked on the source chain function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal view override { if (msg.sender != BEACON_CONTRACT_ADDRESS) { if (_locked(tokenId) || from == address(0)) { revert CallerNotBeacon(); } if (transferValidator != address(0)) { ITransferValidator(transferValidator).validateTransfer(msg.sender, from, to, tokenId); } } } function _locked(uint256 tokenId) internal view returns (bool) { return _getExtraData(tokenId) == LOCKED; } function _guardInitializeOwner() internal pure override returns (bool) { return true; } function _calldataload(uint256 offset) internal pure returns (uint256 value) { /// @solidity memory-safe-assembly assembly { value := calldataload(offset) } } function _shadowFallback() internal { uint256 fnSelector = _calldataload(0x00) >> 224; if (fnSelector == 0x40c10f19) { // msg.sender is the Beacon contract, enforced by _beforeTokenTransfer _mint(address(uint160(_calldataload(0x04))), _calldataload(0x24)); } else if (fnSelector == 0x92772833) { if (msg.sender != BEACON_CONTRACT_ADDRESS) revert CallerNotBeacon(); uint256 tokenIdsLength = _calldataload(0x44); address beneficiary = address(uint160(_calldataload(0x24))); for (uint256 i = 0; i < tokenIdsLength; i++) { _unlockToken(_calldataload(0x64 + i * 32), beneficiary); } } else { revert FnSelectorNotRecognized(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple ERC721 implementation with storage hitchhiking. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol) /// /// @dev Note: /// - The ERC721 standard allows for self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. /// - For performance, methods are made payable where permitted by the ERC721 standard. /// - The `safeTransfer` functions use the identity precompile (0x4) /// to copy memory internally. /// /// If you are overriding: /// - NEVER violate the ERC721 invariant: /// the balance of an owner MUST always be equal to their number of ownership slots. /// The transfer functions do not have an underflow guard for user token balances. /// - Make sure all variables written to storage are properly cleaned // (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood). /// - Check that the overridden function is actually used in the function you want to /// change the behavior of. Much of the code has been manually inlined for performance. abstract contract ERC721 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev An account can hold up to 4294967295 tokens. uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Only the token owner or an approved account can manage the token. error NotOwnerNorApproved(); /// @dev The token does not exist. error TokenDoesNotExist(); /// @dev The token already exists. error TokenAlreadyExists(); /// @dev Cannot query the balance for the zero address. error BalanceQueryForZeroAddress(); /// @dev Cannot mint or transfer to the zero address. error TransferToZeroAddress(); /// @dev The token must be owned by `from`. error TransferFromIncorrectOwner(); /// @dev The recipient's balance has overflowed. error AccountBalanceOverflow(); /// @dev Cannot safely transfer to a contract that does not implement /// the ERC721Receiver interface. error TransferToNonERC721ReceiverImplementer(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when token `id` is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 indexed id); /// @dev Emitted when `owner` enables `account` to manage the `id` token. event Approval(address indexed owner, address indexed account, uint256 indexed id); /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens. event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved); /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`. uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership data slot of `id` is given by: /// ``` /// mstore(0x00, id) /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) /// let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) /// ``` /// Bits Layout: /// - [0..159] `addr` /// - [160..255] `extraData` /// /// The approved address slot is given by: `add(1, ownershipSlot)`. /// /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip /// /// The balance slot of `owner` is given by: /// ``` /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x1c) /// ``` /// Bits Layout: /// - [0..31] `balance` /// - [32..255] `aux` /// /// The `operator` approval slot of `owner` is given by: /// ``` /// mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) /// mstore(0x00, owner) /// let operatorApprovalSlot := keccak256(0x0c, 0x30) /// ``` uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; /// @dev Pre-shifted and pre-masked constant. uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC721 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the token collection name. function name() public view virtual returns (string memory); /// @dev Returns the token collection symbol. function symbol() public view virtual returns (string memory); /// @dev Returns the Uniform Resource Identifier (URI) for token `id`. function tokenURI(uint256 id) public view virtual returns (string memory); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC721 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of token `id`. /// /// Requirements: /// - Token `id` must exist. function ownerOf(uint256 id) public view virtual returns (address result) { result = _ownerOf(id); /// @solidity memory-safe-assembly assembly { if iszero(result) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } } } /// @dev Returns the number of tokens owned by `owner`. /// /// Requirements: /// - `owner` must not be the zero address. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Revert if the `owner` is the zero address. if iszero(owner) { mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`. revert(0x1c, 0x04) } mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE) } } /// @dev Returns the account approved to manage token `id`. /// /// Requirements: /// - Token `id` must exist. function getApproved(uint256 id) public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) if iszero(shl(96, sload(ownershipSlot))) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } result := sload(add(1, ownershipSlot)) } } /// @dev Sets `account` as the approved account to manage token `id`. /// /// Requirements: /// - Token `id` must exist. /// - The caller must be the owner of the token, /// or an approved operator for the token owner. /// /// Emits an {Approval} event. function approve(address account, uint256 id) public payable virtual { _approve(msg.sender, account, id); } /// @dev Returns whether `operator` is approved to manage the tokens of `owner`. function isApprovedForAll(address owner, address operator) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { mstore(0x1c, operator) mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x30)) } } /// @dev Sets whether `operator` is approved to manage the tokens of the caller. /// /// Emits an {ApprovalForAll} event. function setApprovalForAll(address operator, bool isApproved) public virtual { /// @solidity memory-safe-assembly assembly { // Convert to 0 or 1. isApproved := iszero(iszero(isApproved)) // Update the `isApproved` for (`msg.sender`, `operator`). mstore(0x1c, operator) mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x30), isApproved) // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) // forgefmt: disable-next-item log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator))) } } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 id) public payable virtual { _beforeTokenTransfer(from, to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) from := and(bitmaskAddress, from) to := and(bitmaskAddress, to) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller())) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) let owner := and(bitmaskAddress, ownershipPacked) // Revert if the token does not exist, or if `from` is not the owner. if iszero(mul(owner, eq(owner, from))) { // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`. mstore(shl(2, iszero(owner)), 0xceea21b6a1148100) revert(0x1c, 0x04) } // Load, check, and update the token approval. { mstore(0x00, from) let approvedAddress := sload(add(1, ownershipSlot)) // Revert if the caller is not the owner, nor approved. if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Update with the new owner. sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) // Decrement the balance of `from`. { let fromBalanceSlot := keccak256(0x0c, 0x1c) sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) } // Increment the balance of `to`. { mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x1c) let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(toBalanceSlot, toBalanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) } _afterTokenTransfer(from, to, id); } /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`. function safeTransferFrom(address from, address to, uint256 id) public payable virtual { transferFrom(from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function safeTransferFrom(address from, address to, uint256 id, bytes calldata data) public payable virtual { transferFrom(from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /// @dev Returns true if this contract implements the interface defined by `interfaceId`. /// See: https://eips.ethereum.org/EIPS/eip-165 /// This function call must use less than 30000 gas. function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { let s := shr(224, interfaceId) // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL QUERY FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns if token `id` exists. function _exists(uint256 id) internal view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))) } } /// @dev Returns the owner of token `id`. /// Returns the zero address instead of reverting if the token does not exist. function _ownerOf(uint256 id) internal view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL DATA HITCHHIKING FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance, no events are emitted for the hitchhiking setters. // Please emit your own events if required. /// @dev Returns the auxiliary data for `owner`. /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. /// Auxiliary data can be set for any address, even if it does not have any tokens. function _getAux(address owner) internal view virtual returns (uint224 result) { /// @solidity memory-safe-assembly assembly { mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) result := shr(32, sload(keccak256(0x0c, 0x1c))) } } /// @dev Set the auxiliary data for `owner` to `value`. /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. /// Auxiliary data can be set for any address, even if it does not have any tokens. function _setAux(address owner, uint224 value) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) let balanceSlot := keccak256(0x0c, 0x1c) let packed := sload(balanceSlot) sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed))))) } } /// @dev Returns the extra data for token `id`. /// Minting, transferring, burning a token will not change the extra data. /// The extra data can be set on a non-existent token. function _getExtraData(uint256 id) internal view virtual returns (uint96 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Sets the extra data for token `id` to `value`. /// Minting, transferring, burning a token will not change the extra data. /// The extra data can be set on a non-existent token. function _setExtraData(uint256 id, uint96 value) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let packed := sload(ownershipSlot) sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed))))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints token `id` to `to`. /// /// Requirements: /// /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// /// Emits a {Transfer} event. function _mint(address to, uint256 id) internal virtual { _beforeTokenTransfer(address(0), to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. to := shr(96, shl(96, to)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) // Revert if the token already exists. if shl(96, ownershipPacked) { mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`. revert(0x1c, 0x04) } // Update with the owner. sstore(ownershipSlot, or(ownershipPacked, to)) // Increment the balance of the owner. { mstore(0x00, to) let balanceSlot := keccak256(0x0c, 0x1c) let balanceSlotPacked := add(sload(balanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(balanceSlot, balanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id) } _afterTokenTransfer(address(0), to, id); } /// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`. /// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing). /// /// Requirements: /// /// - `to` cannot be the zero address. /// /// Emits a {Transfer} event. function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual { _beforeTokenTransfer(address(0), to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. to := shr(96, shl(96, to)) // Update with the owner and extra data. mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to)) // Increment the balance of the owner. { mstore(0x00, to) let balanceSlot := keccak256(0x0c, 0x1c) let balanceSlotPacked := add(sload(balanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(balanceSlot, balanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id) } _afterTokenTransfer(address(0), to, id); } /// @dev Equivalent to `_safeMint(to, id, "")`. function _safeMint(address to, uint256 id) internal virtual { _safeMint(to, id, ""); } /// @dev Mints token `id` to `to`. /// /// Requirements: /// /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeMint(address to, uint256 id, bytes memory data) internal virtual { _mint(to, id); if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `_burn(address(0), id)`. function _burn(uint256 id) internal virtual { _burn(address(0), id); } /// @dev Destroys token `id`, using `by`. /// /// Requirements: /// /// - Token `id` must exist. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function _burn(address by, uint256 id) internal virtual { address owner = ownerOf(id); _beforeTokenTransfer(owner, address(0), id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. by := shr(96, shl(96, by)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) // Reload the owner in case it is changed in `_beforeTokenTransfer`. owner := shr(96, shl(96, ownershipPacked)) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // Load and check the token approval. { mstore(0x00, owner) let approvedAddress := sload(add(1, ownershipSlot)) // If `by` is not the zero address, do the authorization check. // Revert if the `by` is not the owner, nor approved. if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Clear the owner. sstore(ownershipSlot, xor(ownershipPacked, owner)) // Decrement the balance of `owner`. { let balanceSlot := keccak256(0x0c, 0x1c) sstore(balanceSlot, sub(sload(balanceSlot), 1)) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id) } _afterTokenTransfer(owner, address(0), id); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL APPROVAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it. /// /// Requirements: /// - Token `id` must exist. function _isApprovedOrOwner(address account, uint256 id) internal view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 // Clear the upper 96 bits. account := shr(96, shl(96, account)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let owner := shr(96, shl(96, sload(ownershipSlot))) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // Check if `account` is the `owner`. if iszero(eq(account, owner)) { mstore(0x00, owner) // Check if `account` is approved to manage the token. if iszero(sload(keccak256(0x0c, 0x30))) { result := eq(account, sload(add(1, ownershipSlot))) } } } } /// @dev Returns the account approved to manage token `id`. /// Returns the zero address instead of reverting if the token does not exist. function _getApproved(uint256 id) internal view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Equivalent to `_approve(address(0), account, id)`. function _approve(address account, uint256 id) internal virtual { _approve(address(0), account, id); } /// @dev Sets `account` as the approved account to manage token `id`, using `by`. /// /// Requirements: /// - Token `id` must exist. /// - If `by` is not the zero address, `by` must be the owner /// or an approved operator for the token owner. /// /// Emits a {Approval} event. function _approve(address by, address account, uint256 id) internal virtual { assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) account := and(bitmaskAddress, account) by := and(bitmaskAddress, by) // Load the owner of the token. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let owner := and(bitmaskAddress, sload(ownershipSlot)) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // If `by` is not the zero address, do the authorization check. // Revert if `by` is not the owner, nor approved. if iszero(or(iszero(by), eq(by, owner))) { mstore(0x00, owner) if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Sets `account` as the approved account to manage `id`. sstore(add(1, ownershipSlot), account) // Emit the {Approval} event. log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id) } } /// @dev Approve or remove the `operator` as an operator for `by`, /// without authorization checks. /// /// Emits an {ApprovalForAll} event. function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual { /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. by := shr(96, shl(96, by)) operator := shr(96, shl(96, operator)) // Convert to 0 or 1. isApproved := iszero(iszero(isApproved)) // Update the `isApproved` for (`by`, `operator`). mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) mstore(0x00, by) sstore(keccak256(0x0c, 0x30), isApproved) // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `_transfer(address(0), from, to, id)`. function _transfer(address from, address to, uint256 id) internal virtual { _transfer(address(0), from, to, id); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function _transfer(address by, address from, address to, uint256 id) internal virtual { _beforeTokenTransfer(from, to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) from := and(bitmaskAddress, from) to := and(bitmaskAddress, to) by := and(bitmaskAddress, by) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) let owner := and(bitmaskAddress, ownershipPacked) // Revert if the token does not exist, or if `from` is not the owner. if iszero(mul(owner, eq(owner, from))) { // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`. mstore(shl(2, iszero(owner)), 0xceea21b6a1148100) revert(0x1c, 0x04) } // Load, check, and update the token approval. { mstore(0x00, from) let approvedAddress := sload(add(1, ownershipSlot)) // If `by` is not the zero address, do the authorization check. // Revert if the `by` is not the owner, nor approved. if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Update with the new owner. sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) // Decrement the balance of `from`. { let fromBalanceSlot := keccak256(0x0c, 0x1c) sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) } // Increment the balance of `to`. { mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x1c) let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(toBalanceSlot, toBalanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) } _afterTokenTransfer(from, to, id); } /// @dev Equivalent to `_safeTransfer(from, to, id, "")`. function _safeTransfer(address from, address to, uint256 id) internal virtual { _safeTransfer(from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeTransfer(address from, address to, uint256 id, bytes memory data) internal virtual { _transfer(address(0), from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`. function _safeTransfer(address by, address from, address to, uint256 id) internal virtual { _safeTransfer(by, from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data) internal virtual { _transfer(by, from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS FOR OVERRIDING */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any token transfers, including minting and burning. function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {} /// @dev Hook that is called after any token transfers, including minting and burning. function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns if `a` has bytecode of non-zero length. function _hasCode(address a) private view returns (bool result) { /// @solidity memory-safe-assembly assembly { result := extcodesize(a) // Can handle dirty upper bits. } } /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`. /// Reverts if the target does not support the function correctly. function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data) private { /// @solidity memory-safe-assembly assembly { // Prepare the calldata. let m := mload(0x40) let onERC721ReceivedSelector := 0x150b7a02 mstore(m, onERC721ReceivedSelector) mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`. mstore(add(m, 0x40), shr(96, shl(96, from))) mstore(add(m, 0x60), id) mstore(add(m, 0x80), 0x80) let n := mload(data) mstore(add(m, 0xa0), n) if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) } // Revert if the call reverts. if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) { if returndatasize() { // Bubble up the revert if the call reverts. returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } } // Load the returndata and compare it. if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) { mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`. revert(0x1c, 0x04) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) /// /// @dev Note: /// For performance and bytecode compactness, most of the string operations are restricted to /// byte strings (7-bit ASCII), except where otherwise specified. /// Usage of byte string operations on charsets with runes spanning two or more bytes /// can lead to undefined behavior. library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The length of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// @dev The length of the string is more than 32 bytes. error TooBigForSmallString(); /// @dev The input string must be a 7-bit ASCII. error StringNot7BitASCII(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000; /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000; /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'. uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000; /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000; /// @dev Lookup for '0123456789'. uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000; /// @dev Lookup for '0123456789abcdefABCDEF'. uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000; /// @dev Lookup for '01234567'. uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000; /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'. uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00; /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'. uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000; /// @dev Lookup for ' \t\n\r\x0b\x0c'. uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // The maximum value of a uint256 contains 78 digits (1 byte per digit), but // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. // We will need 1 word for the trailing zeros padding, 1 word for the length, // and 3 words for a maximum of 78 digits. result := add(mload(0x40), 0x80) mstore(0x40, add(result, 0x20)) // Allocate memory. mstore(result, 0) // Zeroize the slot after the string. let end := result // Cache the end of the memory to calculate the length later. let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { result := add(result, w) // `sub(result, 1)`. // Store the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(result, add(48, mod(temp, 10))) temp := div(temp, 10) // Keep dividing `temp` until zero. if iszero(temp) { break } } let n := sub(end, result) result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length. mstore(result, n) // Store the length. } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory result) { if (value >= 0) return toString(uint256(value)); unchecked { result = toString(~uint256(value) + 1); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let n := mload(result) // Load the string length. mstore(result, 0x2d) // Store the '-' character. result := sub(result, 1) // Move back the string pointer by a byte. mstore(result, add(n, 1)) // Update the string length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HEXADECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory result) { result = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. result := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) mstore(0x40, add(result, 0x20)) // Allocate memory. mstore(result, 0) // Zeroize the slot after the string. let end := result // Cache the end to calculate the length later. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(result, add(length, length)) let w := not(1) // Tsk. let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for {} 1 {} { result := add(result, w) // `sub(result, 2)`. mstore8(add(result, 1), mload(and(temp, 15))) mstore8(result, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(result, start)) { break } } if temp { mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`. revert(0x1c, 0x04) } let n := sub(end, result) result := sub(result, 0x20) mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x". /// The output excludes leading "0" from the `toHexString` output. /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. function toMinimalHexString(uint256 value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present. let n := add(mload(result), 2) // Compute the length. mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero. result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero. mstore(result, sub(n, o)) // Store the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output excludes leading "0" from the `toHexStringNoPrefix` output. /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present. let n := mload(result) // Get the length. result := add(result, o) // Move the pointer, accounting for leading zero. mstore(result, sub(n, o)) // Store the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. result := add(mload(0x40), 0x80) mstore(0x40, add(result, 0x20)) // Allocate memory. mstore(result, 0) // Zeroize the slot after the string. let end := result // Cache the end to calculate the length later. mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. let w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { result := add(result, w) // `sub(result, 2)`. mstore8(add(result, 1), mload(and(temp, 15))) mstore8(result, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } let n := sub(end, result) result := sub(result, 0x20) mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory result) { result = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(result, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 {} { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Allocate memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(result, 0x80)) mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. result := add(result, 2) mstore(result, 40) // Store the length. let o := add(result, 0x20) mstore(add(o, 40), 0) // Zeroize the slot after the string. value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 {} { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory result) { result = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let n := mload(raw) result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(result, add(n, n)) // Store the length of the output. mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. let o := add(result, 0x20) let end := add(raw, n) for {} iszero(eq(raw, end)) {} { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /// @dev Returns if this string is a 7-bit ASCII string. /// (i.e. all characters codes are in [0..127]) function is7BitASCII(string memory s) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 let mask := shl(7, div(not(0), 255)) let n := mload(s) if n { let o := add(s, 0x20) let end := add(o, n) let last := mload(end) mstore(end, 0) for {} 1 {} { if and(mask, mload(o)) { result := 0 break } o := add(o, 0x20) if iszero(lt(o, end)) { break } } mstore(end, last) } } } /// @dev Returns if this string is a 7-bit ASCII string, /// AND all characters are in the `allowed` lookup. /// Note: If `s` is empty, returns true regardless of `allowed`. function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 if mload(s) { let allowed_ := shr(128, shl(128, allowed)) let o := add(s, 0x20) for { let end := add(o, mload(s)) } 1 {} { result := and(result, shr(byte(0, mload(o)), allowed_)) o := add(o, 1) if iszero(and(result, lt(o, end))) { break } } } } } /// @dev Converts the bytes in the 7-bit ASCII string `s` to /// an allowed lookup for use in `is7BitASCII(s, allowed)`. /// To save runtime gas, you can cache the result in an immutable variable. function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { let o := add(s, 0x20) for { let end := add(o, mload(s)) } 1 {} { result := or(result, shl(byte(0, mload(o)), 1)) o := add(o, 1) if iszero(lt(o, end)) { break } } if shr(128, result) { mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`. revert(0x1c, 0x04) } } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, byte string operations are restricted // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets. // Usage of byte string operations on charsets with runes spanning two or more bytes // can lead to undefined behavior. /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`. function replace(string memory subject, string memory needle, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let needleLen := mload(needle) let replacementLen := mload(replacement) let d := sub(result, subject) // Memory difference. let i := add(subject, 0x20) // Subject bytes pointer. let end := add(i, mload(subject)) if iszero(gt(needleLen, mload(subject))) { let subjectSearchEnd := add(sub(end, needleLen), 1) let h := 0 // The hash of `needle`. if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) } let s := mload(add(needle, 0x20)) for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} { let t := mload(i) // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(i, needleLen), h)) { mstore(add(i, d), t) i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let j := 0 } 1 {} { mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j))) j := add(j, 0x20) if iszero(lt(j, replacementLen)) { break } } d := sub(add(d, replacementLen), needleLen) if needleLen { i := add(i, needleLen) if iszero(lt(i, subjectSearchEnd)) { break } continue } } mstore(add(i, d), t) i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } } } let n := add(sub(d, add(result, 0x20)), end) // Copy the rest of the string one word at a time. for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) } let o := add(i, d) mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, n) // Store the length. } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function indexOf(string memory subject, string memory needle, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := not(0) // Initialize to `NOT_FOUND`. for { let subjectLen := mload(subject) } 1 {} { if iszero(mload(needle)) { result := from if iszero(gt(from, subjectLen)) { break } result := subjectLen break } let needleLen := mload(needle) let subjectStart := add(subject, 0x20) subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLen), needleLen), 1) let m := shl(3, sub(0x20, and(needleLen, 0x1f))) let s := mload(add(needle, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLen))) { break } if iszero(lt(needleLen, 0x20)) { for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, needleLen), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function indexOf(string memory subject, string memory needle) internal pure returns (uint256 result) { result = indexOf(subject, needle, 0); } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function lastIndexOf(string memory subject, string memory needle, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let needleLen := mload(needle) if gt(needleLen, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), needleLen) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} { if eq(keccak256(subject, needleLen), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function lastIndexOf(string memory subject, string memory needle) internal pure returns (uint256 result) { result = lastIndexOf(subject, needle, type(uint256).max); } /// @dev Returns true if `needle` is found in `subject`, false otherwise. function contains(string memory subject, string memory needle) internal pure returns (bool) { return indexOf(subject, needle) != NOT_FOUND; } /// @dev Returns whether `subject` starts with `needle`. function startsWith(string memory subject, string memory needle) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let needleLen := mload(needle) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(needleLen, mload(subject))), eq( keccak256(add(subject, 0x20), needleLen), keccak256(add(needle, 0x20), needleLen) ) ) } } /// @dev Returns whether `subject` ends with `needle`. function endsWith(string memory subject, string memory needle) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let needleLen := mload(needle) // Whether `needle` is not longer than `subject`. let inRange := iszero(gt(needleLen, mload(subject))) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( eq( keccak256( // `subject + 0x20 + max(subjectLen - needleLen, 0)`. add(add(subject, 0x20), mul(inRange, sub(mload(subject), needleLen))), needleLen ), keccak256(add(needle, 0x20), needleLen) ), inRange ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLen := mload(subject) if iszero(or(iszero(times), iszero(subjectLen))) { result := mload(0x40) subject := add(subject, 0x20) let o := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let j := 0 } 1 {} { mstore(add(o, j), mload(add(subject, j))) j := add(j, 0x20) if iszero(lt(j, subjectLen)) { break } } o := add(o, subjectLen) times := sub(times, 1) if iszero(times) { break } } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, sub(o, add(result, 0x20))) // Store the length. } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLen := mload(subject) if iszero(gt(subjectLen, end)) { end := subjectLen } if iszero(gt(subjectLen, start)) { start := subjectLen } if lt(start, end) { result := mload(0x40) let n := sub(end, start) let i := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let j := and(add(n, 0x1f), w) } 1 {} { mstore(add(result, j), mload(add(i, j))) j := add(j, w) // `sub(j, 0x20)`. if iszero(j) { break } } let o := add(add(result, 0x20), n) mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, n) // Store the length. } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, type(uint256).max); } /// @dev Returns all the indices of `needle` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory needle) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let searchLen := mload(needle) if iszero(gt(searchLen, mload(subject))) { result := mload(0x40) let i := add(subject, 0x20) let o := add(result, 0x20) let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1) let h := 0 // The hash of `needle`. if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) } let s := mload(add(needle, 0x20)) for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} { let t := mload(i) // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(i, searchLen), h)) { i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } continue } } mstore(o, sub(i, add(subject, 0x20))) // Append to `result`. o := add(o, 0x20) i := add(i, searchLen) // Advance `i` by `searchLen`. if searchLen { if iszero(lt(i, subjectSearchEnd)) { break } continue } } i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } } mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`. // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(o, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); /// @solidity memory-safe-assembly assembly { let w := not(0x1f) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) for { let prevIndex := 0 } 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let l := sub(index, prevIndex) mstore(element, l) // Store the length of the element. // Copy the `subject` one word at a time, backwards. for { let o := and(add(l, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the string. // Allocate memory for the length and the bytes, rounded up to a multiple of 32. mstore(0x40, add(element, and(add(l, 0x3f), w))) mstore(indexPtr, element) // Store the `element` into the array. } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let w := not(0x1f) let aLen := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLen, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLen := mload(b) let output := add(result, aLen) // Copy `b` one word at a time, backwards. for { let o := and(add(bLen, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLen := add(aLen, bLen) let last := add(add(result, 0x20), totalLen) mstore(last, 0) // Zeroize the slot after the string. mstore(result, totalLen) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate memory. } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. /// WARNING! This function is only compatible with 7-bit ASCII strings. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let n := mload(subject) if n { result := mload(0x40) let o := add(result, 0x20) let d := sub(subject, result) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) for { let end := add(o, n) } 1 {} { let b := byte(0, mload(add(d, o))) mstore8(o, xor(and(shr(b, flags), 0x20), b)) o := add(o, 1) if eq(o, end) { break } } mstore(result, n) // Store the length. mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. } } } /// @dev Returns a string from a small bytes32 string. /// `s` must be null-terminated, or behavior will be undefined. function fromSmallString(bytes32 s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let n := 0 for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'. mstore(result, n) // Store the length. let o := add(result, 0x20) mstore(o, s) // Store the bytes of the string. mstore(add(o, n), 0) // Zeroize the slot after the string. mstore(0x40, add(result, 0x40)) // Allocate memory. } } /// @dev Returns the small string, with all bytes after the first null byte zeroized. function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'. mstore(0x00, s) mstore(result, 0x00) result := mload(0x00) } } /// @dev Returns the string as a normalized null-terminated small string. function toSmallString(string memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(s) if iszero(lt(result, 33)) { mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`. revert(0x1c, 0x04) } result := shl(shl(3, sub(32, result)), mload(add(s, result))) } } /// @dev Returns a lowercased copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let end := add(s, mload(s)) let o := add(result, 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(o, c) o := add(o, 1) continue } let t := shr(248, mload(c)) mstore(o, mload(and(t, 0x1f))) o := add(o, shr(5, t)) } mstore(o, 0) // Zeroize the slot after the string. mstore(result, sub(o, add(result, 0x20))) // Store the length. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let o := add(result, 0x20) if addDoubleQuotes { mstore8(o, 34) o := add(1, o) } // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(o, c) o := add(o, 1) continue } mstore8(o, 0x5c) // "\\". mstore8(add(o, 1), c) o := add(o, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(o, mload(0x19)) // "\\u00XX". o := add(o, 6) continue } mstore8(o, 0x5c) // "\\". mstore8(add(o, 1), mload(add(c, 8))) o := add(o, 2) } if addDoubleQuotes { mstore8(o, 34) o := add(1, o) } mstore(o, 0) // Zeroize the slot after the string. mstore(result, sub(o, add(result, 0x20))) // Store the length. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { result = escapeJSON(s, false); } /// @dev Encodes `s` so that it can be safely used in a URI, /// just like `encodeURIComponent` in JavaScript. /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent /// See: https://datatracker.ietf.org/doc/html/rfc2396 /// See: https://datatracker.ietf.org/doc/html/rfc3986 function encodeURIComponent(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Store "0123456789ABCDEF" in scratch space. // Uppercased to be consistent with JavaScript's implementation. mstore(0x0f, 0x30313233343536373839414243444546) let o := add(result, 0x20) for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // If not in `[0-9A-Z-a-z-_.!~*'()]`. if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) { mstore8(o, 0x25) // '%'. mstore8(add(o, 1), mload(and(shr(4, c), 15))) mstore8(add(o, 2), mload(and(c, 15))) o := add(o, 3) continue } mstore8(o, c) o := add(o, 1) } mstore(result, sub(o, add(result, 0x20))) // Store the length. mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string. function eqs(string memory a, bytes32 b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { // These should be evaluated on compile time, as far as possible. let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. let x := not(or(m, or(b, add(m, and(b, m))))) let r := shl(7, iszero(iszero(shr(128, x)))) r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behavior is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Grab the free memory pointer. mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(result, 0) // Zeroize the length slot. mstore(add(result, 0x1f), packed) // Store the length and bytes. mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes. } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let aLen := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( or( // Load the length and the bytes of `a` and `b`. shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))), // `totalLen != 0 && totalLen < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLen, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behavior is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { resultA := mload(0x40) // Grab the free memory pointer. resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) let retUnpaddedSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retUnpaddedSize), 0) mstore(retStart, 0x20) // Store the return offset. // End the transaction, returning the string. return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize))) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {Ownable} from "./Ownable.sol"; /// @notice Simple single owner and multiroles authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract OwnableRoles is Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `user`'s roles is updated to `roles`. /// Each bit of `roles` represents whether the role is set. event RolesUpdated(address indexed user, uint256 indexed roles); /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE = 0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The role slot of `user` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED)) /// let roleSlot := keccak256(0x00, 0x20) /// ``` /// This automatically ignores the upper bits of the `user` in case /// they are not clean, as well as keep the `keccak256` under 32-bytes. /// /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`. uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Overwrite the roles directly without authorization guard. function _setRoles(address user, uint256 roles) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Store the new value. sstore(keccak256(0x0c, 0x20), roles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles) } } /// @dev Updates the roles directly without authorization guard. /// If `on` is true, each set bit of `roles` will be turned on, /// otherwise, each set bit of `roles` will be turned off. function _updateRoles(address user, uint256 roles, bool on) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) let roleSlot := keccak256(0x0c, 0x20) // Load the current value. let current := sload(roleSlot) // Compute the updated roles if `on` is true. let updated := or(current, roles) // Compute the updated roles if `on` is false. // Use `and` to compute the intersection of `current` and `roles`, // `xor` it with `current` to flip the bits in the intersection. if iszero(on) { updated := xor(current, and(current, roles)) } // Then, store the new value. sstore(roleSlot, updated) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated) } } /// @dev Grants the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn on. function _grantRoles(address user, uint256 roles) internal virtual { _updateRoles(user, roles, true); } /// @dev Removes the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn off. function _removeRoles(address user, uint256 roles) internal virtual { _updateRoles(user, roles, false); } /// @dev Throws if the sender does not have any of the `roles`. function _checkRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Throws if the sender is not the owner, /// and does not have any of the `roles`. /// Checks for ownership first, then lazily checks for roles. function _checkOwnerOrRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } } /// @dev Throws if the sender does not have any of the `roles`, /// and is not the owner. /// Checks for roles first, then lazily checks for ownership. function _checkRolesOrOwner(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } } /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } { // We don't need to mask the values of `ordinals`, as Solidity // cleans dirty upper bits when storing variables into memory. roles := or(shl(mload(add(ordinals, i)), 1), roles) } } } /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) { /// @solidity memory-safe-assembly assembly { // Grab the pointer to the free memory. ordinals := mload(0x40) let ptr := add(ordinals, 0x20) let o := 0 // The absence of lookup tables, De Bruijn, etc., here is intentional for // smaller bytecode, as this function is not meant to be called on-chain. for { let t := roles } 1 {} { mstore(ptr, o) // `shr` 5 is equivalent to multiplying by 0x20. // Push back into the ordinals array if the bit is set. ptr := add(ptr, shl(5, and(t, 1))) o := add(o, 1) t := shr(o, roles) if iszero(t) { break } } // Store the length of `ordinals`. mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20)))) // Allocate the memory. mstore(0x40, ptr) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to grant `user` `roles`. /// If the `user` already has a role, then it will be an no-op for the role. function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { _grantRoles(user, roles); } /// @dev Allows the owner to remove `user` `roles`. /// If the `user` does not have a role, then it will be an no-op for the role. function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { _removeRoles(user, roles); } /// @dev Allow the caller to remove their own roles. /// If the caller does not have a role, then it will be an no-op for the role. function renounceRoles(uint256 roles) public payable virtual { _removeRoles(msg.sender, roles); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the roles of `user`. function rolesOf(address user) public view virtual returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Load the stored value. roles := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns whether `user` has any of `roles`. function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles != 0; } /// @dev Returns whether `user` has all of `roles`. function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles == roles; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by an account with `roles`. modifier onlyRoles(uint256 roles) virtual { _checkRoles(roles); _; } /// @dev Marks a function as only callable by the owner or by an account /// with `roles`. Checks for ownership first, then lazily checks for roles. modifier onlyOwnerOrRoles(uint256 roles) virtual { _checkOwnerOrRoles(roles); _; } /// @dev Marks a function as only callable by an account with `roles` /// or the owner. Checks for roles first, then lazily checks for ownership. modifier onlyRolesOrOwner(uint256 roles) virtual { _checkRolesOrOwner(roles); _; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ROLE CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // IYKYK uint256 internal constant _ROLE_0 = 1 << 0; uint256 internal constant _ROLE_1 = 1 << 1; uint256 internal constant _ROLE_2 = 1 << 2; uint256 internal constant _ROLE_3 = 1 << 3; uint256 internal constant _ROLE_4 = 1 << 4; uint256 internal constant _ROLE_5 = 1 << 5; uint256 internal constant _ROLE_6 = 1 << 6; uint256 internal constant _ROLE_7 = 1 << 7; uint256 internal constant _ROLE_8 = 1 << 8; uint256 internal constant _ROLE_9 = 1 << 9; uint256 internal constant _ROLE_10 = 1 << 10; uint256 internal constant _ROLE_11 = 1 << 11; uint256 internal constant _ROLE_12 = 1 << 12; uint256 internal constant _ROLE_13 = 1 << 13; uint256 internal constant _ROLE_14 = 1 << 14; uint256 internal constant _ROLE_15 = 1 << 15; uint256 internal constant _ROLE_16 = 1 << 16; uint256 internal constant _ROLE_17 = 1 << 17; uint256 internal constant _ROLE_18 = 1 << 18; uint256 internal constant _ROLE_19 = 1 << 19; uint256 internal constant _ROLE_20 = 1 << 20; uint256 internal constant _ROLE_21 = 1 << 21; uint256 internal constant _ROLE_22 = 1 << 22; uint256 internal constant _ROLE_23 = 1 << 23; uint256 internal constant _ROLE_24 = 1 << 24; uint256 internal constant _ROLE_25 = 1 << 25; uint256 internal constant _ROLE_26 = 1 << 26; uint256 internal constant _ROLE_27 = 1 << 27; uint256 internal constant _ROLE_28 = 1 << 28; uint256 internal constant _ROLE_29 = 1 << 29; uint256 internal constant _ROLE_30 = 1 << 30; uint256 internal constant _ROLE_31 = 1 << 31; uint256 internal constant _ROLE_32 = 1 << 32; uint256 internal constant _ROLE_33 = 1 << 33; uint256 internal constant _ROLE_34 = 1 << 34; uint256 internal constant _ROLE_35 = 1 << 35; uint256 internal constant _ROLE_36 = 1 << 36; uint256 internal constant _ROLE_37 = 1 << 37; uint256 internal constant _ROLE_38 = 1 << 38; uint256 internal constant _ROLE_39 = 1 << 39; uint256 internal constant _ROLE_40 = 1 << 40; uint256 internal constant _ROLE_41 = 1 << 41; uint256 internal constant _ROLE_42 = 1 << 42; uint256 internal constant _ROLE_43 = 1 << 43; uint256 internal constant _ROLE_44 = 1 << 44; uint256 internal constant _ROLE_45 = 1 << 45; uint256 internal constant _ROLE_46 = 1 << 46; uint256 internal constant _ROLE_47 = 1 << 47; uint256 internal constant _ROLE_48 = 1 << 48; uint256 internal constant _ROLE_49 = 1 << 49; uint256 internal constant _ROLE_50 = 1 << 50; uint256 internal constant _ROLE_51 = 1 << 51; uint256 internal constant _ROLE_52 = 1 << 52; uint256 internal constant _ROLE_53 = 1 << 53; uint256 internal constant _ROLE_54 = 1 << 54; uint256 internal constant _ROLE_55 = 1 << 55; uint256 internal constant _ROLE_56 = 1 << 56; uint256 internal constant _ROLE_57 = 1 << 57; uint256 internal constant _ROLE_58 = 1 << 58; uint256 internal constant _ROLE_59 = 1 << 59; uint256 internal constant _ROLE_60 = 1 << 60; uint256 internal constant _ROLE_61 = 1 << 61; uint256 internal constant _ROLE_62 = 1 << 62; uint256 internal constant _ROLE_63 = 1 << 63; uint256 internal constant _ROLE_64 = 1 << 64; uint256 internal constant _ROLE_65 = 1 << 65; uint256 internal constant _ROLE_66 = 1 << 66; uint256 internal constant _ROLE_67 = 1 << 67; uint256 internal constant _ROLE_68 = 1 << 68; uint256 internal constant _ROLE_69 = 1 << 69; uint256 internal constant _ROLE_70 = 1 << 70; uint256 internal constant _ROLE_71 = 1 << 71; uint256 internal constant _ROLE_72 = 1 << 72; uint256 internal constant _ROLE_73 = 1 << 73; uint256 internal constant _ROLE_74 = 1 << 74; uint256 internal constant _ROLE_75 = 1 << 75; uint256 internal constant _ROLE_76 = 1 << 76; uint256 internal constant _ROLE_77 = 1 << 77; uint256 internal constant _ROLE_78 = 1 << 78; uint256 internal constant _ROLE_79 = 1 << 79; uint256 internal constant _ROLE_80 = 1 << 80; uint256 internal constant _ROLE_81 = 1 << 81; uint256 internal constant _ROLE_82 = 1 << 82; uint256 internal constant _ROLE_83 = 1 << 83; uint256 internal constant _ROLE_84 = 1 << 84; uint256 internal constant _ROLE_85 = 1 << 85; uint256 internal constant _ROLE_86 = 1 << 86; uint256 internal constant _ROLE_87 = 1 << 87; uint256 internal constant _ROLE_88 = 1 << 88; uint256 internal constant _ROLE_89 = 1 << 89; uint256 internal constant _ROLE_90 = 1 << 90; uint256 internal constant _ROLE_91 = 1 << 91; uint256 internal constant _ROLE_92 = 1 << 92; uint256 internal constant _ROLE_93 = 1 << 93; uint256 internal constant _ROLE_94 = 1 << 94; uint256 internal constant _ROLE_95 = 1 << 95; uint256 internal constant _ROLE_96 = 1 << 96; uint256 internal constant _ROLE_97 = 1 << 97; uint256 internal constant _ROLE_98 = 1 << 98; uint256 internal constant _ROLE_99 = 1 << 99; uint256 internal constant _ROLE_100 = 1 << 100; uint256 internal constant _ROLE_101 = 1 << 101; uint256 internal constant _ROLE_102 = 1 << 102; uint256 internal constant _ROLE_103 = 1 << 103; uint256 internal constant _ROLE_104 = 1 << 104; uint256 internal constant _ROLE_105 = 1 << 105; uint256 internal constant _ROLE_106 = 1 << 106; uint256 internal constant _ROLE_107 = 1 << 107; uint256 internal constant _ROLE_108 = 1 << 108; uint256 internal constant _ROLE_109 = 1 << 109; uint256 internal constant _ROLE_110 = 1 << 110; uint256 internal constant _ROLE_111 = 1 << 111; uint256 internal constant _ROLE_112 = 1 << 112; uint256 internal constant _ROLE_113 = 1 << 113; uint256 internal constant _ROLE_114 = 1 << 114; uint256 internal constant _ROLE_115 = 1 << 115; uint256 internal constant _ROLE_116 = 1 << 116; uint256 internal constant _ROLE_117 = 1 << 117; uint256 internal constant _ROLE_118 = 1 << 118; uint256 internal constant _ROLE_119 = 1 << 119; uint256 internal constant _ROLE_120 = 1 << 120; uint256 internal constant _ROLE_121 = 1 << 121; uint256 internal constant _ROLE_122 = 1 << 122; uint256 internal constant _ROLE_123 = 1 << 123; uint256 internal constant _ROLE_124 = 1 << 124; uint256 internal constant _ROLE_125 = 1 << 125; uint256 internal constant _ROLE_126 = 1 << 126; uint256 internal constant _ROLE_127 = 1 << 127; uint256 internal constant _ROLE_128 = 1 << 128; uint256 internal constant _ROLE_129 = 1 << 129; uint256 internal constant _ROLE_130 = 1 << 130; uint256 internal constant _ROLE_131 = 1 << 131; uint256 internal constant _ROLE_132 = 1 << 132; uint256 internal constant _ROLE_133 = 1 << 133; uint256 internal constant _ROLE_134 = 1 << 134; uint256 internal constant _ROLE_135 = 1 << 135; uint256 internal constant _ROLE_136 = 1 << 136; uint256 internal constant _ROLE_137 = 1 << 137; uint256 internal constant _ROLE_138 = 1 << 138; uint256 internal constant _ROLE_139 = 1 << 139; uint256 internal constant _ROLE_140 = 1 << 140; uint256 internal constant _ROLE_141 = 1 << 141; uint256 internal constant _ROLE_142 = 1 << 142; uint256 internal constant _ROLE_143 = 1 << 143; uint256 internal constant _ROLE_144 = 1 << 144; uint256 internal constant _ROLE_145 = 1 << 145; uint256 internal constant _ROLE_146 = 1 << 146; uint256 internal constant _ROLE_147 = 1 << 147; uint256 internal constant _ROLE_148 = 1 << 148; uint256 internal constant _ROLE_149 = 1 << 149; uint256 internal constant _ROLE_150 = 1 << 150; uint256 internal constant _ROLE_151 = 1 << 151; uint256 internal constant _ROLE_152 = 1 << 152; uint256 internal constant _ROLE_153 = 1 << 153; uint256 internal constant _ROLE_154 = 1 << 154; uint256 internal constant _ROLE_155 = 1 << 155; uint256 internal constant _ROLE_156 = 1 << 156; uint256 internal constant _ROLE_157 = 1 << 157; uint256 internal constant _ROLE_158 = 1 << 158; uint256 internal constant _ROLE_159 = 1 << 159; uint256 internal constant _ROLE_160 = 1 << 160; uint256 internal constant _ROLE_161 = 1 << 161; uint256 internal constant _ROLE_162 = 1 << 162; uint256 internal constant _ROLE_163 = 1 << 163; uint256 internal constant _ROLE_164 = 1 << 164; uint256 internal constant _ROLE_165 = 1 << 165; uint256 internal constant _ROLE_166 = 1 << 166; uint256 internal constant _ROLE_167 = 1 << 167; uint256 internal constant _ROLE_168 = 1 << 168; uint256 internal constant _ROLE_169 = 1 << 169; uint256 internal constant _ROLE_170 = 1 << 170; uint256 internal constant _ROLE_171 = 1 << 171; uint256 internal constant _ROLE_172 = 1 << 172; uint256 internal constant _ROLE_173 = 1 << 173; uint256 internal constant _ROLE_174 = 1 << 174; uint256 internal constant _ROLE_175 = 1 << 175; uint256 internal constant _ROLE_176 = 1 << 176; uint256 internal constant _ROLE_177 = 1 << 177; uint256 internal constant _ROLE_178 = 1 << 178; uint256 internal constant _ROLE_179 = 1 << 179; uint256 internal constant _ROLE_180 = 1 << 180; uint256 internal constant _ROLE_181 = 1 << 181; uint256 internal constant _ROLE_182 = 1 << 182; uint256 internal constant _ROLE_183 = 1 << 183; uint256 internal constant _ROLE_184 = 1 << 184; uint256 internal constant _ROLE_185 = 1 << 185; uint256 internal constant _ROLE_186 = 1 << 186; uint256 internal constant _ROLE_187 = 1 << 187; uint256 internal constant _ROLE_188 = 1 << 188; uint256 internal constant _ROLE_189 = 1 << 189; uint256 internal constant _ROLE_190 = 1 << 190; uint256 internal constant _ROLE_191 = 1 << 191; uint256 internal constant _ROLE_192 = 1 << 192; uint256 internal constant _ROLE_193 = 1 << 193; uint256 internal constant _ROLE_194 = 1 << 194; uint256 internal constant _ROLE_195 = 1 << 195; uint256 internal constant _ROLE_196 = 1 << 196; uint256 internal constant _ROLE_197 = 1 << 197; uint256 internal constant _ROLE_198 = 1 << 198; uint256 internal constant _ROLE_199 = 1 << 199; uint256 internal constant _ROLE_200 = 1 << 200; uint256 internal constant _ROLE_201 = 1 << 201; uint256 internal constant _ROLE_202 = 1 << 202; uint256 internal constant _ROLE_203 = 1 << 203; uint256 internal constant _ROLE_204 = 1 << 204; uint256 internal constant _ROLE_205 = 1 << 205; uint256 internal constant _ROLE_206 = 1 << 206; uint256 internal constant _ROLE_207 = 1 << 207; uint256 internal constant _ROLE_208 = 1 << 208; uint256 internal constant _ROLE_209 = 1 << 209; uint256 internal constant _ROLE_210 = 1 << 210; uint256 internal constant _ROLE_211 = 1 << 211; uint256 internal constant _ROLE_212 = 1 << 212; uint256 internal constant _ROLE_213 = 1 << 213; uint256 internal constant _ROLE_214 = 1 << 214; uint256 internal constant _ROLE_215 = 1 << 215; uint256 internal constant _ROLE_216 = 1 << 216; uint256 internal constant _ROLE_217 = 1 << 217; uint256 internal constant _ROLE_218 = 1 << 218; uint256 internal constant _ROLE_219 = 1 << 219; uint256 internal constant _ROLE_220 = 1 << 220; uint256 internal constant _ROLE_221 = 1 << 221; uint256 internal constant _ROLE_222 = 1 << 222; uint256 internal constant _ROLE_223 = 1 << 223; uint256 internal constant _ROLE_224 = 1 << 224; uint256 internal constant _ROLE_225 = 1 << 225; uint256 internal constant _ROLE_226 = 1 << 226; uint256 internal constant _ROLE_227 = 1 << 227; uint256 internal constant _ROLE_228 = 1 << 228; uint256 internal constant _ROLE_229 = 1 << 229; uint256 internal constant _ROLE_230 = 1 << 230; uint256 internal constant _ROLE_231 = 1 << 231; uint256 internal constant _ROLE_232 = 1 << 232; uint256 internal constant _ROLE_233 = 1 << 233; uint256 internal constant _ROLE_234 = 1 << 234; uint256 internal constant _ROLE_235 = 1 << 235; uint256 internal constant _ROLE_236 = 1 << 236; uint256 internal constant _ROLE_237 = 1 << 237; uint256 internal constant _ROLE_238 = 1 << 238; uint256 internal constant _ROLE_239 = 1 << 239; uint256 internal constant _ROLE_240 = 1 << 240; uint256 internal constant _ROLE_241 = 1 << 241; uint256 internal constant _ROLE_242 = 1 << 242; uint256 internal constant _ROLE_243 = 1 << 243; uint256 internal constant _ROLE_244 = 1 << 244; uint256 internal constant _ROLE_245 = 1 << 245; uint256 internal constant _ROLE_246 = 1 << 246; uint256 internal constant _ROLE_247 = 1 << 247; uint256 internal constant _ROLE_248 = 1 << 248; uint256 internal constant _ROLE_249 = 1 << 249; uint256 internal constant _ROLE_250 = 1 << 250; uint256 internal constant _ROLE_251 = 1 << 251; uint256 internal constant _ROLE_252 = 1 << 252; uint256 internal constant _ROLE_253 = 1 << 253; uint256 internal constant _ROLE_254 = 1 << 254; uint256 internal constant _ROLE_255 = 1 << 255; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple ERC2981 NFT Royalty Standard implementation. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC2981.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/common/ERC2981.sol) abstract contract ERC2981 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The royalty fee numerator exceeds the fee denominator. error RoyaltyOverflow(); /// @dev The royalty receiver cannot be the zero address. error RoyaltyReceiverIsZeroAddress(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The default royalty info is given by: /// ``` /// let packed := sload(_ERC2981_MASTER_SLOT_SEED) /// let receiver := shr(96, packed) /// let royaltyFraction := xor(packed, shl(96, receiver)) /// ``` /// /// The per token royalty info is given by. /// ``` /// mstore(0x00, tokenId) /// mstore(0x20, _ERC2981_MASTER_SLOT_SEED) /// let packed := sload(keccak256(0x00, 0x40)) /// let receiver := shr(96, packed) /// let royaltyFraction := xor(packed, shl(96, receiver)) /// ``` uint256 private constant _ERC2981_MASTER_SLOT_SEED = 0xaa4ec00224afccfdb7; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC2981 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Checks that `_feeDenominator` is non-zero. constructor() { require(_feeDenominator() != 0, "Fee denominator cannot be zero."); } /// @dev Returns the denominator for the royalty amount. /// Defaults to 10000, which represents fees in basis points. /// Override this function to return a custom amount if needed. function _feeDenominator() internal pure virtual returns (uint96) { return 10000; } /// @dev Returns true if this contract implements the interface defined by `interfaceId`. /// See: https://eips.ethereum.org/EIPS/eip-165 /// This function call must use less than 30000 gas. function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { let s := shr(224, interfaceId) // ERC165: 0x01ffc9a7, ERC2981: 0x2a55205a. result := or(eq(s, 0x01ffc9a7), eq(s, 0x2a55205a)) } } /// @dev Returns the `receiver` and `royaltyAmount` for `tokenId` sold at `salePrice`. function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual returns (address receiver, uint256 royaltyAmount) { uint256 feeDenominator = _feeDenominator(); /// @solidity memory-safe-assembly assembly { mstore(0x00, tokenId) mstore(0x20, _ERC2981_MASTER_SLOT_SEED) let packed := sload(keccak256(0x00, 0x40)) receiver := shr(96, packed) if iszero(receiver) { packed := sload(mload(0x20)) receiver := shr(96, packed) } let x := salePrice let y := xor(packed, shl(96, receiver)) // `feeNumerator`. // Overflow check, equivalent to `require(y == 0 || x <= type(uint256).max / y)`. // Out-of-gas revert. Should not be triggered in practice, but included for safety. returndatacopy(returndatasize(), returndatasize(), mul(y, gt(x, div(not(0), y)))) royaltyAmount := div(mul(x, y), feeDenominator) } } /// @dev Sets the default royalty `receiver` and `feeNumerator`. /// /// Requirements: /// - `receiver` must not be the zero address. /// - `feeNumerator` must not be greater than the fee denominator. function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual { uint256 feeDenominator = _feeDenominator(); /// @solidity memory-safe-assembly assembly { feeNumerator := shr(160, shl(160, feeNumerator)) if gt(feeNumerator, feeDenominator) { mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`. revert(0x1c, 0x04) } let packed := shl(96, receiver) if iszero(packed) { mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`. revert(0x1c, 0x04) } sstore(_ERC2981_MASTER_SLOT_SEED, or(packed, feeNumerator)) } } /// @dev Sets the default royalty `receiver` and `feeNumerator` to zero. function _deleteDefaultRoyalty() internal virtual { /// @solidity memory-safe-assembly assembly { sstore(_ERC2981_MASTER_SLOT_SEED, 0) } } /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId`. /// /// Requirements: /// - `receiver` must not be the zero address. /// - `feeNumerator` must not be greater than the fee denominator. function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual { uint256 feeDenominator = _feeDenominator(); /// @solidity memory-safe-assembly assembly { feeNumerator := shr(160, shl(160, feeNumerator)) if gt(feeNumerator, feeDenominator) { mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`. revert(0x1c, 0x04) } let packed := shl(96, receiver) if iszero(packed) { mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`. revert(0x1c, 0x04) } mstore(0x00, tokenId) mstore(0x20, _ERC2981_MASTER_SLOT_SEED) sstore(keccak256(0x00, 0x40), or(packed, feeNumerator)) } } /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId` to zero. function _resetTokenRoyalty(uint256 tokenId) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x00, tokenId) mstore(0x20, _ERC2981_MASTER_SLOT_SEED) sstore(keccak256(0x00, 0x40), 0) } } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; interface IERC5192 { /// @notice Emitted when the locking status is changed to locked. /// @dev If a token is minted and the status is locked, this event should be emitted. /// @param tokenId The identifier for a token. event Locked(uint256 tokenId); /// @notice Emitted when the locking status is changed to unlocked. /// @dev If a token is minted and the status is unlocked, this event should be emitted. /// @param tokenId The identifier for a token. event Unlocked(uint256 tokenId); /// @notice Returns the locking status of an Soulbound Token /// @dev SBTs assigned to zero address are considered invalid, and queries /// about them do throw. /// @param tokenId The identifier for an SBT. function locked(uint256 tokenId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {CollectionConfig} from "../structs/CollectionConfig.sol"; interface IBeacon { function read( address contractAddress, uint256[] calldata tokenIds, uint32[] calldata eids, address refundRecipient, uint128 callbackGasLimit ) external payable returns (bytes32); function send( uint32 eid, address collection, uint256[] calldata tokenIds, address beneficiary, address refundRecipient, uint128 supplementalGasLimit ) external payable; function getSendOptions(address collectionAddress, uint256[] calldata tokenIds) external view returns (bytes memory); function getReadOptions(address collectionAddress, uint256[] calldata tokenIds, uint128 callbackGasLimit) external view returns (bytes memory); function quoteSend(uint32 eid, address collectionAddress, uint256[] calldata tokenIds, bytes calldata _options) external view returns (uint256 nativeFee, uint256 lzTokenFee); function quoteRead( address collectionAddress, uint256[] calldata tokenIds, uint32[] calldata eids, bytes calldata _options ) external view returns (uint256 nativeFee, uint256 lzTokenFee); function registerCollection( address _shadowAddress, uint32 _baseCollectionChainId, address _baseCollectionAddress, uint32 _baseCollectionEid, uint32 _baseCollectionPerNftOwnershipUpdateCost ) external; function shadowToBase(address _shadowAddress) external view returns (address); function baseToShadow(address _baseAddress) external view returns (address); function collectionConfigs(address _shadowAddress) external view returns (CollectionConfig memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface ICreatorToken { event TransferValidatorUpdated(address oldValidator, address newValidator); function getTransferValidationFunction() external view returns (bytes4 functionSignature, bool isViewFunction); function getTransferValidator() external view returns (address validator); function setTransferValidator(address validator) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface ITransferValidator { function validateTransfer(address caller, address from, address to) external view; function validateTransfer(address caller, address from, address to, uint256 tokenId) external view; function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface IShadowCallbackReceiver { function executeCallback(bytes32 guid) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. bytes32 internal constant _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (bool guard) {} /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(ownerSlot) { mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. revert(0x1c, 0x04) } // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } else { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(_OWNER_SLOT, newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) } } else { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(_OWNER_SLOT))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.28; struct CollectionConfig { uint32 baseCollectionChainId; uint32 baseCollectionEid; uint32 baseCollectionPerNftOwnershipUpdateCost; // cost of transferring an NFT from the base collection. If 0, default of 100_000 is used address shadowAddress; // Local shadow address for this chain }
{ "remappings": [ "solady/=lib/solady/src/", "@layerzerolabs/=node_modules/@layerzerolabs/", "@openzeppelin/=node_modules/@openzeppelin/", "solidity-bytes-utils/=node_modules/solidity-bytes-utils/", "forge-std/=lib/forge-std/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, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"CallerNotBeacon","type":"error"},{"inputs":[],"name":"FnSelectorNotRecognized","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"RoyaltyOverflow","type":"error"},{"inputs":[],"name":"RoyaltyReceiverIsZeroAddress","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TokenLocked","type":"error"},{"inputs":[],"name":"TokenNotLocked","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","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":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"metadataRenderer","type":"address"}],"name":"MetadataRendererSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"ShadowModeEnabled","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":false,"internalType":"address","name":"oldValidator","type":"address"},{"indexed":false,"internalType":"address","name":"newValidator","type":"address"}],"name":"TransferValidatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Unlocked","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"BEACON_CONTRACT_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"METADATA_MANAGER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_MANAGER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHADOW_MODE_MANAGER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_VALIDATOR_MANAGER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"}],"name":"callbacks","outputs":[{"internalType":"address","name":"callbackTarget","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"enableShadowMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"}],"name":"executeCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransferValidationFunction","outputs":[{"internalType":"bytes4","name":"functionSignature","type":"bytes4"},{"internalType":"bool","name":"isViewFunction","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTransferValidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"string","name":"_collectionName","type":"string"},{"internalType":"string","name":"_collectionSymbol","type":"string"},{"internalType":"string","name":"_baseTokenURI","type":"string"},{"internalType":"address","name":"_metadataRenderer","type":"address"},{"internalType":"address","name":"_transferValidator","type":"address"},{"internalType":"uint96","name":"_royaltyFeeNumerator","type":"uint96"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataRenderer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint32[]","name":"eids","type":"uint32[]"}],"name":"read","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint32[]","name":"eids","type":"uint32[]"},{"internalType":"uint128","name":"callbackGasLimit","type":"uint128"}],"name":"readWithCallback","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","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":"payable","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":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"address","name":"refundRecipient","type":"address"},{"internalType":"uint128","name":"supplementalGasLimit","type":"uint128"}],"name":"send","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_metadataRenderer","type":"address"}],"name":"setMetadataRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint96","name":"feeNumerator","type":"uint96"}],"name":"setRoyaltyInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transferValidator_","type":"address"}],"name":"setTransferValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shadowModeEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"transferValidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405234801561000f575f5ffd5b506d87c6dbadc090d39bc10316f20658608052604080518082018252600a808252695130306e69636f726e7360b01b60208084019190915283518085018552918252697130306e69636f726e7360b01b8282015283519081019093525f8084526100a79373e880c31c8118103ab49b604e281283e489a69780939291737df5c32827f0fa448392ce58231ce4f378ce56e590806100ac565b610317565b60026100b8858261025d565b5060036100c5878261025d565b5060046100d2868261025d565b50600580546001600160a01b0319166001600160a01b0385161790556100f787610124565b5f80546001600160a01b0319166001600160a01b03841617905561011b878261017c565b50505050505050565b638b78c6d81980541561013e57630dc149f05f526004601cfd5b6001600160a01b03909116801560ff1b8117909155805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6001600160601b03166127108082111561019d5763350a88b35f526004601cfd5b8260601b806101b35763b4457eaa5f526004601cfd5b90911768aa4ec00224afccfdb7555050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806101ed57607f821691505b60208210810361020b57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561025857805f5260205f20601f840160051c810160208510156102365750805b601f840160051c820191505b81811015610255575f8155600101610242565b50505b505050565b81516001600160401b03811115610276576102766101c5565b61028a8161028484546101d9565b84610211565b6020601f8211600181146102bc575f83156102a55750848201515b5f19600385901b1c1916600184901b178455610255565b5f84815260208120601f198516915b828110156102eb57878501518255602094850194600190920191016102cb565b508482101561030857868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b608051612ba26103675f395f818161030a0152818161094801528181610ca701528181610d1c01528181610e09015281816111a5015281816118e80152818161195501526120220152612ba25ff3fe6080604052600436106102e3575f3560e01c8063529470e81161018f578063a9fc664e116100db578063e985e9c511610094578063f2fde38b1161006e578063f2fde38b1461089a578063f7dec9b4146108ad578063fd4fe8a8146108c0578063fee81cf4146108df576102e3565b8063e985e9c51461083c578063ed06167814610870578063f04e283e14610887576102e3565b8063a9fc664e1461078e578063b1dae618146107ad578063b45a3c0e146107cc578063b88d4fde146107eb578063c87b56dd146107fe578063e0df5b6f1461081d576102e3565b8063715018a6116101485780638da5cb5b116101225780638da5cb5b1461071757806395d89b411461072f57806399a2557a14610743578063a22cb4651461076f576102e3565b8063715018a6146106d05780637347ebb9146106d85780638528e8d3146106f8576102e3565b8063529470e81461063b57806354d1f13d1461065457806358456fb51461065c5780636352211e14610673578063703199701461069257806370a08231146106b1576102e3565b80631c10893f1161024e57806329e38d5e1161020757806342842e0e116101e157806342842e0e146105c157806342966c68146105d45780634a4ee7b1146105f3578063514e62fc14610606576102e3565b806329e38d5e146105345780632a55205a146105525780632de9480714610590576102e3565b80631c10893f146104895780631cd64df41461049c5780631de26e8e146104d157806321f36509146104e557806323b872dd14610519578063256929621461052c576102e3565b8063098144d4116102a0578063098144d4146103ea5780630a212100146104065780630c756a78146104195780630d705df61461043c578063156c2c3214610463578063183a4f6e14610476576102e3565b806301ddc8bf146102f957806301ffc9a71461034957806302fa7c471461037857806306fdde0314610397578063081812fc146103b8578063095ea7b3146103d7575b3480156102ee575f5ffd5b506102f7610910565b005b348015610304575f5ffd5b5061032c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b348015610354575f5ffd5b50610368610363366004612150565b6109e3565b6040519015158152602001610340565b348015610383575f5ffd5b506102f76103923660046121ac565b610a63565b3480156103a2575f5ffd5b506103ab610a80565b60405161034091906121df565b3480156103c3575f5ffd5b5061032c6103d2366004612214565b610b10565b6102f76103e536600461222b565b610b4b565b3480156103f5575f5ffd5b505f546001600160a01b031661032c565b6102f76104143660046122c5565b610b5a565b348015610424575f5ffd5b5061042e61200081565b604051908152602001610340565b348015610447575f5ffd5b506040805163657711f560e11b81526001602082015201610340565b61042e610471366004612347565b610daa565b6102f7610484366004612214565b610dc1565b6102f761049736600461222b565b610dcb565b3480156104a7575f5ffd5b506103686104b636600461222b565b638b78c6d8600c9081525f9290925260209091205481161490565b3480156104dc575f5ffd5b506102f7610ddd565b3480156104f0575f5ffd5b5061032c6104ff366004612214565b60016020525f90815260409020546001600160a01b031681565b6102f76105273660046123b1565b610dff565b6102f7610e94565b34801561053f575f5ffd5b505f5461032c906001600160a01b031681565b34801561055d575f5ffd5b5061057161056c3660046123ef565b610ee0565b604080516001600160a01b039093168352602083019190915201610340565b34801561059b575f5ffd5b5061042e6105aa36600461240f565b638b78c6d8600c9081525f91909152602090205490565b6102f76105cf3660046123b1565b610f33565b3480156105df575f5ffd5b506102f76105ee366004612214565b610f5f565b6102f761060136600461222b565b610f80565b348015610611575f5ffd5b5061036861062036600461222b565b638b78c6d8600c9081525f9290925260209091205416151590565b348015610646575f5ffd5b5061042e6504000000000081565b6102f7610f92565b348015610667575f5ffd5b5061042e600160581b81565b34801561067e575f5ffd5b5061032c61068d366004612214565b610fcb565b34801561069d575f5ffd5b5060055461032c906001600160a01b031681565b3480156106bc575f5ffd5b5061042e6106cb36600461240f565b610fee565b6102f7611026565b3480156106e3575f5ffd5b5060055461036890600160a01b900460ff1681565b348015610703575f5ffd5b506102f76107123660046124e6565b611039565b348015610722575f5ffd5b50638b78c6d8195461032c565b34801561073a575f5ffd5b506103ab6110b1565b34801561074e575f5ffd5b5061076261075d3660046125b4565b6110c0565b60405161034091906125e6565b34801561077a575f5ffd5b506102f7610789366004612628565b6110d7565b348015610799575f5ffd5b506102f76107a836600461240f565b61112a565b3480156107b8575f5ffd5b506102f76107c7366004612214565b61119a565b3480156107d7575f5ffd5b506103686107e6366004612214565b611277565b6102f76107f9366004612663565b611281565b348015610809575f5ffd5b506103ab610818366004612214565b6112db565b348015610828575f5ffd5b506102f76108373660046126fa565b611439565b348015610847575f5ffd5b50610368610856366004612733565b601c52670a5a2e7a000000006008525f526030600c205490565b34801561087b575f5ffd5b5061042e600160451b81565b6102f761089536600461240f565b611494565b6102f76108a836600461240f565b6114ce565b61042e6108bb36600461275f565b6114f4565b3480156108cb575f5ffd5b506102f76108da36600461240f565b61152c565b3480156108ea575f5ffd5b5061042e6108f936600461240f565b63389a75e1600c9081525f91909152602090205490565b5f3560e01c6340c10f198190036109325761092f6004356024356115b8565b50565b806392772833036109ca57336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109865760405163db70dad160e01b815260040160405180910390fd5b6044356024355f5b828110156109c4576109bc6109b66109a78360206127f1565b6109b2906064612808565b3590565b8361164c565b60010161098e565b50505050565b604051631e085ca760e11b815260040160405180910390fd5b5f6001600160e01b03198216635a2d1e0760e11b1480610a245750610a24826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b80610a425750632a55205a60e083901c9081146301ffc9a791909114175b80610a5d57506001600160e01b03198216632483248360e11b145b92915050565b600160581b610a71816116fc565b610a7b8383611720565b505050565b606060038054610a8f9061281b565b80601f0160208091040260200160405190810160405280929190818152602001828054610abb9061281b565b8015610b065780601f10610add57610100808354040283529160200191610b06565b820191905f5260205f20905b815481529060010190602001808311610ae957829003601f168201915b5050505050905090565b5f815f52673ec412a9852d173d60c11b601c5260205f2082018201805460601b610b415763ceea21b65f526004601cfd5b6001015492915050565b610b56338383611769565b5050565b5f5b84811015610c8f57610b8633878784818110610b7a57610b7a612853565b90506020020135611803565b610ba35760405163096dcfe360e31b815260040160405180910390fd5b610bc4868683818110610bb857610bb8612853565b9050602002013561186c565b15610be257604051635a8181f760e01b815260040160405180910390fd5b600554600160a01b900460ff16610c1457610c14868683818110610c0857610c08612853565b9050602002013561188e565b610c36868683818110610c2957610c29612853565b905060200201355f611898565b7f032bc66be43dbccb7487781d168eb7bda224628a3b2c3388bdf69b532a3a1611868683818110610c6957610c69612853565b90506020020135604051610c7f91815260200190565b60405180910390a1600101610b5c565b5060405163f174883560e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063f174883590602401602060405180830381865afa158015610cf4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d189190612867565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c9df81673489848a8a8a8a8a6040518963ffffffff1660e01b8152600401610d7397969594939291906128b2565b5f604051808303818588803b158015610d8a575f5ffd5b505af1158015610d9c573d5f5f3e3d5ffd5b505050505050505050505050565b5f610db8858585855f6118c7565b95945050505050565b61092f33826119f8565b610dd3611a03565b610b568282611a1d565b612000610de9816116fc565b506005805460ff60a01b1916600160a01b179055565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610e89576001610e56825f818152673ec412a9852d173d60c11b601c52602090208101015460a01c90565b6001600160601b031603610e7d576040516305b166a360e41b815260040160405180910390fd5b610a7b5f848484611a29565b610a7b838383611b23565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f82815268aa4ec00224afccfdb76020526040812054606081901c91906127109083610f13576020515490508060601c93505b606084901b18845f19829004811182023d3d3e9396930204935090915050565b610f3e838383610dff565b813b15610a7b57610a7b83838360405180602001604052805f815250611c14565b610f688161186c565b15610f765761092f8161188e565b61092f3382611c95565b610f88611a03565b610b5682826119f8565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b5f610fd582611d53565b905080610fe95763ceea21b65f526004601cfd5b919050565b5f8161100157638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b61102e611a03565b6110375f611d7a565b565b6002611045858261295a565b506003611052878261295a565b50600461105f868261295a565b50600580546001600160a01b0319166001600160a01b03851617905561108487611dc0565b5f80546001600160a01b0319166001600160a01b0384161790556110a88782611720565b50505050505050565b606060048054610a8f9061281b565b60606110cd848484611e18565b90505b9392505050565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b600160451b611138816116fc565b5f80546001600160a01b038481166001600160a01b031983168117909355604080519190921680825260208201939093527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac91015b60405180910390a1505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111e35760405163db70dad160e01b815260040160405180910390fd5b5f818152600160205260409020546001600160a01b031680611203575050565b5f828152600160205260409081902080546001600160a01b03191690555163163b5cc360e31b8152600481018390526001600160a01b0382169063b1dae618906024015f604051808303815f87803b15801561125d575f5ffd5b505af115801561126f573d5f5f3e3d5ffd5b505050505050565b5f610a5d8261186c565b61128c858585610dff565b833b156112d4576112d485858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611c1492505050565b5050505050565b6060611304825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6113215760405163677510db60e11b815260040160405180910390fd5b6005546001600160a01b03161561139f5760055460405163c87b56dd60e01b8152600481018490526001600160a01b039091169063c87b56dd906024015f60405180830381865afa158015611378573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a5d9190810190612a14565b610a5d600280546113af9061281b565b80601f01602080910402602001604051908101604052809291908181526020018280546113db9061281b565b80156114265780601f106113fd57610100808354040283529160200191611426565b820191905f5260205f20905b81548152906001019060200180831161140957829003601f168201915b505050505061143484611f7a565b611fbc565b65040000000000611449816116fc565b6002611455838261295a565b50604080515f81525f1960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c91015b60405180910390a15050565b61149c611a03565b63389a75e1600c52805f526020600c2080544211156114c257636f5e88185f526004601cfd5b5f905561092f81611d7a565b6114d6611a03565b8060601b6114eb57637448fbae5f526004601cfd5b61092f81611d7a565b5f5f61150387878787876118c7565b5f81815260016020526040902080546001600160a01b0319163317905591505095945050505050565b6504000000000061153c816116fc565b600580546001600160a01b0319166001600160a01b0384169081179091556040517faa56f403bb636768e9e1fec12c1968db814767675df6bc4a8c2488fc094c6a03905f90a2604080515f81525f1960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9101611488565b6115c35f8383612017565b8160601b60601c9150805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b156115fe5763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff811684026116325767ea553b3401336cea841560021b526004601cfd5b905580825f5f516020612b4d5f395f51905f528138a45050565b6116558261186c565b156116e3575f61166483611d53565b90506001600160a01b0381166116835761167e82846115b8565b6116a8565b806001600160a01b0316826001600160a01b0316146116a8576116a85f828486611a29565b6116b3836001611898565b6040518381527ff27b6ce5b2f5e68ddb2fd95a8a909d4ecf1daaac270935fff052feacb24f18429060200161118d565b6040516305b166a360e41b815260040160405180910390fd5b638b78c6d8600c52335f52806020600c20541661092f576382b429005f526004601cfd5b6001600160601b0316612710808211156117415763350a88b35f526004601cfd5b8260601b806117575763b4457eaa5f526004601cfd5b90911768aa4ec00224afccfdb7555050565b5f1960601c82811692508381169350815f5283673ec412a9852d173d60c11b17601c5260205f2082018201805482169150816117ac5763ceea21b65f526004601cfd5b8185148515176117d057815f526030600c20546117d057634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a450505050565b5f8181526001600160a01b03928316673ec412a9852d173d60c11b8117601c526020909120820182018054919360019216806118465763ceea21b65f526004601cfd5b80851461186457805f526030600c2054611864578160010154851492505b505092915050565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c1590565b61092f5f82611c95565b815f52673ec412a9852d173d60c11b601c5260205f208201820180548060a01c831860a01b8118825550505050565b60405163f174883560e01b81523060048201525f9081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f174883590602401602060405180830381865afa15801561192d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119519190612867565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fe084c8f34838a8a8a8a338b6040518963ffffffff1660e01b81526004016119ac9796959493929190612a88565b60206040518083038185885af11580156119c8573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906119ed9190612b1d565b979650505050505050565b610b5682825f6120f9565b638b78c6d819543314611037576382b429005f526004601cfd5b610b56828260016120f9565b611a34838383612017565b5f8181526001600160a01b03948516673ec412a9852d173d60c11b8117601c526020909120820182018054919594851694938416938216918286148302611a8a5767ceea21b6a1148100831560021b526004601cfd5b855f528160010154925082871486881417871517611ab9576030600c2054611ab957634b6e7f185f526004601cfd5b8215611ac6575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff81168402611b075767ea553b3401336cea841560021b526004601cfd5b90558082845f516020612b4d5f395f51905f525f38a450505050565b611b2e838383612017565b5f818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b039485169493841693811691908286148302611b7f5767ceea21b6a1148100831560021b526004601cfd5b855f528160010154925082331486331417611bab576030600c2054611bab57634b6e7f185f526004601cfd5b8215611bb8575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff81168402611bf95767ea553b3401336cea841560021b526004601cfd5b90558082845f516020612b4d5f395f51905f525f38a4505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611c5b578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1611c7c573d15611c7c573d5f843e3d83fd5b508060e01b82511461126f5763d1a57ed65f526004601cfd5b5f611c9f82610fcb565b9050611cac815f84612017565b505f8181526001600160a01b03928316673ec412a9852d173d60c11b8117601c526020909120820182018054919382169182611cef5763ceea21b65f526004601cfd5b825f52816001015480861484871417861517611d1c576030600c2054611d1c57634b6e7f185f526004601cfd5b8015611d29575f83600101555b5082189055601c600c2080545f19019055815f825f516020612b4d5f395f51905f528238a4505050565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b638b78c6d819805415611dda57630dc149f05f526004601cfd5b6001600160a01b03909116801560ff1b8117909155805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b60605f611e2485610fee565b90505f80826001600160401b03811115611e4057611e4061242a565b604051908082528060200260200182016040528015611e69578160200160208202803683370190505b509050855b858111611edf57876001600160a01b0316611e8882611d53565b6001600160a01b031603611ecd5780828481518110611ea957611ea9612853565b602090810291909101015260018084019385900301611ecd575092506110d0915050565b80611ed781612b34565b915050611e6e565b505f826001600160401b03811115611ef957611ef961242a565b604051908082528060200260200182016040528015611f22578160200160208202803683370190505b5090505f5b83811015611f6e57828181518110611f4157611f41612853565b6020026020010151828281518110611f5b57611f5b612853565b6020908102919091010152600101611f27565b50979650505050505050565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480611f93575050819003601f19909101908152919050565b6040518251601f19906020810182165b8581015184820152820180611fcc575083518184018360208301165b8681015182820152840180611fe85750505f910183810160208101929092528352604090810190525092915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a7b576120508161186c565b8061206257506001600160a01b038316155b156120805760405163db70dad160e01b815260040160405180910390fd5b5f546001600160a01b031615610a7b575f5460405163657711f560e11b81523360048201526001600160a01b0385811660248301528481166044830152606482018490529091169063caee23ea906084015f6040518083038186803b1580156120e7575f5ffd5b505afa1580156110a8573d5f5f3e3d5ffd5b638b78c6d8600c52825f526020600c2080548381178361211a575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f5fa3505050505050565b5f60208284031215612160575f5ffd5b81356001600160e01b0319811681146110d0575f5ffd5b6001600160a01b038116811461092f575f5ffd5b8035610fe981612177565b80356001600160601b0381168114610fe9575f5ffd5b5f5f604083850312156121bd575f5ffd5b82356121c881612177565b91506121d660208401612196565b90509250929050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215612224575f5ffd5b5035919050565b5f5f6040838503121561223c575f5ffd5b823561224781612177565b946020939093013593505050565b803563ffffffff81168114610fe9575f5ffd5b5f5f83601f840112612278575f5ffd5b5081356001600160401b0381111561228e575f5ffd5b6020830191508360208260051b85010111156122a8575f5ffd5b9250929050565b80356001600160801b0381168114610fe9575f5ffd5b5f5f5f5f5f5f60a087890312156122da575f5ffd5b6122e387612255565b955060208701356001600160401b038111156122fd575f5ffd5b61230989828a01612268565b909650945050604087013561231d81612177565b9250606087013561232d81612177565b915061233b608088016122af565b90509295509295509295565b5f5f5f5f6040858703121561235a575f5ffd5b84356001600160401b0381111561236f575f5ffd5b61237b87828801612268565b90955093505060208501356001600160401b03811115612399575f5ffd5b6123a587828801612268565b95989497509550505050565b5f5f5f606084860312156123c3575f5ffd5b83356123ce81612177565b925060208401356123de81612177565b929592945050506040919091013590565b5f5f60408385031215612400575f5ffd5b50508035926020909101359150565b5f6020828403121561241f575f5ffd5b81356110d081612177565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156124665761246661242a565b604052919050565b5f6001600160401b038211156124865761248661242a565b50601f01601f191660200190565b5f82601f8301126124a3575f5ffd5b81356124b66124b18261246e565b61243e565b8181528460208386010111156124ca575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f5f5f5f5f5f5f60e0888a0312156124fc575f5ffd5b873561250781612177565b965060208801356001600160401b03811115612521575f5ffd5b61252d8a828b01612494565b96505060408801356001600160401b03811115612548575f5ffd5b6125548a828b01612494565b95505060608801356001600160401b0381111561256f575f5ffd5b61257b8a828b01612494565b94505061258a6080890161218b565b925061259860a0890161218b565b91506125a660c08901612196565b905092959891949750929550565b5f5f5f606084860312156125c6575f5ffd5b83356125d181612177565b95602085013595506040909401359392505050565b602080825282518282018190525f918401906040840190835b8181101561261d5783518352602093840193909201916001016125ff565b509095945050505050565b5f5f60408385031215612639575f5ffd5b823561264481612177565b915060208301358015158114612658575f5ffd5b809150509250929050565b5f5f5f5f5f60808688031215612677575f5ffd5b853561268281612177565b9450602086013561269281612177565b93506040860135925060608601356001600160401b038111156126b3575f5ffd5b8601601f810188136126c3575f5ffd5b80356001600160401b038111156126d8575f5ffd5b8860208284010111156126e9575f5ffd5b959894975092955050506020019190565b5f6020828403121561270a575f5ffd5b81356001600160401b0381111561271f575f5ffd5b61272b84828501612494565b949350505050565b5f5f60408385031215612744575f5ffd5b823561274f81612177565b9150602083013561265881612177565b5f5f5f5f5f60608688031215612773575f5ffd5b85356001600160401b03811115612788575f5ffd5b61279488828901612268565b90965094505060208601356001600160401b038111156127b2575f5ffd5b6127be88828901612268565b90945092506127d19050604087016122af565b90509295509295909350565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610a5d57610a5d6127dd565b80820180821115610a5d57610a5d6127dd565b600181811c9082168061282f57607f821691505b60208210810361284d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215612877575f5ffd5b81516110d081612177565b8183525f6001600160fb1b03831115612899575f5ffd5b8260051b80836020870137939093016020019392505050565b63ffffffff881681526001600160a01b038716602082015260c0604082018190525f906128e29083018789612882565b6001600160a01b0395861660608401529390941660808201526001600160801b039190911660a09091015295945050505050565b601f821115610a7b57805f5260205f20601f840160051c8101602085101561293b5750805b601f840160051c820191505b818110156112d4575f8155600101612947565b81516001600160401b038111156129735761297361242a565b61298781612981845461281b565b84612916565b6020601f8211600181146129b9575f83156129a25750848201515b5f19600385901b1c1916600184901b1784556112d4565b5f84815260208120601f198516915b828110156129e857878501518255602094850194600190920191016129c8565b5084821015612a0557868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f60208284031215612a24575f5ffd5b81516001600160401b03811115612a39575f5ffd5b8201601f81018413612a49575f5ffd5b8051612a576124b18261246e565b818152856020838501011115612a6b575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b6001600160a01b038816815260a0602082018190525f90612aac908301888a612882565b828103604084015285815286906020015f5b87811015612ae95763ffffffff612ad484612255565b16825260209283019290910190600101612abe565b506001600160a01b03861660608501526001600160801b03851660808501529150612b119050565b98975050505050505050565b5f60208284031215612b2d575f5ffd5b5051919050565b5f60018201612b4557612b456127dd565b506001019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220219ca2c4e151f52f36eefa7285d729169795728e4108e9694586b17e38e4803064736f6c634300081c0033
Deployed Bytecode
0x6080604052600436106102e3575f3560e01c8063529470e81161018f578063a9fc664e116100db578063e985e9c511610094578063f2fde38b1161006e578063f2fde38b1461089a578063f7dec9b4146108ad578063fd4fe8a8146108c0578063fee81cf4146108df576102e3565b8063e985e9c51461083c578063ed06167814610870578063f04e283e14610887576102e3565b8063a9fc664e1461078e578063b1dae618146107ad578063b45a3c0e146107cc578063b88d4fde146107eb578063c87b56dd146107fe578063e0df5b6f1461081d576102e3565b8063715018a6116101485780638da5cb5b116101225780638da5cb5b1461071757806395d89b411461072f57806399a2557a14610743578063a22cb4651461076f576102e3565b8063715018a6146106d05780637347ebb9146106d85780638528e8d3146106f8576102e3565b8063529470e81461063b57806354d1f13d1461065457806358456fb51461065c5780636352211e14610673578063703199701461069257806370a08231146106b1576102e3565b80631c10893f1161024e57806329e38d5e1161020757806342842e0e116101e157806342842e0e146105c157806342966c68146105d45780634a4ee7b1146105f3578063514e62fc14610606576102e3565b806329e38d5e146105345780632a55205a146105525780632de9480714610590576102e3565b80631c10893f146104895780631cd64df41461049c5780631de26e8e146104d157806321f36509146104e557806323b872dd14610519578063256929621461052c576102e3565b8063098144d4116102a0578063098144d4146103ea5780630a212100146104065780630c756a78146104195780630d705df61461043c578063156c2c3214610463578063183a4f6e14610476576102e3565b806301ddc8bf146102f957806301ffc9a71461034957806302fa7c471461037857806306fdde0314610397578063081812fc146103b8578063095ea7b3146103d7575b3480156102ee575f5ffd5b506102f7610910565b005b348015610304575f5ffd5b5061032c7f00000000000000000000000000000000000087c6dbadc090d39bc10316f2065881565b6040516001600160a01b0390911681526020015b60405180910390f35b348015610354575f5ffd5b50610368610363366004612150565b6109e3565b6040519015158152602001610340565b348015610383575f5ffd5b506102f76103923660046121ac565b610a63565b3480156103a2575f5ffd5b506103ab610a80565b60405161034091906121df565b3480156103c3575f5ffd5b5061032c6103d2366004612214565b610b10565b6102f76103e536600461222b565b610b4b565b3480156103f5575f5ffd5b505f546001600160a01b031661032c565b6102f76104143660046122c5565b610b5a565b348015610424575f5ffd5b5061042e61200081565b604051908152602001610340565b348015610447575f5ffd5b506040805163657711f560e11b81526001602082015201610340565b61042e610471366004612347565b610daa565b6102f7610484366004612214565b610dc1565b6102f761049736600461222b565b610dcb565b3480156104a7575f5ffd5b506103686104b636600461222b565b638b78c6d8600c9081525f9290925260209091205481161490565b3480156104dc575f5ffd5b506102f7610ddd565b3480156104f0575f5ffd5b5061032c6104ff366004612214565b60016020525f90815260409020546001600160a01b031681565b6102f76105273660046123b1565b610dff565b6102f7610e94565b34801561053f575f5ffd5b505f5461032c906001600160a01b031681565b34801561055d575f5ffd5b5061057161056c3660046123ef565b610ee0565b604080516001600160a01b039093168352602083019190915201610340565b34801561059b575f5ffd5b5061042e6105aa36600461240f565b638b78c6d8600c9081525f91909152602090205490565b6102f76105cf3660046123b1565b610f33565b3480156105df575f5ffd5b506102f76105ee366004612214565b610f5f565b6102f761060136600461222b565b610f80565b348015610611575f5ffd5b5061036861062036600461222b565b638b78c6d8600c9081525f9290925260209091205416151590565b348015610646575f5ffd5b5061042e6504000000000081565b6102f7610f92565b348015610667575f5ffd5b5061042e600160581b81565b34801561067e575f5ffd5b5061032c61068d366004612214565b610fcb565b34801561069d575f5ffd5b5060055461032c906001600160a01b031681565b3480156106bc575f5ffd5b5061042e6106cb36600461240f565b610fee565b6102f7611026565b3480156106e3575f5ffd5b5060055461036890600160a01b900460ff1681565b348015610703575f5ffd5b506102f76107123660046124e6565b611039565b348015610722575f5ffd5b50638b78c6d8195461032c565b34801561073a575f5ffd5b506103ab6110b1565b34801561074e575f5ffd5b5061076261075d3660046125b4565b6110c0565b60405161034091906125e6565b34801561077a575f5ffd5b506102f7610789366004612628565b6110d7565b348015610799575f5ffd5b506102f76107a836600461240f565b61112a565b3480156107b8575f5ffd5b506102f76107c7366004612214565b61119a565b3480156107d7575f5ffd5b506103686107e6366004612214565b611277565b6102f76107f9366004612663565b611281565b348015610809575f5ffd5b506103ab610818366004612214565b6112db565b348015610828575f5ffd5b506102f76108373660046126fa565b611439565b348015610847575f5ffd5b50610368610856366004612733565b601c52670a5a2e7a000000006008525f526030600c205490565b34801561087b575f5ffd5b5061042e600160451b81565b6102f761089536600461240f565b611494565b6102f76108a836600461240f565b6114ce565b61042e6108bb36600461275f565b6114f4565b3480156108cb575f5ffd5b506102f76108da36600461240f565b61152c565b3480156108ea575f5ffd5b5061042e6108f936600461240f565b63389a75e1600c9081525f91909152602090205490565b5f3560e01c6340c10f198190036109325761092f6004356024356115b8565b50565b806392772833036109ca57336001600160a01b037f00000000000000000000000000000000000087c6dbadc090d39bc10316f2065816146109865760405163db70dad160e01b815260040160405180910390fd5b6044356024355f5b828110156109c4576109bc6109b66109a78360206127f1565b6109b2906064612808565b3590565b8361164c565b60010161098e565b50505050565b604051631e085ca760e11b815260040160405180910390fd5b5f6001600160e01b03198216635a2d1e0760e11b1480610a245750610a24826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b80610a425750632a55205a60e083901c9081146301ffc9a791909114175b80610a5d57506001600160e01b03198216632483248360e11b145b92915050565b600160581b610a71816116fc565b610a7b8383611720565b505050565b606060038054610a8f9061281b565b80601f0160208091040260200160405190810160405280929190818152602001828054610abb9061281b565b8015610b065780601f10610add57610100808354040283529160200191610b06565b820191905f5260205f20905b815481529060010190602001808311610ae957829003601f168201915b5050505050905090565b5f815f52673ec412a9852d173d60c11b601c5260205f2082018201805460601b610b415763ceea21b65f526004601cfd5b6001015492915050565b610b56338383611769565b5050565b5f5b84811015610c8f57610b8633878784818110610b7a57610b7a612853565b90506020020135611803565b610ba35760405163096dcfe360e31b815260040160405180910390fd5b610bc4868683818110610bb857610bb8612853565b9050602002013561186c565b15610be257604051635a8181f760e01b815260040160405180910390fd5b600554600160a01b900460ff16610c1457610c14868683818110610c0857610c08612853565b9050602002013561188e565b610c36868683818110610c2957610c29612853565b905060200201355f611898565b7f032bc66be43dbccb7487781d168eb7bda224628a3b2c3388bdf69b532a3a1611868683818110610c6957610c69612853565b90506020020135604051610c7f91815260200190565b60405180910390a1600101610b5c565b5060405163f174883560e01b81523060048201525f907f00000000000000000000000000000000000087c6dbadc090d39bc10316f206586001600160a01b03169063f174883590602401602060405180830381865afa158015610cf4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d189190612867565b90507f00000000000000000000000000000000000087c6dbadc090d39bc10316f206586001600160a01b031663c9df81673489848a8a8a8a8a6040518963ffffffff1660e01b8152600401610d7397969594939291906128b2565b5f604051808303818588803b158015610d8a575f5ffd5b505af1158015610d9c573d5f5f3e3d5ffd5b505050505050505050505050565b5f610db8858585855f6118c7565b95945050505050565b61092f33826119f8565b610dd3611a03565b610b568282611a1d565b612000610de9816116fc565b506005805460ff60a01b1916600160a01b179055565b6001600160a01b037f00000000000000000000000000000000000087c6dbadc090d39bc10316f20658163303610e89576001610e56825f818152673ec412a9852d173d60c11b601c52602090208101015460a01c90565b6001600160601b031603610e7d576040516305b166a360e41b815260040160405180910390fd5b610a7b5f848484611a29565b610a7b838383611b23565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f82815268aa4ec00224afccfdb76020526040812054606081901c91906127109083610f13576020515490508060601c93505b606084901b18845f19829004811182023d3d3e9396930204935090915050565b610f3e838383610dff565b813b15610a7b57610a7b83838360405180602001604052805f815250611c14565b610f688161186c565b15610f765761092f8161188e565b61092f3382611c95565b610f88611a03565b610b5682826119f8565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b5f610fd582611d53565b905080610fe95763ceea21b65f526004601cfd5b919050565b5f8161100157638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b61102e611a03565b6110375f611d7a565b565b6002611045858261295a565b506003611052878261295a565b50600461105f868261295a565b50600580546001600160a01b0319166001600160a01b03851617905561108487611dc0565b5f80546001600160a01b0319166001600160a01b0384161790556110a88782611720565b50505050505050565b606060048054610a8f9061281b565b60606110cd848484611e18565b90505b9392505050565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b600160451b611138816116fc565b5f80546001600160a01b038481166001600160a01b031983168117909355604080519190921680825260208201939093527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac91015b60405180910390a1505050565b336001600160a01b037f00000000000000000000000000000000000087c6dbadc090d39bc10316f2065816146111e35760405163db70dad160e01b815260040160405180910390fd5b5f818152600160205260409020546001600160a01b031680611203575050565b5f828152600160205260409081902080546001600160a01b03191690555163163b5cc360e31b8152600481018390526001600160a01b0382169063b1dae618906024015f604051808303815f87803b15801561125d575f5ffd5b505af115801561126f573d5f5f3e3d5ffd5b505050505050565b5f610a5d8261186c565b61128c858585610dff565b833b156112d4576112d485858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611c1492505050565b5050505050565b6060611304825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6113215760405163677510db60e11b815260040160405180910390fd5b6005546001600160a01b03161561139f5760055460405163c87b56dd60e01b8152600481018490526001600160a01b039091169063c87b56dd906024015f60405180830381865afa158015611378573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a5d9190810190612a14565b610a5d600280546113af9061281b565b80601f01602080910402602001604051908101604052809291908181526020018280546113db9061281b565b80156114265780601f106113fd57610100808354040283529160200191611426565b820191905f5260205f20905b81548152906001019060200180831161140957829003601f168201915b505050505061143484611f7a565b611fbc565b65040000000000611449816116fc565b6002611455838261295a565b50604080515f81525f1960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c91015b60405180910390a15050565b61149c611a03565b63389a75e1600c52805f526020600c2080544211156114c257636f5e88185f526004601cfd5b5f905561092f81611d7a565b6114d6611a03565b8060601b6114eb57637448fbae5f526004601cfd5b61092f81611d7a565b5f5f61150387878787876118c7565b5f81815260016020526040902080546001600160a01b0319163317905591505095945050505050565b6504000000000061153c816116fc565b600580546001600160a01b0319166001600160a01b0384169081179091556040517faa56f403bb636768e9e1fec12c1968db814767675df6bc4a8c2488fc094c6a03905f90a2604080515f81525f1960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9101611488565b6115c35f8383612017565b8160601b60601c9150805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b156115fe5763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff811684026116325767ea553b3401336cea841560021b526004601cfd5b905580825f5f516020612b4d5f395f51905f528138a45050565b6116558261186c565b156116e3575f61166483611d53565b90506001600160a01b0381166116835761167e82846115b8565b6116a8565b806001600160a01b0316826001600160a01b0316146116a8576116a85f828486611a29565b6116b3836001611898565b6040518381527ff27b6ce5b2f5e68ddb2fd95a8a909d4ecf1daaac270935fff052feacb24f18429060200161118d565b6040516305b166a360e41b815260040160405180910390fd5b638b78c6d8600c52335f52806020600c20541661092f576382b429005f526004601cfd5b6001600160601b0316612710808211156117415763350a88b35f526004601cfd5b8260601b806117575763b4457eaa5f526004601cfd5b90911768aa4ec00224afccfdb7555050565b5f1960601c82811692508381169350815f5283673ec412a9852d173d60c11b17601c5260205f2082018201805482169150816117ac5763ceea21b65f526004601cfd5b8185148515176117d057815f526030600c20546117d057634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a450505050565b5f8181526001600160a01b03928316673ec412a9852d173d60c11b8117601c526020909120820182018054919360019216806118465763ceea21b65f526004601cfd5b80851461186457805f526030600c2054611864578160010154851492505b505092915050565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c1590565b61092f5f82611c95565b815f52673ec412a9852d173d60c11b601c5260205f208201820180548060a01c831860a01b8118825550505050565b60405163f174883560e01b81523060048201525f9081906001600160a01b037f00000000000000000000000000000000000087c6dbadc090d39bc10316f20658169063f174883590602401602060405180830381865afa15801561192d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119519190612867565b90507f00000000000000000000000000000000000087c6dbadc090d39bc10316f206586001600160a01b031663fe084c8f34838a8a8a8a338b6040518963ffffffff1660e01b81526004016119ac9796959493929190612a88565b60206040518083038185885af11580156119c8573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906119ed9190612b1d565b979650505050505050565b610b5682825f6120f9565b638b78c6d819543314611037576382b429005f526004601cfd5b610b56828260016120f9565b611a34838383612017565b5f8181526001600160a01b03948516673ec412a9852d173d60c11b8117601c526020909120820182018054919594851694938416938216918286148302611a8a5767ceea21b6a1148100831560021b526004601cfd5b855f528160010154925082871486881417871517611ab9576030600c2054611ab957634b6e7f185f526004601cfd5b8215611ac6575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff81168402611b075767ea553b3401336cea841560021b526004601cfd5b90558082845f516020612b4d5f395f51905f525f38a450505050565b611b2e838383612017565b5f818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b039485169493841693811691908286148302611b7f5767ceea21b6a1148100831560021b526004601cfd5b855f528160010154925082331486331417611bab576030600c2054611bab57634b6e7f185f526004601cfd5b8215611bb8575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff81168402611bf95767ea553b3401336cea841560021b526004601cfd5b90558082845f516020612b4d5f395f51905f525f38a4505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611c5b578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1611c7c573d15611c7c573d5f843e3d83fd5b508060e01b82511461126f5763d1a57ed65f526004601cfd5b5f611c9f82610fcb565b9050611cac815f84612017565b505f8181526001600160a01b03928316673ec412a9852d173d60c11b8117601c526020909120820182018054919382169182611cef5763ceea21b65f526004601cfd5b825f52816001015480861484871417861517611d1c576030600c2054611d1c57634b6e7f185f526004601cfd5b8015611d29575f83600101555b5082189055601c600c2080545f19019055815f825f516020612b4d5f395f51905f528238a4505050565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b638b78c6d819805415611dda57630dc149f05f526004601cfd5b6001600160a01b03909116801560ff1b8117909155805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b60605f611e2485610fee565b90505f80826001600160401b03811115611e4057611e4061242a565b604051908082528060200260200182016040528015611e69578160200160208202803683370190505b509050855b858111611edf57876001600160a01b0316611e8882611d53565b6001600160a01b031603611ecd5780828481518110611ea957611ea9612853565b602090810291909101015260018084019385900301611ecd575092506110d0915050565b80611ed781612b34565b915050611e6e565b505f826001600160401b03811115611ef957611ef961242a565b604051908082528060200260200182016040528015611f22578160200160208202803683370190505b5090505f5b83811015611f6e57828181518110611f4157611f41612853565b6020026020010151828281518110611f5b57611f5b612853565b6020908102919091010152600101611f27565b50979650505050505050565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480611f93575050819003601f19909101908152919050565b6040518251601f19906020810182165b8581015184820152820180611fcc575083518184018360208301165b8681015182820152840180611fe85750505f910183810160208101929092528352604090810190525092915050565b336001600160a01b037f00000000000000000000000000000000000087c6dbadc090d39bc10316f206581614610a7b576120508161186c565b8061206257506001600160a01b038316155b156120805760405163db70dad160e01b815260040160405180910390fd5b5f546001600160a01b031615610a7b575f5460405163657711f560e11b81523360048201526001600160a01b0385811660248301528481166044830152606482018490529091169063caee23ea906084015f6040518083038186803b1580156120e7575f5ffd5b505afa1580156110a8573d5f5f3e3d5ffd5b638b78c6d8600c52825f526020600c2080548381178361211a575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f5fa3505050505050565b5f60208284031215612160575f5ffd5b81356001600160e01b0319811681146110d0575f5ffd5b6001600160a01b038116811461092f575f5ffd5b8035610fe981612177565b80356001600160601b0381168114610fe9575f5ffd5b5f5f604083850312156121bd575f5ffd5b82356121c881612177565b91506121d660208401612196565b90509250929050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215612224575f5ffd5b5035919050565b5f5f6040838503121561223c575f5ffd5b823561224781612177565b946020939093013593505050565b803563ffffffff81168114610fe9575f5ffd5b5f5f83601f840112612278575f5ffd5b5081356001600160401b0381111561228e575f5ffd5b6020830191508360208260051b85010111156122a8575f5ffd5b9250929050565b80356001600160801b0381168114610fe9575f5ffd5b5f5f5f5f5f5f60a087890312156122da575f5ffd5b6122e387612255565b955060208701356001600160401b038111156122fd575f5ffd5b61230989828a01612268565b909650945050604087013561231d81612177565b9250606087013561232d81612177565b915061233b608088016122af565b90509295509295509295565b5f5f5f5f6040858703121561235a575f5ffd5b84356001600160401b0381111561236f575f5ffd5b61237b87828801612268565b90955093505060208501356001600160401b03811115612399575f5ffd5b6123a587828801612268565b95989497509550505050565b5f5f5f606084860312156123c3575f5ffd5b83356123ce81612177565b925060208401356123de81612177565b929592945050506040919091013590565b5f5f60408385031215612400575f5ffd5b50508035926020909101359150565b5f6020828403121561241f575f5ffd5b81356110d081612177565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156124665761246661242a565b604052919050565b5f6001600160401b038211156124865761248661242a565b50601f01601f191660200190565b5f82601f8301126124a3575f5ffd5b81356124b66124b18261246e565b61243e565b8181528460208386010111156124ca575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f5f5f5f5f5f5f60e0888a0312156124fc575f5ffd5b873561250781612177565b965060208801356001600160401b03811115612521575f5ffd5b61252d8a828b01612494565b96505060408801356001600160401b03811115612548575f5ffd5b6125548a828b01612494565b95505060608801356001600160401b0381111561256f575f5ffd5b61257b8a828b01612494565b94505061258a6080890161218b565b925061259860a0890161218b565b91506125a660c08901612196565b905092959891949750929550565b5f5f5f606084860312156125c6575f5ffd5b83356125d181612177565b95602085013595506040909401359392505050565b602080825282518282018190525f918401906040840190835b8181101561261d5783518352602093840193909201916001016125ff565b509095945050505050565b5f5f60408385031215612639575f5ffd5b823561264481612177565b915060208301358015158114612658575f5ffd5b809150509250929050565b5f5f5f5f5f60808688031215612677575f5ffd5b853561268281612177565b9450602086013561269281612177565b93506040860135925060608601356001600160401b038111156126b3575f5ffd5b8601601f810188136126c3575f5ffd5b80356001600160401b038111156126d8575f5ffd5b8860208284010111156126e9575f5ffd5b959894975092955050506020019190565b5f6020828403121561270a575f5ffd5b81356001600160401b0381111561271f575f5ffd5b61272b84828501612494565b949350505050565b5f5f60408385031215612744575f5ffd5b823561274f81612177565b9150602083013561265881612177565b5f5f5f5f5f60608688031215612773575f5ffd5b85356001600160401b03811115612788575f5ffd5b61279488828901612268565b90965094505060208601356001600160401b038111156127b2575f5ffd5b6127be88828901612268565b90945092506127d19050604087016122af565b90509295509295909350565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610a5d57610a5d6127dd565b80820180821115610a5d57610a5d6127dd565b600181811c9082168061282f57607f821691505b60208210810361284d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215612877575f5ffd5b81516110d081612177565b8183525f6001600160fb1b03831115612899575f5ffd5b8260051b80836020870137939093016020019392505050565b63ffffffff881681526001600160a01b038716602082015260c0604082018190525f906128e29083018789612882565b6001600160a01b0395861660608401529390941660808201526001600160801b039190911660a09091015295945050505050565b601f821115610a7b57805f5260205f20601f840160051c8101602085101561293b5750805b601f840160051c820191505b818110156112d4575f8155600101612947565b81516001600160401b038111156129735761297361242a565b61298781612981845461281b565b84612916565b6020601f8211600181146129b9575f83156129a25750848201515b5f19600385901b1c1916600184901b1784556112d4565b5f84815260208120601f198516915b828110156129e857878501518255602094850194600190920191016129c8565b5084821015612a0557868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f60208284031215612a24575f5ffd5b81516001600160401b03811115612a39575f5ffd5b8201601f81018413612a49575f5ffd5b8051612a576124b18261246e565b818152856020838501011115612a6b575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b6001600160a01b038816815260a0602082018190525f90612aac908301888a612882565b828103604084015285815286906020015f5b87811015612ae95763ffffffff612ad484612255565b16825260209283019290910190600101612abe565b506001600160a01b03861660608501526001600160801b03851660808501529150612b119050565b98975050505050505050565b5f60208284031215612b2d575f5ffd5b5051919050565b5f60018201612b4557612b456127dd565b506001019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220219ca2c4e151f52f36eefa7285d729169795728e4108e9694586b17e38e4803064736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.