Overview
APE Balance
APE Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
9049947 | 11 days ago | Contract Creation | 0 APE |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ShadowFactory
Compiler Version
v0.8.28+commit.7893614a
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.28; import {Ownable} from "solady/auth/Ownable.sol"; import {NFTShadow} from "../NFTShadow.sol"; import {IBeacon} from "../interfaces/IBeacon.sol"; contract ShadowFactory is Ownable { event ShadowDeployed(address indexed shadow, address indexed initialOwner); address public immutable BEACON_CONTRACT_ADDRESS; constructor(address _beacon) { BEACON_CONTRACT_ADDRESS = _beacon; _initializeOwner(tx.origin); } /** * @notice Deploys a new shadow contract and registers it with the Beacon. * @param _initialOwner The owner of the Shadow Contract. * @param _collectionName The name of the collection. * @param _collectionSymbol The symbol of the collection. * @param _baseTokenURI The base token URI (unused if _metadataRenderer is set) * @param _metadataRenderer The address of the metadata renderer (address(0) to derive from baseTokenURI) * @param _transferValidator The address of the ERC721C transfer validator. If 0, no transfer validation will be performed. * @param _royaltyFeeNumerator The ERC2981 royalty fee numerator. * @param _baseCollectionAddress The address of the base collection. * @param _baseCollectionChainId The chain ID of the base collection. * @param _baseCollectionEid The endpoint ID of the base collection. * @param _baseCollectionPerNftOwnershipUpdateCost The cost of updating the ownership of an NFT. Set to 0 to use Beacon's default. */ function deployAndRegister( address _initialOwner, string memory _collectionName, string memory _collectionSymbol, string memory _baseTokenURI, address _metadataRenderer, address _transferValidator, uint96 _royaltyFeeNumerator, address _baseCollectionAddress, uint32 _baseCollectionChainId, uint32 _baseCollectionEid, uint32 _baseCollectionPerNftOwnershipUpdateCost, bytes32 _salt ) external onlyOwner { NFTShadow _shadow = new NFTShadow{salt: _salt}(BEACON_CONTRACT_ADDRESS); _shadow.initialize( _initialOwner, _collectionName, _collectionSymbol, _baseTokenURI, _metadataRenderer, _transferValidator, _royaltyFeeNumerator ); IBeacon(BEACON_CONTRACT_ADDRESS).registerCollection( address(_shadow), _baseCollectionChainId, _baseCollectionAddress, _baseCollectionEid, _baseCollectionPerNftOwnershipUpdateCost ); emit ShadowDeployed(address(_shadow), _initialOwner); } }
// 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; 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 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 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 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 { 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 { 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 { 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 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 override returns (string memory) { return _name; } /** * @notice Returns the symbol of the collection. * @return _symbol the symbol of the collection. */ function symbol() public view 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; 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; /// @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; 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.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", "abi" ] } }, "evmVersion": "cancun", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_beacon","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"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":"shadow","type":"address"},{"indexed":true,"internalType":"address","name":"initialOwner","type":"address"}],"name":"ShadowDeployed","type":"event"},{"inputs":[],"name":"BEACON_CONTRACT_ADDRESS","outputs":[{"internalType":"address","name":"","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":[{"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"},{"internalType":"address","name":"_baseCollectionAddress","type":"address"},{"internalType":"uint32","name":"_baseCollectionChainId","type":"uint32"},{"internalType":"uint32","name":"_baseCollectionEid","type":"uint32"},{"internalType":"uint32","name":"_baseCollectionPerNftOwnershipUpdateCost","type":"uint32"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"deployAndRegister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","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":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
60a034609b57601f61367138819003918201601f19168301916001600160401b03831184841017609f57808492602094604052833981010312609b57516001600160a01b0381168103609b5760805232638b78c6d81955325f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36040516135bd90816100b4823960805181818161028201526105480152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ddc8bf146105365750806325692962146104ed57806354d1f13d146104a9578063715018a6146104605780638da5cb5b146104345780639a84037a14610143578063f04e283e146100f5578063f2fde38b146100b65763fee81cf414610081575f80fd5b346100b35760203660031901126100b35761009a610577565b9063389a75e1600c5252602080600c2054604051908152f35b80fd5b5060203660031901126100b3576100cb610577565b6100d3610629565b8060601b156100e8576100e590610645565b80f35b637448fbae82526004601cfd5b5060203660031901126100b35761010a610577565b610112610629565b63389a75e1600c528082526020600c20805442116101365790826100e59255610645565b636f5e881883526004601cfd5b503461041c5761018036600319011261041c5761015e610577565b9060243567ffffffffffffffff811161041c5761017f9036906004016105af565b60443567ffffffffffffffff811161041c5761019f9036906004016105af565b9260643567ffffffffffffffff811161041c576101c09036906004016105af565b936084356001600160a01b038116929083900361041c5760a4356001600160a01b0381169081900361041c5760c435936bffffffffffffffffffffffff851680950361041c5760e4356001600160a01b038116929083900361041c57610104359063ffffffff821680920361041c57610124359563ffffffff871680970361041c57610144359563ffffffff871680970361041c5761025d610629565b60405194612f058087019087821067ffffffffffffffff8311176104205761068388397f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316808252966101643591819003602001905ff58015610411576001600160a01b0316998a3b1561041c578a946040519e8f958695638528e8d360e01b8752600160a01b60019003169e8f60048801526024870160e0905260e4870161030d91610605565b86810360031901604488015261032291610605565b85810360031901606487015261033791610605565b92608485015260a484015260c483015203815a5f948591f18015610411576103f7575b87985081979596973b156103f3578560a4928195604051978896879563da383b9760e01b87528c600488015260248701526044860152606485015260848401525af180156103e8576103cf575b50807f058f67f456321f47e4e011c1b382aff3cfe6a6a472dc1b7a548256458862602491a380f35b816103d99161058d565b6103e457825f6103a7565b8280fd5b6040513d84823e3d90fd5b8580fd5b94959650965f6104069161058d565b5f959493879061035a565b6040513d5f823e3d90fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b3461041c575f36600319011261041c57638b78c6d819546040516001600160a01b039091168152602090f35b5f36600319011261041c57610473610629565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35f638b78c6d81955005b5f36600319011261041c5763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b5f36600319011261041c5763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b3461041c575f36600319011261041c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b600435906001600160a01b038216820361041c57565b90601f8019910116810190811067ffffffffffffffff82111761042057604052565b81601f8201121561041c5780359067ffffffffffffffff821161042057604051926105e4601f8401601f19166020018561058d565b8284526020838301011161041c57815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b638b78c6d81954330361063857565b6382b429005f526004601cfd5b60018060a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3638b78c6d8195556fe60a0346100a957601f612f0538819003918201601f19168301916001600160401b038311848410176100ad578084926020946040528339810103126100a957516001600160a01b03811681036100a957608052604051612e4390816100c2823960805181818161036f015281816107ff01528181610a7d01528181611ab00152818161201b015281816123f70152818161294701528181612a3701528181612af60152612b9c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610021575b3461001d5761001b612913565b005b5f80fd5b5f3560e01c806301ddc8bf1461034b57806301ffc9a71461034657806302fa7c471461034157806306fdde031461033c578063081812fc14610337578063095ea7b314610332578063098144d4146102f65780630a2121001461032d5780630c756a78146103285780630d705df614610323578063156c2c321461031e578063183a4f6e146103195780631c10893f146103145780631cd64df41461030f5780631de26e8e1461030a57806321f365091461030557806323b872dd1461030057806325692962146102fb57806329e38d5e146102f65780632a55205a146102f15780632de94807146102ec57806342842e0e146102e757806342966c68146102e25780634a4ee7b1146102dd578063514e62fc146102d8578063529470e8146102d357806354d1f13d146102ce57806358456fb5146102c95780636352211e146102c457806370319970146102bf57806370a08231146102ba578063715018a6146102b55780637347ebb9146102b05780638528e8d3146102ab5780638da5cb5b146102a657806395d89b41146102a157806399a2557a1461029c578063a22cb46514610297578063a9fc664e14610292578063b1dae6181461028d578063b45a3c0e14610288578063b88d4fde14610283578063c87b56dd1461027e578063e0df5b6f14610279578063e985e9c514610274578063ed0616781461026f578063f04e283e1461026a578063f2fde38b14610265578063f7dec9b414610260578063fd4fe8a81461025b5763fee81cf40361000e576118c4565b611829565b61179d565b61175e565b61170d565b6116ef565b6116a3565b611558565b611539565b6114c8565b61147d565b611467565b6113d9565b611363565b6112f3565b61124e565b611222565b611182565b6110b4565b611067565b611040565b611018565b610fe8565b610fc9565b610f85565b610f65565b610f21565b610ef5565b610e10565b610dd7565b610d9a565b610d0d565b610701565b610cc4565b610cb2565b610c5a565b610c09565b610bc4565b610b5e565b610b46565b610a11565b6109ea565b6109ce565b610781565b610655565b6105f7565b610521565b610495565b61039e565b61035a565b5f91031261001d57565b3461001d575f36600319011261001d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461001d57602036600319011261001d576004356001600160e01b031981169081810361001d576103fd91635a2d1e0760e11b811491821561042e575b8215610412575b508115610401575b5060405190151581529081906020820190565b0390f35b632483248360e11b1490505f6103ea565b90915060e01c6301ffc9a7632a55205a8214911417905f6103e2565b80925060e01c635b5e139f8114906301ffc9a76380ac58cd821491141717916103db565b6001600160a01b0381160361001d57565b6084359061047082610452565b565b60a4359061047082610452565b60c435906001600160601b038216820361001d57565b3461001d57604036600319011261001d576004356104b281610452565b6024356001600160601b038116810361001d57638b78c6d8600c52335f52600160581b6020600c205416156104ea5761001b9161226d565b6382b429005f526004601cfd5b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b3461001d575f36600319011261001d576040515f600354610541816118fa565b80845290600181169081156105d35750600114610575575b6103fd83610569818503826110ed565b604051918291826104f7565b60035f9081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b8082106105b957509091508101602001610569610559565b9192600181602092548385880101520191019092916105a1565b60ff191660208086019190915291151560051b840190910191506105699050610559565b3461001d57602036600319011261001d576004355f818152673ec412a9852d173d60c11b601c5260209020810101805460601b1561064857600101546040516001600160a01b039091168152602090f35b63ceea21b65f526004601cfd5b604036600319011261001d5760043561066d81610452565b6024355f818152673ec412a9852d173d60c11b3317601c526020902081018101805491926001600160a01b039081169216908115610648578290823314331517156106dd575b600101557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b9050815f526030600c2054156106f45782906106b3565b634b6e7f185f526004601cfd5b3461001d575f36600319011261001d575f546040516001600160a01b039091168152602090f35b6004359063ffffffff8216820361001d57565b9181601f8401121561001d578235916001600160401b03831161001d576020808501948460051b01011161001d57565b608435906001600160801b038216820361001d57565b60a036600319011261001d57610795610728565b6024356001600160401b03811161001d576107b490369060040161073b565b91906044356107c281610452565b606435926107cf84610452565b6107d761076b565b905f5b8681106108c0575060405163f174883560e01b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016939092602084602481885afa93841561088a575f9461088f575b50843b1561001d575f9661086492604051998a988997889763c9df816760e01b895260048901611a54565b039134905af1801561088a5761087657005b806108845f61001b936110ed565b80610350565b611a25565b6108b291945060203d6020116108b9575b6108aa81836110ed565b810190611a10565b925f610839565b503d6108a0565b6108dc6108d86108d1838a896119fb565b35336122b2565b1590565b6109bf576108f46108ee8289886119fb565b35612310565b6109b05760019061090e6108d860055460ff9060a01c1690565b610997575b61094e610921828a896119fb565b355f818152673ec412a9852d173d60c11b601c526020902081010180546001600160a01b03198116189055565b7f032bc66be43dbccb7487781d168eb7bda224628a3b2c3388bdf69b532a3a161161098e61097d838b8a6119fb565b604051903581529081906020820190565b0390a1016107da565b6109ab6109a5828a896119fb565b356125ca565b610913565b635a8181f760e01b5f5260045ffd5b63096dcfe360e31b5f5260045ffd5b3461001d575f36600319011261001d5760206040516120008152f35b3461001d575f36600319011261001d576040805163657711f560e11b815260016020820152f35b604036600319011261001d576004356001600160401b03811161001d57610a3c90369060040161073b565b906024356001600160401b03811161001d57610a5c90369060040161073b565b60405163f174883560e01b8152306004820152909390926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602085602481865afa801561088a575f610add916020978291610b29575b5060405163fe084c8f60e01b815298899788968796339460048901612341565b039134905af1801561088a576020915f91610afc575b50604051908152f35b610b1c9150823d8411610b22575b610b1481836110ed565b810190612332565b5f610af3565b503d610b0a565b610b409150883d8a116108b9576108aa81836110ed565b5f610abd565b602036600319011261001d5761001b600435336129c5565b604036600319011261001d57600435610b7681610452565b60243590610b82612487565b638b78c6d8600c525f526020600c2090815417809155600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3005b3461001d57604036600319011261001d576020610bff600435610be681610452565b602435918291638b78c6d8600c525f526020600c205490565b1614604051908152f35b3461001d575f36600319011261001d57638b78c6d8600c52335f526120006020600c205416156104ea576005805460ff60a01b1916600160a01b179055005b602090600319011261001d5760043590565b3461001d57610c6836610c48565b5f526001602052602060018060a01b0360405f205416604051908152f35b606090600319011261001d57600435610c9e81610452565b90602435610cab81610452565b9060443590565b61001b610cbe36610c86565b91611aab565b5f36600319011261001d5763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b3461001d57604036600319011261001d57600435602435905f5268aa4ec00224afccfdb760205260405f2054908160601c918215610d82575b6103fd908360601b1892835f1904831184023d3d3e6127106040519485940204908360209093929193604081019460018060a01b031681520152565b5068aa4ec00224afccfdb754606081901c9250610d46565b3461001d57602036600319011261001d576020610dcf600435610dbc81610452565b638b78c6d8600c525f526020600c205490565b604051908152f35b610de036610c86565b610ded8183859495611aab565b823b610df557005b61001b9260405192610e086020856110ed565b5f8452612538565b3461001d57602036600319011261001d576004355f818152673ec412a9852d173d60c11b601c5260209020810181015460a01c610e505761001b906125ca565b610e6281610e5d81611c1a565b612a35565b5f818152673ec412a9852d173d60c11b3317601c5260209020810181018054906001600160a01b03821690811561064857815f52806001019283548033148433141733151715610ee3575b905f948492610eda575b50189055601c600c20821981540190555f516020612dee5f395f51905f528280a4005b8590555f610eb7565b906030600c2054156106f45790610ead565b604036600319011261001d5761001b600435610f1081610452565b60243590610f1c612487565b6129c5565b3461001d57604036600319011261001d576020600435610f4081610452565b610f5a60243591638b78c6d8600c525f526020600c205490565b161515604051908152f35b3461001d575f36600319011261001d576020604051650400000000008152f35b5f36600319011261001d5763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b3461001d575f36600319011261001d57604051600160581b8152602090f35b3461001d57602036600319011261001d576020611006600435611c1a565b6040516001600160a01b039091168152f35b3461001d575f36600319011261001d576005546040516001600160a01b039091168152602090f35b3461001d57602036600319011261001d576020610dcf60043561106281610452565b611c47565b5f36600319011261001d5761107a612487565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3600160ff1b638b78c6d81955005b3461001d575f36600319011261001d57602060ff60055460a01c166040519015158152f35b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761110e57604052565b6110d9565b6001600160401b03811161110e57601f01601f191660200190565b92919261113a82611113565b9161114860405193846110ed565b82948184528183011161001d578281602093845f960137010152565b9080601f8301121561001d5781602061117f9335910161112e565b90565b3461001d5760e036600319011261001d5760043561119f81610452565b6024356001600160401b03811161001d576111be903690600401611164565b906044356001600160401b03811161001d576111de903690600401611164565b91606435926001600160401b03841161001d5761120261001b943690600401611164565b61120a610463565b91611213610472565b9361121c61047f565b95611ec7565b3461001d575f36600319011261001d57638b78c6d819546040516001600160a01b039091168152602090f35b3461001d575f36600319011261001d576040515f60045461126e816118fa565b80845290600181169081156105d35750600114611295576103fd83610569818503826110ed565b60045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b8082106112d957509091508101602001610569610559565b9192600181602092548385880101520191019092916112c1565b3461001d57606036600319011261001d5761131f60043561131381610452565b60243560443591612780565b6040518091602082016020835281518091526020604084019201905f5b81811061134a575050500390f35b825184528594506020938401939092019160010161133c565b3461001d57604036600319011261001d5760043561138081610452565b60243580151580910361001d5781601c52670a5a2e7a00000000600852335f52806030600c20555f5260018060a01b0316337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa3005b3461001d57602036600319011261001d576004356113f681610452565b638b78c6d8600c52335f52600160451b6020600c205416156104ea575f80546001600160a01b039283166001600160a01b0319821681179092556040805193909116835260208301919091527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac91a1005b3461001d5761001b61147836610c48565b612019565b3461001d57602036600319011261001d5760206001600160601b036114be600435805f52673ec412a9852d173d60c11b601c5260205f208101015460a01c90565b1615604051908152f35b608036600319011261001d576004356114e081610452565b6024356114ec81610452565b606435916044356001600160401b03841161001d573660238501121561001d578360040135926001600160401b03841161001d57366024858701011161001d57602461001b9501926120e2565b3461001d57602036600319011261001d576103fd61056960043561217e565b3461001d57602036600319011261001d576004356001600160401b03811161001d57611588903690600401611164565b61159061224d565b80516001600160401b03811161110e576115b4816115af6002546118fa565b611c7a565b602091601f8211600114611625576115e2925f918361161a575b50508160011b915f199060031b1c19161790565b6002555b604080515f81525f1960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190a1005b015190505f806115ce565b60025f52601f198216927f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f5b85811061168b57508360019510611673575b505050811b016002556115e6565b01515f1960f88460031b161c191690555f8080611665565b91926020600181928685015181550194019201611653565b3461001d57604036600319011261001d576004356116c081610452565b6024356116cc81610452565b601c52670a5a2e7a000000006008525f5260206030600c20546040519015158152f35b3461001d575f36600319011261001d576020604051600160451b8152f35b602036600319011261001d5760043561172581610452565b61172d612487565b63389a75e1600c52805f526020600c209081544211611751575f61001b9255612663565b636f5e88185f526004601cfd5b602036600319011261001d5760043561177681610452565b61177e612487565b8060601b156117905761001b90612663565b637448fbae5f526004601cfd5b606036600319011261001d576004356001600160401b03811161001d576117c890369060040161073b565b906024356001600160401b03811161001d576117e890369060040161073b565b604435916001600160801b038316830361001d57602094611808946123d7565b5f818152600183526040902080546001600160a01b03191633179055610dcf565b3461001d57602036600319011261001d577f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c604060043561186981610452565b61187161224d565b600580546001600160a01b0319166001600160a01b039290921691821790558151907faa56f403bb636768e9e1fec12c1968db814767675df6bc4a8c2488fc094c6a035f80a25f81525f196020820152a1005b3461001d57602036600319011261001d576004356118e181610452565b63389a75e1600c525f52602080600c2054604051908152f35b90600182811c92168015611928575b602083101461191457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611909565b604051905f8260025491611945836118fa565b80835292600181169081156119c85750600114611969575b610470925003836110ed565b5060025f90815290917f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b8183106119ac5750509060206104709282010161195d565b6020919350806001915483858901015201910190918492611994565b6020925061047094915060ff191682840152151560051b82010161195d565b634e487b7160e01b5f52603260045260245ffd5b9190811015611a0b5760051b0190565b6119e7565b9081602091031261001d575161117f81610452565b6040513d5f823e3d90fd5b81835290916001600160fb1b03831161001d5760209260051b809284830137010190565b9591926001600160801b039460a09699989463ffffffff611a8f94168952600180891b0316602089015260c0604089015260c0880191611a30565b5f196001861b0197881660608701529616608085015216910152565b9190337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603611b295760016001600160601b03611b0b84805f52673ec412a9852d173d60c11b601c5260205f208101015460a01c90565b1614611b1a5761047092612496565b6305b166a360e41b5f5260045ffd5b9091611b36838383612b98565b5f838152673ec412a9852d173d60c11b3317601c52602090208301830180546001600160a01b0393841693928316928116808414810215611c055750825f528160010180548033148533141715611bee575b611be5575b50838318189055601c600c205f198154019055815f52601c600c2060018154019063ffffffff8216840215611bd057555f516020612dee5f395f51905f525f80a4565b67ea553b3401336cea841560021b526004601cfd5b5f90555f611b8d565b6030600c2054611b8857634b6e7f185f526004601cfd5b67ceea21b6a1148100901560021b526004601cfd5b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690811561064857565b8015611c6d57673ec412a9852d173d60c11b601c525f5263ffffffff601c600c20541690565b638f4eb6045f526004601cfd5b601f8111611c86575050565b60025f5260205f20906020601f840160051c83019310611cc0575b601f0160051c01905b818110611cb5575050565b5f8155600101611caa565b9091508190611ca1565b601f8211611cd757505050565b5f5260205f20906020601f840160051c83019310611d0f575b601f0160051c01905b818110611d04575050565b5f8155600101611cf9565b9091508190611cf0565b9081516001600160401b03811161110e57611d4081611d396003546118fa565b6003611cca565b602092601f8211600114611d7457611d6f929382915f9261161a5750508160011b915f199060031b1c19161790565b600355565b60035f52601f198216937fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f5b868110611dd85750836001959610611dc0575b505050811b01600355565b01515f1960f88460031b161c191690555f8080611db5565b91926020600181928685015181550194019201611da2565b9081516001600160401b03811161110e57611e1781611e106004546118fa565b6004611cca565b602092601f8211600114611e4b57611e46929382915f9261161a5750508160011b915f199060031b1c19161790565b600455565b60045f52601f198216937f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b915f5b868110611eaf5750836001959610611e97575b505050811b01600455565b01515f1960f88460031b161c191690555f8080611e8c565b91926020600181928685015181550194019201611e79565b939190969594929687516001600160401b03811161110e57611eee816115af6002546118fa565b6020601f8211600114611f835792611f38611f7e969593611f3084611f3d95611f5a986104709e9f5f9261161a5750508160011b915f199060031b1c19161790565b600255611d19565b611df0565b60018060a01b03166001600160601b0360a01b6005541617600555565b611f63836126a6565b60018060a01b03166001600160601b0360a01b5f5416175f55565b61226d565b60025f52601f198216997f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9a5f5b8181106120015750936001846104709c9d611f5a9895611f3895611f3d98611f7e9d9c9a10611fe9575b505050811b01600255611d19565b01515f1960f88460031b161c191690555f8080611fdb565b838301518d556001909c019b60209384019301611fb1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036120d3575f818152600160205260409020546001600160a01b031680156120cf5761208b61207b835f52600160205260405f2090565b80546001600160a01b0319169055565b803b1561001d5760405163163b5cc360e31b815260048101929092525f908290602490829084905af1801561088a576120c15750565b806108845f610470936110ed565b5050565b63db70dad160e01b5f5260045ffd5b92936120ef838386611aab565b813b6120fd575b5050505050565b6121129461210c91369161112e565b92612538565b5f808080806120f6565b60208183031261001d578051906001600160401b03821161001d570181601f8201121561001d5780519061214f82611113565b9261215d60405194856110ed565b8284526020838301011161001d57815f9260208093018386015e8301015290565b5f818152673ec412a9852d173d60c11b601c5260209020810181015460601b1561223e576005546001600160a01b0316806121cd57506121c061117f9161283b565b6121c8611932565b612880565b61220d915f916121ed906001600160a01b03165b6001600160a01b031690565b604051808095819463c87b56dd60e01b8352600483019190602083019252565b03915afa90811561088a575f91612222575090565b61117f91503d805f833e61223681836110ed565b81019061211c565b63677510db60e11b5f5260045ffd5b638b78c6d8600c52335f52650400000000006020600c205416156104ea57565b906001600160601b03169061271082116122a55760601b8015612298571768aa4ec00224afccfdb755565b63b4457eaa5f526004601cfd5b63350a88b35f526004601cfd5b5f8281526001600160a01b03918216673ec412a9852d173d60c11b8117601c526020909120830190920180546001949392168015610648578083036122f657505050565b5f526030600c205415612307575050565b60010154149150565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c1590565b9081602091031261001d575190565b949291602092612369929998979960018060a01b0316875260a08488015260a0870191611a30565b84810360408601528281520191905f905b8082106123aa575050506001600160a01b0390941660608201526104709190608001906001600160801b03169052565b90919283359063ffffffff8216820361001d576020809163ffffffff60019416815201940192019061237a565b60405163f174883560e01b81523060048201529394936001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016929190602081602481875afa95861561088a57602096612457925f91610b29575060405163fe084c8f60e01b815298899788968796339460048901612341565b039134905af190811561088a575f9161246e575090565b61117f915060203d602011610b2257610b1481836110ed565b638b78c6d8195433036104ea57565b6124a1838383612b98565b5f838152673ec412a9852d173d60c11b601c52602090208301830180546001600160a01b0393841693928316928116808414810215611c055750825f52816001018054801585151760011715611bee57611be55750838318189055601c600c205f198154019055815f52601c600c2060018154019063ffffffff8216840215611bd057555f516020612dee5f395f51905f525f80a4565b9060a46020939460405195869463150b7a028652338787015260018060a01b03166040860152606085015260808085015280518091818060a08801526125b6575b505001905f601c8401915af1156125a8575b5163757a42ff60e11b0161259b57565b63d1a57ed65f526004601cfd5b3d1561258b573d5f823e3d90fd5b818760c08801920160045afa50805f612579565b6125d781610e5d81611c1a565b5f818152673ec412a9852d173d60c11b601c5260209020810181018054906001600160a01b03821690811561064857815f5280600101928354801560011715612651575b905f948492612648575b50189055601c600c20821981540190555f516020612dee5f395f51905f528280a4565b8590555f612625565b906030600c2054156106f4579061261b565b60018060a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3801560ff1b17638b78c6d81955565b638b78c6d819546126ef576001600160a01b0316801560ff1b8117638b78c6d819555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3565b630dc149f05f526004601cfd5b6001600160401b03811161110e5760051b60200190565b9061271d826126fc565b61272a60405191826110ed565b828152809261273b601f19916126fc565b0190602036910137565b634e487b7160e01b5f52601160045260245ffd5b5f1981146127675760010190565b612745565b8051821015611a0b5760209160051b010190565b90929161278c82611c47565b5f9261279782612713565b955b838111156127de57505050506127ae81612713565b905f5b8181106127bf575090925050565b806127cc6001928761276c565b516127d7828661276c565b52016127b1565b5f818152673ec412a9852d173d60c11b601c526020902081018101546001600160a01b0383811691161461281b575b61281690612759565b612799565b93808561282a6001938a61276c565b52019382850361280d575050505050565b90604051600a608082019360a083016040525f8552935b5f19019360308282060185530492831561286e57600a90612852565b809350608091030191601f1901918252565b6040518151909392909160208301601f19165b8181015186820152601f19019081156128ac5790612893565b505080519084830160208301601f19165b8281015182820152601f19019182156128d657916128bd565b50505060409101808401905f6020830152845201604052565b908160051b918083046020149015171561276757565b606401908160641161276757565b5f3560e01c6340c10f19810361293b57506104706024356004356001600160a01b0316612d60565b6392772833036129b6577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036120d3576044356024356001600160a01b03165f5b82811061299257505050565b806129b0836129aa6129a56001956128ef565b612905565b35612c73565b01612986565b631e085ca760e11b5f5260045ffd5b638b78c6d8600c525f526020600c2090815490811618809155600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3565b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612a69575050565b612a7282612310565b8015612ae3575b6120d3575f546001600160a01b031680612a9257505050565b612aa4906001600160a01b03166121e1565b91823b1561001d5760405163657711f560e11b8152925f92849283918291612ad29187903360048601612a0a565b03915afa801561088a576120c15750565b506001600160a01b03811615612a79565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612b28575050565b612b3182612310565b8015612b90575b6120d3575f546001600160a01b031680612b5157505050565b612b63906001600160a01b03166121e1565b91823b1561001d5760405163657711f560e11b8152925f92849283918291612ad291873360048601612a0a565b506001612b38565b90917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612bcf57505050565b612bd881612310565b8015612c62575b6120d3575f546001600160a01b031680612bfa575b50505050565b612c0c906001600160a01b03166121e1565b803b1561001d57612c37935f936040519586948593849363657711f560e11b85523360048601612a0a565b03915afa801561088a57612c4e575b808080612bf4565b806108845f612c5c936110ed565b5f612c46565b506001600160a01b03821615612bdf565b906001600160601b03612ca083805f52673ec412a9852d173d60c11b601c5260205f208101015460a01c90565b16611b1a575f828152673ec412a9852d173d60c11b601c5260209081902083018301547ff27b6ce5b2f5e68ddb2fd95a8a909d4ecf1daaac270935fff052feacb24f18429391926001600160a01b039091169082908280612d375750612d069250612d60565b805f52673ec412a9852d173d60c11b601c52815f208101810180548060a01c60011860a01b189055604051908152a1565b6001600160a01b03821603612d4f575b505050612d06565b612d5892612496565b5f8181612d47565b612d6a8282612af4565b60018060a01b0316815f52673ec412a9852d173d60c11b601c5260205f208201820180548060601b612de05782179055805f52601c600c2060018154019063ffffffff8216830215612dcb57555f5f516020612dee5f395f51905f528180a4565b67ea553b3401336cea831560021b526004601cfd5b63c991cbb15f526004601cfdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212203b6e448b6f88baf08c8a05a0359bed9f6c31f3c66f692d0052780e9d24becebd64736f6c634300081c0033a264697066735822122032e301aa960dc374cda555b133c2ab1f1e34287fc9f68010df0b8ca04800048264736f6c634300081c0033000000000000000000000000000000000000f87b8230c9233b00474eda1e1b3b
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ddc8bf146105365750806325692962146104ed57806354d1f13d146104a9578063715018a6146104605780638da5cb5b146104345780639a84037a14610143578063f04e283e146100f5578063f2fde38b146100b65763fee81cf414610081575f80fd5b346100b35760203660031901126100b35761009a610577565b9063389a75e1600c5252602080600c2054604051908152f35b80fd5b5060203660031901126100b3576100cb610577565b6100d3610629565b8060601b156100e8576100e590610645565b80f35b637448fbae82526004601cfd5b5060203660031901126100b35761010a610577565b610112610629565b63389a75e1600c528082526020600c20805442116101365790826100e59255610645565b636f5e881883526004601cfd5b503461041c5761018036600319011261041c5761015e610577565b9060243567ffffffffffffffff811161041c5761017f9036906004016105af565b60443567ffffffffffffffff811161041c5761019f9036906004016105af565b9260643567ffffffffffffffff811161041c576101c09036906004016105af565b936084356001600160a01b038116929083900361041c5760a4356001600160a01b0381169081900361041c5760c435936bffffffffffffffffffffffff851680950361041c5760e4356001600160a01b038116929083900361041c57610104359063ffffffff821680920361041c57610124359563ffffffff871680970361041c57610144359563ffffffff871680970361041c5761025d610629565b60405194612f058087019087821067ffffffffffffffff8311176104205761068388397f000000000000000000000000000000000000f87b8230c9233b00474eda1e1b3b6001600160a01b0316808252966101643591819003602001905ff58015610411576001600160a01b0316998a3b1561041c578a946040519e8f958695638528e8d360e01b8752600160a01b60019003169e8f60048801526024870160e0905260e4870161030d91610605565b86810360031901604488015261032291610605565b85810360031901606487015261033791610605565b92608485015260a484015260c483015203815a5f948591f18015610411576103f7575b87985081979596973b156103f3578560a4928195604051978896879563da383b9760e01b87528c600488015260248701526044860152606485015260848401525af180156103e8576103cf575b50807f058f67f456321f47e4e011c1b382aff3cfe6a6a472dc1b7a548256458862602491a380f35b816103d99161058d565b6103e457825f6103a7565b8280fd5b6040513d84823e3d90fd5b8580fd5b94959650965f6104069161058d565b5f959493879061035a565b6040513d5f823e3d90fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b3461041c575f36600319011261041c57638b78c6d819546040516001600160a01b039091168152602090f35b5f36600319011261041c57610473610629565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35f638b78c6d81955005b5f36600319011261041c5763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b5f36600319011261041c5763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b3461041c575f36600319011261041c577f000000000000000000000000000000000000f87b8230c9233b00474eda1e1b3b6001600160a01b03168152602090f35b600435906001600160a01b038216820361041c57565b90601f8019910116810190811067ffffffffffffffff82111761042057604052565b81601f8201121561041c5780359067ffffffffffffffff821161042057604051926105e4601f8401601f19166020018561058d565b8284526020838301011161041c57815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b638b78c6d81954330361063857565b6382b429005f526004601cfd5b60018060a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3638b78c6d8195556fe60a0346100a957601f612f0538819003918201601f19168301916001600160401b038311848410176100ad578084926020946040528339810103126100a957516001600160a01b03811681036100a957608052604051612e4390816100c2823960805181818161036f015281816107ff01528181610a7d01528181611ab00152818161201b015281816123f70152818161294701528181612a3701528181612af60152612b9c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610021575b3461001d5761001b612913565b005b5f80fd5b5f3560e01c806301ddc8bf1461034b57806301ffc9a71461034657806302fa7c471461034157806306fdde031461033c578063081812fc14610337578063095ea7b314610332578063098144d4146102f65780630a2121001461032d5780630c756a78146103285780630d705df614610323578063156c2c321461031e578063183a4f6e146103195780631c10893f146103145780631cd64df41461030f5780631de26e8e1461030a57806321f365091461030557806323b872dd1461030057806325692962146102fb57806329e38d5e146102f65780632a55205a146102f15780632de94807146102ec57806342842e0e146102e757806342966c68146102e25780634a4ee7b1146102dd578063514e62fc146102d8578063529470e8146102d357806354d1f13d146102ce57806358456fb5146102c95780636352211e146102c457806370319970146102bf57806370a08231146102ba578063715018a6146102b55780637347ebb9146102b05780638528e8d3146102ab5780638da5cb5b146102a657806395d89b41146102a157806399a2557a1461029c578063a22cb46514610297578063a9fc664e14610292578063b1dae6181461028d578063b45a3c0e14610288578063b88d4fde14610283578063c87b56dd1461027e578063e0df5b6f14610279578063e985e9c514610274578063ed0616781461026f578063f04e283e1461026a578063f2fde38b14610265578063f7dec9b414610260578063fd4fe8a81461025b5763fee81cf40361000e576118c4565b611829565b61179d565b61175e565b61170d565b6116ef565b6116a3565b611558565b611539565b6114c8565b61147d565b611467565b6113d9565b611363565b6112f3565b61124e565b611222565b611182565b6110b4565b611067565b611040565b611018565b610fe8565b610fc9565b610f85565b610f65565b610f21565b610ef5565b610e10565b610dd7565b610d9a565b610d0d565b610701565b610cc4565b610cb2565b610c5a565b610c09565b610bc4565b610b5e565b610b46565b610a11565b6109ea565b6109ce565b610781565b610655565b6105f7565b610521565b610495565b61039e565b61035a565b5f91031261001d57565b3461001d575f36600319011261001d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461001d57602036600319011261001d576004356001600160e01b031981169081810361001d576103fd91635a2d1e0760e11b811491821561042e575b8215610412575b508115610401575b5060405190151581529081906020820190565b0390f35b632483248360e11b1490505f6103ea565b90915060e01c6301ffc9a7632a55205a8214911417905f6103e2565b80925060e01c635b5e139f8114906301ffc9a76380ac58cd821491141717916103db565b6001600160a01b0381160361001d57565b6084359061047082610452565b565b60a4359061047082610452565b60c435906001600160601b038216820361001d57565b3461001d57604036600319011261001d576004356104b281610452565b6024356001600160601b038116810361001d57638b78c6d8600c52335f52600160581b6020600c205416156104ea5761001b9161226d565b6382b429005f526004601cfd5b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b3461001d575f36600319011261001d576040515f600354610541816118fa565b80845290600181169081156105d35750600114610575575b6103fd83610569818503826110ed565b604051918291826104f7565b60035f9081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b8082106105b957509091508101602001610569610559565b9192600181602092548385880101520191019092916105a1565b60ff191660208086019190915291151560051b840190910191506105699050610559565b3461001d57602036600319011261001d576004355f818152673ec412a9852d173d60c11b601c5260209020810101805460601b1561064857600101546040516001600160a01b039091168152602090f35b63ceea21b65f526004601cfd5b604036600319011261001d5760043561066d81610452565b6024355f818152673ec412a9852d173d60c11b3317601c526020902081018101805491926001600160a01b039081169216908115610648578290823314331517156106dd575b600101557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b9050815f526030600c2054156106f45782906106b3565b634b6e7f185f526004601cfd5b3461001d575f36600319011261001d575f546040516001600160a01b039091168152602090f35b6004359063ffffffff8216820361001d57565b9181601f8401121561001d578235916001600160401b03831161001d576020808501948460051b01011161001d57565b608435906001600160801b038216820361001d57565b60a036600319011261001d57610795610728565b6024356001600160401b03811161001d576107b490369060040161073b565b91906044356107c281610452565b606435926107cf84610452565b6107d761076b565b905f5b8681106108c0575060405163f174883560e01b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016939092602084602481885afa93841561088a575f9461088f575b50843b1561001d575f9661086492604051998a988997889763c9df816760e01b895260048901611a54565b039134905af1801561088a5761087657005b806108845f61001b936110ed565b80610350565b611a25565b6108b291945060203d6020116108b9575b6108aa81836110ed565b810190611a10565b925f610839565b503d6108a0565b6108dc6108d86108d1838a896119fb565b35336122b2565b1590565b6109bf576108f46108ee8289886119fb565b35612310565b6109b05760019061090e6108d860055460ff9060a01c1690565b610997575b61094e610921828a896119fb565b355f818152673ec412a9852d173d60c11b601c526020902081010180546001600160a01b03198116189055565b7f032bc66be43dbccb7487781d168eb7bda224628a3b2c3388bdf69b532a3a161161098e61097d838b8a6119fb565b604051903581529081906020820190565b0390a1016107da565b6109ab6109a5828a896119fb565b356125ca565b610913565b635a8181f760e01b5f5260045ffd5b63096dcfe360e31b5f5260045ffd5b3461001d575f36600319011261001d5760206040516120008152f35b3461001d575f36600319011261001d576040805163657711f560e11b815260016020820152f35b604036600319011261001d576004356001600160401b03811161001d57610a3c90369060040161073b565b906024356001600160401b03811161001d57610a5c90369060040161073b565b60405163f174883560e01b8152306004820152909390926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602085602481865afa801561088a575f610add916020978291610b29575b5060405163fe084c8f60e01b815298899788968796339460048901612341565b039134905af1801561088a576020915f91610afc575b50604051908152f35b610b1c9150823d8411610b22575b610b1481836110ed565b810190612332565b5f610af3565b503d610b0a565b610b409150883d8a116108b9576108aa81836110ed565b5f610abd565b602036600319011261001d5761001b600435336129c5565b604036600319011261001d57600435610b7681610452565b60243590610b82612487565b638b78c6d8600c525f526020600c2090815417809155600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3005b3461001d57604036600319011261001d576020610bff600435610be681610452565b602435918291638b78c6d8600c525f526020600c205490565b1614604051908152f35b3461001d575f36600319011261001d57638b78c6d8600c52335f526120006020600c205416156104ea576005805460ff60a01b1916600160a01b179055005b602090600319011261001d5760043590565b3461001d57610c6836610c48565b5f526001602052602060018060a01b0360405f205416604051908152f35b606090600319011261001d57600435610c9e81610452565b90602435610cab81610452565b9060443590565b61001b610cbe36610c86565b91611aab565b5f36600319011261001d5763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b3461001d57604036600319011261001d57600435602435905f5268aa4ec00224afccfdb760205260405f2054908160601c918215610d82575b6103fd908360601b1892835f1904831184023d3d3e6127106040519485940204908360209093929193604081019460018060a01b031681520152565b5068aa4ec00224afccfdb754606081901c9250610d46565b3461001d57602036600319011261001d576020610dcf600435610dbc81610452565b638b78c6d8600c525f526020600c205490565b604051908152f35b610de036610c86565b610ded8183859495611aab565b823b610df557005b61001b9260405192610e086020856110ed565b5f8452612538565b3461001d57602036600319011261001d576004355f818152673ec412a9852d173d60c11b601c5260209020810181015460a01c610e505761001b906125ca565b610e6281610e5d81611c1a565b612a35565b5f818152673ec412a9852d173d60c11b3317601c5260209020810181018054906001600160a01b03821690811561064857815f52806001019283548033148433141733151715610ee3575b905f948492610eda575b50189055601c600c20821981540190555f516020612dee5f395f51905f528280a4005b8590555f610eb7565b906030600c2054156106f45790610ead565b604036600319011261001d5761001b600435610f1081610452565b60243590610f1c612487565b6129c5565b3461001d57604036600319011261001d576020600435610f4081610452565b610f5a60243591638b78c6d8600c525f526020600c205490565b161515604051908152f35b3461001d575f36600319011261001d576020604051650400000000008152f35b5f36600319011261001d5763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b3461001d575f36600319011261001d57604051600160581b8152602090f35b3461001d57602036600319011261001d576020611006600435611c1a565b6040516001600160a01b039091168152f35b3461001d575f36600319011261001d576005546040516001600160a01b039091168152602090f35b3461001d57602036600319011261001d576020610dcf60043561106281610452565b611c47565b5f36600319011261001d5761107a612487565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3600160ff1b638b78c6d81955005b3461001d575f36600319011261001d57602060ff60055460a01c166040519015158152f35b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761110e57604052565b6110d9565b6001600160401b03811161110e57601f01601f191660200190565b92919261113a82611113565b9161114860405193846110ed565b82948184528183011161001d578281602093845f960137010152565b9080601f8301121561001d5781602061117f9335910161112e565b90565b3461001d5760e036600319011261001d5760043561119f81610452565b6024356001600160401b03811161001d576111be903690600401611164565b906044356001600160401b03811161001d576111de903690600401611164565b91606435926001600160401b03841161001d5761120261001b943690600401611164565b61120a610463565b91611213610472565b9361121c61047f565b95611ec7565b3461001d575f36600319011261001d57638b78c6d819546040516001600160a01b039091168152602090f35b3461001d575f36600319011261001d576040515f60045461126e816118fa565b80845290600181169081156105d35750600114611295576103fd83610569818503826110ed565b60045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b8082106112d957509091508101602001610569610559565b9192600181602092548385880101520191019092916112c1565b3461001d57606036600319011261001d5761131f60043561131381610452565b60243560443591612780565b6040518091602082016020835281518091526020604084019201905f5b81811061134a575050500390f35b825184528594506020938401939092019160010161133c565b3461001d57604036600319011261001d5760043561138081610452565b60243580151580910361001d5781601c52670a5a2e7a00000000600852335f52806030600c20555f5260018060a01b0316337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa3005b3461001d57602036600319011261001d576004356113f681610452565b638b78c6d8600c52335f52600160451b6020600c205416156104ea575f80546001600160a01b039283166001600160a01b0319821681179092556040805193909116835260208301919091527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac91a1005b3461001d5761001b61147836610c48565b612019565b3461001d57602036600319011261001d5760206001600160601b036114be600435805f52673ec412a9852d173d60c11b601c5260205f208101015460a01c90565b1615604051908152f35b608036600319011261001d576004356114e081610452565b6024356114ec81610452565b606435916044356001600160401b03841161001d573660238501121561001d578360040135926001600160401b03841161001d57366024858701011161001d57602461001b9501926120e2565b3461001d57602036600319011261001d576103fd61056960043561217e565b3461001d57602036600319011261001d576004356001600160401b03811161001d57611588903690600401611164565b61159061224d565b80516001600160401b03811161110e576115b4816115af6002546118fa565b611c7a565b602091601f8211600114611625576115e2925f918361161a575b50508160011b915f199060031b1c19161790565b6002555b604080515f81525f1960208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190a1005b015190505f806115ce565b60025f52601f198216927f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f5b85811061168b57508360019510611673575b505050811b016002556115e6565b01515f1960f88460031b161c191690555f8080611665565b91926020600181928685015181550194019201611653565b3461001d57604036600319011261001d576004356116c081610452565b6024356116cc81610452565b601c52670a5a2e7a000000006008525f5260206030600c20546040519015158152f35b3461001d575f36600319011261001d576020604051600160451b8152f35b602036600319011261001d5760043561172581610452565b61172d612487565b63389a75e1600c52805f526020600c209081544211611751575f61001b9255612663565b636f5e88185f526004601cfd5b602036600319011261001d5760043561177681610452565b61177e612487565b8060601b156117905761001b90612663565b637448fbae5f526004601cfd5b606036600319011261001d576004356001600160401b03811161001d576117c890369060040161073b565b906024356001600160401b03811161001d576117e890369060040161073b565b604435916001600160801b038316830361001d57602094611808946123d7565b5f818152600183526040902080546001600160a01b03191633179055610dcf565b3461001d57602036600319011261001d577f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c604060043561186981610452565b61187161224d565b600580546001600160a01b0319166001600160a01b039290921691821790558151907faa56f403bb636768e9e1fec12c1968db814767675df6bc4a8c2488fc094c6a035f80a25f81525f196020820152a1005b3461001d57602036600319011261001d576004356118e181610452565b63389a75e1600c525f52602080600c2054604051908152f35b90600182811c92168015611928575b602083101461191457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611909565b604051905f8260025491611945836118fa565b80835292600181169081156119c85750600114611969575b610470925003836110ed565b5060025f90815290917f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b8183106119ac5750509060206104709282010161195d565b6020919350806001915483858901015201910190918492611994565b6020925061047094915060ff191682840152151560051b82010161195d565b634e487b7160e01b5f52603260045260245ffd5b9190811015611a0b5760051b0190565b6119e7565b9081602091031261001d575161117f81610452565b6040513d5f823e3d90fd5b81835290916001600160fb1b03831161001d5760209260051b809284830137010190565b9591926001600160801b039460a09699989463ffffffff611a8f94168952600180891b0316602089015260c0604089015260c0880191611a30565b5f196001861b0197881660608701529616608085015216910152565b9190337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603611b295760016001600160601b03611b0b84805f52673ec412a9852d173d60c11b601c5260205f208101015460a01c90565b1614611b1a5761047092612496565b6305b166a360e41b5f5260045ffd5b9091611b36838383612b98565b5f838152673ec412a9852d173d60c11b3317601c52602090208301830180546001600160a01b0393841693928316928116808414810215611c055750825f528160010180548033148533141715611bee575b611be5575b50838318189055601c600c205f198154019055815f52601c600c2060018154019063ffffffff8216840215611bd057555f516020612dee5f395f51905f525f80a4565b67ea553b3401336cea841560021b526004601cfd5b5f90555f611b8d565b6030600c2054611b8857634b6e7f185f526004601cfd5b67ceea21b6a1148100901560021b526004601cfd5b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690811561064857565b8015611c6d57673ec412a9852d173d60c11b601c525f5263ffffffff601c600c20541690565b638f4eb6045f526004601cfd5b601f8111611c86575050565b60025f5260205f20906020601f840160051c83019310611cc0575b601f0160051c01905b818110611cb5575050565b5f8155600101611caa565b9091508190611ca1565b601f8211611cd757505050565b5f5260205f20906020601f840160051c83019310611d0f575b601f0160051c01905b818110611d04575050565b5f8155600101611cf9565b9091508190611cf0565b9081516001600160401b03811161110e57611d4081611d396003546118fa565b6003611cca565b602092601f8211600114611d7457611d6f929382915f9261161a5750508160011b915f199060031b1c19161790565b600355565b60035f52601f198216937fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f5b868110611dd85750836001959610611dc0575b505050811b01600355565b01515f1960f88460031b161c191690555f8080611db5565b91926020600181928685015181550194019201611da2565b9081516001600160401b03811161110e57611e1781611e106004546118fa565b6004611cca565b602092601f8211600114611e4b57611e46929382915f9261161a5750508160011b915f199060031b1c19161790565b600455565b60045f52601f198216937f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b915f5b868110611eaf5750836001959610611e97575b505050811b01600455565b01515f1960f88460031b161c191690555f8080611e8c565b91926020600181928685015181550194019201611e79565b939190969594929687516001600160401b03811161110e57611eee816115af6002546118fa565b6020601f8211600114611f835792611f38611f7e969593611f3084611f3d95611f5a986104709e9f5f9261161a5750508160011b915f199060031b1c19161790565b600255611d19565b611df0565b60018060a01b03166001600160601b0360a01b6005541617600555565b611f63836126a6565b60018060a01b03166001600160601b0360a01b5f5416175f55565b61226d565b60025f52601f198216997f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9a5f5b8181106120015750936001846104709c9d611f5a9895611f3895611f3d98611f7e9d9c9a10611fe9575b505050811b01600255611d19565b01515f1960f88460031b161c191690555f8080611fdb565b838301518d556001909c019b60209384019301611fb1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036120d3575f818152600160205260409020546001600160a01b031680156120cf5761208b61207b835f52600160205260405f2090565b80546001600160a01b0319169055565b803b1561001d5760405163163b5cc360e31b815260048101929092525f908290602490829084905af1801561088a576120c15750565b806108845f610470936110ed565b5050565b63db70dad160e01b5f5260045ffd5b92936120ef838386611aab565b813b6120fd575b5050505050565b6121129461210c91369161112e565b92612538565b5f808080806120f6565b60208183031261001d578051906001600160401b03821161001d570181601f8201121561001d5780519061214f82611113565b9261215d60405194856110ed565b8284526020838301011161001d57815f9260208093018386015e8301015290565b5f818152673ec412a9852d173d60c11b601c5260209020810181015460601b1561223e576005546001600160a01b0316806121cd57506121c061117f9161283b565b6121c8611932565b612880565b61220d915f916121ed906001600160a01b03165b6001600160a01b031690565b604051808095819463c87b56dd60e01b8352600483019190602083019252565b03915afa90811561088a575f91612222575090565b61117f91503d805f833e61223681836110ed565b81019061211c565b63677510db60e11b5f5260045ffd5b638b78c6d8600c52335f52650400000000006020600c205416156104ea57565b906001600160601b03169061271082116122a55760601b8015612298571768aa4ec00224afccfdb755565b63b4457eaa5f526004601cfd5b63350a88b35f526004601cfd5b5f8281526001600160a01b03918216673ec412a9852d173d60c11b8117601c526020909120830190920180546001949392168015610648578083036122f657505050565b5f526030600c205415612307575050565b60010154149150565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c1590565b9081602091031261001d575190565b949291602092612369929998979960018060a01b0316875260a08488015260a0870191611a30565b84810360408601528281520191905f905b8082106123aa575050506001600160a01b0390941660608201526104709190608001906001600160801b03169052565b90919283359063ffffffff8216820361001d576020809163ffffffff60019416815201940192019061237a565b60405163f174883560e01b81523060048201529394936001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016929190602081602481875afa95861561088a57602096612457925f91610b29575060405163fe084c8f60e01b815298899788968796339460048901612341565b039134905af190811561088a575f9161246e575090565b61117f915060203d602011610b2257610b1481836110ed565b638b78c6d8195433036104ea57565b6124a1838383612b98565b5f838152673ec412a9852d173d60c11b601c52602090208301830180546001600160a01b0393841693928316928116808414810215611c055750825f52816001018054801585151760011715611bee57611be55750838318189055601c600c205f198154019055815f52601c600c2060018154019063ffffffff8216840215611bd057555f516020612dee5f395f51905f525f80a4565b9060a46020939460405195869463150b7a028652338787015260018060a01b03166040860152606085015260808085015280518091818060a08801526125b6575b505001905f601c8401915af1156125a8575b5163757a42ff60e11b0161259b57565b63d1a57ed65f526004601cfd5b3d1561258b573d5f823e3d90fd5b818760c08801920160045afa50805f612579565b6125d781610e5d81611c1a565b5f818152673ec412a9852d173d60c11b601c5260209020810181018054906001600160a01b03821690811561064857815f5280600101928354801560011715612651575b905f948492612648575b50189055601c600c20821981540190555f516020612dee5f395f51905f528280a4565b8590555f612625565b906030600c2054156106f4579061261b565b60018060a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3801560ff1b17638b78c6d81955565b638b78c6d819546126ef576001600160a01b0316801560ff1b8117638b78c6d819555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3565b630dc149f05f526004601cfd5b6001600160401b03811161110e5760051b60200190565b9061271d826126fc565b61272a60405191826110ed565b828152809261273b601f19916126fc565b0190602036910137565b634e487b7160e01b5f52601160045260245ffd5b5f1981146127675760010190565b612745565b8051821015611a0b5760209160051b010190565b90929161278c82611c47565b5f9261279782612713565b955b838111156127de57505050506127ae81612713565b905f5b8181106127bf575090925050565b806127cc6001928761276c565b516127d7828661276c565b52016127b1565b5f818152673ec412a9852d173d60c11b601c526020902081018101546001600160a01b0383811691161461281b575b61281690612759565b612799565b93808561282a6001938a61276c565b52019382850361280d575050505050565b90604051600a608082019360a083016040525f8552935b5f19019360308282060185530492831561286e57600a90612852565b809350608091030191601f1901918252565b6040518151909392909160208301601f19165b8181015186820152601f19019081156128ac5790612893565b505080519084830160208301601f19165b8281015182820152601f19019182156128d657916128bd565b50505060409101808401905f6020830152845201604052565b908160051b918083046020149015171561276757565b606401908160641161276757565b5f3560e01c6340c10f19810361293b57506104706024356004356001600160a01b0316612d60565b6392772833036129b6577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036120d3576044356024356001600160a01b03165f5b82811061299257505050565b806129b0836129aa6129a56001956128ef565b612905565b35612c73565b01612986565b631e085ca760e11b5f5260045ffd5b638b78c6d8600c525f526020600c2090815490811618809155600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3565b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612a69575050565b612a7282612310565b8015612ae3575b6120d3575f546001600160a01b031680612a9257505050565b612aa4906001600160a01b03166121e1565b91823b1561001d5760405163657711f560e11b8152925f92849283918291612ad29187903360048601612a0a565b03915afa801561088a576120c15750565b506001600160a01b03811615612a79565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612b28575050565b612b3182612310565b8015612b90575b6120d3575f546001600160a01b031680612b5157505050565b612b63906001600160a01b03166121e1565b91823b1561001d5760405163657711f560e11b8152925f92849283918291612ad291873360048601612a0a565b506001612b38565b90917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612bcf57505050565b612bd881612310565b8015612c62575b6120d3575f546001600160a01b031680612bfa575b50505050565b612c0c906001600160a01b03166121e1565b803b1561001d57612c37935f936040519586948593849363657711f560e11b85523360048601612a0a565b03915afa801561088a57612c4e575b808080612bf4565b806108845f612c5c936110ed565b5f612c46565b506001600160a01b03821615612bdf565b906001600160601b03612ca083805f52673ec412a9852d173d60c11b601c5260205f208101015460a01c90565b16611b1a575f828152673ec412a9852d173d60c11b601c5260209081902083018301547ff27b6ce5b2f5e68ddb2fd95a8a909d4ecf1daaac270935fff052feacb24f18429391926001600160a01b039091169082908280612d375750612d069250612d60565b805f52673ec412a9852d173d60c11b601c52815f208101810180548060a01c60011860a01b189055604051908152a1565b6001600160a01b03821603612d4f575b505050612d06565b612d5892612496565b5f8181612d47565b612d6a8282612af4565b60018060a01b0316815f52673ec412a9852d173d60c11b601c5260205f208201820180548060601b612de05782179055805f52601c600c2060018154019063ffffffff8216830215612dcb57555f5f516020612dee5f395f51905f528180a4565b67ea553b3401336cea831560021b526004601cfd5b63c991cbb15f526004601cfdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212203b6e448b6f88baf08c8a05a0359bed9f6c31f3c66f692d0052780e9d24becebd64736f6c634300081c0033a264697066735822122032e301aa960dc374cda555b133c2ab1f1e34287fc9f68010df0b8ca04800048264736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000f87b8230c9233b00474eda1e1b3b
-----Decoded View---------------
Arg [0] : _beacon (address): 0x000000000000F87b8230c9233b00474eda1E1b3b
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000f87b8230c9233b00474eda1e1b3b
Deployed Bytecode Sourcemap
202:2488:12:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;202:2488:12;;;;;;:::i;:::-;11885:237:0;;;;;202:2488:12;11885:237:0;;;;202:2488:12;;;;;;;;;;-1:-1:-1;202:2488:12;;-1:-1:-1;;202:2488:12;;;;;;:::i;:::-;12478:70:0;;:::i;:::-;8479:183;;;;;;8681:8;;;:::i;:::-;202:2488:12;;8479:183:0;;;;202:2488:12;8479:183:0;;202:2488:12;-1:-1:-1;202:2488:12;;-1:-1:-1;;202:2488:12;;;;;;:::i;:::-;12478:70:0;;:::i;:::-;10506:526;;;;;;202:2488:12;10506:526:0;;;;;;;;;;11051:12;10506:526;;11051:12;:::i;10506:526::-;;;;202:2488:12;10506:526:0;;202:2488:12;;;;;;;-1:-1:-1;;202:2488:12;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;202:2488:12;;;;;;;;;;;-1:-1:-1;;;;;202:2488:12;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;202:2488:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12478:70:0;;:::i;:::-;202:2488:12;;2057:51;;;;;;;;;202:2488;2057:51;;;;;;;;2084:23;-1:-1:-1;;;;;202:2488:12;;;;;;;;2057:51;;;202:2488;2057:51;;202:2488;2057:51;;;;;-1:-1:-1;;;;;202:2488:12;;2119:239;;;;;202:2488;;;;;;;;;;;;2119:239;;202:2488;;;;;;;2119:239;;202:2488;2119:239;;202:2488;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;202:2488:12;;;;;;;;:::i;:::-;;;;-1:-1:-1;;202:2488:12;;;;;;;;:::i;:::-;;;;;;;;;;;;;;2119:239;;;202:2488;2119:239;;;;;;;;;;202:2488;2369:249;;;;;;;;;;;;202:2488;;;;;;;;;;;;;;;2369:249;;;202:2488;2369:249;;202:2488;;;;;;;;;;;;;;;;;2369:249;;;;;;;;202:2488;2634:47;;;;;202:2488;;2369:249;;;;;:::i;:::-;202:2488;;2369:249;;;;202:2488;;;;2369:249;202:2488;;;;;;;;;2369:249;202:2488;;;2119:239;;;;;;202:2488;2119:239;;;:::i;:::-;202:2488;2119:239;;;;;;;;202:2488;;;;;;;;;2119:239;202:2488;;;2057:51;202:2488;;;;;;;;;;;;;;;;;-1:-1:-1;;202:2488:12;;;;-1:-1:-1;;11523:61:0;202:2488:12;;-1:-1:-1;;;;;202:2488:12;;;;;;;;;;;-1:-1:-1;;202:2488:12;;;;12478:70:0;;:::i;:::-;202:2488:12;6813:405:0;;;;;;;202:2488:12;-1:-1:-1;;6813:405:0;202:2488:12;;;;-1:-1:-1;;202:2488:12;;;;9831:339:0;;;;202:2488:12;9831:339:0;202:2488:12;9831:339:0;;;;;;202:2488:12;9831:339:0;;202:2488:12;;;;-1:-1:-1;;202:2488:12;;;;9239:383:0;;;;202:2488:12;9239:383:0;7972:9;9132:15;202:2488:12;9239:383:0;;;;;;202:2488:12;9239:383:0;;202:2488:12;;;;;;;-1:-1:-1;;202:2488:12;;;;323:48;-1:-1:-1;;;;;202:2488:12;;;;;;;;;;-1:-1:-1;;;;;202:2488:12;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;202:2488:12;;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;202:2488:12;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;202:2488:12;;;;;;;;-1:-1:-1;;202:2488:12;;;;:::o;7292:355:0:-;-1:-1:-1;;7390:251:0;;;;;7292:355::o;7390:251::-;;;;;;;6145:1089;202:2488:12;;;;;6813:405:0;;;;;;-1:-1:-1;6813:405:0;;-1:-1:-1;;6813:405:0;6145:1089::o
Swarm Source
ipfs://32e301aa960dc374cda555b133c2ab1f1e34287fc9f68010df0b8ca048000482
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.